* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "empathy-auth-factory.h"
+#include <config.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/simple-handler.h>
-#include <telepathy-glib/util.h>
+#include "empathy-auth-factory.h"
#define DEBUG_FLAG EMPATHY_DEBUG_TLS
#include "empathy-debug.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"
+#ifdef HAVE_GOA
+#include "empathy-goa-auth-handler.h"
+#endif /* HAVE_GOA */
+
+#ifdef HAVE_UOA
+#include "empathy-uoa-auth-handler.h"
+#endif /* HAVE_UOA */
+
#include "extensions/extensions.h"
G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, TP_TYPE_BASE_CLIENT);
-typedef struct {
+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. */
- EmpathyServerSASLHandler *sasl_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;
-} EmpathyAuthFactoryPriv;
+};
enum {
NEW_SERVER_TLS_HANDLER,
NEW_SERVER_SASL_HANDLER,
+ AUTH_PASSWORD_FAILED,
LAST_SIGNAL,
};
{
EmpathyServerTLSHandler *handler;
GError *error = NULL;
- EmpathyAuthFactoryPriv *priv;
HandlerContextData *data = user_data;
- priv = GET_PRIV (data->self);
handler = empathy_server_tls_handler_new_finish (res, &error);
if (error != NULL)
{
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;
- DEBUG ("SASL handler is invalidated, unref it");
+ account = empathy_server_sasl_handler_get_account (handler);
- tp_clear_object (&priv->sasl_handler);
+ g_signal_emit (self, signals[AUTH_PASSWORD_FAILED], 0, account, password);
}
static void
EmpathyAuthFactoryPriv *priv;
GError *error = NULL;
HandlerContextData *data = user_data;
+ EmpathyServerSASLHandler *handler;
priv = GET_PRIV (data->self);
- priv->sasl_handler = empathy_server_sasl_handler_new_finish (res, &error);
+ handler = empathy_server_sasl_handler_new_finish (res, &error);
if (error != NULL)
{
}
else
{
+ TpChannel *channel;
+ const gchar *password;
+ TpAccount *account;
+
if (data->context != NULL)
tp_handle_channels_context_accept (data->context);
- g_signal_connect (priv->sasl_handler, "invalidated",
- G_CALLBACK (sasl_handler_invalidated_cb), data->self);
+ 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,
- priv->sasl_handler);
+ handler);
}
handler_context_data_free (data);
static gboolean
common_checks (EmpathyAuthFactory *self,
GList *channels,
- gboolean must_be_sasl,
+ gboolean observe,
GError **error)
{
EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
TpChannel *channel;
- GHashTable *props;
- const gchar * const *available_mechanisms;
const GError *dbus_error;
+ EmpathyServerSASLHandler *handler;
/* there can't be more than one ServerTLSConnection or
* ServerAuthentication channels at the same time, for the same
*/
if (g_list_length (channels) != 1)
{
- g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
- "Can't handle more than one ServerTLSConnection or ServerAuthentication "
- "channel for the same connection.");
+ 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;
}
if (tp_channel_get_channel_type_id (channel) !=
TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
{
- if (must_be_sasl
+ /* 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) !=
EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
{
g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
- "Can only handle ServerTLSConnection or ServerAuthentication channels, "
- "this was a %s channel", tp_channel_get_channel_type (channel));
+ "Can only %s ServerTLSConnection or ServerAuthentication channels, "
+ "this was a %s channel", observe ? "observe" : "handle",
+ tp_channel_get_channel_type (channel));
return FALSE;
}
}
- if (tp_channel_get_channel_type_id (channel) ==
- TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
- && priv->sasl_handler != NULL)
- {
- g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
- "Can't handle more than one ServerAuthentication channel at one time");
-
- return FALSE;
- }
-
- props = tp_channel_borrow_immutable_properties (channel);
- available_mechanisms = tp_asv_get_boxed (props,
- TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS,
- G_TYPE_STRV);
+ 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
- && !tp_strv_contains (available_mechanisms, "X-TELEPATHY-PASSWORD"))
+ && handler != NULL &&
+ !observe)
{
- g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
- "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported");
+ 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);
/* 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);
}
static void
-claim_cb (GObject *source,
+password_claim_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
ObserveChannelsData *data = user_data;
GError *error = NULL;
- if (!tp_channel_dispatch_operation_claim_finish (
+ if (!tp_channel_dispatch_operation_claim_with_finish (
TP_CHANNEL_DISPATCH_OPERATION (source), result, &error))
{
DEBUG ("Failed to call Claim: %s", error->message);
DEBUG ("We have a password for account %s, calling Claim",
tp_proxy_get_object_path (source));
- tp_channel_dispatch_operation_claim_async (data->dispatch_operation,
- claim_cb, data);
+ 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,
return;
}
- /* We're now sure this is a server auth channel using the SASL auth
- * type and X-TELEPATHY-PASSWORD is available. Great. */
-
+ /* The common checks above have checked this is fine. */
channel = channels->data;
data = g_slice_new0 (ObserveChannelsData);
data->account = g_object_ref (account);
data->channel = g_object_ref (channel);
- empathy_keyring_get_account_password_async (account, get_password_cb, data);
+#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;
+ }
- tp_observe_channels_context_delay (context);
+ /* 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 *
{
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
{
EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (obj);
TpBaseClient *client = TP_BASE_CLIENT (self);
- GError *error = NULL;
/* chain up to TpBaseClient first */
G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructed (obj);
- if (error != NULL)
- {
- g_critical ("Failed to get TpDBusDaemon: %s", error->message);
- g_error_free (error);
- return;
- }
-
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,
* 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_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
priv->dispose_run = TRUE;
- tp_clear_object (&priv->sasl_handler);
+ 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);
}
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0,
NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
+ g_cclosure_marshal_generic,
G_TYPE_NONE,
1, EMPATHY_TYPE_SERVER_TLS_HANDLER);
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0,
NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
+ 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_dup_singleton (void)
+empathy_auth_factory_new (TpSimpleClientFactory *factory)
{
- EmpathyAuthFactory *out = NULL;
- TpDBusDaemon *bus;
-
- bus = tp_dbus_daemon_dup (NULL);
- out = g_object_new (EMPATHY_TYPE_AUTH_FACTORY,
- "dbus-daemon", bus,
+ return g_object_new (EMPATHY_TYPE_AUTH_FACTORY,
+ "factory", factory,
"name", "Empathy.Auth",
NULL);
- g_object_unref (bus);
-
- return out;
}
gboolean
{
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));
+}