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>
28 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
29 #include "empathy-debug.h"
30 #include "empathy-keyring.h"
45 static guint signals
[LAST_SIGNAL
] = {0};
51 GSimpleAsyncResult
*result
;
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
,
63 G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE
, async_initable_iface_init
));
65 static const gchar
*sasl_statuses
[] = {
76 empathy_server_sasl_handler_set_password_cb (GObject
*source
,
82 if (!empathy_keyring_set_account_password_finish (TP_ACCOUNT (source
), result
,
85 DEBUG ("Failed to set password: %s", error
->message
);
86 g_clear_error (&error
);
90 DEBUG ("Password set successfully.");
95 sasl_status_changed_cb (TpChannel
*channel
,
100 GObject
*weak_object
)
102 EmpathyServerSASLHandler
*self
= EMPATHY_SERVER_SASL_HANDLER (weak_object
);
103 EmpathyServerSASLHandlerPriv
*priv
= EMPATHY_SERVER_SASL_HANDLER (weak_object
)->priv
;
106 if (status
>= G_N_ELEMENTS (sasl_statuses
))
108 DEBUG ("SASL status changed to unknown status");
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
,
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
);
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
);
157 empathy_server_sasl_handler_get_password_async_cb (GObject
*source
,
158 GAsyncResult
*result
,
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
),
170 if (password
!= NULL
)
172 priv
->password
= g_strdup (password
);
174 /* Do this in an idle so the async result will get there
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
);
184 empathy_server_sasl_handler_init_async (GAsyncInitable
*initable
,
186 GCancellable
*cancellable
,
187 GAsyncReadyCallback callback
,
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
);
203 empathy_server_sasl_handler_init_finish (GAsyncInitable
*initable
,
207 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res
),
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
;
222 channel_invalidated_cb (TpProxy
*proxy
,
226 EmpathyServerSASLHandler
*self
)
228 g_signal_emit (self
, signals
[INVALIDATED
], 0);
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
);
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);
251 empathy_server_sasl_handler_get_property (GObject
*object
,
256 EmpathyServerSASLHandlerPriv
*priv
= EMPATHY_SERVER_SASL_HANDLER (object
)->priv
;
261 g_value_set_object (value
, priv
->channel
);
264 g_value_set_object (value
, priv
->account
);
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
273 empathy_server_sasl_handler_set_property (GObject
*object
,
278 EmpathyServerSASLHandlerPriv
*priv
= EMPATHY_SERVER_SASL_HANDLER (object
)->priv
;
283 priv
->channel
= g_value_dup_object (value
);
286 priv
->account
= g_value_dup_object (value
);
289 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
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
);
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
);
320 empathy_server_sasl_handler_class_init (EmpathyServerSASLHandlerClass
*klass
)
322 GObjectClass
*oclass
= G_OBJECT_CLASS (klass
);
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.",
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.",
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,
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,
356 g_cclosure_marshal_generic
,
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
,
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
),
377 g_object_unref (source_object
);
380 return EMPATHY_SERVER_SASL_HANDLER (object
);
386 empathy_server_sasl_handler_new_async (TpAccount
*account
,
388 GAsyncReadyCallback callback
,
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
,
403 start_mechanism_with_data_cb (TpChannel
*proxy
,
406 GObject
*weak_object
)
410 DEBUG ("Failed to start mechanism: %s", error
->message
);
414 DEBUG ("Started mechanism successfully");
418 empathy_server_sasl_handler_provide_password (
419 EmpathyServerSASLHandler
*handler
,
420 const gchar
*password
,
423 EmpathyServerSASLHandlerPriv
*priv
;
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
;
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");
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
);
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
);
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
;
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
;
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:
552 * Returns: %TRUE if the response can be saved somewhere, either the keyring
553 * or via Ch.I.CredentialsStorage
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
);
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
;