]> git.0d.be Git - empathy.git/blob - libempathy/empathy-server-sasl-handler.c
Updated Greek translation
[empathy.git] / libempathy / empathy-server-sasl-handler.c
1 /*
2  * empathy-server-sasl-handler.c - Source for EmpathyServerSASLHandler
3  * Copyright (C) 2010 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "empathy-server-sasl-handler.h"
22
23 #include <tp-account-widgets/tpaw-keyring.h>
24 #include <telepathy-glib/telepathy-glib-dbus.h>
25
26 #include "empathy-sasl-mechanisms.h"
27 #include "extensions.h"
28
29 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
30 #include "empathy-debug.h"
31
32 enum {
33   PROP_CHANNEL = 1,
34   PROP_ACCOUNT,
35   LAST_PROPERTY,
36 };
37
38 /* signal enum */
39 enum {
40   AUTH_PASSWORD_FAILED,
41   INVALIDATED,
42   LAST_SIGNAL,
43 };
44
45 static guint signals[LAST_SIGNAL] = {0};
46
47 typedef struct {
48   TpChannel *channel;
49   TpAccount *account;
50
51   GSimpleAsyncResult *result;
52
53   gchar *password;
54   gboolean save_password;
55
56   GSimpleAsyncResult *async_init_res;
57 } EmpathyServerSASLHandlerPriv;
58
59 static void async_initable_iface_init (GAsyncInitableIface *iface);
60
61 G_DEFINE_TYPE_WITH_CODE (EmpathyServerSASLHandler, empathy_server_sasl_handler,
62     G_TYPE_OBJECT,
63     G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init));
64
65 static void
66 empathy_server_sasl_handler_set_password_cb (GObject *source,
67     GAsyncResult *result,
68     gpointer user_data)
69 {
70   GError *error = NULL;
71
72   if (!tpaw_keyring_set_account_password_finish (TP_ACCOUNT (source), result,
73           &error))
74     {
75       DEBUG ("Failed to set password: %s", error->message);
76       g_clear_error (&error);
77     }
78   else
79     {
80       DEBUG ("Password set successfully.");
81     }
82 }
83
84 static gboolean
85 empathy_server_sasl_handler_give_password (gpointer data)
86 {
87   EmpathyServerSASLHandler *self = data;
88   EmpathyServerSASLHandlerPriv *priv = self->priv;
89
90   empathy_server_sasl_handler_provide_password (self,
91       priv->password, FALSE);
92
93   return FALSE;
94 }
95
96 static void
97 empathy_server_sasl_handler_get_password_async_cb (GObject *source,
98     GAsyncResult *result,
99     gpointer user_data)
100 {
101   EmpathyServerSASLHandlerPriv *priv;
102   const gchar *password;
103   GError *error = NULL;
104
105   priv = EMPATHY_SERVER_SASL_HANDLER (user_data)->priv;
106
107   password = tpaw_keyring_get_account_password_finish (TP_ACCOUNT (source),
108       result, &error);
109
110   if (password != NULL)
111     {
112       priv->password = g_strdup (password);
113
114       /* Do this in an idle so the async result will get there
115        * first. */
116       g_idle_add (empathy_server_sasl_handler_give_password, user_data);
117     }
118
119   g_simple_async_result_complete (priv->async_init_res);
120   tp_clear_object (&priv->async_init_res);
121 }
122
123 static void
124 empathy_server_sasl_handler_init_async (GAsyncInitable *initable,
125     gint io_priority,
126     GCancellable *cancellable,
127     GAsyncReadyCallback callback,
128     gpointer user_data)
129 {
130   EmpathyServerSASLHandler *self = EMPATHY_SERVER_SASL_HANDLER (initable);
131   EmpathyServerSASLHandlerPriv *priv = self->priv;
132
133   g_assert (priv->account != NULL);
134
135   priv->async_init_res = g_simple_async_result_new (G_OBJECT (self),
136       callback, user_data, empathy_server_sasl_handler_new_async);
137
138   tpaw_keyring_get_account_password_async (priv->account,
139       empathy_server_sasl_handler_get_password_async_cb, self);
140 }
141
142 static gboolean
143 empathy_server_sasl_handler_init_finish (GAsyncInitable *initable,
144     GAsyncResult *res,
145     GError **error)
146 {
147   if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
148           error))
149     return FALSE;
150
151   return TRUE;
152 }
153
154 static void
155 async_initable_iface_init (GAsyncInitableIface *iface)
156 {
157   iface->init_async = empathy_server_sasl_handler_init_async;
158   iface->init_finish = empathy_server_sasl_handler_init_finish;
159 }
160
161 static void
162 channel_invalidated_cb (TpProxy *proxy,
163     guint domain,
164     gint code,
165     gchar *message,
166     EmpathyServerSASLHandler *self)
167 {
168   g_signal_emit (self, signals[INVALIDATED], 0);
169 }
170
171 static void
172 empathy_server_sasl_handler_constructed (GObject *object)
173 {
174   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
175   GError *error = NULL;
176
177   if (error != NULL)
178     {
179       DEBUG ("Failed to connect to SASLStatusChanged: %s", error->message);
180       g_clear_error (&error);
181     }
182
183   tp_g_signal_connect_object (priv->channel, "invalidated",
184       G_CALLBACK (channel_invalidated_cb), object, 0);
185 }
186
187 static void
188 empathy_server_sasl_handler_get_property (GObject *object,
189     guint property_id,
190     GValue *value,
191     GParamSpec *pspec)
192 {
193   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
194
195   switch (property_id)
196     {
197     case PROP_CHANNEL:
198       g_value_set_object (value, priv->channel);
199       break;
200     case PROP_ACCOUNT:
201       g_value_set_object (value, priv->account);
202       break;
203     default:
204       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
205       break;
206     }
207 }
208
209 static void
210 empathy_server_sasl_handler_set_property (GObject *object,
211     guint property_id,
212     const GValue *value,
213     GParamSpec *pspec)
214 {
215   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
216
217   switch (property_id)
218     {
219     case PROP_CHANNEL:
220       priv->channel = g_value_dup_object (value);
221       break;
222     case PROP_ACCOUNT:
223       priv->account = g_value_dup_object (value);
224       break;
225     default:
226       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
227       break;
228     }
229 }
230
231 static void
232 empathy_server_sasl_handler_dispose (GObject *object)
233 {
234   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
235
236   DEBUG ("%p", object);
237
238   tp_clear_object (&priv->channel);
239   tp_clear_object (&priv->account);
240
241   G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->dispose (object);
242 }
243
244 static void
245 empathy_server_sasl_handler_finalize (GObject *object)
246 {
247   EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
248
249   DEBUG ("%p", object);
250
251   tp_clear_pointer (&priv->password, g_free);
252
253   G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->finalize (object);
254 }
255
256 static void
257 empathy_server_sasl_handler_class_init (EmpathyServerSASLHandlerClass *klass)
258 {
259   GObjectClass *oclass = G_OBJECT_CLASS (klass);
260   GParamSpec *pspec;
261
262   oclass->constructed = empathy_server_sasl_handler_constructed;
263   oclass->get_property = empathy_server_sasl_handler_get_property;
264   oclass->set_property = empathy_server_sasl_handler_set_property;
265   oclass->dispose = empathy_server_sasl_handler_dispose;
266   oclass->finalize = empathy_server_sasl_handler_finalize;
267
268   g_type_class_add_private (klass, sizeof (EmpathyServerSASLHandlerPriv));
269
270   pspec = g_param_spec_object ("channel", "The TpChannel",
271       "The TpChannel this handler is supposed to handle.",
272       TP_TYPE_CHANNEL,
273       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
274   g_object_class_install_property (oclass, PROP_CHANNEL, pspec);
275
276   pspec = g_param_spec_object ("account", "The TpAccount",
277       "The TpAccount this channel belongs to.",
278       TP_TYPE_ACCOUNT,
279       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
280   g_object_class_install_property (oclass, PROP_ACCOUNT, pspec);
281
282   signals[AUTH_PASSWORD_FAILED] = g_signal_new ("auth-password-failed",
283       G_TYPE_FROM_CLASS (klass),
284       G_SIGNAL_RUN_LAST, 0,
285       NULL, NULL,
286       g_cclosure_marshal_generic,
287       G_TYPE_NONE, 1, G_TYPE_STRING);
288
289   signals[INVALIDATED] = g_signal_new ("invalidated",
290       G_TYPE_FROM_CLASS (klass),
291       G_SIGNAL_RUN_LAST, 0,
292       NULL, NULL,
293       g_cclosure_marshal_generic,
294       G_TYPE_NONE, 0);
295 }
296
297 static void
298 empathy_server_sasl_handler_init (EmpathyServerSASLHandler *self)
299 {
300   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
301       EMPATHY_TYPE_SERVER_SASL_HANDLER, EmpathyServerSASLHandlerPriv);
302 }
303
304 EmpathyServerSASLHandler *
305 empathy_server_sasl_handler_new_finish (GAsyncResult *result,
306     GError **error)
307 {
308   GObject *object, *source_object;
309
310   source_object = g_async_result_get_source_object (result);
311
312   object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
313       result, error);
314   g_object_unref (source_object);
315
316   if (object != NULL)
317     return EMPATHY_SERVER_SASL_HANDLER (object);
318   else
319     return NULL;
320 }
321
322 void
323 empathy_server_sasl_handler_new_async (TpAccount *account,
324     TpChannel *channel,
325     GAsyncReadyCallback callback,
326     gpointer user_data)
327 {
328   g_return_if_fail (TP_IS_ACCOUNT (account));
329   g_return_if_fail (TP_IS_CHANNEL (channel));
330   g_return_if_fail (callback != NULL);
331
332   g_async_initable_new_async (EMPATHY_TYPE_SERVER_SASL_HANDLER,
333       G_PRIORITY_DEFAULT, NULL, callback, user_data,
334       "account", account,
335       "channel", channel,
336       NULL);
337 }
338
339 static void
340 auth_cb (GObject *source,
341     GAsyncResult *result,
342     gpointer user_data)
343 {
344   EmpathyServerSASLHandler *self = user_data;
345   EmpathyServerSASLHandlerPriv *priv = self->priv;
346   GError *error = NULL;
347
348   if (!empathy_sasl_auth_finish (priv->channel, result, &error))
349     {
350       if (g_error_matches (error, TP_ERROR, TP_ERROR_AUTHENTICATION_FAILED))
351         {
352           g_signal_emit (self, signals[AUTH_PASSWORD_FAILED], 0, priv->password);
353         }
354       g_clear_error (&error);
355     }
356   else
357     {
358       DEBUG ("Saving password in keyring");
359       tpaw_keyring_set_account_password_async (priv->account,
360           priv->password, priv->save_password,
361           empathy_server_sasl_handler_set_password_cb,
362           NULL);
363     }
364
365   tp_channel_close_async (priv->channel, NULL, NULL);
366   g_object_unref (self);
367 }
368
369 static gboolean
370 channel_has_may_save_response (TpChannel *channel)
371 {
372   /* determine if we are permitted to save the password locally */
373   GVariant *props;
374   gboolean may_save_response;
375
376   props = tp_channel_dup_immutable_properties (channel);
377
378   if (!g_variant_lookup (props,
379         TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_MAY_SAVE_RESPONSE,
380         "b", &may_save_response))
381     {
382       DEBUG ("MaySaveResponse unknown, assuming TRUE");
383       may_save_response = TRUE;
384     }
385
386   g_variant_unref (props);
387   return may_save_response;
388 }
389
390 void
391 empathy_server_sasl_handler_provide_password (
392     EmpathyServerSASLHandler *handler,
393     const gchar *password,
394     gboolean remember)
395 {
396   EmpathyServerSASLHandlerPriv *priv;
397   gboolean may_save_response;
398
399   g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
400
401   priv = handler->priv;
402
403   empathy_sasl_auth_password_async (priv->channel, password,
404       auth_cb, g_object_ref (handler));
405
406   DEBUG ("%sremembering the password", remember ? "" : "not ");
407
408   may_save_response = channel_has_may_save_response (priv->channel);
409
410   if (remember)
411     {
412       if (may_save_response)
413         {
414           g_free (priv->password);
415
416           /* We'll save the password if we manage to connect */
417           priv->password = g_strdup (password);
418           priv->save_password = TRUE;
419         }
420       else if (tp_proxy_has_interface_by_id (priv->channel,
421             EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE))
422         {
423           DEBUG ("Channel implements Ch.I.CredentialsStorage");
424         }
425       else
426         {
427           DEBUG ("Asked to remember password, but doing so is not permitted");
428         }
429     }
430
431   if (!may_save_response)
432     {
433       /* delete any password present, it shouldn't be there */
434       tpaw_keyring_delete_account_password_async (priv->account, NULL, NULL);
435     }
436
437   /* Additionally, if we implement Ch.I.CredentialsStorage, inform that
438    * whether we want to remember the password */
439   if (tp_proxy_has_interface_by_id (priv->channel,
440         EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE))
441     {
442       emp_cli_channel_interface_credentials_storage_call_store_credentials (
443           TP_PROXY (priv->channel), -1, remember, NULL, NULL, NULL, NULL);
444     }
445 }
446
447 void
448 empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler *handler)
449 {
450   EmpathyServerSASLHandlerPriv *priv;
451
452   g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
453
454   priv = handler->priv;
455
456   DEBUG ("Cancelling SASL mechanism...");
457
458   tp_cli_channel_interface_sasl_authentication_call_abort_sasl (
459       priv->channel, -1, TP_SASL_ABORT_REASON_USER_ABORT,
460       "User cancelled the authentication",
461       NULL, NULL, NULL, NULL);
462 }
463
464 TpAccount *
465 empathy_server_sasl_handler_get_account (EmpathyServerSASLHandler *handler)
466 {
467   EmpathyServerSASLHandlerPriv *priv;
468
469   g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL);
470
471   priv = handler->priv;
472
473   return priv->account;
474 }
475
476 TpChannel *
477 empathy_server_sasl_handler_get_channel (EmpathyServerSASLHandler *handler)
478 {
479   EmpathyServerSASLHandlerPriv *priv;
480
481   g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL);
482
483   priv = handler->priv;
484
485   return priv->channel;
486 }
487
488 gboolean
489 empathy_server_sasl_handler_has_password (EmpathyServerSASLHandler *handler)
490 {
491   EmpathyServerSASLHandlerPriv *priv;
492
493   g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), FALSE);
494
495   priv = handler->priv;
496
497   return (priv->password != NULL);
498 }
499
500 /**
501  * empathy_server_sasl_handler_can_save_response_somewhere:
502  * @self:
503  *
504  * Returns: %TRUE if the response can be saved somewhere, either the keyring
505  *   or via Ch.I.CredentialsStorage
506  */
507 gboolean
508 empathy_server_sasl_handler_can_save_response_somewhere (
509     EmpathyServerSASLHandler *self)
510 {
511   EmpathyServerSASLHandlerPriv *priv;
512   gboolean may_save_response;
513   gboolean has_storage_iface;
514
515   g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (self), FALSE);
516
517   priv = self->priv;
518
519   may_save_response = channel_has_may_save_response (priv->channel);
520
521   has_storage_iface = tp_proxy_has_interface_by_id (priv->channel,
522       EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE);
523
524   return may_save_response || has_storage_iface;
525 }