build: remove -enumtypes rules
[empathy-mirror.git] / libempathy / empathy-auth-factory.c
blob56b3b73eb9981c598056e31bcc03cbfa74a93286
1 /*
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)
41 * gets invalidated.
43 * The channel path of the handler's channel (borrowed gchar *) ->
44 * reffed (EmpathyServerSASLHandler *)
45 * */
46 GHashTable *sasl_handlers;
48 gboolean dispose_run;
51 enum {
52 NEW_SERVER_TLS_HANDLER,
53 NEW_SERVER_SASL_HANDLER,
54 LAST_SIGNAL,
57 static guint signals[LAST_SIGNAL] = { 0, };
59 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAuthFactory)
61 static EmpathyAuthFactory *auth_factory_singleton = NULL;
63 typedef struct {
64 TpHandleChannelsContext *context;
65 EmpathyAuthFactory *self;
66 } HandlerContextData;
68 static void
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);
86 if (context != NULL)
87 data->context = g_object_ref (context);
89 return data;
92 static void
93 server_tls_handler_ready_cb (GObject *source,
94 GAsyncResult *res,
95 gpointer user_data)
97 EmpathyServerTLSHandler *handler;
98 GError *error = NULL;
99 HandlerContextData *data = user_data;
101 handler = empathy_server_tls_handler_new_finish (res, &error);
103 if (error != NULL)
105 DEBUG ("Failed to create a server TLS handler; error %s",
106 error->message);
107 tp_handle_channels_context_fail (data->context, error);
109 g_error_free (error);
111 else
113 tp_handle_channels_context_accept (data->context);
114 g_signal_emit (data->self, signals[NEW_SERVER_TLS_HANDLER], 0,
115 handler);
117 g_object_unref (handler);
120 handler_context_data_free (data);
123 static void
124 sasl_handler_invalidated_cb (EmpathyServerSASLHandler *handler,
125 gpointer user_data)
127 EmpathyAuthFactory *self = user_data;
128 EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
129 TpChannel * channel;
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));
140 static void
141 server_sasl_handler_ready_cb (GObject *source,
142 GAsyncResult *res,
143 gpointer user_data)
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);
153 if (error != NULL)
155 DEBUG ("Failed to create a server SASL handler; error %s",
156 error->message);
158 if (data->context != NULL)
159 tp_handle_channels_context_fail (data->context, error);
161 g_error_free (error);
163 else
165 TpChannel *channel;
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,
181 handler);
184 handler_context_data_free (data);
187 static gboolean
188 common_checks (EmpathyAuthFactory *self,
189 GList *channels,
190 gboolean observe,
191 GError **error)
193 EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
194 TpChannel *channel;
195 GHashTable *props;
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");
210 return FALSE;
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. */
221 if (observe
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));
230 return FALSE;
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 &&
240 !observe)
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));
246 return FALSE;
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,
252 G_TYPE_STRV);
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");
261 return FALSE;
264 dbus_error = tp_proxy_get_invalidated (channel);
266 if (dbus_error != NULL)
268 *error = g_error_copy (dbus_error);
269 return FALSE;
272 return TRUE;
275 static void
276 handle_channels (TpBaseClient *handler,
277 TpAccount *account,
278 TpConnection *connection,
279 GList *channels,
280 GList *requests_satisfied,
281 gint64 user_action_time,
282 TpHandleChannelsContext *context)
284 TpChannel *channel;
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);
296 return;
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,
310 data);
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);
320 typedef struct
322 EmpathyAuthFactory *self;
323 TpObserveChannelsContext *context;
324 TpChannelDispatchOperation *dispatch_operation;
325 TpAccount *account;
326 TpChannel *channel;
327 } ObserveChannelsData;
329 static void
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);
339 static void
340 claim_cb (GObject *source,
341 GAsyncResult *result,
342 gpointer user_data)
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);
353 else
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);
368 static void
369 get_password_cb (GObject *source,
370 GAsyncResult *result,
371 gpointer user_data)
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);
386 else
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);
398 static void
399 observe_channels (TpBaseClient *client,
400 TpAccount *account,
401 TpConnection *connection,
402 GList *channels,
403 TpChannelDispatchOperation *dispatch_operation,
404 GList *requests,
405 TpObserveChannelsContext *context)
407 EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (client);
408 TpChannel *channel;
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);
419 return;
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);
428 data->self = self;
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);
439 static GObject *
440 empathy_auth_factory_constructor (GType type,
441 guint n_params,
442 GObjectConstructParam *params)
444 GObject *retval;
446 if (auth_factory_singleton != NULL)
448 retval = g_object_ref (auth_factory_singleton);
450 else
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);
459 return retval;
462 static void
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);
472 static void
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 (
485 /* ChannelType */
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 (
493 /* ChannelType */
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,
499 NULL));
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
505 * sense. */
507 /* Observe ServerAuthentication channels */
508 tp_base_client_take_observer_filter (client, tp_asv_new (
509 /* ChannelType */
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,
515 NULL));
517 tp_base_client_set_observer_delay_approvers (client, TRUE);
520 static void
521 empathy_auth_factory_dispose (GObject *object)
523 EmpathyAuthFactoryPriv *priv = GET_PRIV (object);
525 if (priv->dispose_run)
526 return;
528 priv->dispose_run = TRUE;
530 g_hash_table_unref (priv->sasl_handlers);
532 G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object);
535 static void
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,
554 NULL, NULL,
555 g_cclosure_marshal_VOID__OBJECT,
556 G_TYPE_NONE,
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,
563 NULL, NULL,
564 g_cclosure_marshal_VOID__OBJECT,
565 G_TYPE_NONE,
566 1, EMPATHY_TYPE_SERVER_SASL_HANDLER);
569 EmpathyAuthFactory *
570 empathy_auth_factory_dup_singleton (void)
572 EmpathyAuthFactory *out = NULL;
573 TpDBusDaemon *bus;
575 bus = tp_dbus_daemon_dup (NULL);
576 out = g_object_new (EMPATHY_TYPE_AUTH_FACTORY,
577 "dbus-daemon", bus,
578 "name", "Empathy.Auth",
579 NULL);
580 g_object_unref (bus);
582 return out;
585 gboolean
586 empathy_auth_factory_register (EmpathyAuthFactory *self,
587 GError **error)
589 return tp_base_client_register (TP_BASE_CLIENT (self), error);