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
22 #include "empathy-server-sasl-handler.h"
24 #include <telepathy-glib/telepathy-glib.h>
26 #include <extensions/extensions.h>
30 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
31 #include "empathy-debug.h"
32 #include "empathy-keyring.h"
47 static guint signals[LAST_SIGNAL] = {0};
53 GSimpleAsyncResult *result;
56 gboolean save_password;
58 GSimpleAsyncResult *async_init_res;
59 } EmpathyServerSASLHandlerPriv;
61 static void async_initable_iface_init (GAsyncInitableIface *iface);
63 G_DEFINE_TYPE_WITH_CODE (EmpathyServerSASLHandler, empathy_server_sasl_handler,
65 G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init));
67 static const gchar *sasl_statuses[] = {
78 empathy_server_sasl_handler_set_password_cb (GObject *source,
84 if (!empathy_keyring_set_account_password_finish (TP_ACCOUNT (source), result,
87 DEBUG ("Failed to set password: %s", error->message);
88 g_clear_error (&error);
92 DEBUG ("Password set successfully.");
97 sasl_status_changed_cb (TpChannel *channel,
102 GObject *weak_object)
104 EmpathyServerSASLHandler *self = EMPATHY_SERVER_SASL_HANDLER (weak_object);
105 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (weak_object)->priv;
108 if (status >= G_N_ELEMENTS (sasl_statuses))
110 DEBUG ("SASL status changed to unknown status");
114 DEBUG ("SASL status changed to '%s'", sasl_statuses[status]);
116 if (status == TP_SASL_STATUS_SERVER_SUCCEEDED)
118 if (priv->save_password)
120 DEBUG ("Saving password in keyring");
122 empathy_keyring_set_account_password_async (priv->account,
123 priv->password, empathy_server_sasl_handler_set_password_cb,
127 DEBUG ("Calling AcceptSASL");
128 tp_cli_channel_interface_sasl_authentication_call_accept_sasl (
129 priv->channel, -1, NULL, NULL, NULL, NULL);
131 else if (status == TP_SASL_STATUS_SUCCEEDED)
133 DEBUG ("SASL succeeded, calling Close");
134 tp_cli_channel_call_close (priv->channel, -1,
135 NULL, NULL, NULL, NULL);
137 else if (status == TP_SASL_STATUS_SERVER_FAILED)
139 if (!tp_strdiff (error, TP_ERROR_STR_AUTHENTICATION_FAILED))
141 g_signal_emit (self, signals[AUTH_PASSWORD_FAILED], 0, priv->password);
147 empathy_server_sasl_handler_give_password (gpointer data)
149 EmpathyServerSASLHandler *self = data;
150 EmpathyServerSASLHandlerPriv *priv = self->priv;
152 empathy_server_sasl_handler_provide_password (self,
153 priv->password, FALSE);
159 empathy_server_sasl_handler_get_password_async_cb (GObject *source,
160 GAsyncResult *result,
163 EmpathyServerSASLHandlerPriv *priv;
164 const gchar *password;
165 GError *error = NULL;
167 priv = EMPATHY_SERVER_SASL_HANDLER (user_data)->priv;
169 password = empathy_keyring_get_account_password_finish (TP_ACCOUNT (source),
172 if (password != NULL)
174 priv->password = g_strdup (password);
176 /* Do this in an idle so the async result will get there
178 g_idle_add (empathy_server_sasl_handler_give_password, user_data);
181 g_simple_async_result_complete (priv->async_init_res);
182 tp_clear_object (&priv->async_init_res);
186 empathy_server_sasl_handler_init_async (GAsyncInitable *initable,
188 GCancellable *cancellable,
189 GAsyncReadyCallback callback,
192 EmpathyServerSASLHandler *self = EMPATHY_SERVER_SASL_HANDLER (initable);
193 EmpathyServerSASLHandlerPriv *priv = self->priv;
195 g_assert (priv->account != NULL);
197 priv->async_init_res = g_simple_async_result_new (G_OBJECT (self),
198 callback, user_data, empathy_server_sasl_handler_new_async);
200 empathy_keyring_get_account_password_async (priv->account,
201 empathy_server_sasl_handler_get_password_async_cb, self);
205 empathy_server_sasl_handler_init_finish (GAsyncInitable *initable,
209 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
217 async_initable_iface_init (GAsyncInitableIface *iface)
219 iface->init_async = empathy_server_sasl_handler_init_async;
220 iface->init_finish = empathy_server_sasl_handler_init_finish;
224 channel_invalidated_cb (TpProxy *proxy,
228 EmpathyServerSASLHandler *self)
230 g_signal_emit (self, signals[INVALIDATED], 0);
234 empathy_server_sasl_handler_constructed (GObject *object)
236 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
237 GError *error = NULL;
239 tp_cli_channel_interface_sasl_authentication_connect_to_sasl_status_changed (
240 priv->channel, sasl_status_changed_cb, NULL, NULL, object, &error);
244 DEBUG ("Failed to connect to SASLStatusChanged: %s", error->message);
245 g_clear_error (&error);
248 tp_g_signal_connect_object (priv->channel, "invalidated",
249 G_CALLBACK (channel_invalidated_cb), object, 0);
253 empathy_server_sasl_handler_get_property (GObject *object,
258 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
263 g_value_set_object (value, priv->channel);
266 g_value_set_object (value, priv->account);
269 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
275 empathy_server_sasl_handler_set_property (GObject *object,
280 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
285 priv->channel = g_value_dup_object (value);
288 priv->account = g_value_dup_object (value);
291 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
297 empathy_server_sasl_handler_dispose (GObject *object)
299 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
301 DEBUG ("%p", object);
303 tp_clear_object (&priv->channel);
304 tp_clear_object (&priv->account);
306 G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->dispose (object);
310 empathy_server_sasl_handler_finalize (GObject *object)
312 EmpathyServerSASLHandlerPriv *priv = EMPATHY_SERVER_SASL_HANDLER (object)->priv;
314 DEBUG ("%p", object);
316 tp_clear_pointer (&priv->password, g_free);
318 G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class)->finalize (object);
322 empathy_server_sasl_handler_class_init (EmpathyServerSASLHandlerClass *klass)
324 GObjectClass *oclass = G_OBJECT_CLASS (klass);
327 oclass->constructed = empathy_server_sasl_handler_constructed;
328 oclass->get_property = empathy_server_sasl_handler_get_property;
329 oclass->set_property = empathy_server_sasl_handler_set_property;
330 oclass->dispose = empathy_server_sasl_handler_dispose;
331 oclass->finalize = empathy_server_sasl_handler_finalize;
333 g_type_class_add_private (klass, sizeof (EmpathyServerSASLHandlerPriv));
335 pspec = g_param_spec_object ("channel", "The TpChannel",
336 "The TpChannel this handler is supposed to handle.",
338 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
339 g_object_class_install_property (oclass, PROP_CHANNEL, pspec);
341 pspec = g_param_spec_object ("account", "The TpAccount",
342 "The TpAccount this channel belongs to.",
344 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
345 g_object_class_install_property (oclass, PROP_ACCOUNT, pspec);
347 signals[AUTH_PASSWORD_FAILED] = g_signal_new ("auth-password-failed",
348 G_TYPE_FROM_CLASS (klass),
349 G_SIGNAL_RUN_LAST, 0,
351 g_cclosure_marshal_generic,
352 G_TYPE_NONE, 1, G_TYPE_STRING);
354 signals[INVALIDATED] = g_signal_new ("invalidated",
355 G_TYPE_FROM_CLASS (klass),
356 G_SIGNAL_RUN_LAST, 0,
358 g_cclosure_marshal_generic,
363 empathy_server_sasl_handler_init (EmpathyServerSASLHandler *self)
365 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
366 EMPATHY_TYPE_SERVER_SASL_HANDLER, EmpathyServerSASLHandlerPriv);
369 EmpathyServerSASLHandler *
370 empathy_server_sasl_handler_new_finish (GAsyncResult *result,
373 GObject *object, *source_object;
375 source_object = g_async_result_get_source_object (result);
377 object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
379 g_object_unref (source_object);
382 return EMPATHY_SERVER_SASL_HANDLER (object);
388 empathy_server_sasl_handler_new_async (TpAccount *account,
390 GAsyncReadyCallback callback,
393 g_return_if_fail (TP_IS_ACCOUNT (account));
394 g_return_if_fail (TP_IS_CHANNEL (channel));
395 g_return_if_fail (callback != NULL);
397 g_async_initable_new_async (EMPATHY_TYPE_SERVER_SASL_HANDLER,
398 G_PRIORITY_DEFAULT, NULL, callback, user_data,
405 start_mechanism_with_data_cb (TpChannel *proxy,
408 GObject *weak_object)
412 DEBUG ("Failed to start mechanism: %s", error->message);
416 DEBUG ("Started mechanism successfully");
420 empathy_server_sasl_handler_provide_password (
421 EmpathyServerSASLHandler *handler,
422 const gchar *password,
425 EmpathyServerSASLHandlerPriv *priv;
427 gboolean may_save_response, may_save_response_valid;
429 g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
431 priv = handler->priv;
433 array = g_array_sized_new (TRUE, FALSE,
434 sizeof (gchar), strlen (password));
436 g_array_append_vals (array, password, strlen (password));
438 DEBUG ("Calling StartMechanismWithData with our password");
440 tp_cli_channel_interface_sasl_authentication_call_start_mechanism_with_data (
441 priv->channel, -1, "X-TELEPATHY-PASSWORD", array,
442 start_mechanism_with_data_cb, NULL, NULL, G_OBJECT (handler));
444 g_array_unref (array);
446 DEBUG ("%sremembering the password", remember ? "" : "not ");
448 /* determine if we are permitted to save the password locally */
449 may_save_response = tp_asv_get_boolean (
450 tp_channel_borrow_immutable_properties (priv->channel),
451 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_MAY_SAVE_RESPONSE,
452 &may_save_response_valid);
454 if (!may_save_response_valid)
456 DEBUG ("MaySaveResponse unknown, assuming TRUE");
457 may_save_response = TRUE;
462 if (may_save_response)
464 g_free (priv->password);
466 /* We'll save the password if we manage to connect */
467 priv->password = g_strdup (password);
468 priv->save_password = TRUE;
470 else if (tp_proxy_has_interface_by_id (priv->channel,
471 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE))
473 DEBUG ("Channel implements Ch.I.CredentialsStorage");
477 DEBUG ("Asked to remember password, but doing so is not permitted");
481 if (!may_save_response)
483 /* delete any password present, it shouldn't be there */
484 empathy_keyring_delete_account_password_async (priv->account, NULL, NULL);
487 /* Additionally, if we implement Ch.I.CredentialsStorage, inform that
488 * whether we want to remember the password */
489 if (tp_proxy_has_interface_by_id (priv->channel,
490 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE))
492 emp_cli_channel_interface_credentials_storage_call_store_credentials (
493 TP_PROXY (priv->channel), -1, remember, NULL, NULL, NULL, NULL);
498 empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler *handler)
500 EmpathyServerSASLHandlerPriv *priv;
502 g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler));
504 priv = handler->priv;
506 DEBUG ("Cancelling SASL mechanism...");
508 tp_cli_channel_interface_sasl_authentication_call_abort_sasl (
509 priv->channel, -1, TP_SASL_ABORT_REASON_USER_ABORT,
510 "User cancelled the authentication",
511 NULL, NULL, NULL, NULL);
515 empathy_server_sasl_handler_get_account (EmpathyServerSASLHandler *handler)
517 EmpathyServerSASLHandlerPriv *priv;
519 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL);
521 priv = handler->priv;
523 return priv->account;
527 empathy_server_sasl_handler_get_channel (EmpathyServerSASLHandler *handler)
529 EmpathyServerSASLHandlerPriv *priv;
531 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), NULL);
533 priv = handler->priv;
535 return priv->channel;
539 empathy_server_sasl_handler_has_password (EmpathyServerSASLHandler *handler)
541 EmpathyServerSASLHandlerPriv *priv;
543 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler), FALSE);
545 priv = handler->priv;
547 return (priv->password != NULL);
551 * empathy_server_sasl_handler_can_save_response_somewhere:
554 * Returns: %TRUE if the response can be saved somewhere, either the keyring
555 * or via Ch.I.CredentialsStorage
558 empathy_server_sasl_handler_can_save_response_somewhere (
559 EmpathyServerSASLHandler *self)
561 EmpathyServerSASLHandlerPriv *priv;
562 gboolean may_save_response, may_save_response_valid;
563 gboolean has_storage_iface;
565 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (self), FALSE);
569 /* determine if we are permitted to save the password locally */
570 may_save_response = tp_asv_get_boolean (
571 tp_channel_borrow_immutable_properties (priv->channel),
572 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_MAY_SAVE_RESPONSE,
573 &may_save_response_valid);
575 if (!may_save_response_valid)
577 DEBUG ("MaySaveResponse unknown, assuming TRUE");
578 may_save_response = TRUE;
581 has_storage_iface = tp_proxy_has_interface_by_id (priv->channel,
582 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE);
584 return may_save_response || has_storage_iface;