diff --git a/empathy-auth-client.c b/empathy-auth-client.c new file mode 100644 index 0000000..3d48aea --- /dev/null +++ b/empathy-auth-client.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2010 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Cosimo Cecchi + */ + +#include +#include + +#include + +#include "empathy-debug.h" +#include "empathy-utils.h" +#include +#include +#include +#if 0 +#include +#define DEBUG_FLAG EMPATHY_DEBUG_TLS +#include +#include + +#include +#include +#include +#include + +#include "empathy-sanity-cleaning.h" + +#include + +#include +#endif + +#define TIMEOUT 60 + +static gboolean use_timer = TRUE; +static guint timeout_id = 0; +static guint num_windows = 0; +static GMainLoop *main_loop = 0; + +static gboolean +timeout_cb (gpointer p) +{ + DEBUG ("Timeout reached; exiting..."); + + g_main_loop_quit(main_loop); + return FALSE; +} + +static void +start_timer (void) +{ + if (!use_timer) + return; + + if (timeout_id != 0) + return; + + DEBUG ("Start timer"); + + timeout_id = g_timeout_add_seconds (TIMEOUT, timeout_cb, NULL); +} + +static void +stop_timer (void) +{ + if (timeout_id == 0) + return; + + DEBUG ("Stop timer"); + + g_source_remove (timeout_id); + timeout_id = 0; +} + +#if 0 +static void +tls_dialog_response_cb (GtkDialog *dialog, + gint response_id, + gpointer user_data) +{ + TpTLSCertificate *certificate = NULL; + TpTLSCertificateRejectReason reason = 0; + GHashTable *details = NULL; + gboolean remember = FALSE; + EmpathyTLSVerifier *verifier = EMPATHY_TLS_VERIFIER (user_data); + + g_object_get (tls_dialog, + "certificate", &certificate, + "reason", &reason, + "remember", &remember, + "details", &details, + NULL); + + DEBUG ("Response %d (remember: %d)", response_id, remember); + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + if (response_id == GTK_RESPONSE_YES) + { + tp_tls_certificate_accept_async (certificate, NULL, NULL); + } + else + { + tp_asv_set_boolean (details, "user-requested", TRUE); + tp_tls_certificate_add_rejection (certificate, reason, NULL, + g_variant_new_parsed ("{ 'user-requested': <%b> }", TRUE)); + + tp_tls_certificate_reject_async (certificate, NULL, NULL); + } + + if (remember) + empathy_tls_verifier_store_exception (verifier); + + g_object_unref (certificate); + g_hash_table_unref (details); + + /* restart the timeout */ + num_windows--; + + if (num_windows > 0) + return; + + start_timer (); +} +#endif + +#if 0 +static void +verifier_verify_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpTLSCertificateRejectReason reason; + GError *error = NULL; + TpTLSCertificate *certificate = NULL; + GHashTable *details = NULL; + gchar *hostname = NULL; + + g_object_get (source, + "certificate", &certificate, + NULL); + + empathy_tls_verifier_verify_finish (EMPATHY_TLS_VERIFIER (source), + result, &reason, &details, &error); + + if (error != NULL) + { + DEBUG ("Error: %s", error->message); + + g_error_free (error); + } + else + { + tp_tls_certificate_accept_async (certificate, NULL, NULL); + } + + g_free (hostname); + g_object_unref (certificate); +} +#endif + +#if 0 +static void +auth_factory_new_tls_handler_cb (EmpathyAuthFactory *factory, + EmpathyServerTLSHandler *handler, + gpointer user_data) +{ + TpTLSCertificate *certificate = NULL; + gchar *hostname = NULL; + gchar **reference_identities = NULL; + EmpathyTLSVerifier *verifier; + + DEBUG ("New TLS server handler received from the factory"); + + g_object_get (handler, + "certificate", &certificate, + "hostname", &hostname, + "reference-identities", &reference_identities, + NULL); + + verifier = empathy_tls_verifier_new (certificate, hostname, + (const gchar **) reference_identities); + empathy_tls_verifier_verify_async (verifier, + verifier_verify_cb, NULL); + + g_object_unref (verifier); + g_object_unref (certificate); + g_free (hostname); + g_strfreev (reference_identities); +} +#endif + +static void +auth_factory_new_sasl_handler_cb (EmpathyAuthFactory *factory, + EmpathyServerSASLHandler *handler, + gpointer user_data) +{ + 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)) + { + DEBUG ("SASL handler doesn't have a password, prompt for one"); + } +} + +#if 0 +static void +retry_account_cb (GtkWidget *dialog, + TpAccount *account, + const gchar *password, + EmpathyAuthFactory *factory) +{ + DEBUG ("Try reconnecting to %s", tp_account_get_path_suffix (account)); + + empathy_auth_factory_save_retry_password (factory, account, password); + + tp_account_reconnect_async (account, NULL, NULL); +} +#endif + +static void +auth_factory_auth_passsword_failed (EmpathyAuthFactory *factory, + TpAccount *account, + const gchar *password, + gpointer user_data) +{ + DEBUG ("Authentication on %s failed, popup password dialog", + tp_account_get_path_suffix (account)); +} + +static void +sanity_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + start_timer (); +} + +int +main (int argc, + char **argv) +{ + GOptionContext *context; + GError *error = NULL; + EmpathyAuthFactory *factory; + TpDebugSender *debug_sender; + TpSimpleClientFactory *tp_factory; + TpDBusDaemon *dbus; + + //gnutls_global_init (); + g_type_init(); + main_loop = g_main_loop_new(NULL, FALSE); + +#ifdef ENABLE_DEBUG + /* Set up debug sender */ + debug_sender = tp_debug_sender_dup (); + g_log_set_default_handler (tp_debug_sender_log_handler, G_LOG_DOMAIN); +#endif + + dbus = tp_dbus_daemon_dup (NULL); + tp_factory = tp_simple_client_factory_new (dbus); + tp_simple_client_factory_add_account_features_varargs (tp_factory, + TP_ACCOUNT_FEATURE_STORAGE, + 0); + + factory = empathy_auth_factory_new (tp_factory); + g_object_unref (tp_factory); + g_object_unref (dbus); + + //g_signal_connect (factory, "new-server-tls-handler", + // 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); + + g_signal_connect (factory, "auth-password-failed", + G_CALLBACK (auth_factory_auth_passsword_failed), NULL); + + if (!empathy_auth_factory_register (factory, &error)) + { + g_critical ("Failed to register the auth factory: %s\n", error->message); + g_error_free (error); + g_object_unref (factory); + + return EXIT_FAILURE; + } + + DEBUG ("Empathy auth client started."); + + if (g_getenv ("EMPATHY_PERSIST") != NULL) + { + DEBUG ("Timed-exit disabled"); + + use_timer = FALSE; + } + + g_main_loop_run(main_loop); + + g_object_unref (factory); + g_object_unref (debug_sender); + + return EXIT_SUCCESS; +} diff --git a/empathy-auth-factory.c b/empathy-auth-factory.c new file mode 100644 index 0000000..eeedcac --- /dev/null +++ b/empathy-auth-factory.c @@ -0,0 +1,778 @@ +/* + * empathy-auth-factory.c - Source for EmpathyAuthFactory + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi + * + * 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-auth-factory.h" +#include "empathy-debug.h" +#include "empathy-utils.h" + +//#include "empathy-keyring.h" +//#include "empathy-sasl-mechanisms.h" +#include "empathy-server-sasl-handler.h" +#include "empathy-server-tls-handler.h" +//#include "empathy-utils.h" + +// XXX +#ifdef HAVE_UOA +#include "empathy-uoa-auth-handler.h" +#endif /* HAVE_UOA */ + +G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, TP_TYPE_BASE_CLIENT); + +struct _EmpathyAuthFactoryPriv { + /* 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. + * + * The channel path of the handler's channel (borrowed gchar *) -> + * reffed (EmpathyServerSASLHandler *) + * */ + GHashTable *sasl_handlers; + +#ifdef HAVE_GOA + EmpathyGoaAuthHandler *goa_handler; +#endif /* HAVE_GOA */ + +#ifdef HAVE_UOA + EmpathyUoaAuthHandler *uoa_handler; +#endif /* HAVE_UOA */ + + /* If an account failed to connect and user enters a new password to try, we + * store it in this hash table and will try to use it next time the account + * attemps to connect. + * + * reffed TpAccount -> owned password (gchar *) */ + GHashTable *retry_passwords; + + gboolean dispose_run; +}; + +enum { + NEW_SERVER_TLS_HANDLER, + NEW_SERVER_SASL_HANDLER, + AUTH_PASSWORD_FAILED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0, }; + +#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAuthFactory) + +static EmpathyAuthFactory *auth_factory_singleton = NULL; + +typedef struct { + TpHandleChannelsContext *context; + EmpathyAuthFactory *self; +} HandlerContextData; + +static void +handler_context_data_free (HandlerContextData *data) +{ + tp_clear_object (&data->self); + tp_clear_object (&data->context); + + g_slice_free (HandlerContextData, data); +} + +static HandlerContextData * +handler_context_data_new (EmpathyAuthFactory *self, + TpHandleChannelsContext *context) +{ + HandlerContextData *data; + + data = g_slice_new0 (HandlerContextData); + data->self = g_object_ref (self); + + if (context != NULL) + data->context = g_object_ref (context); + + return data; +} + +static void +server_tls_handler_ready_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + EmpathyServerTLSHandler *handler; + GError *error = NULL; + HandlerContextData *data = user_data; + + handler = empathy_server_tls_handler_new_finish (res, &error); + + if (error != NULL) + { + DEBUG ("Failed to create a server TLS 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_emit (data->self, signals[NEW_SERVER_TLS_HANDLER], 0, + handler); + + g_object_unref (handler); + } + + handler_context_data_free (data); +} + +static void +sasl_handler_invalidated_cb (EmpathyServerSASLHandler *handler, + gpointer user_data) +{ + EmpathyAuthFactory *self = user_data; + EmpathyAuthFactoryPriv *priv = GET_PRIV (self); + TpChannel * channel; + + channel = empathy_server_sasl_handler_get_channel (handler); + g_assert (channel != NULL); + + DEBUG ("SASL handler for channel %s is invalidated, unref it", + tp_proxy_get_object_path (channel)); + + g_hash_table_remove (priv->sasl_handlers, tp_proxy_get_object_path (channel)); +} + +static void +sasl_handler_auth_password_failed_cb (EmpathyServerSASLHandler *handler, + const gchar *password, + EmpathyAuthFactory *self) +{ + TpAccount *account; + + account = empathy_server_sasl_handler_get_account (handler); + + g_signal_emit (self, signals[AUTH_PASSWORD_FAILED], 0, account, password); +} + +static void +server_sasl_handler_ready_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + EmpathyAuthFactoryPriv *priv; + GError *error = NULL; + HandlerContextData *data = user_data; + EmpathyServerSASLHandler *handler; + + priv = GET_PRIV (data->self); + handler = empathy_server_sasl_handler_new_finish (res, &error); + + if (error != NULL) + { + DEBUG ("Failed to create a server SASL handler; error %s", + error->message); + + if (data->context != NULL) + tp_handle_channels_context_fail (data->context, error); + + g_error_free (error); + } + else + { + TpChannel *channel; + const gchar *password; + TpAccount *account; + + if (data->context != NULL) + tp_handle_channels_context_accept (data->context); + + channel = empathy_server_sasl_handler_get_channel (handler); + g_assert (channel != NULL); + + /* Pass the ref to the hash table */ + g_hash_table_insert (priv->sasl_handlers, + (gpointer) tp_proxy_get_object_path (channel), handler); + + tp_g_signal_connect_object (handler, "invalidated", + G_CALLBACK (sasl_handler_invalidated_cb), data->self, 0); + + tp_g_signal_connect_object (handler, "auth-password-failed", + G_CALLBACK (sasl_handler_auth_password_failed_cb), data->self, 0); + + /* Is there a retry password? */ + account = empathy_server_sasl_handler_get_account (handler); + + password = g_hash_table_lookup (data->self->priv->retry_passwords, + account); + if (password != NULL) + { + gboolean save; + + DEBUG ("Use retry password"); + + /* We want to save this new password only if there is another + * (wrong) password saved. The SASL handler will only save it if it + * manages to connect. */ + save = empathy_server_sasl_handler_has_password (handler); + + empathy_server_sasl_handler_provide_password (handler, + password, save); + + /* We only want to try this password once */ + g_hash_table_remove (data->self->priv->retry_passwords, account); + } + + g_signal_emit (data->self, signals[NEW_SERVER_SASL_HANDLER], 0, + handler); + } + + handler_context_data_free (data); +} + +static gboolean +common_checks (EmpathyAuthFactory *self, + GList *channels, + gboolean observe, + GError **error) +{ + EmpathyAuthFactoryPriv *priv = GET_PRIV (self); + TpChannel *channel; + const GError *dbus_error; + EmpathyServerSASLHandler *handler; + + /* 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 (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Can't %s more than one ServerTLSConnection or ServerAuthentication " + "channel for the same connection.", observe ? "observe" : "handle"); + + return FALSE; + } + + channel = channels->data; + + if (tp_channel_get_channel_type_id (channel) != + TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION) + { + /* If we are observing we care only about ServerAuthentication channels. + * If we are handling we care about ServerAuthentication and + * ServerTLSConnection channels. */ + if (observe + || tp_channel_get_channel_type_id (channel) != + TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Can only %s ServerTLSConnection or ServerAuthentication channels, " + "this was a %s channel", observe ? "observe" : "handle", + tp_channel_get_channel_type (channel)); + + return FALSE; + } + } + + handler = g_hash_table_lookup (priv->sasl_handlers, + tp_proxy_get_object_path (channel)); + + if (tp_channel_get_channel_type_id (channel) == + TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION + && handler != NULL && + !observe) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "We are already handling this channel: %s", + tp_proxy_get_object_path (channel)); + + return FALSE; + } + + dbus_error = tp_proxy_get_invalidated (channel); + if (dbus_error != NULL) + { + *error = g_error_copy (dbus_error); + return FALSE; + } + + return TRUE; +} + +static void +handle_channels (TpBaseClient *handler, + TpAccount *account, + TpConnection *connection, + GList *channels, + GList *requests_satisfied, + gint64 user_action_time, + TpHandleChannelsContext *context) +{ + TpChannel *channel; + GError *error = NULL; + EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (handler); + HandlerContextData *data; + + DEBUG ("Handle TLS or SASL carrier channels."); + + if (!common_checks (self, channels, FALSE, &error)) + { + DEBUG ("Failed checks: %s", error->message); + tp_handle_channels_context_fail (context, error); + g_clear_error (&error); + return; + } + + /* 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); + + /* create a handler */ + if (tp_channel_get_channel_type_id (channel) == + TP_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); + } +} + +typedef struct +{ + EmpathyAuthFactory *self; + TpObserveChannelsContext *context; + TpChannelDispatchOperation *dispatch_operation; + TpAccount *account; + TpChannel *channel; +} ObserveChannelsData; + +static void +observe_channels_data_free (ObserveChannelsData *data) +{ + g_object_unref (data->context); + g_object_unref (data->account); + g_object_unref (data->channel); + g_object_unref (data->dispatch_operation); + g_slice_free (ObserveChannelsData, data); +} + +static void +password_claim_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + ObserveChannelsData *data = user_data; + GError *error = NULL; + + if (!tp_channel_dispatch_operation_claim_with_finish ( + TP_CHANNEL_DISPATCH_OPERATION (source), result, &error)) + { + DEBUG ("Failed to call Claim: %s", error->message); + g_clear_error (&error); + } + else + { + HandlerContextData *h_data; + + DEBUG ("Claim called successfully"); + + h_data = handler_context_data_new (data->self, NULL); + + empathy_server_sasl_handler_new_async (TP_ACCOUNT (data->account), + data->channel, server_sasl_handler_ready_cb, h_data); + } + + observe_channels_data_free (data); +} + +static void +get_password_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + ObserveChannelsData *data = user_data; + + if (empathy_keyring_get_account_password_finish (TP_ACCOUNT (source), result, NULL) == NULL) + { + /* We don't actually mind if this fails, just let the approver + * go ahead and take the channel. */ + + DEBUG ("We don't have a password for account %s, letting the event " + "manager approver take it", tp_proxy_get_object_path (source)); + + tp_observe_channels_context_accept (data->context); + observe_channels_data_free (data); + } + else + { + DEBUG ("We have a password for account %s, calling Claim", + tp_proxy_get_object_path (source)); + + tp_channel_dispatch_operation_claim_with_async (data->dispatch_operation, + TP_BASE_CLIENT (data->self), password_claim_cb, data); + + tp_observe_channels_context_accept (data->context); + } +} + +#ifdef HAVE_GOA +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); +} +#endif /* HAVE_GOA */ + +#ifdef HAVE_UOA +static void +uoa_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_uoa_auth_handler_start (self->priv->uoa_handler, + data->channel, data->account); + } + + observe_channels_data_free (data); +} +#endif /* HAVE_UOA */ + +static void +observe_channels (TpBaseClient *client, + TpAccount *account, + TpConnection *connection, + GList *channels, + TpChannelDispatchOperation *dispatch_operation, + GList *requests, + TpObserveChannelsContext *context) +{ + EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (client); + TpChannel *channel; + GError *error = NULL; + ObserveChannelsData *data; + + DEBUG ("New auth channel to observe"); + + if (!common_checks (self, channels, TRUE, &error)) + { + DEBUG ("Failed checks: %s", error->message); + tp_observe_channels_context_fail (context, error); + g_clear_error (&error); + return; + } + + /* The common checks above have checked this is fine. */ + channel = channels->data; + + data = g_slice_new0 (ObserveChannelsData); + data->self = self; + data->context = g_object_ref (context); + data->dispatch_operation = g_object_ref (dispatch_operation); + data->account = g_object_ref (account); + data->channel = g_object_ref (channel); + +#ifdef HAVE_GOA + /* 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; + } +#endif /* HAVE_GOA */ + +#ifdef HAVE_UOA + /* UOA auth? */ + if (empathy_uoa_auth_handler_supports (self->priv->uoa_handler, channel, account)) + { + DEBUG ("Supported UOA account (%s), claim SASL channel", + tp_proxy_get_object_path (account)); + + tp_channel_dispatch_operation_claim_with_async (dispatch_operation, + client, uoa_claim_cb, data); + tp_observe_channels_context_accept (context); + return; + } +#endif /* HAVE_UOA */ + + /* Password auth? */ + if (empathy_sasl_channel_supports_mechanism (data->channel, + "X-TELEPATHY-PASSWORD")) + { + if (g_hash_table_lookup (self->priv->retry_passwords, account) != NULL) + { + DEBUG ("We have a retry password for account %s, calling Claim", + tp_account_get_path_suffix (account)); + + tp_channel_dispatch_operation_claim_with_async (dispatch_operation, + client, password_claim_cb, data); + + tp_observe_channels_context_accept (context); + return; + } + + 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 * +empathy_auth_factory_constructor (GType type, + guint n_params, + GObjectConstructParam *params) +{ + GObject *retval; + + if (auth_factory_singleton != NULL) + { + retval = g_object_ref (auth_factory_singleton); + } + else + { + retval = G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructor + (type, n_params, params); + + auth_factory_singleton = EMPATHY_AUTH_FACTORY (retval); + g_object_add_weak_pointer (retval, (gpointer *) &auth_factory_singleton); + } + + return retval; +} + +static void +empathy_auth_factory_init (EmpathyAuthFactory *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_AUTH_FACTORY, EmpathyAuthFactoryPriv); + + self->priv->sasl_handlers = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, g_object_unref); + +#ifdef HAVE_GOA + self->priv->goa_handler = empathy_goa_auth_handler_new (); +#endif /* HAVE_GOA */ + +#ifdef HAVE_UOA + self->priv->uoa_handler = empathy_uoa_auth_handler_new (); +#endif /* HAVE_UOA */ + + self->priv->retry_passwords = g_hash_table_new_full (NULL, NULL, + g_object_unref, g_free); +} + +static void +empathy_auth_factory_constructed (GObject *obj) +{ + EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (obj); + TpBaseClient *client = TP_BASE_CLIENT (self); + + /* chain up to TpBaseClient first */ + G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructed (obj); + + tp_base_client_set_handler_bypass_approval (client, FALSE); + + /* Handle ServerTLSConnection and ServerAuthentication channels */ + tp_base_client_take_handler_filter (client, tp_asv_new ( + /* ChannelType */ + TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, + TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, + /* AuthenticationMethod */ + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, + TP_HANDLE_TYPE_NONE, NULL)); + + tp_base_client_take_handler_filter (client, 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)); + + /* We are also an observer so that we can see new auth channels + * popping up and if we have the password already saved to one + * account where an auth channel has just appeared we can call + * Claim() on the CDO so the approver won't get it, which makes + * sense. */ + + /* Observe ServerAuthentication channels */ + tp_base_client_take_observer_filter (client, 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)); + + tp_base_client_set_observer_delay_approvers (client, TRUE); +} + +static void +empathy_auth_factory_dispose (GObject *object) +{ + EmpathyAuthFactoryPriv *priv = GET_PRIV (object); + + if (priv->dispose_run) + return; + + priv->dispose_run = TRUE; + + g_hash_table_unref (priv->sasl_handlers); + +#ifdef HAVE_GOA + g_object_unref (priv->goa_handler); +#endif /* HAVE_GOA */ + +#ifdef HAVE_UOA + g_object_unref (priv->uoa_handler); +#endif /* HAVE_UOA */ + + g_hash_table_unref (priv->retry_passwords); + + G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object); +} + +static void +empathy_auth_factory_class_init (EmpathyAuthFactoryClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + TpBaseClientClass *base_client_cls = TP_BASE_CLIENT_CLASS (klass); + + oclass->constructor = empathy_auth_factory_constructor; + oclass->constructed = empathy_auth_factory_constructed; + oclass->dispose = empathy_auth_factory_dispose; + + base_client_cls->handle_channels = handle_channels; + base_client_cls->observe_channels = observe_channels; + + g_type_class_add_private (klass, sizeof (EmpathyAuthFactoryPriv)); + + signals[NEW_SERVER_TLS_HANDLER] = + g_signal_new ("new-server-tls-handler", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, + g_cclosure_marshal_generic, + 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_generic, + G_TYPE_NONE, + 1, EMPATHY_TYPE_SERVER_SASL_HANDLER); + + signals[AUTH_PASSWORD_FAILED] = + g_signal_new ("auth-password-failed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 2, TP_TYPE_ACCOUNT, G_TYPE_STRING); +} + +EmpathyAuthFactory * +empathy_auth_factory_new (TpSimpleClientFactory *factory) +{ + return g_object_new (EMPATHY_TYPE_AUTH_FACTORY, + "factory", factory, + "name", "Empathy.Auth", + NULL); +} + +gboolean +empathy_auth_factory_register (EmpathyAuthFactory *self, + GError **error) +{ + return tp_base_client_register (TP_BASE_CLIENT (self), error); +} + +void +empathy_auth_factory_save_retry_password (EmpathyAuthFactory *self, + TpAccount *account, + const gchar *password) +{ + g_hash_table_insert (self->priv->retry_passwords, + g_object_ref (account), g_strdup (password)); +} diff --git a/empathy-auth-factory.h b/empathy-auth-factory.h new file mode 100644 index 0000000..16d6ad3 --- /dev/null +++ b/empathy-auth-factory.h @@ -0,0 +1,73 @@ +/* + * empathy-auth-factory.h - Header for EmpathyAuthFactory + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi + * + * 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_AUTH_FACTORY_H__ +#define __EMPATHY_AUTH_FACTORY_H__ + +#include + +#include + +G_BEGIN_DECLS + +typedef struct _EmpathyAuthFactory EmpathyAuthFactory; +typedef struct _EmpathyAuthFactoryClass EmpathyAuthFactoryClass; +typedef struct _EmpathyAuthFactoryPriv EmpathyAuthFactoryPriv; + +struct _EmpathyAuthFactoryClass { + TpBaseClientClass parent_class; +}; + +struct _EmpathyAuthFactory { + TpBaseClient parent; + EmpathyAuthFactoryPriv *priv; +}; + +GType empathy_auth_factory_get_type (void); + +/* TYPE MACROS */ +#define EMPATHY_TYPE_AUTH_FACTORY \ + (empathy_auth_factory_get_type ()) +#define EMPATHY_AUTH_FACTORY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_AUTH_FACTORY, \ + EmpathyAuthFactory)) +#define EMPATHY_AUTH_FACTORY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_AUTH_FACTORY, \ + EmpathyAuthFactoryClass)) +#define EMPATHY_IS_AUTH_FACTORY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_AUTH_FACTORY)) +#define EMPATHY_IS_AUTH_FACTORY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_AUTH_FACTORY)) +#define EMPATHY_AUTH_FACTORY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_AUTH_FACTORY, \ + EmpathyAuthFactoryClass)) + +EmpathyAuthFactory * empathy_auth_factory_new (TpSimpleClientFactory *factory); + +gboolean empathy_auth_factory_register (EmpathyAuthFactory *self, + GError **error); + +void empathy_auth_factory_save_retry_password (EmpathyAuthFactory *self, + TpAccount *account, + const gchar *password); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_AUTH_FACTORY_H__*/ diff --git a/empathy-debug.h b/empathy-debug.h new file mode 100644 index 0000000..3891fb0 --- /dev/null +++ b/empathy-debug.h @@ -0,0 +1 @@ +#define DEBUG g_debug diff --git a/empathy-keyring.c b/empathy-keyring.c new file mode 100644 index 0000000..b4ed906 --- /dev/null +++ b/empathy-keyring.c @@ -0,0 +1,512 @@ +/* + * 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-keyring.h" +#include "empathy-debug.h" +#include "empathy-utils.h" + +#include + +#include +#include +#include +#include +#include +#include +#include "empathy-uoa-utils.h" + +#define EMPATHY_UOA_SERVICE_TYPE "IM" +#define EMPATHY_UOA_PROVIDER "im.telepathy.Account.Storage.UOA" + +static AgAccountService * +uoa_password_common (TpAccount *tp_account, + GSimpleAsyncResult *result, + AgAuthData **ret_auth_data) +{ + const GValue *storage_id; + AgAccountId account_id; + AgManager *manager = NULL; + AgAccount *account = NULL; + GList *l; + AgAccountService *service = NULL; + AgAuthData *auth_data = NULL; + + g_assert (ret_auth_data != NULL); + *ret_auth_data = NULL; + + storage_id = tp_account_get_storage_identifier (tp_account); + account_id = g_value_get_uint (storage_id); + if (account_id == 0) + { + g_simple_async_result_set_error (result, + TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "StorageId is invalid, cannot get the AgAccount for this TpAccount"); + g_simple_async_result_complete_in_idle (result); + goto error; + } + + manager = empathy_uoa_manager_dup (); + account = ag_manager_get_account (manager, account_id); + + /* Assuming there is only one IM service */ + l = ag_account_list_services_by_type (account, EMPATHY_UOA_SERVICE_TYPE); + if (l == NULL) + { + g_simple_async_result_set_error (result, + TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "AgAccount has no IM service"); + g_simple_async_result_complete_in_idle (result); + goto error; + } + service = ag_account_service_new (account, l->data); + ag_service_list_free (l); + + auth_data = ag_account_service_get_auth_data (service); + if (auth_data == NULL) + { + g_simple_async_result_set_error (result, + TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Service has no AgAuthData"); + g_simple_async_result_complete_in_idle (result); + goto error; + } + + if (tp_strdiff (ag_auth_data_get_mechanism (auth_data), "password") || + tp_strdiff (ag_auth_data_get_method (auth_data), "password")) + { + g_simple_async_result_set_error (result, + TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Service does not use password authentication"); + g_simple_async_result_complete_in_idle (result); + goto error; + } + + g_object_unref (manager); + g_object_unref (account); + + *ret_auth_data = auth_data; + return service; + +error: + g_clear_object (&manager); + g_clear_object (&account); + g_clear_object (&service); + tp_clear_pointer (&auth_data, ag_auth_data_unref); + return NULL; +} + +static void +uoa_session_process_cb (SignonAuthSession *session, + GHashTable *session_data, + const GError *error, + gpointer user_data) +{ + GSimpleAsyncResult *result = user_data; + const gchar *password; + + if (error != NULL) + { + g_simple_async_result_set_from_error (result, error); + goto out; + } + + password = tp_asv_get_string (session_data, "Secret"); + if (tp_str_empty (password)) + { + g_simple_async_result_set_error (result, TP_ERROR, + TP_ERROR_DOES_NOT_EXIST, "Password not found"); + goto out; + } + + g_simple_async_result_set_op_res_gpointer (result, g_strdup (password), + g_free); + +out: + /* libaccounts-glib API does not guarantee the callback happens after + * reentering mainloop */ + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + g_object_unref (session); +} + +static void +uoa_get_account_password (TpAccount *tp_account, + GSimpleAsyncResult *result) +{ + AgAccountService *service; + AgAuthData *auth_data; + guint cred_id; + SignonIdentity *identity; + SignonAuthSession *session; + GError *error = NULL; + + DEBUG ("Store password for %s in signond", + tp_account_get_path_suffix (tp_account)); + + service = uoa_password_common (tp_account, result, &auth_data); + if (service == NULL) + return; + + cred_id = ag_auth_data_get_credentials_id (auth_data); + if (cred_id == 0) + { + g_simple_async_result_set_error (result, + TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "AgAccount has no CredentialsId"); + g_simple_async_result_complete_in_idle (result); + goto out; + } + + identity = signon_identity_new_from_db (cred_id); + session = signon_identity_create_session (identity, + ag_auth_data_get_method (auth_data), &error); + g_object_unref (identity); + + if (session == NULL) + { + g_simple_async_result_set_from_error (result, error); + g_simple_async_result_complete_in_idle (result); + goto out; + } + + signon_auth_session_process (session, + ag_auth_data_get_parameters (auth_data), + ag_auth_data_get_mechanism (auth_data), + uoa_session_process_cb, + g_object_ref (result)); + +out: + ag_auth_data_unref (auth_data); + g_object_unref (service); +} + +void +empathy_keyring_get_account_password_async (TpAccount *account, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + 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_account_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); + + const gchar *provider; + + provider = tp_account_get_storage_provider (account); + if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER)) + { + uoa_get_account_password (account, simple); + g_object_unref (simple); + return; + } + + g_object_unref(simple); +} + +const gchar * +empathy_keyring_get_account_password_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + empathy_implement_finish_return_pointer (account, + empathy_keyring_get_account_password_async); +} + +/* set */ + +typedef struct +{ + AgAccountService *service; + gchar *password; + gboolean remember; + GSimpleAsyncResult *result; +} UoaChangePasswordData; + +static UoaChangePasswordData * +uoa_change_password_data_new (AgAccountService *service, + const gchar *password, + gboolean remember, + GSimpleAsyncResult *result) +{ + UoaChangePasswordData *data; + + data = g_slice_new0 (UoaChangePasswordData); + data->service = g_object_ref (service); + data->password = g_strdup (password); + data->remember = remember; + data->result = g_object_ref (result); + + return data; +} + +static void +uoa_change_password_data_free (UoaChangePasswordData *data) +{ + g_object_unref (data->service); + g_free (data->password); + g_object_unref (data->result); + g_slice_free (UoaChangePasswordData, data); +} + +static void +uoa_identity_store_cb (SignonIdentity *identity, + guint32 id, + const GError *error, + gpointer user_data) +{ + UoaChangePasswordData *data = user_data; + + if (error != NULL) + g_simple_async_result_set_from_error (data->result, error); + + g_simple_async_result_complete (data->result); + uoa_change_password_data_free (data); + g_object_unref (identity); +} + +static void +uoa_identity_query_info_cb (SignonIdentity *identity, + const SignonIdentityInfo *info, + const GError *error, + gpointer user_data) +{ + UoaChangePasswordData *data = user_data; + + if (error != NULL) + { + g_simple_async_result_set_from_error (data->result, error); + /* libaccounts-glib API does not guarantee the callback happens after + * reentering mainloop */ + g_simple_async_result_complete_in_idle (data->result); + uoa_change_password_data_free (data); + g_object_unref (identity); + return; + } + + /* const SignonIdentityInfo is a lie, cast it! - Mardy */ + signon_identity_info_set_secret ((SignonIdentityInfo *) info, + data->password, data->remember); + + signon_identity_store_credentials_with_info (identity, info, + uoa_identity_store_cb, data); +} + +static void +uoa_initial_account_store_cb (AgAccount *account, + const GError *error, + gpointer user_data) +{ + UoaChangePasswordData *data = user_data; + + if (error != NULL) + g_simple_async_result_set_from_error (data->result, error); + + /* libaccounts-glib API does not guarantee the callback happens after + * reentering mainloop */ + g_simple_async_result_complete_in_idle (data->result); + uoa_change_password_data_free (data); +} + +static void +uoa_initial_identity_store_cb (SignonIdentity *identity, + guint32 id, + const GError *error, + gpointer user_data) +{ + UoaChangePasswordData *data = user_data; + AgAccount *account = ag_account_service_get_account (data->service); + GValue value = G_VALUE_INIT; + + if (error != NULL) + { + g_simple_async_result_set_from_error (data->result, error); + /* libaccounts-glib API does not guarantee the callback happens after + * reentering mainloop */ + g_simple_async_result_complete_in_idle (data->result); + uoa_change_password_data_free (data); + g_object_unref (identity); + return; + } + + g_value_init (&value, G_TYPE_UINT); + g_value_set_uint (&value, id); + ag_account_select_service (account, NULL); + ag_account_set_value (account, "CredentialsId", &value); + g_value_unset (&value); + + ag_account_store (account, uoa_initial_account_store_cb, data); + + g_object_unref (identity); +} + +static void +uoa_set_account_password (TpAccount *tp_account, + const gchar *password, + gboolean remember, + GSimpleAsyncResult *result) +{ + AgAccountService *service; + AgAuthData *auth_data; + guint cred_id; + UoaChangePasswordData *data; + SignonIdentity *identity; + + DEBUG ("Store password for %s in signond", + tp_account_get_path_suffix (tp_account)); + + service = uoa_password_common (tp_account, result, &auth_data); + if (service == NULL) + return; + + data = uoa_change_password_data_new (service, password, remember, result); + + cred_id = ag_auth_data_get_credentials_id (auth_data); + if (cred_id == 0) + { + SignonIdentityInfo *info; + const GHashTable *params; + const gchar *username; + const gchar *acl_all[] = { "*", NULL }; + + /* This is the first time we store password for this account. + * First check if we have an 'username' param as this is more accurate + * in the tp-idle case. */ + params = tp_account_get_parameters (tp_account); + username = tp_asv_get_string (params, "username"); + if (username == NULL) + username = tp_asv_get_string (params, "account"); + + identity = signon_identity_new (); + info = signon_identity_info_new (); + signon_identity_info_set_username (info, username); + signon_identity_info_set_secret (info, password, remember); + signon_identity_info_set_access_control_list (info, acl_all); + + /* Give identity and data ownership to the callback */ + signon_identity_store_credentials_with_info (identity, info, + uoa_initial_identity_store_cb, data); + + signon_identity_info_free (info); + } + else + { + /* There is already a password stored, query info to update it. + * Give identity and data ownership to the callback */ + identity = signon_identity_new_from_db (cred_id); + signon_identity_query_info (identity, + uoa_identity_query_info_cb, data); + } + + g_object_unref (service); + ag_auth_data_unref (auth_data); +} + +void +empathy_keyring_set_account_password_async (TpAccount *account, + const gchar *password, + gboolean remember, + 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_account_password_async); + + account_id = tp_proxy_get_object_path (account) + + strlen (TP_ACCOUNT_OBJECT_PATH_BASE); + + DEBUG ("Remembering password for %s", account_id); + + const gchar *provider; + + provider = tp_account_get_storage_provider (account); + if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER)) + { + uoa_set_account_password (account, password, remember, simple); + g_object_unref (simple); + return; + } + + g_object_unref(simple); +} + +gboolean +empathy_keyring_set_account_password_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + empathy_implement_finish_void (account, empathy_keyring_set_account_password_async); +} + +/* delete */ + +void +empathy_keyring_delete_account_password_async (TpAccount *account, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + 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_account_password_async); + + account_id = tp_proxy_get_object_path (account) + + strlen (TP_ACCOUNT_OBJECT_PATH_BASE); + + DEBUG ("Deleting password for %s", account_id); + + const gchar *provider; + + provider = tp_account_get_storage_provider (account); + if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER)) + { + /* I see no other way to forget the stored password than overwriting + * with an empty one. */ + uoa_set_account_password (account, "", FALSE, simple); + g_object_unref (simple); + return; + } + + g_object_unref(simple); +} + +gboolean +empathy_keyring_delete_account_password_finish (TpAccount *account, + GAsyncResult *result, + GError **error) +{ + empathy_implement_finish_void (account, empathy_keyring_delete_account_password_async); +} diff --git a/empathy-keyring.h b/empathy-keyring.h new file mode 100644 index 0000000..7e945dc --- /dev/null +++ b/empathy-keyring.h @@ -0,0 +1,66 @@ +/* + * 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 + +#include + +G_BEGIN_DECLS + +gboolean empathy_keyring_is_available (void); + +void empathy_keyring_get_account_password_async (TpAccount *account, + GAsyncReadyCallback callback, gpointer user_data); + +const gchar * empathy_keyring_get_account_password_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void empathy_keyring_get_room_password_async (TpAccount *account, + const gchar *id, + GAsyncReadyCallback callback, gpointer user_data); + +const gchar * empathy_keyring_get_room_password_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void empathy_keyring_set_account_password_async (TpAccount *account, + const gchar *password, gboolean remember, GAsyncReadyCallback callback, + gpointer user_data); + +gboolean empathy_keyring_set_account_password_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void empathy_keyring_set_room_password_async (TpAccount *account, + const gchar *id, const gchar *password, GAsyncReadyCallback callback, + gpointer user_data); + +gboolean empathy_keyring_set_room_password_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +void empathy_keyring_delete_account_password_async (TpAccount *account, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean empathy_keyring_delete_account_password_finish (TpAccount *account, + GAsyncResult *result, GError **error); + +G_END_DECLS + +#endif /* __EMPATHY_KEYRING_H__ */ + diff --git a/empathy-sasl-mechanisms.c b/empathy-sasl-mechanisms.c new file mode 100644 index 0000000..c47a702 --- /dev/null +++ b/empathy-sasl-mechanisms.c @@ -0,0 +1,375 @@ +/* + * empathy-sasl-mechanisms.h - Header for SASL authentication mechanisms + * Copyright (C) 2012 Collabora Ltd. + * @author Xavier Claessens + * + * 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 +#include + +#include "empathy-debug.h" +#include "empathy-utils.h" +#include "empathy-sasl-mechanisms.h" + +#define MECH_FACEBOOK "X-FACEBOOK-PLATFORM" +#define MECH_WLM "X-MESSENGER-OAUTH2" +#define MECH_GOOGLE "X-OAUTH2" +#define MECH_PASSWORD "X-TELEPATHY-PASSWORD" + +typedef struct +{ + EmpathySaslMechanism id; + const gchar *name; +} SupportedMech; + +static SupportedMech supported_mechanisms[] = { + { EMPATHY_SASL_MECHANISM_FACEBOOK, MECH_FACEBOOK }, + { EMPATHY_SASL_MECHANISM_WLM, MECH_WLM }, + { EMPATHY_SASL_MECHANISM_GOOGLE, MECH_GOOGLE }, + + /* Must be the last one, otherwise empathy_sasl_channel_select_mechanism() + * will prefer password over web auth for servers supporting both. */ + { EMPATHY_SASL_MECHANISM_PASSWORD, MECH_PASSWORD } +}; + +static void +generic_cb (TpChannel *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = user_data; + + if (error != NULL) + { + g_simple_async_result_set_from_error (result, error); + g_simple_async_result_complete (result); + } +} + +static void +sasl_status_changed_cb (TpChannel *channel, + guint status, + const gchar *dbus_error, + GHashTable *details, + gpointer user_data, + GObject *self) +{ + GSimpleAsyncResult *result = user_data; + + switch (status) + { + case TP_SASL_STATUS_SERVER_SUCCEEDED: + tp_cli_channel_interface_sasl_authentication_call_accept_sasl (channel, + -1, generic_cb, g_object_ref (result), g_object_unref, NULL); + break; + + case TP_SASL_STATUS_SERVER_FAILED: + case TP_SASL_STATUS_CLIENT_FAILED: + { + GError *error = NULL; + + tp_proxy_dbus_error_to_gerror (channel, dbus_error, + tp_asv_get_string (details, "debug-message"), &error); + + DEBUG ("SASL failed: %s", error->message); + + g_simple_async_result_take_error (result, error); + g_simple_async_result_complete (result); + } + break; + + case TP_SASL_STATUS_SUCCEEDED: + DEBUG ("SASL succeeded"); + + g_simple_async_result_complete (result); + break; + + default: + break; + } +} + +static GSimpleAsyncResult * +empathy_sasl_auth_common_async (TpChannel *channel, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + GError *error = NULL; + + g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL); + g_return_val_if_fail (tp_proxy_has_interface_by_id (channel, + TP_IFACE_QUARK_CHANNEL_INTERFACE_SASL_AUTHENTICATION), NULL); + + result = g_simple_async_result_new ((GObject *) channel, + callback, user_data, empathy_sasl_auth_common_async); + + tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed ( + channel, sasl_status_changed_cb, + g_object_ref (result), g_object_unref, NULL, &error); + g_assert_no_error (error); + + return result; +} + +typedef struct +{ + TpChannel *channel; + gchar *client_id; + gchar *access_token; +} FacebookData; + +static void +facebook_data_free (FacebookData *data) +{ + g_object_unref (data->channel); + g_free (data->client_id); + g_free (data->access_token); + g_slice_free (FacebookData, data); +} + +static void +facebook_new_challenge_cb (TpChannel *channel, + const GArray *challenge, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = user_data; + FacebookData *data; + GHashTable *h; + GHashTable *params; + gchar *response; + GArray *response_array; + + DEBUG ("new challenge: %s", challenge->data); + + data = g_simple_async_result_get_op_res_gpointer (result); + + h = soup_form_decode (challenge->data); + + /* 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", data->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, generic_cb, g_object_ref (result), g_object_unref, NULL); + + g_hash_table_unref (h); + g_hash_table_unref (params); + g_free (response); + g_array_unref (response_array); +} + +void +empathy_sasl_auth_facebook_async (TpChannel *channel, + const gchar *client_id, + const gchar *access_token, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + FacebookData *data; + GError *error = NULL; + + result = empathy_sasl_auth_common_async (channel, callback, user_data); + + g_return_if_fail (result != NULL); + g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel, + MECH_FACEBOOK)); + g_return_if_fail (!tp_str_empty (client_id)); + g_return_if_fail (!tp_str_empty (access_token)); + + DEBUG ("Start %s mechanism", MECH_FACEBOOK); + + data = g_slice_new0 (FacebookData); + data->channel = g_object_ref (channel); + data->client_id = g_strdup (client_id); + data->access_token = g_strdup (access_token); + + g_simple_async_result_set_op_res_gpointer (result, data, + (GDestroyNotify) facebook_data_free); + + tp_cli_channel_interface_sasl_authentication_connect_to_new_challenge ( + channel, facebook_new_challenge_cb, + g_object_ref (result), g_object_unref, + NULL, &error); + g_assert_no_error (error); + + tp_cli_channel_interface_sasl_authentication_call_start_mechanism ( + channel, -1, MECH_FACEBOOK, generic_cb, + g_object_ref (result), g_object_unref, NULL); + + g_object_unref (result); +} + +void +empathy_sasl_auth_wlm_async (TpChannel *channel, + const gchar *access_token, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + guchar *token_decoded; + gsize token_decoded_len; + GArray *token_decoded_array; + + result = empathy_sasl_auth_common_async (channel, callback, user_data); + + g_return_if_fail (result != NULL); + g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel, + MECH_WLM)); + g_return_if_fail (!tp_str_empty (access_token)); + + DEBUG ("Start %s mechanism", MECH_WLM); + + /* 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 (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); + + tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data ( + channel, -1, MECH_WLM, token_decoded_array, + generic_cb, g_object_ref (result), g_object_unref, NULL); + + g_array_unref (token_decoded_array); + g_free (token_decoded); + g_object_unref (result); +} + +void +empathy_sasl_auth_google_async (TpChannel *channel, + const gchar *username, + const gchar *access_token, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + GArray *credential; + + result = empathy_sasl_auth_common_async (channel, callback, user_data); + + g_return_if_fail (result != NULL); + g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel, + MECH_GOOGLE)); + g_return_if_fail (!tp_str_empty (username)); + g_return_if_fail (!tp_str_empty (access_token)); + + DEBUG ("Start %s mechanism", MECH_GOOGLE); + + credential = g_array_sized_new (FALSE, FALSE, sizeof (gchar), + strlen (access_token) + strlen (username) + 2); + + g_array_append_val (credential, "\0"); + g_array_append_vals (credential, username, strlen (username)); + g_array_append_val (credential, "\0"); + g_array_append_vals (credential, access_token, strlen (access_token)); + + tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data ( + channel, -1, MECH_GOOGLE, credential, + generic_cb, g_object_ref (result), g_object_unref, NULL); + + g_array_unref (credential); + g_object_unref (result); +} + +void +empathy_sasl_auth_password_async (TpChannel *channel, + const gchar *password, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + GArray *password_array; + + result = empathy_sasl_auth_common_async (channel, callback, user_data); + + g_return_if_fail (result != NULL); + g_return_if_fail (empathy_sasl_channel_supports_mechanism (channel, + MECH_PASSWORD)); + g_return_if_fail (!tp_str_empty (password)); + + DEBUG ("Start %s mechanism", MECH_PASSWORD); + + password_array = g_array_sized_new (FALSE, FALSE, sizeof (gchar), + strlen (password)); + g_array_append_vals (password_array, password, strlen (password)); + + tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data ( + channel, -1, MECH_PASSWORD, password_array, + generic_cb, g_object_ref (result), g_object_unref, NULL); + + g_array_unref (password_array); + g_object_unref (result); +} + +gboolean +empathy_sasl_auth_finish (TpChannel *channel, + GAsyncResult *result, + GError **error) +{ + empathy_implement_finish_void (channel, empathy_sasl_auth_common_async); +} + +gboolean +empathy_sasl_channel_supports_mechanism (TpChannel *channel, + const gchar *mechanism) +{ + GVariant *props; + GStrv available_mechanisms; + gboolean result; + + props = tp_channel_dup_immutable_properties (channel); + + g_variant_lookup (props, + TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS, + "^as", &available_mechanisms); + + result = tp_strv_contains ((const gchar * const *) available_mechanisms, + mechanism); + + g_variant_unref (props); + g_strfreev (available_mechanisms); + return result; +} + +EmpathySaslMechanism +empathy_sasl_channel_select_mechanism (TpChannel *channel) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (supported_mechanisms); i++) + { + if (empathy_sasl_channel_supports_mechanism (channel, + supported_mechanisms[i].name)) + return supported_mechanisms[i].id; + } + + return EMPATHY_SASL_MECHANISM_UNSUPPORTED; +} diff --git a/empathy-sasl-mechanisms.h b/empathy-sasl-mechanisms.h new file mode 100644 index 0000000..ef7ccd6 --- /dev/null +++ b/empathy-sasl-mechanisms.h @@ -0,0 +1,70 @@ +/* + * empathy-sasl-mechanisms.h - Header for SASL authentication mechanisms + * Copyright (C) 2012 Collabora Ltd. + * @author Xavier Claessens + * + * 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_SASL_MECHANISMS_H__ +#define __EMPATHY_SASL_MECHANISMS_H__ + +#include + +G_BEGIN_DECLS + +typedef enum +{ + EMPATHY_SASL_MECHANISM_UNSUPPORTED, + EMPATHY_SASL_MECHANISM_FACEBOOK, + EMPATHY_SASL_MECHANISM_WLM, + EMPATHY_SASL_MECHANISM_GOOGLE, + EMPATHY_SASL_MECHANISM_PASSWORD, +} EmpathySaslMechanism; + +void empathy_sasl_auth_facebook_async (TpChannel *channel, + const gchar *client_id, + const gchar *access_token, + GAsyncReadyCallback callback, + gpointer user_data); + +void empathy_sasl_auth_wlm_async (TpChannel *channel, + const gchar *access_token, + GAsyncReadyCallback callback, + gpointer user_data); + +void empathy_sasl_auth_google_async (TpChannel *channel, + const gchar *username, + const gchar *access_token, + GAsyncReadyCallback callback, + gpointer user_data); + +void empathy_sasl_auth_password_async (TpChannel *channel, + const gchar *password, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean empathy_sasl_auth_finish (TpChannel *channel, + GAsyncResult *result, + GError **error); + +gboolean empathy_sasl_channel_supports_mechanism (TpChannel *channel, + const gchar *mechanism); + +EmpathySaslMechanism empathy_sasl_channel_select_mechanism (TpChannel *channel); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_SASL_MECHANISMS_H__*/ diff --git a/empathy-server-sasl-handler.c b/empathy-server-sasl-handler.c new file mode 100644 index 0000000..6faae79 --- /dev/null +++ b/empathy-server-sasl-handler.c @@ -0,0 +1,525 @@ +/* + * 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 + +#include "empathy-debug.h" +//#include "empathy-keyring.h" +//#include "empathy-sasl-mechanisms.h" + +enum { + PROP_CHANNEL = 1, + PROP_ACCOUNT, + LAST_PROPERTY, +}; + +/* signal enum */ +enum { + AUTH_PASSWORD_FAILED, + INVALIDATED, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef struct { + TpChannel *channel; + TpAccount *account; + + GSimpleAsyncResult *result; + + gchar *password; + gboolean save_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 void +empathy_server_sasl_handler_set_password_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + + if (!empathy_keyring_set_account_password_finish (TP_ACCOUNT (source), result, + &error)) + { + DEBUG ("Failed to set password: %s", error->message); + g_clear_error (&error); + } + else + { + DEBUG ("Password set successfully."); + } +} + +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_account_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_account_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; + + 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->finalize = 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[AUTH_PASSWORD_FAILED] = g_signal_new ("auth-password-failed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[INVALIDATED] = g_signal_new ("invalidated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, + g_cclosure_marshal_generic, + 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 +auth_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + EmpathyServerSASLHandler *self = user_data; + EmpathyServerSASLHandlerPriv *priv = self->priv; + GError *error = NULL; + + if (!empathy_sasl_auth_finish (priv->channel, result, &error)) + { + if (g_error_matches (error, TP_ERROR, TP_ERROR_AUTHENTICATION_FAILED)) + { + g_signal_emit (self, signals[AUTH_PASSWORD_FAILED], 0, priv->password); + } + g_clear_error (&error); + } + else + { + DEBUG ("Saving password in keyring"); + empathy_keyring_set_account_password_async (priv->account, + priv->password, priv->save_password, + empathy_server_sasl_handler_set_password_cb, + NULL); + } + + tp_channel_close_async (priv->channel, NULL, NULL); + g_object_unref (self); +} + +static gboolean +channel_has_may_save_response (TpChannel *channel) +{ + /* determine if we are permitted to save the password locally */ + GVariant *props; + gboolean may_save_response; + + props = tp_channel_dup_immutable_properties (channel); + + if (!g_variant_lookup (props, + TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_MAY_SAVE_RESPONSE, + "b", &may_save_response)) + { + DEBUG ("MaySaveResponse unknown, assuming TRUE"); + may_save_response = TRUE; + } + + g_variant_unref (props); + return may_save_response; +} + +void +empathy_server_sasl_handler_provide_password ( + EmpathyServerSASLHandler *handler, + const gchar *password, + gboolean remember) +{ + EmpathyServerSASLHandlerPriv *priv; + gboolean may_save_response; + + g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler)); + + priv = handler->priv; + + empathy_sasl_auth_password_async (priv->channel, password, + auth_cb, g_object_ref (handler)); + + DEBUG ("%sremembering the password", remember ? "" : "not "); + + may_save_response = channel_has_may_save_response (priv->channel); + + if (remember) + { + if (may_save_response) + { + g_free (priv->password); + + /* We'll save the password if we manage to connect */ + priv->password = g_strdup (password); + priv->save_password = TRUE; + } + else if (//tp_proxy_has_interface_by_id (priv->channel, + //EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE)) + FALSE) + { + DEBUG ("Channel implements Ch.I.CredentialsStorage"); + } + else + { + DEBUG ("Asked to remember password, but doing so is not permitted"); + } + } + + if (!may_save_response) + { + /* delete any password present, it shouldn't be there */ + empathy_keyring_delete_account_password_async (priv->account, NULL, NULL); + } + +#if 0 + /* Additionally, if we implement Ch.I.CredentialsStorage, inform that + * whether we want to remember the password */ + if (tp_proxy_has_interface_by_id (priv->channel, + EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE)) + { + emp_cli_channel_interface_credentials_storage_call_store_credentials ( + TP_PROXY (priv->channel), -1, remember, NULL, NULL, NULL, NULL); + } +#endif +} + +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; +} + +TpChannel * +empathy_server_sasl_handler_get_channel (EmpathyServerSASLHandler *handler) +{ + EmpathyServerSASLHandlerPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL); + + priv = handler->priv; + + return priv->channel; +} + +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); +} + +/** + * empathy_server_sasl_handler_can_save_response_somewhere: + * @self: + * + * Returns: %TRUE if the response can be saved somewhere, either the keyring + * or via Ch.I.CredentialsStorage + */ +gboolean +empathy_server_sasl_handler_can_save_response_somewhere ( + EmpathyServerSASLHandler *self) +{ + EmpathyServerSASLHandlerPriv *priv; + gboolean may_save_response; + gboolean has_storage_iface; + + g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (self), FALSE); + + priv = self->priv; + + may_save_response = channel_has_may_save_response (priv->channel); + + // XXX + has_storage_iface = FALSE; //tp_proxy_has_interface_by_id (priv->channel, + //EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE); + + return may_save_response || has_storage_iface; +} diff --git a/empathy-server-sasl-handler.h b/empathy-server-sasl-handler.h new file mode 100644 index 0000000..d25d90a --- /dev/null +++ b/empathy-server-sasl-handler.h @@ -0,0 +1,87 @@ +/* + * 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 +#include + +#include + +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); + +TpChannel * empathy_server_sasl_handler_get_channel ( + EmpathyServerSASLHandler *handler); + +gboolean empathy_server_sasl_handler_has_password ( + EmpathyServerSASLHandler *handler); + +gboolean empathy_server_sasl_handler_can_save_response_somewhere ( + EmpathyServerSASLHandler *self); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_SERVER_SASL_HANDLER_H__*/ diff --git a/empathy-server-tls-handler.c b/empathy-server-tls-handler.c new file mode 100644 index 0000000..ad337c9 --- /dev/null +++ b/empathy-server-tls-handler.c @@ -0,0 +1,323 @@ +/* + * empathy-server-tls-handler.c - Source for EmpathyServerTLSHandler + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi + * + * 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-tls-handler.h" +#include "empathy-utils.h" +#include "empathy-debug.h" + +static void async_initable_iface_init (GAsyncInitableIface *iface); + +enum { + PROP_CHANNEL = 1, + PROP_TLS_CERTIFICATE, + PROP_HOSTNAME, + PROP_REFERENCE_IDENTITIES, + LAST_PROPERTY, +}; + +typedef struct { + TpChannel *channel; + + TpTLSCertificate *certificate; + gchar *hostname; + gchar **reference_identities; + + GSimpleAsyncResult *async_init_res; +} EmpathyServerTLSHandlerPriv; + +G_DEFINE_TYPE_WITH_CODE (EmpathyServerTLSHandler, empathy_server_tls_handler, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)); + +#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyServerTLSHandler); + +static void +tls_certificate_prepared_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpTLSCertificate *certificate = TP_TLS_CERTIFICATE (source); + EmpathyServerTLSHandler *self = user_data; + GError *error = NULL; + EmpathyServerTLSHandlerPriv *priv = GET_PRIV (self); + + if (!tp_proxy_prepare_finish (certificate, result, &error)) + { + g_simple_async_result_set_from_error (priv->async_init_res, error); + g_error_free (error); + } + + g_simple_async_result_complete_in_idle (priv->async_init_res); + tp_clear_object (&priv->async_init_res); +} + +static gboolean +tls_handler_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + gboolean retval = TRUE; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), + error)) + retval = FALSE; + + return retval; +} + +static void +tls_handler_init_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVariant *properties; + const gchar *cert_object_path; + const gchar *bus_name; + GError *error = NULL; + GQuark features[] = { TP_TLS_CERTIFICATE_FEATURE_CORE, 0 }; + /* + * Used when channel doesn't implement ReferenceIdentities. A GStrv + * with [0] the hostname, and [1] a NULL terminator. + */ + gchar *default_identities[2]; + EmpathyServerTLSHandler *self = EMPATHY_SERVER_TLS_HANDLER (initable); + EmpathyServerTLSHandlerPriv *priv = GET_PRIV (self); + + g_assert (priv->channel != NULL); + + priv->async_init_res = g_simple_async_result_new (G_OBJECT (self), + callback, user_data, empathy_server_tls_handler_new_async); + properties = tp_channel_dup_immutable_properties (priv->channel); + + g_variant_lookup (properties, + TP_PROP_CHANNEL_TYPE_SERVER_TLS_CONNECTION_HOSTNAME, + "s", &priv->hostname); + + DEBUG ("Received hostname: %s", priv->hostname); + + g_variant_lookup (properties, + TP_PROP_CHANNEL_TYPE_SERVER_TLS_CONNECTION_REFERENCE_IDENTITIES, + "^as", &priv->reference_identities); + + /* + * If the channel doesn't implement the ReferenceIdentities parameter + * then fallback to the hostname. + */ + if (priv->reference_identities == NULL) + { + default_identities[0] = (gchar *) priv->hostname; + default_identities[1] = NULL; + priv->reference_identities = g_strdupv (default_identities); + } + else + { +#ifdef ENABLE_DEBUG + gchar *output = g_strjoinv (", ", (gchar **) priv->reference_identities); + DEBUG ("Received reference identities: %s", output); + g_free (output); +#endif /* ENABLE_DEBUG */ + } + + g_variant_lookup (properties, + TP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION ".ServerCertificate", + "&o", &cert_object_path); + bus_name = tp_proxy_get_bus_name (TP_PROXY (priv->channel)); + + DEBUG ("Creating an TpTLSCertificate for path %s, bus name %s", + cert_object_path, bus_name); + + priv->certificate = tp_tls_certificate_new (TP_PROXY (priv->channel), + cert_object_path, &error); + + g_variant_unref (properties); + + if (error != NULL) + { + DEBUG ("Unable to create the TpTLSCertificate: error %s", + error->message); + + g_simple_async_result_set_from_error (priv->async_init_res, error); + g_simple_async_result_complete_in_idle (priv->async_init_res); + + g_error_free (error); + tp_clear_object (&priv->async_init_res); + + return; + } + + tp_proxy_prepare_async (priv->certificate, features, + tls_certificate_prepared_cb, self); +} + +static void +async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = tls_handler_init_async; + iface->init_finish = tls_handler_init_finish; +} + +static void +empathy_server_tls_handler_finalize (GObject *object) +{ + EmpathyServerTLSHandlerPriv *priv = GET_PRIV (object); + + DEBUG ("%p", object); + + tp_clear_object (&priv->channel); + tp_clear_object (&priv->certificate); + g_strfreev (priv->reference_identities); + g_free (priv->hostname); + + G_OBJECT_CLASS (empathy_server_tls_handler_parent_class)->finalize (object); +} + +static void +empathy_server_tls_handler_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyServerTLSHandlerPriv *priv = GET_PRIV (object); + + switch (property_id) + { + case PROP_CHANNEL: + g_value_set_object (value, priv->channel); + break; + case PROP_TLS_CERTIFICATE: + g_value_set_object (value, priv->certificate); + break; + case PROP_HOSTNAME: + g_value_set_string (value, priv->hostname); + break; + case PROP_REFERENCE_IDENTITIES: + g_value_set_boxed (value, priv->reference_identities); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_server_tls_handler_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyServerTLSHandlerPriv *priv = GET_PRIV (object); + + switch (property_id) + { + case PROP_CHANNEL: + priv->channel = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_server_tls_handler_class_init (EmpathyServerTLSHandlerClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + oclass->get_property = empathy_server_tls_handler_get_property; + oclass->set_property = empathy_server_tls_handler_set_property; + oclass->finalize = empathy_server_tls_handler_finalize; + + g_type_class_add_private (klass, sizeof (EmpathyServerTLSHandlerPriv)); + + 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 ("certificate", "The TpTLSCertificate", + "The TpTLSCertificate carried by the channel.", + TP_TYPE_TLS_CERTIFICATE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_TLS_CERTIFICATE, pspec); + + pspec = g_param_spec_string ("hostname", "The hostname", + "The hostname the user is expecting to connect to.", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_HOSTNAME, pspec); + + pspec = g_param_spec_boxed ("reference-identities", "Reference Identities", + "The server certificate should certify one of these identities", + G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_REFERENCE_IDENTITIES, pspec); + +} + +static void +empathy_server_tls_handler_init (EmpathyServerTLSHandler *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_SERVER_TLS_HANDLER, EmpathyServerTLSHandlerPriv); +} + +void +empathy_server_tls_handler_new_async (TpChannel *channel, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_assert (TP_IS_CHANNEL (channel)); + g_assert (channel != NULL); + + g_async_initable_new_async (EMPATHY_TYPE_SERVER_TLS_HANDLER, + G_PRIORITY_DEFAULT, NULL, callback, user_data, + "channel", channel, NULL); +} + +EmpathyServerTLSHandler * +empathy_server_tls_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_TLS_HANDLER (object); + else + return NULL; +} + +TpTLSCertificate * +empathy_server_tls_handler_get_certificate (EmpathyServerTLSHandler *self) +{ + EmpathyServerTLSHandlerPriv *priv = GET_PRIV (self); + + g_assert (priv->certificate != NULL); + + return priv->certificate; +} diff --git a/empathy-server-tls-handler.h b/empathy-server-tls-handler.h new file mode 100644 index 0000000..f4caf35 --- /dev/null +++ b/empathy-server-tls-handler.h @@ -0,0 +1,71 @@ +/* + * empathy-server-tls-handler.h - Header for EmpathyServerTLSHandler + * Copyright (C) 2010 Collabora Ltd. + * @author Cosimo Cecchi + * + * 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_TLS_HANDLER_H__ +#define __EMPATHY_SERVER_TLS_HANDLER_H__ + +#include +#include + +#include + +G_BEGIN_DECLS + +typedef struct _EmpathyServerTLSHandler EmpathyServerTLSHandler; +typedef struct _EmpathyServerTLSHandlerClass EmpathyServerTLSHandlerClass; + +struct _EmpathyServerTLSHandlerClass { + GObjectClass parent_class; +}; + +struct _EmpathyServerTLSHandler { + GObject parent; + gpointer priv; +}; + +GType empathy_server_tls_handler_get_type (void); + +#define EMPATHY_TYPE_SERVER_TLS_HANDLER \ + (empathy_server_tls_handler_get_type ()) +#define EMPATHY_SERVER_TLS_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_SERVER_TLS_HANDLER, \ + EmpathyServerTLSHandler)) +#define EMPATHY_SERVER_TLS_HANDLER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_SERVER_TLS_HANDLER, \ + EmpathyServerTLSHandlerClass)) +#define EMPATHY_IS_SERVER_TLS_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_SERVER_TLS_HANDLER)) +#define EMPATHY_IS_SERVER_TLS_HANDLER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_SERVER_TLS_HANDLER)) +#define EMPATHY_SERVER_TLS_HANDLER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_SERVER_TLS_HANDLER, \ + EmpathyServerTLSHandlerClass)) + +void empathy_server_tls_handler_new_async (TpChannel *channel, + GAsyncReadyCallback callback, gpointer user_data); +EmpathyServerTLSHandler * empathy_server_tls_handler_new_finish ( + GAsyncResult *result, GError **error); + +TpTLSCertificate * empathy_server_tls_handler_get_certificate ( + EmpathyServerTLSHandler *self); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_SERVER_TLS_HANDLER_H__*/ diff --git a/empathy-uoa-utils.c b/empathy-uoa-utils.c new file mode 100644 index 0000000..b867fc9 --- /dev/null +++ b/empathy-uoa-utils.c @@ -0,0 +1,46 @@ +/* + * empathy-uoa-utils.c - Source for UOA utilities + * Copyright (C) 2012 Collabora Ltd. + * @author Xavier Claessens + * + * 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-debug.h" +#include "empathy-uoa-utils.h" + +static AgManager *singleton = NULL; + +void +empathy_uoa_manager_set_default (AgManager *manager) +{ + if (singleton != NULL) + return; + + singleton = manager; + g_object_add_weak_pointer ((GObject *) singleton, (gpointer) &singleton); +} + +AgManager * +empathy_uoa_manager_dup (void) +{ + if (singleton != NULL) + return g_object_ref (singleton); + + singleton = ag_manager_new_for_service_type (EMPATHY_UOA_SERVICE_TYPE); + g_object_add_weak_pointer ((GObject *) singleton, (gpointer) &singleton); + + return singleton; +} diff --git a/empathy-uoa-utils.h b/empathy-uoa-utils.h new file mode 100644 index 0000000..2cd7a41 --- /dev/null +++ b/empathy-uoa-utils.h @@ -0,0 +1,35 @@ +/* + * empathy-utils.h - Header for UOA utilities + * Copyright (C) 2012 Collabora Ltd. + * @author Xavier Claessens + * + * 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_UOA_UTILS_H__ +#define __EMPATHY_UOA_UTILS_H__ + +#include + +#define EMPATHY_UOA_SERVICE_TYPE "IM" + +G_BEGIN_DECLS + +void empathy_uoa_manager_set_default (AgManager *manager); +AgManager *empathy_uoa_manager_dup (void); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_UOA_UTILS_H__*/ diff --git a/empathy-utils.h b/empathy-utils.h new file mode 100644 index 0000000..749c89a --- /dev/null +++ b/empathy-utils.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2003-2007 Imendio AB + * Copyright (C) 2007-2011 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * Authors: Richard Hult + * Martyn Russell + * Xavier Claessens + */ + +#ifndef __EMPATHY_UTILS_H__ +#define __EMPATHY_UTILS_H__ + +#include +#include +#include + +#define EMPATHY_GET_PRIV(obj,type) ((type##Priv *) ((type *) obj)->priv) +#define EMP_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0') + +/* Copied from wocky/wocky-utils.h */ + +#define empathy_implement_finish_void(source, tag) \ + if (g_simple_async_result_propagate_error (\ + G_SIMPLE_ASYNC_RESULT (result), error)) \ + return FALSE; \ + g_return_val_if_fail (g_simple_async_result_is_valid (result, \ + G_OBJECT(source), tag), \ + FALSE); \ + return TRUE; + +#define empathy_implement_finish_copy_pointer(source, tag, copy_func, \ + out_param) \ + GSimpleAsyncResult *_simple; \ + _simple = (GSimpleAsyncResult *) 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 (source), tag), \ + FALSE); \ + if (out_param != NULL) \ + *out_param = copy_func ( \ + g_simple_async_result_get_op_res_gpointer (_simple)); \ + return TRUE; + +#define empathy_implement_finish_return_copy_pointer(source, tag, copy_func) \ + GSimpleAsyncResult *_simple; \ + _simple = (GSimpleAsyncResult *) 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 (source), tag), \ + NULL); \ + return copy_func (g_simple_async_result_get_op_res_gpointer (_simple)); + +#define empathy_implement_finish_return_pointer(source, tag) \ + GSimpleAsyncResult *_simple; \ + _simple = (GSimpleAsyncResult *) 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 (source), tag), \ + NULL); \ + return g_simple_async_result_get_op_res_gpointer (_simple); + +#endif /* __EMPATHY_UTILS_H__ */ diff --git a/telepathy-sasl-client b/telepathy-sasl-client new file mode 100755 index 0000000..d91451e Binary files /dev/null and b/telepathy-sasl-client differ diff --git a/telepathy-sasl-client.pro b/telepathy-sasl-client.pro new file mode 100644 index 0000000..af7e2c4 --- /dev/null +++ b/telepathy-sasl-client.pro @@ -0,0 +1,13 @@ +TEMPLATE = app + +CONFIG += link_pkgconfig +PKGCONFIG += libsignon-glib telepathy-glib libaccounts-glib libsoup-2.4 + +SOURCES += empathy-auth-client.c \ + empathy-auth-factory.c \ + empathy-server-sasl-handler.c \ + empathy-server-tls-handler.c \ + empathy-keyring.c \ + empathy-uoa-utils.c \ + empathy-sasl-mechanisms.c +