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