2 * empathy-auth-factory.c - Source for EmpathyAuthFactory
3 * Copyright (C) 2010 Collabora Ltd.
4 * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "empathy-auth-factory.h"
23 #include <telepathy-glib/telepathy-glib.h>
25 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
26 #include "empathy-debug.h"
27 #include "empathy-keyring.h"
28 #include "empathy-server-sasl-handler.h"
29 #include "empathy-server-tls-handler.h"
30 #include "empathy-utils.h"
32 #include "extensions/extensions.h"
34 G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, TP_TYPE_BASE_CLIENT);
36 struct _EmpathyAuthFactoryPriv {
37 /* Keep a ref here so the auth client doesn't have to mess with
38 * refs. It will be cleared when the channel (and so the handler)
41 * The channel path of the handler's channel (borrowed gchar *) ->
42 * reffed (EmpathyServerSASLHandler *)
44 GHashTable *sasl_handlers;
50 NEW_SERVER_TLS_HANDLER,
51 NEW_SERVER_SASL_HANDLER,
55 static guint signals[LAST_SIGNAL] = { 0, };
57 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAuthFactory)
59 static EmpathyAuthFactory *auth_factory_singleton = NULL;
62 TpHandleChannelsContext *context;
63 EmpathyAuthFactory *self;
67 handler_context_data_free (HandlerContextData *data)
69 tp_clear_object (&data->self);
70 tp_clear_object (&data->context);
72 g_slice_free (HandlerContextData, data);
75 static HandlerContextData *
76 handler_context_data_new (EmpathyAuthFactory *self,
77 TpHandleChannelsContext *context)
79 HandlerContextData *data;
81 data = g_slice_new0 (HandlerContextData);
82 data->self = g_object_ref (self);
85 data->context = g_object_ref (context);
91 server_tls_handler_ready_cb (GObject *source,
95 EmpathyServerTLSHandler *handler;
97 HandlerContextData *data = user_data;
99 handler = empathy_server_tls_handler_new_finish (res, &error);
103 DEBUG ("Failed to create a server TLS handler; error %s",
105 tp_handle_channels_context_fail (data->context, error);
107 g_error_free (error);
111 tp_handle_channels_context_accept (data->context);
112 g_signal_emit (data->self, signals[NEW_SERVER_TLS_HANDLER], 0,
115 g_object_unref (handler);
118 handler_context_data_free (data);
122 sasl_handler_invalidated_cb (EmpathyServerSASLHandler *handler,
125 EmpathyAuthFactory *self = user_data;
126 EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
129 channel = empathy_server_sasl_handler_get_channel (handler);
130 g_assert (channel != NULL);
132 DEBUG ("SASL handler for channel %s is invalidated, unref it",
133 tp_proxy_get_object_path (channel));
135 g_hash_table_remove (priv->sasl_handlers, tp_proxy_get_object_path (channel));
139 server_sasl_handler_ready_cb (GObject *source,
143 EmpathyAuthFactoryPriv *priv;
144 GError *error = NULL;
145 HandlerContextData *data = user_data;
146 EmpathyServerSASLHandler *handler;
148 priv = GET_PRIV (data->self);
149 handler = empathy_server_sasl_handler_new_finish (res, &error);
153 DEBUG ("Failed to create a server SASL handler; error %s",
156 if (data->context != NULL)
157 tp_handle_channels_context_fail (data->context, error);
159 g_error_free (error);
165 if (data->context != NULL)
166 tp_handle_channels_context_accept (data->context);
168 channel = empathy_server_sasl_handler_get_channel (handler);
169 g_assert (channel != NULL);
171 /* Pass the ref to the hash table */
172 g_hash_table_insert (priv->sasl_handlers,
173 (gpointer) tp_proxy_get_object_path (channel), handler);
175 tp_g_signal_connect_object (handler, "invalidated",
176 G_CALLBACK (sasl_handler_invalidated_cb), data->self, 0);
178 g_signal_emit (data->self, signals[NEW_SERVER_SASL_HANDLER], 0,
182 handler_context_data_free (data);
186 common_checks (EmpathyAuthFactory *self,
191 EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
194 const gchar * const *available_mechanisms;
195 const GError *dbus_error;
196 EmpathyServerSASLHandler *handler;
198 /* there can't be more than one ServerTLSConnection or
199 * ServerAuthentication channels at the same time, for the same
200 * connection/account.
202 if (g_list_length (channels) != 1)
204 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
205 "Can't %s more than one ServerTLSConnection or ServerAuthentication "
206 "channel for the same connection.", observe ? "observe" : "handle");
211 channel = channels->data;
213 if (tp_channel_get_channel_type_id (channel) !=
214 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
216 /* If we are observing we care only about ServerAuthentication channels.
217 * If we are handling we care about ServerAuthentication and
218 * ServerTLSConnection channels. */
220 || tp_channel_get_channel_type_id (channel) !=
221 EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
223 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
224 "Can only %s ServerTLSConnection or ServerAuthentication channels, "
225 "this was a %s channel", observe ? "observe" : "handle",
226 tp_channel_get_channel_type (channel));
232 handler = g_hash_table_lookup (priv->sasl_handlers,
233 tp_proxy_get_object_path (channel));
235 if (tp_channel_get_channel_type_id (channel) ==
236 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
237 && handler != NULL &&
240 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
241 "We are already handling this channel: %s",
242 tp_proxy_get_object_path (channel));
247 props = tp_channel_borrow_immutable_properties (channel);
248 available_mechanisms = tp_asv_get_boxed (props,
249 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS,
252 if (tp_channel_get_channel_type_id (channel) ==
253 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
254 && !tp_strv_contains (available_mechanisms, "X-TELEPATHY-PASSWORD"))
256 g_set_error_literal (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
257 "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported");
262 dbus_error = tp_proxy_get_invalidated (channel);
264 if (dbus_error != NULL)
266 *error = g_error_copy (dbus_error);
274 handle_channels (TpBaseClient *handler,
276 TpConnection *connection,
278 GList *requests_satisfied,
279 gint64 user_action_time,
280 TpHandleChannelsContext *context)
283 GError *error = NULL;
284 EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (handler);
285 HandlerContextData *data;
287 DEBUG ("Handle TLS or SASL carrier channels.");
289 if (!common_checks (self, channels, FALSE, &error))
291 DEBUG ("Failed checks: %s", error->message);
292 tp_handle_channels_context_fail (context, error);
293 g_clear_error (&error);
297 /* The common checks above have checked this is fine. */
298 channel = channels->data;
300 data = handler_context_data_new (self, context);
301 tp_handle_channels_context_delay (context);
303 /* create a handler */
304 if (tp_channel_get_channel_type_id (channel) ==
305 EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
307 empathy_server_tls_handler_new_async (channel, server_tls_handler_ready_cb,
310 else if (tp_channel_get_channel_type_id (channel) ==
311 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
313 empathy_server_sasl_handler_new_async (account, channel,
314 server_sasl_handler_ready_cb, data);
320 EmpathyAuthFactory *self;
321 TpObserveChannelsContext *context;
322 TpChannelDispatchOperation *dispatch_operation;
325 } ObserveChannelsData;
328 observe_channels_data_free (ObserveChannelsData *data)
330 g_object_unref (data->context);
331 g_object_unref (data->account);
332 g_object_unref (data->channel);
333 g_object_unref (data->dispatch_operation);
334 g_slice_free (ObserveChannelsData, data);
338 claim_cb (GObject *source,
339 GAsyncResult *result,
342 ObserveChannelsData *data = user_data;
343 GError *error = NULL;
345 if (!tp_channel_dispatch_operation_claim_with_finish (
346 TP_CHANNEL_DISPATCH_OPERATION (source), result, &error))
348 DEBUG ("Failed to call Claim: %s", error->message);
349 g_clear_error (&error);
353 HandlerContextData *h_data;
355 DEBUG ("Claim called successfully");
357 h_data = handler_context_data_new (data->self, NULL);
359 empathy_server_sasl_handler_new_async (TP_ACCOUNT (data->account),
360 data->channel, server_sasl_handler_ready_cb, h_data);
363 observe_channels_data_free (data);
367 get_password_cb (GObject *source,
368 GAsyncResult *result,
371 ObserveChannelsData *data = user_data;
373 if (empathy_keyring_get_account_password_finish (TP_ACCOUNT (source), result, NULL) == NULL)
375 /* We don't actually mind if this fails, just let the approver
376 * go ahead and take the channel. */
378 DEBUG ("We don't have a password for account %s, letting the event "
379 "manager approver take it", tp_proxy_get_object_path (source));
381 tp_observe_channels_context_accept (data->context);
382 observe_channels_data_free (data);
386 DEBUG ("We have a password for account %s, calling Claim",
387 tp_proxy_get_object_path (source));
389 tp_channel_dispatch_operation_claim_with_async (data->dispatch_operation,
390 TP_BASE_CLIENT (data->self), claim_cb, data);
392 tp_observe_channels_context_accept (data->context);
397 observe_channels (TpBaseClient *client,
399 TpConnection *connection,
401 TpChannelDispatchOperation *dispatch_operation,
403 TpObserveChannelsContext *context)
405 EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (client);
407 GError *error = NULL;
408 ObserveChannelsData *data;
410 DEBUG ("New auth channel to observe");
412 if (!common_checks (self, channels, TRUE, &error))
414 DEBUG ("Failed checks: %s", error->message);
415 tp_observe_channels_context_fail (context, error);
416 g_clear_error (&error);
420 /* We're now sure this is a server auth channel using the SASL auth
421 * type and X-TELEPATHY-PASSWORD is available. Great. */
423 channel = channels->data;
425 data = g_slice_new0 (ObserveChannelsData);
427 data->context = g_object_ref (context);
428 data->dispatch_operation = g_object_ref (dispatch_operation);
429 data->account = g_object_ref (account);
430 data->channel = g_object_ref (channel);
432 empathy_keyring_get_account_password_async (account, get_password_cb, data);
434 tp_observe_channels_context_delay (context);
438 empathy_auth_factory_constructor (GType type,
440 GObjectConstructParam *params)
444 if (auth_factory_singleton != NULL)
446 retval = g_object_ref (auth_factory_singleton);
450 retval = G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructor
451 (type, n_params, params);
453 auth_factory_singleton = EMPATHY_AUTH_FACTORY (retval);
454 g_object_add_weak_pointer (retval, (gpointer *) &auth_factory_singleton);
461 empathy_auth_factory_init (EmpathyAuthFactory *self)
463 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
464 EMPATHY_TYPE_AUTH_FACTORY, EmpathyAuthFactoryPriv);
466 self->priv->sasl_handlers = g_hash_table_new_full (g_str_hash, g_str_equal,
467 NULL, g_object_unref);
471 empathy_auth_factory_constructed (GObject *obj)
473 EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (obj);
474 TpBaseClient *client = TP_BASE_CLIENT (self);
476 /* chain up to TpBaseClient first */
477 G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructed (obj);
479 tp_base_client_set_handler_bypass_approval (client, FALSE);
481 /* Handle ServerTLSConnection and ServerAuthentication channels */
482 tp_base_client_take_handler_filter (client, tp_asv_new (
484 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
485 EMP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION,
486 /* AuthenticationMethod */
487 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
488 TP_HANDLE_TYPE_NONE, NULL));
490 tp_base_client_take_handler_filter (client, tp_asv_new (
492 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
493 TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
494 /* AuthenticationMethod */
495 TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
496 G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
499 /* We are also an observer so that we can see new auth channels
500 * popping up and if we have the password already saved to one
501 * account where an auth channel has just appeared we can call
502 * Claim() on the CDO so the approver won't get it, which makes
505 /* Observe ServerAuthentication channels */
506 tp_base_client_take_observer_filter (client, tp_asv_new (
508 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
509 TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
510 /* AuthenticationMethod */
511 TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
512 G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
515 tp_base_client_set_observer_delay_approvers (client, TRUE);
519 empathy_auth_factory_dispose (GObject *object)
521 EmpathyAuthFactoryPriv *priv = GET_PRIV (object);
523 if (priv->dispose_run)
526 priv->dispose_run = TRUE;
528 g_hash_table_unref (priv->sasl_handlers);
530 G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object);
534 empathy_auth_factory_class_init (EmpathyAuthFactoryClass *klass)
536 GObjectClass *oclass = G_OBJECT_CLASS (klass);
537 TpBaseClientClass *base_client_cls = TP_BASE_CLIENT_CLASS (klass);
539 oclass->constructor = empathy_auth_factory_constructor;
540 oclass->constructed = empathy_auth_factory_constructed;
541 oclass->dispose = empathy_auth_factory_dispose;
543 base_client_cls->handle_channels = handle_channels;
544 base_client_cls->observe_channels = observe_channels;
546 g_type_class_add_private (klass, sizeof (EmpathyAuthFactoryPriv));
548 signals[NEW_SERVER_TLS_HANDLER] =
549 g_signal_new ("new-server-tls-handler",
550 G_TYPE_FROM_CLASS (klass),
551 G_SIGNAL_RUN_LAST, 0,
553 g_cclosure_marshal_generic,
555 1, EMPATHY_TYPE_SERVER_TLS_HANDLER);
557 signals[NEW_SERVER_SASL_HANDLER] =
558 g_signal_new ("new-server-sasl-handler",
559 G_TYPE_FROM_CLASS (klass),
560 G_SIGNAL_RUN_LAST, 0,
562 g_cclosure_marshal_generic,
564 1, EMPATHY_TYPE_SERVER_SASL_HANDLER);
568 empathy_auth_factory_dup_singleton (void)
570 EmpathyAuthFactory *out = NULL;
573 bus = tp_dbus_daemon_dup (NULL);
574 out = g_object_new (EMPATHY_TYPE_AUTH_FACTORY,
576 "name", "Empathy.Auth",
578 g_object_unref (bus);
584 empathy_auth_factory_register (EmpathyAuthFactory *self,
587 return tp_base_client_register (TP_BASE_CLIENT (self), error);