]> git.0d.be Git - empathy.git/blob - libempathy/empathy-auth-factory.c
keyring: move from Empathy to tp-account-widgets
[empathy.git] / libempathy / empathy-auth-factory.c
1 /*
2  * empathy-auth-factory.c - Source for EmpathyAuthFactory
3  * Copyright (C) 2010 Collabora Ltd.
4  * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22 #include "empathy-auth-factory.h"
23
24 #include <tp-account-widgets/tpaw-keyring.h>
25
26 #include "empathy-sasl-mechanisms.h"
27 #include "empathy-server-sasl-handler.h"
28 #include "empathy-server-tls-handler.h"
29 #include "empathy-utils.h"
30
31 #ifdef HAVE_GOA
32 #include "empathy-goa-auth-handler.h"
33 #endif /* HAVE_GOA */
34
35 #ifdef HAVE_UOA
36 #include "empathy-uoa-auth-handler.h"
37 #endif /* HAVE_UOA */
38
39 #include "extensions.h"
40
41 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
42 #include "empathy-debug.h"
43
44 G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, TP_TYPE_BASE_CLIENT);
45
46 struct _EmpathyAuthFactoryPriv {
47   /* Keep a ref here so the auth client doesn't have to mess with
48    * refs. It will be cleared when the channel (and so the handler)
49    * gets invalidated.
50    *
51    * The channel path of the handler's channel (borrowed gchar *) ->
52    * reffed (EmpathyServerSASLHandler *)
53    * */
54   GHashTable *sasl_handlers;
55
56 #ifdef HAVE_GOA
57   EmpathyGoaAuthHandler *goa_handler;
58 #endif /* HAVE_GOA */
59
60 #ifdef HAVE_UOA
61   EmpathyUoaAuthHandler *uoa_handler;
62 #endif /* HAVE_UOA */
63
64   /* If an account failed to connect and user enters a new password to try, we
65    * store it in this hash table and will try to use it next time the account
66    * attemps to connect.
67    *
68    * reffed TpAccount -> owned password (gchar *) */
69   GHashTable *retry_passwords;
70
71   gboolean dispose_run;
72 };
73
74 enum {
75   NEW_SERVER_TLS_HANDLER,
76   NEW_SERVER_SASL_HANDLER,
77   AUTH_PASSWORD_FAILED,
78   LAST_SIGNAL,
79 };
80
81 static guint signals[LAST_SIGNAL] = { 0, };
82
83 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAuthFactory)
84
85 static EmpathyAuthFactory *auth_factory_singleton = NULL;
86
87 typedef struct {
88   TpHandleChannelsContext *context;
89   EmpathyAuthFactory *self;
90 } HandlerContextData;
91
92 static void
93 handler_context_data_free (HandlerContextData *data)
94 {
95   tp_clear_object (&data->self);
96   tp_clear_object (&data->context);
97
98   g_slice_free (HandlerContextData, data);
99 }
100
101 static HandlerContextData *
102 handler_context_data_new (EmpathyAuthFactory *self,
103     TpHandleChannelsContext *context)
104 {
105   HandlerContextData *data;
106
107   data = g_slice_new0 (HandlerContextData);
108   data->self = g_object_ref (self);
109
110   if (context != NULL)
111     data->context = g_object_ref (context);
112
113   return data;
114 }
115
116 static void
117 server_tls_handler_ready_cb (GObject *source,
118     GAsyncResult *res,
119     gpointer user_data)
120 {
121   EmpathyServerTLSHandler *handler;
122   GError *error = NULL;
123   HandlerContextData *data = user_data;
124
125   handler = empathy_server_tls_handler_new_finish (res, &error);
126
127   if (error != NULL)
128     {
129       DEBUG ("Failed to create a server TLS handler; error %s",
130           error->message);
131       tp_handle_channels_context_fail (data->context, error);
132
133       g_error_free (error);
134     }
135   else
136     {
137       tp_handle_channels_context_accept (data->context);
138       g_signal_emit (data->self, signals[NEW_SERVER_TLS_HANDLER], 0,
139           handler);
140
141       g_object_unref (handler);
142     }
143
144   handler_context_data_free (data);
145 }
146
147 static void
148 sasl_handler_invalidated_cb (EmpathyServerSASLHandler *handler,
149     gpointer user_data)
150 {
151   EmpathyAuthFactory *self = user_data;
152   EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
153   TpChannel * channel;
154
155   channel = empathy_server_sasl_handler_get_channel (handler);
156   g_assert (channel != NULL);
157
158   DEBUG ("SASL handler for channel %s is invalidated, unref it",
159       tp_proxy_get_object_path (channel));
160
161   g_hash_table_remove (priv->sasl_handlers, tp_proxy_get_object_path (channel));
162 }
163
164 static void
165 sasl_handler_auth_password_failed_cb (EmpathyServerSASLHandler *handler,
166     const gchar *password,
167     EmpathyAuthFactory *self)
168 {
169   TpAccount *account;
170
171   account = empathy_server_sasl_handler_get_account (handler);
172
173   g_signal_emit (self, signals[AUTH_PASSWORD_FAILED], 0, account, password);
174 }
175
176 static void
177 server_sasl_handler_ready_cb (GObject *source,
178     GAsyncResult *res,
179     gpointer user_data)
180 {
181   EmpathyAuthFactoryPriv *priv;
182   GError *error = NULL;
183   HandlerContextData *data = user_data;
184   EmpathyServerSASLHandler *handler;
185
186   priv = GET_PRIV (data->self);
187   handler = empathy_server_sasl_handler_new_finish (res, &error);
188
189   if (error != NULL)
190     {
191       DEBUG ("Failed to create a server SASL handler; error %s",
192           error->message);
193
194       if (data->context != NULL)
195         tp_handle_channels_context_fail (data->context, error);
196
197       g_error_free (error);
198     }
199   else
200     {
201       TpChannel *channel;
202       const gchar *password;
203       TpAccount *account;
204
205       if (data->context != NULL)
206         tp_handle_channels_context_accept (data->context);
207
208       channel = empathy_server_sasl_handler_get_channel (handler);
209       g_assert (channel != NULL);
210
211       /* Pass the ref to the hash table */
212       g_hash_table_insert (priv->sasl_handlers,
213           (gpointer) tp_proxy_get_object_path (channel), handler);
214
215       tp_g_signal_connect_object (handler, "invalidated",
216           G_CALLBACK (sasl_handler_invalidated_cb), data->self, 0);
217
218       tp_g_signal_connect_object (handler, "auth-password-failed",
219           G_CALLBACK (sasl_handler_auth_password_failed_cb), data->self, 0);
220
221       /* Is there a retry password? */
222       account = empathy_server_sasl_handler_get_account (handler);
223
224       password = g_hash_table_lookup (data->self->priv->retry_passwords,
225           account);
226       if (password != NULL)
227         {
228           gboolean save;
229
230           DEBUG ("Use retry password");
231
232           /* We want to save this new password only if there is another
233            * (wrong) password saved. The SASL handler will only save it if it
234            * manages to connect. */
235           save = empathy_server_sasl_handler_has_password (handler);
236
237           empathy_server_sasl_handler_provide_password (handler,
238               password, save);
239
240           /* We only want to try this password once */
241           g_hash_table_remove (data->self->priv->retry_passwords, account);
242         }
243
244       g_signal_emit (data->self, signals[NEW_SERVER_SASL_HANDLER], 0,
245           handler);
246     }
247
248   handler_context_data_free (data);
249 }
250
251 static gboolean
252 common_checks (EmpathyAuthFactory *self,
253     GList *channels,
254     gboolean observe,
255     GError **error)
256 {
257   EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
258   TpChannel *channel;
259   const GError *dbus_error;
260   EmpathyServerSASLHandler *handler;
261
262   /* there can't be more than one ServerTLSConnection or
263    * ServerAuthentication channels at the same time, for the same
264    * connection/account.
265    */
266   if (g_list_length (channels) != 1)
267     {
268       g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
269           "Can't %s more than one ServerTLSConnection or ServerAuthentication "
270           "channel for the same connection.", observe ? "observe" : "handle");
271
272       return FALSE;
273     }
274
275   channel = channels->data;
276
277   if (tp_channel_get_channel_type_id (channel) !=
278       TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
279     {
280       /* If we are observing we care only about ServerAuthentication channels.
281        * If we are handling we care about ServerAuthentication and
282        * ServerTLSConnection channels. */
283       if (observe
284           || tp_channel_get_channel_type_id (channel) !=
285           EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
286         {
287           g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
288               "Can only %s ServerTLSConnection or ServerAuthentication channels, "
289               "this was a %s channel", observe ? "observe" : "handle",
290               tp_channel_get_channel_type (channel));
291
292           return FALSE;
293         }
294     }
295
296   handler = g_hash_table_lookup (priv->sasl_handlers,
297           tp_proxy_get_object_path (channel));
298
299   if (tp_channel_get_channel_type_id (channel) ==
300       TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
301       && handler != NULL &&
302       !observe)
303     {
304       g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
305           "We are already handling this channel: %s",
306           tp_proxy_get_object_path (channel));
307
308       return FALSE;
309     }
310
311   dbus_error = tp_proxy_get_invalidated (channel);
312   if (dbus_error != NULL)
313     {
314       *error = g_error_copy (dbus_error);
315       return FALSE;
316     }
317
318   return TRUE;
319 }
320
321 static void
322 handle_channels (TpBaseClient *handler,
323     TpAccount *account,
324     TpConnection *connection,
325     GList *channels,
326     GList *requests_satisfied,
327     gint64 user_action_time,
328     TpHandleChannelsContext *context)
329 {
330   TpChannel *channel;
331   GError *error = NULL;
332   EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (handler);
333   HandlerContextData *data;
334
335   DEBUG ("Handle TLS or SASL carrier channels.");
336
337   if (!common_checks (self, channels, FALSE, &error))
338     {
339       DEBUG ("Failed checks: %s", error->message);
340       tp_handle_channels_context_fail (context, error);
341       g_clear_error (&error);
342       return;
343     }
344
345   /* The common checks above have checked this is fine. */
346   channel = channels->data;
347
348   /* Only password authentication is supported from here */
349   if (tp_channel_get_channel_type_id (channel) ==
350       TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION &&
351       !empathy_sasl_channel_supports_mechanism (channel,
352           "X-TELEPATHY-PASSWORD"))
353     {
354       g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
355           "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported");
356       DEBUG ("%s", error->message);
357       tp_handle_channels_context_fail (context, error);
358       g_clear_error (&error);
359       return;
360     }
361
362   data = handler_context_data_new (self, context);
363   tp_handle_channels_context_delay (context);
364
365   /* create a handler */
366   if (tp_channel_get_channel_type_id (channel) ==
367       EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
368     {
369       empathy_server_tls_handler_new_async (channel, server_tls_handler_ready_cb,
370           data);
371     }
372   else if (tp_channel_get_channel_type_id (channel) ==
373       TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
374     {
375       empathy_server_sasl_handler_new_async (account, channel,
376           server_sasl_handler_ready_cb, data);
377     }
378 }
379
380 typedef struct
381 {
382   EmpathyAuthFactory *self;
383   TpObserveChannelsContext *context;
384   TpChannelDispatchOperation *dispatch_operation;
385   TpAccount *account;
386   TpChannel *channel;
387 } ObserveChannelsData;
388
389 static void
390 observe_channels_data_free (ObserveChannelsData *data)
391 {
392   g_object_unref (data->context);
393   g_object_unref (data->account);
394   g_object_unref (data->channel);
395   g_object_unref (data->dispatch_operation);
396   g_slice_free (ObserveChannelsData, data);
397 }
398
399 static void
400 password_claim_cb (GObject *source,
401     GAsyncResult *result,
402     gpointer user_data)
403 {
404   ObserveChannelsData *data = user_data;
405   GError *error = NULL;
406
407   if (!tp_channel_dispatch_operation_claim_with_finish (
408           TP_CHANNEL_DISPATCH_OPERATION (source), result, &error))
409     {
410       DEBUG ("Failed to call Claim: %s", error->message);
411       g_clear_error (&error);
412     }
413   else
414     {
415       HandlerContextData *h_data;
416
417       DEBUG ("Claim called successfully");
418
419       h_data = handler_context_data_new (data->self, NULL);
420
421       empathy_server_sasl_handler_new_async (TP_ACCOUNT (data->account),
422           data->channel, server_sasl_handler_ready_cb, h_data);
423     }
424
425   observe_channels_data_free (data);
426 }
427
428 static void
429 get_password_cb (GObject *source,
430     GAsyncResult *result,
431     gpointer user_data)
432 {
433   ObserveChannelsData *data = user_data;
434
435   if (tpaw_keyring_get_account_password_finish (TP_ACCOUNT (source), result, NULL) == NULL)
436     {
437       /* We don't actually mind if this fails, just let the approver
438        * go ahead and take the channel. */
439
440       DEBUG ("We don't have a password for account %s, letting the event "
441           "manager approver take it", tp_proxy_get_object_path (source));
442
443       tp_observe_channels_context_accept (data->context);
444       observe_channels_data_free (data);
445     }
446   else
447     {
448       DEBUG ("We have a password for account %s, calling Claim",
449           tp_proxy_get_object_path (source));
450
451       tp_channel_dispatch_operation_claim_with_async (data->dispatch_operation,
452           TP_BASE_CLIENT (data->self), password_claim_cb, data);
453
454       tp_observe_channels_context_accept (data->context);
455     }
456 }
457
458 #ifdef HAVE_GOA
459 static void
460 goa_claim_cb (GObject *source,
461     GAsyncResult *result,
462     gpointer user_data)
463 {
464   ObserveChannelsData *data = user_data;
465   EmpathyAuthFactory *self = data->self;
466   GError *error = NULL;
467
468   if (!tp_channel_dispatch_operation_claim_with_finish (data->dispatch_operation,
469           result, &error))
470     {
471       DEBUG ("Failed to claim: %s", error->message);
472       g_clear_error (&error);
473     }
474   else
475     {
476       empathy_goa_auth_handler_start (self->priv->goa_handler,
477           data->channel, data->account);
478     }
479
480   observe_channels_data_free (data);
481 }
482 #endif /* HAVE_GOA */
483
484 #ifdef HAVE_UOA
485 static void
486 uoa_claim_cb (GObject *source,
487     GAsyncResult *result,
488     gpointer user_data)
489 {
490   ObserveChannelsData *data = user_data;
491   EmpathyAuthFactory *self = data->self;
492   GError *error = NULL;
493
494   if (!tp_channel_dispatch_operation_claim_with_finish (data->dispatch_operation,
495           result, &error))
496     {
497       DEBUG ("Failed to claim: %s", error->message);
498       g_clear_error (&error);
499     }
500   else
501     {
502       empathy_uoa_auth_handler_start (self->priv->uoa_handler,
503           data->channel, data->account);
504     }
505
506   observe_channels_data_free (data);
507 }
508 #endif /* HAVE_UOA */
509
510 static void
511 observe_channels (TpBaseClient *client,
512     TpAccount *account,
513     TpConnection *connection,
514     GList *channels,
515     TpChannelDispatchOperation *dispatch_operation,
516     GList *requests,
517     TpObserveChannelsContext *context)
518 {
519   EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (client);
520   TpChannel *channel;
521   GError *error = NULL;
522   ObserveChannelsData *data;
523
524   DEBUG ("New auth channel to observe");
525
526   if (!common_checks (self, channels, TRUE, &error))
527     {
528       DEBUG ("Failed checks: %s", error->message);
529       tp_observe_channels_context_fail (context, error);
530       g_clear_error (&error);
531       return;
532     }
533
534   /* The common checks above have checked this is fine. */
535   channel = channels->data;
536
537   data = g_slice_new0 (ObserveChannelsData);
538   data->self = self;
539   data->context = g_object_ref (context);
540   data->dispatch_operation = g_object_ref (dispatch_operation);
541   data->account = g_object_ref (account);
542   data->channel = g_object_ref (channel);
543
544 #ifdef HAVE_GOA
545   /* GOA auth? */
546   if (empathy_goa_auth_handler_supports (self->priv->goa_handler, channel, account))
547     {
548       DEBUG ("Supported GOA account (%s), claim SASL channel",
549           tp_proxy_get_object_path (account));
550
551       tp_channel_dispatch_operation_claim_with_async (dispatch_operation,
552           client, goa_claim_cb, data);
553       tp_observe_channels_context_accept (context);
554       return;
555     }
556 #endif /* HAVE_GOA */
557
558 #ifdef HAVE_UOA
559   /* UOA auth? */
560   if (empathy_uoa_auth_handler_supports (self->priv->uoa_handler, channel, account))
561     {
562       DEBUG ("Supported UOA account (%s), claim SASL channel",
563           tp_proxy_get_object_path (account));
564
565       tp_channel_dispatch_operation_claim_with_async (dispatch_operation,
566           client, uoa_claim_cb, data);
567       tp_observe_channels_context_accept (context);
568       return;
569     }
570 #endif /* HAVE_UOA */
571
572   /* Password auth? */
573   if (empathy_sasl_channel_supports_mechanism (data->channel,
574           "X-TELEPATHY-PASSWORD"))
575     {
576       if (g_hash_table_lookup (self->priv->retry_passwords, account) != NULL)
577         {
578           DEBUG ("We have a retry password for account %s, calling Claim",
579               tp_account_get_path_suffix (account));
580
581           tp_channel_dispatch_operation_claim_with_async (dispatch_operation,
582               client, password_claim_cb, data);
583
584           tp_observe_channels_context_accept (context);
585           return;
586         }
587
588       tpaw_keyring_get_account_password_async (data->account,
589           get_password_cb, data);
590       tp_observe_channels_context_delay (context);
591       return;
592     }
593
594   /* Unknown auth */
595   error = g_error_new_literal (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
596       "Unknown auth mechanism");
597   tp_observe_channels_context_fail (context, error);
598   g_clear_error (&error);
599
600   observe_channels_data_free (data);
601 }
602
603 static GObject *
604 empathy_auth_factory_constructor (GType type,
605     guint n_params,
606     GObjectConstructParam *params)
607 {
608   GObject *retval;
609
610   if (auth_factory_singleton != NULL)
611     {
612       retval = g_object_ref (auth_factory_singleton);
613     }
614   else
615     {
616       retval = G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructor
617         (type, n_params, params);
618
619       auth_factory_singleton = EMPATHY_AUTH_FACTORY (retval);
620       g_object_add_weak_pointer (retval, (gpointer *) &auth_factory_singleton);
621     }
622
623   return retval;
624 }
625
626 static void
627 empathy_auth_factory_init (EmpathyAuthFactory *self)
628 {
629   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
630       EMPATHY_TYPE_AUTH_FACTORY, EmpathyAuthFactoryPriv);
631
632   self->priv->sasl_handlers = g_hash_table_new_full (g_str_hash, g_str_equal,
633       NULL, g_object_unref);
634
635 #ifdef HAVE_GOA
636   self->priv->goa_handler = empathy_goa_auth_handler_new ();
637 #endif /* HAVE_GOA */
638
639 #ifdef HAVE_UOA
640   self->priv->uoa_handler = empathy_uoa_auth_handler_new ();
641 #endif /* HAVE_UOA */
642
643   self->priv->retry_passwords = g_hash_table_new_full (NULL, NULL,
644       g_object_unref, g_free);
645 }
646
647 static void
648 empathy_auth_factory_constructed (GObject *obj)
649 {
650   EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (obj);
651   TpBaseClient *client = TP_BASE_CLIENT (self);
652
653   /* chain up to TpBaseClient first */
654   G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructed (obj);
655
656   tp_base_client_set_handler_bypass_approval (client, FALSE);
657
658   /* Handle ServerTLSConnection and ServerAuthentication channels */
659   tp_base_client_take_handler_filter (client, tp_asv_new (
660           /* ChannelType */
661           TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
662           EMP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION,
663           /* AuthenticationMethod */
664           TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
665           TP_HANDLE_TYPE_NONE, NULL));
666
667   tp_base_client_take_handler_filter (client, tp_asv_new (
668           /* ChannelType */
669           TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
670           TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
671           /* AuthenticationMethod */
672           TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
673           G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
674           NULL));
675
676   /* We are also an observer so that we can see new auth channels
677    * popping up and if we have the password already saved to one
678    * account where an auth channel has just appeared we can call
679    * Claim() on the CDO so the approver won't get it, which makes
680    * sense. */
681
682   /* Observe ServerAuthentication channels */
683   tp_base_client_take_observer_filter (client, tp_asv_new (
684           /* ChannelType */
685           TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
686           TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
687           /* AuthenticationMethod */
688           TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
689           G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
690           NULL));
691
692   tp_base_client_set_observer_delay_approvers (client, TRUE);
693 }
694
695 static void
696 empathy_auth_factory_dispose (GObject *object)
697 {
698   EmpathyAuthFactoryPriv *priv = GET_PRIV (object);
699
700   if (priv->dispose_run)
701     return;
702
703   priv->dispose_run = TRUE;
704
705   g_hash_table_unref (priv->sasl_handlers);
706
707 #ifdef HAVE_GOA
708   g_object_unref (priv->goa_handler);
709 #endif /* HAVE_GOA */
710
711 #ifdef HAVE_UOA
712   g_object_unref (priv->uoa_handler);
713 #endif /* HAVE_UOA */
714
715   g_hash_table_unref (priv->retry_passwords);
716
717   G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object);
718 }
719
720 static void
721 empathy_auth_factory_class_init (EmpathyAuthFactoryClass *klass)
722 {
723   GObjectClass *oclass = G_OBJECT_CLASS (klass);
724   TpBaseClientClass *base_client_cls = TP_BASE_CLIENT_CLASS (klass);
725
726   oclass->constructor = empathy_auth_factory_constructor;
727   oclass->constructed = empathy_auth_factory_constructed;
728   oclass->dispose = empathy_auth_factory_dispose;
729
730   base_client_cls->handle_channels = handle_channels;
731   base_client_cls->observe_channels = observe_channels;
732
733   g_type_class_add_private (klass, sizeof (EmpathyAuthFactoryPriv));
734
735   signals[NEW_SERVER_TLS_HANDLER] =
736     g_signal_new ("new-server-tls-handler",
737       G_TYPE_FROM_CLASS (klass),
738       G_SIGNAL_RUN_LAST, 0,
739       NULL, NULL,
740       g_cclosure_marshal_generic,
741       G_TYPE_NONE,
742       1, EMPATHY_TYPE_SERVER_TLS_HANDLER);
743
744   signals[NEW_SERVER_SASL_HANDLER] =
745     g_signal_new ("new-server-sasl-handler",
746       G_TYPE_FROM_CLASS (klass),
747       G_SIGNAL_RUN_LAST, 0,
748       NULL, NULL,
749       g_cclosure_marshal_generic,
750       G_TYPE_NONE,
751       1, EMPATHY_TYPE_SERVER_SASL_HANDLER);
752
753   signals[AUTH_PASSWORD_FAILED] =
754     g_signal_new ("auth-password-failed",
755       G_TYPE_FROM_CLASS (klass),
756       G_SIGNAL_RUN_LAST, 0,
757       NULL, NULL,
758       g_cclosure_marshal_generic,
759       G_TYPE_NONE,
760       2, TP_TYPE_ACCOUNT, G_TYPE_STRING);
761 }
762
763 EmpathyAuthFactory *
764 empathy_auth_factory_new (TpSimpleClientFactory *factory)
765 {
766   return g_object_new (EMPATHY_TYPE_AUTH_FACTORY,
767       "factory", factory,
768       "name", "Empathy.Auth",
769       NULL);
770 }
771
772 gboolean
773 empathy_auth_factory_register (EmpathyAuthFactory *self,
774     GError **error)
775 {
776   return tp_base_client_register (TP_BASE_CLIENT (self), error);
777 }
778
779 void
780 empathy_auth_factory_save_retry_password (EmpathyAuthFactory *self,
781     TpAccount *account,
782     const gchar *password)
783 {
784   g_hash_table_insert (self->priv->retry_passwords,
785       g_object_ref (account), g_strdup (password));
786 }