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/interfaces.h>
24 #include <telepathy-glib/simple-handler.h>
25 #include <telepathy-glib/util.h>
27 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
28 #include "empathy-debug.h"
29 #include "empathy-keyring.h"
30 #include "empathy-server-sasl-handler.h"
31 #include "empathy-server-tls-handler.h"
32 #include "empathy-utils.h"
34 #include "extensions/extensions.h"
36 G_DEFINE_TYPE (EmpathyAuthFactory
, empathy_auth_factory
, TP_TYPE_BASE_CLIENT
);
38 struct _EmpathyAuthFactoryPriv
{
39 /* Keep a ref here so the auth client doesn't have to mess with
40 * refs. It will be cleared when the channel (and so the handler)
43 * The channel path of the handler's channel (borrowed gchar *) ->
44 * reffed (EmpathyServerSASLHandler *)
46 GHashTable
*sasl_handlers
;
52 NEW_SERVER_TLS_HANDLER
,
53 NEW_SERVER_SASL_HANDLER
,
57 static guint signals
[LAST_SIGNAL
] = { 0, };
59 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAuthFactory)
61 static EmpathyAuthFactory
*auth_factory_singleton
= NULL
;
64 TpHandleChannelsContext
*context
;
65 EmpathyAuthFactory
*self
;
69 handler_context_data_free (HandlerContextData
*data
)
71 tp_clear_object (&data
->self
);
72 tp_clear_object (&data
->context
);
74 g_slice_free (HandlerContextData
, data
);
77 static HandlerContextData
*
78 handler_context_data_new (EmpathyAuthFactory
*self
,
79 TpHandleChannelsContext
*context
)
81 HandlerContextData
*data
;
83 data
= g_slice_new0 (HandlerContextData
);
84 data
->self
= g_object_ref (self
);
87 data
->context
= g_object_ref (context
);
93 server_tls_handler_ready_cb (GObject
*source
,
97 EmpathyServerTLSHandler
*handler
;
99 HandlerContextData
*data
= user_data
;
101 handler
= empathy_server_tls_handler_new_finish (res
, &error
);
105 DEBUG ("Failed to create a server TLS handler; error %s",
107 tp_handle_channels_context_fail (data
->context
, error
);
109 g_error_free (error
);
113 tp_handle_channels_context_accept (data
->context
);
114 g_signal_emit (data
->self
, signals
[NEW_SERVER_TLS_HANDLER
], 0,
117 g_object_unref (handler
);
120 handler_context_data_free (data
);
124 sasl_handler_invalidated_cb (EmpathyServerSASLHandler
*handler
,
127 EmpathyAuthFactory
*self
= user_data
;
128 EmpathyAuthFactoryPriv
*priv
= GET_PRIV (self
);
131 channel
= empathy_server_sasl_handler_get_channel (handler
);
132 g_assert (channel
!= NULL
);
134 DEBUG ("SASL handler for channel %s is invalidated, unref it",
135 tp_proxy_get_object_path (channel
));
137 g_hash_table_remove (priv
->sasl_handlers
, tp_proxy_get_object_path (channel
));
141 server_sasl_handler_ready_cb (GObject
*source
,
145 EmpathyAuthFactoryPriv
*priv
;
146 GError
*error
= NULL
;
147 HandlerContextData
*data
= user_data
;
148 EmpathyServerSASLHandler
*handler
;
150 priv
= GET_PRIV (data
->self
);
151 handler
= empathy_server_sasl_handler_new_finish (res
, &error
);
155 DEBUG ("Failed to create a server SASL handler; error %s",
158 if (data
->context
!= NULL
)
159 tp_handle_channels_context_fail (data
->context
, error
);
161 g_error_free (error
);
167 if (data
->context
!= NULL
)
168 tp_handle_channels_context_accept (data
->context
);
170 channel
= empathy_server_sasl_handler_get_channel (handler
);
171 g_assert (channel
!= NULL
);
173 /* Pass the ref to the hash table */
174 g_hash_table_insert (priv
->sasl_handlers
,
175 (gpointer
) tp_proxy_get_object_path (channel
), handler
);
177 tp_g_signal_connect_object (handler
, "invalidated",
178 G_CALLBACK (sasl_handler_invalidated_cb
), data
->self
, 0);
180 g_signal_emit (data
->self
, signals
[NEW_SERVER_SASL_HANDLER
], 0,
184 handler_context_data_free (data
);
188 common_checks (EmpathyAuthFactory
*self
,
193 EmpathyAuthFactoryPriv
*priv
= GET_PRIV (self
);
196 const gchar
* const *available_mechanisms
;
197 const GError
*dbus_error
;
198 EmpathyServerSASLHandler
*handler
;
200 /* there can't be more than one ServerTLSConnection or
201 * ServerAuthentication channels at the same time, for the same
202 * connection/account.
204 if (g_list_length (channels
) != 1)
206 g_set_error (error
, TP_ERROR
, TP_ERROR_INVALID_ARGUMENT
,
207 "Can't %s more than one ServerTLSConnection or ServerAuthentication "
208 "channel for the same connection.", observe
? "observe" : "handle");
213 channel
= channels
->data
;
215 if (tp_channel_get_channel_type_id (channel
) !=
216 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
)
218 /* If we are observing we care only about ServerAuthentication channels.
219 * If we are handling we care about ServerAuthentication and
220 * ServerTLSConnection channels. */
222 || tp_channel_get_channel_type_id (channel
) !=
223 EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION
)
225 g_set_error (error
, TP_ERROR
, TP_ERROR_INVALID_ARGUMENT
,
226 "Can only %s ServerTLSConnection or ServerAuthentication channels, "
227 "this was a %s channel", observe
? "observe" : "handle",
228 tp_channel_get_channel_type (channel
));
234 handler
= g_hash_table_lookup (priv
->sasl_handlers
,
235 tp_proxy_get_object_path (channel
));
237 if (tp_channel_get_channel_type_id (channel
) ==
238 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
239 && handler
!= NULL
&&
242 g_set_error (error
, TP_ERROR
, TP_ERROR_INVALID_ARGUMENT
,
243 "We are already handling this channel: %s",
244 tp_proxy_get_object_path (channel
));
249 props
= tp_channel_borrow_immutable_properties (channel
);
250 available_mechanisms
= tp_asv_get_boxed (props
,
251 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_AVAILABLE_MECHANISMS
,
254 if (tp_channel_get_channel_type_id (channel
) ==
255 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
256 && !tp_strv_contains (available_mechanisms
, "X-TELEPATHY-PASSWORD"))
258 g_set_error_literal (error
, TP_ERROR
, TP_ERROR_INVALID_ARGUMENT
,
259 "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported");
264 dbus_error
= tp_proxy_get_invalidated (channel
);
266 if (dbus_error
!= NULL
)
268 *error
= g_error_copy (dbus_error
);
276 handle_channels (TpBaseClient
*handler
,
278 TpConnection
*connection
,
280 GList
*requests_satisfied
,
281 gint64 user_action_time
,
282 TpHandleChannelsContext
*context
)
285 GError
*error
= NULL
;
286 EmpathyAuthFactory
*self
= EMPATHY_AUTH_FACTORY (handler
);
287 HandlerContextData
*data
;
289 DEBUG ("Handle TLS or SASL carrier channels.");
291 if (!common_checks (self
, channels
, FALSE
, &error
))
293 DEBUG ("Failed checks: %s", error
->message
);
294 tp_handle_channels_context_fail (context
, error
);
295 g_clear_error (&error
);
299 /* The common checks above have checked this is fine. */
300 channel
= channels
->data
;
302 data
= handler_context_data_new (self
, context
);
303 tp_handle_channels_context_delay (context
);
305 /* create a handler */
306 if (tp_channel_get_channel_type_id (channel
) ==
307 EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION
)
309 empathy_server_tls_handler_new_async (channel
, server_tls_handler_ready_cb
,
312 else if (tp_channel_get_channel_type_id (channel
) ==
313 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
)
315 empathy_server_sasl_handler_new_async (account
, channel
,
316 server_sasl_handler_ready_cb
, data
);
322 EmpathyAuthFactory
*self
;
323 TpObserveChannelsContext
*context
;
324 TpChannelDispatchOperation
*dispatch_operation
;
327 } ObserveChannelsData
;
330 observe_channels_data_free (ObserveChannelsData
*data
)
332 g_object_unref (data
->context
);
333 g_object_unref (data
->account
);
334 g_object_unref (data
->channel
);
335 g_object_unref (data
->dispatch_operation
);
336 g_slice_free (ObserveChannelsData
, data
);
340 claim_cb (GObject
*source
,
341 GAsyncResult
*result
,
344 ObserveChannelsData
*data
= user_data
;
345 GError
*error
= NULL
;
347 if (!tp_channel_dispatch_operation_claim_with_finish (
348 TP_CHANNEL_DISPATCH_OPERATION (source
), result
, &error
))
350 DEBUG ("Failed to call Claim: %s", error
->message
);
351 g_clear_error (&error
);
355 HandlerContextData
*h_data
;
357 DEBUG ("Claim called successfully");
359 h_data
= handler_context_data_new (data
->self
, NULL
);
361 empathy_server_sasl_handler_new_async (TP_ACCOUNT (data
->account
),
362 data
->channel
, server_sasl_handler_ready_cb
, h_data
);
365 observe_channels_data_free (data
);
369 get_password_cb (GObject
*source
,
370 GAsyncResult
*result
,
373 ObserveChannelsData
*data
= user_data
;
375 if (empathy_keyring_get_account_password_finish (TP_ACCOUNT (source
), result
, NULL
) == NULL
)
377 /* We don't actually mind if this fails, just let the approver
378 * go ahead and take the channel. */
380 DEBUG ("We don't have a password for account %s, letting the event "
381 "manager approver take it", tp_proxy_get_object_path (source
));
383 tp_observe_channels_context_accept (data
->context
);
384 observe_channels_data_free (data
);
388 DEBUG ("We have a password for account %s, calling Claim",
389 tp_proxy_get_object_path (source
));
391 tp_channel_dispatch_operation_claim_with_async (data
->dispatch_operation
,
392 TP_BASE_CLIENT (data
->self
), claim_cb
, data
);
394 tp_observe_channels_context_accept (data
->context
);
399 observe_channels (TpBaseClient
*client
,
401 TpConnection
*connection
,
403 TpChannelDispatchOperation
*dispatch_operation
,
405 TpObserveChannelsContext
*context
)
407 EmpathyAuthFactory
*self
= EMPATHY_AUTH_FACTORY (client
);
409 GError
*error
= NULL
;
410 ObserveChannelsData
*data
;
412 DEBUG ("New auth channel to observe");
414 if (!common_checks (self
, channels
, TRUE
, &error
))
416 DEBUG ("Failed checks: %s", error
->message
);
417 tp_observe_channels_context_fail (context
, error
);
418 g_clear_error (&error
);
422 /* We're now sure this is a server auth channel using the SASL auth
423 * type and X-TELEPATHY-PASSWORD is available. Great. */
425 channel
= channels
->data
;
427 data
= g_slice_new0 (ObserveChannelsData
);
429 data
->context
= g_object_ref (context
);
430 data
->dispatch_operation
= g_object_ref (dispatch_operation
);
431 data
->account
= g_object_ref (account
);
432 data
->channel
= g_object_ref (channel
);
434 empathy_keyring_get_account_password_async (account
, get_password_cb
, data
);
436 tp_observe_channels_context_delay (context
);
440 empathy_auth_factory_constructor (GType type
,
442 GObjectConstructParam
*params
)
446 if (auth_factory_singleton
!= NULL
)
448 retval
= g_object_ref (auth_factory_singleton
);
452 retval
= G_OBJECT_CLASS (empathy_auth_factory_parent_class
)->constructor
453 (type
, n_params
, params
);
455 auth_factory_singleton
= EMPATHY_AUTH_FACTORY (retval
);
456 g_object_add_weak_pointer (retval
, (gpointer
*) &auth_factory_singleton
);
463 empathy_auth_factory_init (EmpathyAuthFactory
*self
)
465 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
466 EMPATHY_TYPE_AUTH_FACTORY
, EmpathyAuthFactoryPriv
);
468 self
->priv
->sasl_handlers
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
469 NULL
, g_object_unref
);
473 empathy_auth_factory_constructed (GObject
*obj
)
475 EmpathyAuthFactory
*self
= EMPATHY_AUTH_FACTORY (obj
);
476 TpBaseClient
*client
= TP_BASE_CLIENT (self
);
478 /* chain up to TpBaseClient first */
479 G_OBJECT_CLASS (empathy_auth_factory_parent_class
)->constructed (obj
);
481 tp_base_client_set_handler_bypass_approval (client
, FALSE
);
483 /* Handle ServerTLSConnection and ServerAuthentication channels */
484 tp_base_client_take_handler_filter (client
, tp_asv_new (
486 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
487 EMP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION
,
488 /* AuthenticationMethod */
489 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE
, G_TYPE_UINT
,
490 TP_HANDLE_TYPE_NONE
, NULL
));
492 tp_base_client_take_handler_filter (client
, tp_asv_new (
494 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
495 TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION
,
496 /* AuthenticationMethod */
497 TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD
,
498 G_TYPE_STRING
, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION
,
501 /* We are also an observer so that we can see new auth channels
502 * popping up and if we have the password already saved to one
503 * account where an auth channel has just appeared we can call
504 * Claim() on the CDO so the approver won't get it, which makes
507 /* Observe ServerAuthentication channels */
508 tp_base_client_take_observer_filter (client
, tp_asv_new (
510 TP_PROP_CHANNEL_CHANNEL_TYPE
, G_TYPE_STRING
,
511 TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION
,
512 /* AuthenticationMethod */
513 TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD
,
514 G_TYPE_STRING
, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION
,
517 tp_base_client_set_observer_delay_approvers (client
, TRUE
);
521 empathy_auth_factory_dispose (GObject
*object
)
523 EmpathyAuthFactoryPriv
*priv
= GET_PRIV (object
);
525 if (priv
->dispose_run
)
528 priv
->dispose_run
= TRUE
;
530 g_hash_table_unref (priv
->sasl_handlers
);
532 G_OBJECT_CLASS (empathy_auth_factory_parent_class
)->dispose (object
);
536 empathy_auth_factory_class_init (EmpathyAuthFactoryClass
*klass
)
538 GObjectClass
*oclass
= G_OBJECT_CLASS (klass
);
539 TpBaseClientClass
*base_client_cls
= TP_BASE_CLIENT_CLASS (klass
);
541 oclass
->constructor
= empathy_auth_factory_constructor
;
542 oclass
->constructed
= empathy_auth_factory_constructed
;
543 oclass
->dispose
= empathy_auth_factory_dispose
;
545 base_client_cls
->handle_channels
= handle_channels
;
546 base_client_cls
->observe_channels
= observe_channels
;
548 g_type_class_add_private (klass
, sizeof (EmpathyAuthFactoryPriv
));
550 signals
[NEW_SERVER_TLS_HANDLER
] =
551 g_signal_new ("new-server-tls-handler",
552 G_TYPE_FROM_CLASS (klass
),
553 G_SIGNAL_RUN_LAST
, 0,
555 g_cclosure_marshal_VOID__OBJECT
,
557 1, EMPATHY_TYPE_SERVER_TLS_HANDLER
);
559 signals
[NEW_SERVER_SASL_HANDLER
] =
560 g_signal_new ("new-server-sasl-handler",
561 G_TYPE_FROM_CLASS (klass
),
562 G_SIGNAL_RUN_LAST
, 0,
564 g_cclosure_marshal_VOID__OBJECT
,
566 1, EMPATHY_TYPE_SERVER_SASL_HANDLER
);
570 empathy_auth_factory_dup_singleton (void)
572 EmpathyAuthFactory
*out
= NULL
;
575 bus
= tp_dbus_daemon_dup (NULL
);
576 out
= g_object_new (EMPATHY_TYPE_AUTH_FACTORY
,
578 "name", "Empathy.Auth",
580 g_object_unref (bus
);
586 empathy_auth_factory_register (EmpathyAuthFactory
*self
,
589 return tp_base_client_register (TP_BASE_CLIENT (self
), error
);