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