Confirm closing windows containing chat rooms.
[empathy-mirror.git] / libempathy / empathy-server-sasl-handler.c
blob117d88f03880834291f642f06f26839c112a6f9d
1 /*
2 * empathy-server-sasl-handler.c - Source for EmpathyServerSASLHandler
3 * Copyright (C) 2010 Collabora Ltd.
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.
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.
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
20 #include "empathy-server-sasl-handler.h"
22 #include <telepathy-glib/telepathy-glib.h>
24 #include <extensions/extensions.h>
26 #include <string.h>
28 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
29 #include "empathy-debug.h"
30 #include "empathy-keyring.h"
32 enum {
33 PROP_CHANNEL = 1,
34 PROP_ACCOUNT,
35 LAST_PROPERTY,
38 /* signal enum */
39 enum {
40 AUTH_PASSWORD_FAILED,
41 INVALIDATED,
42 LAST_SIGNAL,
45 static guint signals[LAST_SIGNAL] = {0};
47 typedef struct {
48 TpChannel *channel;
49 TpAccount *account;
51 GSimpleAsyncResult *result;
53 gchar *password;
54 gboolean save_password;
56 GSimpleAsyncResult *async_init_res;
57 } EmpathyServerSASLHandlerPriv;
59 static void async_initable_iface_init (GAsyncInitableIface *iface);
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));
65 static const gchar *sasl_statuses[] = {
66 "not started",
67 "in progress",
68 "server succeeded",
69 "client accepted",
70 "succeeded",
71 "server failed",
72 "client failed",
75 static void
76 empathy_server_sasl_handler_set_password_cb (GObject *source,
77 GAsyncResult *result,
78 gpointer user_data)
80 GError *error = NULL;
82 if (!empathy_keyring_set_account_password_finish (TP_ACCOUNT (source), result,
83 &error))
85 DEBUG ("Failed to set password: %s", error->message);
86 g_clear_error (&error);
88 else
90 DEBUG ("Password set successfully.");
94 static void
95 sasl_status_changed_cb (TpChannel *channel,
96 TpSASLStatus status,
97 const gchar *error,
98 GHashTable *details,
99 gpointer user_data,
100 GObject *weak_object)
102 EmpathyServerSASLHandler *self = EMPATHY_SERVER_SASL_HANDLER (weak_object);
103 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (weak_object)->priv;
105 /* buh boh */
106 if (status >= G_N_ELEMENTS (sasl_statuses))
108 DEBUG ("SASL status changed to unknown status");
109 return;
112 DEBUG ("SASL status changed to '%s'", sasl_statuses[status]);
114 if (status == TP_SASL_STATUS_SERVER_SUCCEEDED)
116 if (priv->save_password)
118 DEBUG ("Saving password in keyring");
120 empathy_keyring_set_account_password_async (priv->account,
121 priv->password, empathy_server_sasl_handler_set_password_cb,
122 NULL);
125 DEBUG ("Calling AcceptSASL");
126 tp_cli_channel_interface_sasl_authentication_call_accept_sasl (
127 priv->channel, -1, NULL, NULL, NULL, NULL);
129 else if (status == TP_SASL_STATUS_SUCCEEDED)
131 DEBUG ("SASL succeeded, calling Close");
132 tp_cli_channel_call_close (priv->channel, -1,
133 NULL, NULL, NULL, NULL);
135 else if (status == TP_SASL_STATUS_SERVER_FAILED)
137 if (!tp_strdiff (error, TP_ERROR_STR_AUTHENTICATION_FAILED))
139 g_signal_emit (self, signals[AUTH_PASSWORD_FAILED], 0, priv->password);
144 static gboolean
145 empathy_server_sasl_handler_give_password (gpointer data)
147 EmpathyServerSASLHandler *self = data;
148 EmpathyServerSASLHandlerPriv *priv = self->priv;
150 empathy_server_sasl_handler_provide_password (self,
151 priv->password, FALSE);
153 return FALSE;
156 static void
157 empathy_server_sasl_handler_get_password_async_cb (GObject *source,
158 GAsyncResult *result,
159 gpointer user_data)
161 EmpathyServerSASLHandlerPriv *priv;
162 const gchar *password;
163 GError *error = NULL;
165 priv = EMPATHY_SERVER_SASL_HANDLER (user_data)->priv;
167 password = empathy_keyring_get_account_password_finish (TP_ACCOUNT (source),
168 result, &error);
170 if (password != NULL)
172 priv->password = g_strdup (password);
174 /* Do this in an idle so the async result will get there
175 * first. */
176 g_idle_add (empathy_server_sasl_handler_give_password, user_data);
179 g_simple_async_result_complete (priv->async_init_res);
180 tp_clear_object (&priv->async_init_res);
183 static void
184 empathy_server_sasl_handler_init_async (GAsyncInitable *initable,
185 gint io_priority,
186 GCancellable *cancellable,
187 GAsyncReadyCallback callback,
188 gpointer user_data)
190 EmpathyServerSASLHandler *self = EMPATHY_SERVER_SASL_HANDLER (initable);
191 EmpathyServerSASLHandlerPriv *priv = self->priv;
193 g_assert (priv->account != NULL);
195 priv->async_init_res = g_simple_async_result_new (G_OBJECT (self),
196 callback, user_data, empathy_server_sasl_handler_new_async);
198 empathy_keyring_get_account_password_async (priv->account,
199 empathy_server_sasl_handler_get_password_async_cb, self);
202 static gboolean
203 empathy_server_sasl_handler_init_finish (GAsyncInitable *initable,
204 GAsyncResult *res,
205 GError **error)
207 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
208 error))
209 return FALSE;
211 return TRUE;
214 static void
215 async_initable_iface_init (GAsyncInitableIface *iface)
217 iface->init_async = empathy_server_sasl_handler_init_async;
218 iface->init_finish = empathy_server_sasl_handler_init_finish;
221 static void
222 channel_invalidated_cb (TpProxy *proxy,
223 guint domain,
224 gint code,
225 gchar *message,
226 EmpathyServerSASLHandler *self)
228 g_signal_emit (self, signals[INVALIDATED], 0);
231 static void
232 empathy_server_sasl_handler_constructed (GObject *object)
234 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
235 GError *error = NULL;
237 tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed (
238 priv->channel, sasl_status_changed_cb, NULL, NULL, object, &error);
240 if (error != NULL)
242 DEBUG ("Failed to connect to SASLStatusChanged: %s", error->message);
243 g_clear_error (&error);
246 tp_g_signal_connect_object (priv->channel, "invalidated",
247 G_CALLBACK (channel_invalidated_cb), object, 0);
250 static void
251 empathy_server_sasl_handler_get_property (GObject *object,
252 guint property_id,
253 GValue *value,
254 GParamSpec *pspec)
256 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
258 switch (property_id)
260 case PROP_CHANNEL:
261 g_value_set_object (value, priv->channel);
262 break;
263 case PROP_ACCOUNT:
264 g_value_set_object (value, priv->account);
265 break;
266 default:
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
268 break;
272 static void
273 empathy_server_sasl_handler_set_property (GObject *object,
274 guint property_id,
275 const GValue *value,
276 GParamSpec *pspec)
278 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
280 switch (property_id)
282 case PROP_CHANNEL:
283 priv->channel = g_value_dup_object (value);
284 break;
285 case PROP_ACCOUNT:
286 priv->account = g_value_dup_object (value);
287 break;
288 default:
289 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
290 break;
294 static void
295 empathy_server_sasl_handler_dispose (GObject *object)
297 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
299 DEBUG ("%p", object);
301 tp_clear_object (&priv->channel);
302 tp_clear_object (&priv->account);
304 G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->dispose (object);
307 static void
308 empathy_server_sasl_handler_finalize (GObject *object)
310 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
312 DEBUG ("%p", object);
314 tp_clear_pointer (&priv->password, g_free);
316 G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->finalize (object);
319 static void
320 empathy_server_sasl_handler_class_init (EmpathyServerSASLHandlerClass *klass)
322 GObjectClass *oclass = G_OBJECT_CLASS (klass);
323 GParamSpec *pspec;
325 oclass->constructed = empathy_server_sasl_handler_constructed;
326 oclass->get_property = empathy_server_sasl_handler_get_property;
327 oclass->set_property = empathy_server_sasl_handler_set_property;
328 oclass->dispose = empathy_server_sasl_handler_dispose;
329 oclass->finalize = empathy_server_sasl_handler_finalize;
331 g_type_class_add_private (klass, sizeof (EmpathyServerSASLHandlerPriv));
333 pspec = g_param_spec_object ("channel", "The TpChannel",
334 "The TpChannel this handler is supposed to handle.",
335 TP_TYPE_CHANNEL,
336 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
337 g_object_class_install_property (oclass, PROP_CHANNEL, pspec);
339 pspec = g_param_spec_object ("account", "The TpAccount",
340 "The TpAccount this channel belongs to.",
341 TP_TYPE_ACCOUNT,
342 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
343 g_object_class_install_property (oclass, PROP_ACCOUNT, pspec);
345 signals[AUTH_PASSWORD_FAILED] = g_signal_new ("auth-password-failed",
346 G_TYPE_FROM_CLASS (klass),
347 G_SIGNAL_RUN_LAST, 0,
348 NULL, NULL,
349 g_cclosure_marshal_generic,
350 G_TYPE_NONE, 1, G_TYPE_STRING);
352 signals[INVALIDATED] = g_signal_new ("invalidated",
353 G_TYPE_FROM_CLASS (klass),
354 G_SIGNAL_RUN_LAST, 0,
355 NULL, NULL,
356 g_cclosure_marshal_generic,
357 G_TYPE_NONE, 0);
360 static void
361 empathy_server_sasl_handler_init (EmpathyServerSASLHandler *self)
363 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
364 EMPATHY_TYPE_SERVER_SASL_HANDLER, EmpathyServerSASLHandlerPriv);
367 EmpathyServerSASLHandler *
368 empathy_server_sasl_handler_new_finish (GAsyncResult *result,
369 GError **error)
371 GObject *object, *source_object;
373 source_object = g_async_result_get_source_object (result);
375 object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
376 result, error);
377 g_object_unref (source_object);
379 if (object != NULL)
380 return EMPATHY_SERVER_SASL_HANDLER (object);
381 else
382 return NULL;
385 void
386 empathy_server_sasl_handler_new_async (TpAccount *account,
387 TpChannel *channel,
388 GAsyncReadyCallback callback,
389 gpointer user_data)
391 g_return_if_fail (TP_IS_ACCOUNT (account));
392 g_return_if_fail (TP_IS_CHANNEL (channel));
393 g_return_if_fail (callback != NULL);
395 g_async_initable_new_async (EMPATHY_TYPE_SERVER_SASL_HANDLER,
396 G_PRIORITY_DEFAULT, NULL, callback, user_data,
397 "account", account,
398 "channel", channel,
399 NULL);
402 static void
403 start_mechanism_with_data_cb (TpChannel *proxy,
404 const GError *error,
405 gpointer user_data,
406 GObject *weak_object)
408 if (error != NULL)
410 DEBUG ("Failed to start mechanism: %s", error->message);
411 return;
414 DEBUG ("Started mechanism successfully");
417 void
418 empathy_server_sasl_handler_provide_password (
419 EmpathyServerSASLHandler *handler,
420 const gchar *password,
421 gboolean remember)
423 EmpathyServerSASLHandlerPriv *priv;
424 GArray *array;
425 gboolean may_save_response, may_save_response_valid;
427 g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
429 priv = handler->priv;
431 array = g_array_sized_new (TRUE, FALSE,
432 sizeof (gchar), strlen (password));
434 g_array_append_vals (array, password, strlen (password));
436 DEBUG ("Calling StartMechanismWithData with our password");
438 tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
439 priv->channel, -1, "X-TELEPATHY-PASSWORD", array,
440 start_mechanism_with_data_cb, NULL, NULL, G_OBJECT (handler));
442 g_array_unref (array);
444 DEBUG ("%sremembering the password", remember ? "" : "not ");
446 /* determine if we are permitted to save the password locally */
447 may_save_response = tp_asv_get_boolean (
448 tp_channel_borrow_immutable_properties (priv->channel),
449 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_MAY_SAVE_RESPONSE,
450 &may_save_response_valid);
452 if (!may_save_response_valid)
454 DEBUG ("MaySaveResponse unknown, assuming TRUE");
455 may_save_response = TRUE;
458 if (remember)
460 if (may_save_response)
462 g_free (priv->password);
464 /* We'll save the password if we manage to connect */
465 priv->password = g_strdup (password);
466 priv->save_password = TRUE;
468 else if (tp_proxy_has_interface_by_id (priv->channel,
469 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE))
471 DEBUG ("Channel implements Ch.I.CredentialsStorage");
473 else
475 DEBUG ("Asked to remember password, but doing so is not permitted");
479 if (!may_save_response)
481 /* delete any password present, it shouldn't be there */
482 empathy_keyring_delete_account_password_async (priv->account, NULL, NULL);
485 /* Additionally, if we implement Ch.I.CredentialsStorage, inform that
486 * whether we want to remember the password */
487 if (tp_proxy_has_interface_by_id (priv->channel,
488 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE))
490 emp_cli_channel_interface_credentials_storage_call_store_credentials (
491 TP_PROXY (priv->channel), -1, remember, NULL, NULL, NULL, NULL);
495 void
496 empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler *handler)
498 EmpathyServerSASLHandlerPriv *priv;
500 g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
502 priv = handler->priv;
504 DEBUG ("Cancelling SASL mechanism...");
506 tp_cli_channel_interface_sasl_authentication_call_abort_sasl (
507 priv->channel, -1, TP_SASL_ABORT_REASON_USER_ABORT,
508 "User cancelled the authentication",
509 NULL, NULL, NULL, NULL);
512 TpAccount *
513 empathy_server_sasl_handler_get_account (EmpathyServerSASLHandler *handler)
515 EmpathyServerSASLHandlerPriv *priv;
517 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL);
519 priv = handler->priv;
521 return priv->account;
524 TpChannel *
525 empathy_server_sasl_handler_get_channel (EmpathyServerSASLHandler *handler)
527 EmpathyServerSASLHandlerPriv *priv;
529 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL);
531 priv = handler->priv;
533 return priv->channel;
536 gboolean
537 empathy_server_sasl_handler_has_password (EmpathyServerSASLHandler *handler)
539 EmpathyServerSASLHandlerPriv *priv;
541 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), FALSE);
543 priv = handler->priv;
545 return (priv->password != NULL);
549 * empathy_server_sasl_handler_can_save_response_somewhere:
550 * @self:
552 * Returns: %TRUE if the response can be saved somewhere, either the keyring
553 * or via Ch.I.CredentialsStorage
555 gboolean
556 empathy_server_sasl_handler_can_save_response_somewhere (
557 EmpathyServerSASLHandler *self)
559 EmpathyServerSASLHandlerPriv *priv;
560 gboolean may_save_response, may_save_response_valid;
561 gboolean has_storage_iface;
563 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (self), FALSE);
565 priv = self->priv;
567 /* determine if we are permitted to save the password locally */
568 may_save_response = tp_asv_get_boolean (
569 tp_channel_borrow_immutable_properties (priv->channel),
570 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_MAY_SAVE_RESPONSE,
571 &may_save_response_valid);
573 if (!may_save_response_valid)
575 DEBUG ("MaySaveResponse unknown, assuming TRUE");
576 may_save_response = TRUE;
579 has_storage_iface = tp_proxy_has_interface_by_id (priv->channel,
580 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE);
582 return may_save_response || has_storage_iface;