diff options
Diffstat (limited to 'libempathy/empathy-auth-factory.c')
-rw-r--r-- | libempathy/empathy-auth-factory.c | 280 |
1 files changed, 226 insertions, 54 deletions
diff --git a/libempathy/empathy-auth-factory.c b/libempathy/empathy-auth-factory.c index 578b6d6cd..443962a7c 100644 --- a/libempathy/empathy-auth-factory.c +++ b/libempathy/empathy-auth-factory.c @@ -26,17 +26,16 @@ #define DEBUG_FLAG EMPATHY_DEBUG_TLS #include "empathy-debug.h" +#include "empathy-keyring.h" #include "empathy-server-sasl-handler.h" #include "empathy-server-tls-handler.h" #include "empathy-utils.h" #include "extensions/extensions.h" -G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, G_TYPE_OBJECT); +G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, TP_TYPE_BASE_CLIENT); typedef struct { - TpBaseClient *handler; - /* Keep a ref here so the auth client doesn't have to mess with * refs. It will be cleared when the channel (and so the handler) * gets invalidated. */ @@ -79,7 +78,9 @@ handler_context_data_new (EmpathyAuthFactory *self, data = g_slice_new0 (HandlerContextData); data->self = g_object_ref (self); - data->context = g_object_ref (context); + + if (context != NULL) + data->context = g_object_ref (context); return data; } @@ -145,13 +146,16 @@ server_sasl_handler_ready_cb (GObject *source, { DEBUG ("Failed to create a server SASL handler; error %s", error->message); - tp_handle_channels_context_fail (data->context, error); + + if (data->context != NULL) + tp_handle_channels_context_fail (data->context, error); g_error_free (error); } else { - tp_handle_channels_context_accept (data->context); + 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); @@ -163,26 +167,17 @@ server_sasl_handler_ready_cb (GObject *source, handler_context_data_free (data); } -static void -handle_channels_cb (TpSimpleHandler *handler, - TpAccount *account, - TpConnection *connection, +static gboolean +common_checks (EmpathyAuthFactory *self, GList *channels, - GList *requests_satisfied, - gint64 user_action_time, - TpHandleChannelsContext *context, - gpointer user_data) + gboolean must_be_sasl, + GError **error) { - TpChannel *channel; - const GError *dbus_error; - GError *error = NULL; - EmpathyAuthFactory *self = user_data; EmpathyAuthFactoryPriv *priv = GET_PRIV (self); - HandlerContextData *data; + TpChannel *channel; GHashTable *props; const gchar * const *available_mechanisms; - - DEBUG ("Handle TLS or SASL carrier channels."); + const GError *dbus_error; /* there can't be more than one ServerTLSConnection or * ServerAuthentication channels at the same time, for the same @@ -190,35 +185,38 @@ handle_channels_cb (TpSimpleHandler *handler, */ if (g_list_length (channels) != 1) { - g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + 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."); - goto error; + return FALSE; } channel = channels->data; if (tp_channel_get_channel_type_id (channel) != - EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION - && tp_channel_get_channel_type_id (channel) != TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION) { - g_set_error (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Can only handle ServerTLSConnection or ServerAuthentication channels, " - "this was a %s channel", tp_channel_get_channel_type (channel)); - - goto error; + if (must_be_sasl + || 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)); + + 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, + g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Can't handle more than one ServerAuthentication channel at one time"); - goto error; + return FALSE; } props = tp_channel_borrow_immutable_properties (channel); @@ -230,20 +228,50 @@ handle_channels_cb (TpSimpleHandler *handler, TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION && !tp_strv_contains (available_mechanisms, "X-TELEPATHY-PASSWORD")) { - g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported"); - goto error; + return FALSE; } dbus_error = tp_proxy_get_invalidated (channel); if (dbus_error != NULL) { - error = g_error_copy (dbus_error); - goto error; + *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; + data = handler_context_data_new (self, context); tp_handle_channels_context_delay (context); @@ -260,11 +288,125 @@ handle_channels_cb (TpSimpleHandler *handler, empathy_server_sasl_handler_new_async (account, channel, server_sasl_handler_ready_cb, data); } - return; +} - error: - tp_handle_channels_context_fail (context, error); - g_clear_error (&error); +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 +claim_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + ObserveChannelsData *data = user_data; + GError *error = NULL; + + if (!tp_channel_dispatch_operation_claim_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_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_async (data->dispatch_operation, + claim_cb, data); + + tp_observe_channels_context_accept (data->context); + } +} + +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; + } + + /* We're now sure this is a server auth channel using the SASL auth + * type and X-TELEPATHY-PASSWORD is available. Great. */ + + 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); + + empathy_keyring_get_password_async (account, get_password_cb, data); + + tp_observe_channels_context_delay (context); } static GObject * @@ -293,14 +435,20 @@ empathy_auth_factory_constructor (GType type, static void empathy_auth_factory_init (EmpathyAuthFactory *self) { - EmpathyAuthFactoryPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_AUTH_FACTORY, EmpathyAuthFactoryPriv); - TpDBusDaemon *bus; +} + +static void +empathy_auth_factory_constructed (GObject *obj) +{ + EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (obj); + TpBaseClient *client = TP_BASE_CLIENT (self); GError *error = NULL; - self->priv = priv; + /* chain up to TpBaseClient first */ + G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructed (obj); - bus = tp_dbus_daemon_dup (&error); if (error != NULL) { g_critical ("Failed to get TpDBusDaemon: %s", error->message); @@ -308,10 +456,9 @@ empathy_auth_factory_init (EmpathyAuthFactory *self) return; } - priv->handler = tp_simple_handler_new (bus, FALSE, FALSE, "Empathy.Auth", - FALSE, handle_channels_cb, self, NULL); + tp_base_client_set_handler_bypass_approval (client, FALSE); - tp_base_client_take_handler_filter (priv->handler, tp_asv_new ( + tp_base_client_take_handler_filter (client, tp_asv_new ( /* ChannelType */ TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, EMP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION, @@ -319,7 +466,7 @@ empathy_auth_factory_init (EmpathyAuthFactory *self) TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_NONE, NULL)); - tp_base_client_take_handler_filter (priv->handler, tp_asv_new ( + 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, @@ -328,7 +475,20 @@ empathy_auth_factory_init (EmpathyAuthFactory *self) G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION, NULL)); - g_object_unref (bus); + /* 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. */ + + 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)); } static void @@ -341,7 +501,6 @@ empathy_auth_factory_dispose (GObject *object) priv->dispose_run = TRUE; - tp_clear_object (&priv->handler); tp_clear_object (&priv->sasl_handler); G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object); @@ -351,10 +510,15 @@ 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] = @@ -379,14 +543,22 @@ empathy_auth_factory_class_init (EmpathyAuthFactoryClass *klass) EmpathyAuthFactory * empathy_auth_factory_dup_singleton (void) { - return g_object_new (EMPATHY_TYPE_AUTH_FACTORY, NULL); + EmpathyAuthFactory *out = NULL; + TpDBusDaemon *bus; + + bus = tp_dbus_daemon_dup (NULL); + out = g_object_new (EMPATHY_TYPE_AUTH_FACTORY, + "dbus-daemon", bus, + "name", "Empathy.Auth", + NULL); + g_object_unref (bus); + + return out; } gboolean empathy_auth_factory_register (EmpathyAuthFactory *self, GError **error) { - EmpathyAuthFactoryPriv *priv = GET_PRIV (self); - - return tp_base_client_register (priv->handler, error); + return tp_base_client_register (TP_BASE_CLIENT (self), error); } |