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
21 #include "empathy-server-sasl-handler.h"
23 #include <tp-account-widgets/tpaw-keyring.h>
24 #include <telepathy-glib/telepathy-glib-dbus.h>
26 #include "empathy-sasl-mechanisms.h"
27 #include "extensions.h"
29 #define DEBUG_FLAG EMPATHY_DEBUG_SASL
30 #include "empathy-debug.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
));
66 empathy_server_sasl_handler_set_password_cb (GObject
*source
,
72 if (!tpaw_keyring_set_account_password_finish (TP_ACCOUNT (source
), result
,
75 DEBUG ("Failed to set password: %s", error
->message
);
76 g_clear_error (&error
);
80 DEBUG ("Password set successfully.");
85 empathy_server_sasl_handler_give_password (gpointer data
)
87 EmpathyServerSASLHandler
*self
= data
;
88 EmpathyServerSASLHandlerPriv
*priv
= self
->priv
;
90 empathy_server_sasl_handler_provide_password (self
,
91 priv
->password
, FALSE
);
97 empathy_server_sasl_handler_get_password_async_cb (GObject
*source
,
101 EmpathyServerSASLHandlerPriv
*priv
;
102 const gchar
*password
;
103 GError
*error
= NULL
;
105 priv
= EMPATHY_SERVER_SASL_HANDLER (user_data
)->priv
;
107 password
= tpaw_keyring_get_account_password_finish (TP_ACCOUNT (source
),
110 if (password
!= NULL
)
112 priv
->password
= g_strdup (password
);
114 /* Do this in an idle so the async result will get there
116 g_idle_add (empathy_server_sasl_handler_give_password
, user_data
);
119 g_simple_async_result_complete (priv
->async_init_res
);
120 tp_clear_object (&priv
->async_init_res
);
124 empathy_server_sasl_handler_init_async (GAsyncInitable
*initable
,
126 GCancellable
*cancellable
,
127 GAsyncReadyCallback callback
,
130 EmpathyServerSASLHandler
*self
= EMPATHY_SERVER_SASL_HANDLER (initable
);
131 EmpathyServerSASLHandlerPriv
*priv
= self
->priv
;
133 g_assert (priv
->account
!= NULL
);
135 priv
->async_init_res
= g_simple_async_result_new (G_OBJECT (self
),
136 callback
, user_data
, empathy_server_sasl_handler_new_async
);
138 tpaw_keyring_get_account_password_async (priv
->account
,
139 empathy_server_sasl_handler_get_password_async_cb
, self
);
143 empathy_server_sasl_handler_init_finish (GAsyncInitable
*initable
,
147 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res
),
155 async_initable_iface_init (GAsyncInitableIface
*iface
)
157 iface
->init_async
= empathy_server_sasl_handler_init_async
;
158 iface
->init_finish
= empathy_server_sasl_handler_init_finish
;
162 channel_invalidated_cb (TpProxy
*proxy
,
166 EmpathyServerSASLHandler
*self
)
168 g_signal_emit (self
, signals
[INVALIDATED
], 0);
172 empathy_server_sasl_handler_constructed (GObject
*object
)
174 EmpathyServerSASLHandlerPriv
*priv
= EMPATHY_SERVER_SASL_HANDLER (object
)->priv
;
175 GError
*error
= NULL
;
179 DEBUG ("Failed to connect to SASLStatusChanged: %s", error
->message
);
180 g_clear_error (&error
);
183 tp_g_signal_connect_object (priv
->channel
, "invalidated",
184 G_CALLBACK (channel_invalidated_cb
), object
, 0);
188 empathy_server_sasl_handler_get_property (GObject
*object
,
193 EmpathyServerSASLHandlerPriv
*priv
= EMPATHY_SERVER_SASL_HANDLER (object
)->priv
;
198 g_value_set_object (value
, priv
->channel
);
201 g_value_set_object (value
, priv
->account
);
204 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
210 empathy_server_sasl_handler_set_property (GObject
*object
,
215 EmpathyServerSASLHandlerPriv
*priv
= EMPATHY_SERVER_SASL_HANDLER (object
)->priv
;
220 priv
->channel
= g_value_dup_object (value
);
223 priv
->account
= g_value_dup_object (value
);
226 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
232 empathy_server_sasl_handler_dispose (GObject
*object
)
234 EmpathyServerSASLHandlerPriv
*priv
= EMPATHY_SERVER_SASL_HANDLER (object
)->priv
;
236 DEBUG ("%p", object
);
238 tp_clear_object (&priv
->channel
);
239 tp_clear_object (&priv
->account
);
241 G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class
)->dispose (object
);
245 empathy_server_sasl_handler_finalize (GObject
*object
)
247 EmpathyServerSASLHandlerPriv
*priv
= EMPATHY_SERVER_SASL_HANDLER (object
)->priv
;
249 DEBUG ("%p", object
);
251 tp_clear_pointer (&priv
->password
, g_free
);
253 G_OBJECT_CLASS (empathy_server_sasl_handler_parent_class
)->finalize (object
);
257 empathy_server_sasl_handler_class_init (EmpathyServerSASLHandlerClass
*klass
)
259 GObjectClass
*oclass
= G_OBJECT_CLASS (klass
);
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
;
268 g_type_class_add_private (klass
, sizeof (EmpathyServerSASLHandlerPriv
));
270 pspec
= g_param_spec_object ("channel", "The TpChannel",
271 "The TpChannel this handler is supposed to handle.",
273 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
);
274 g_object_class_install_property (oclass
, PROP_CHANNEL
, pspec
);
276 pspec
= g_param_spec_object ("account", "The TpAccount",
277 "The TpAccount this channel belongs to.",
279 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
);
280 g_object_class_install_property (oclass
, PROP_ACCOUNT
, pspec
);
282 signals
[AUTH_PASSWORD_FAILED
] = g_signal_new ("auth-password-failed",
283 G_TYPE_FROM_CLASS (klass
),
284 G_SIGNAL_RUN_LAST
, 0,
286 g_cclosure_marshal_generic
,
287 G_TYPE_NONE
, 1, G_TYPE_STRING
);
289 signals
[INVALIDATED
] = g_signal_new ("invalidated",
290 G_TYPE_FROM_CLASS (klass
),
291 G_SIGNAL_RUN_LAST
, 0,
293 g_cclosure_marshal_generic
,
298 empathy_server_sasl_handler_init (EmpathyServerSASLHandler
*self
)
300 self
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
301 EMPATHY_TYPE_SERVER_SASL_HANDLER
, EmpathyServerSASLHandlerPriv
);
304 EmpathyServerSASLHandler
*
305 empathy_server_sasl_handler_new_finish (GAsyncResult
*result
,
308 GObject
*object
, *source_object
;
310 source_object
= g_async_result_get_source_object (result
);
312 object
= g_async_initable_new_finish (G_ASYNC_INITABLE (source_object
),
314 g_object_unref (source_object
);
317 return EMPATHY_SERVER_SASL_HANDLER (object
);
323 empathy_server_sasl_handler_new_async (TpAccount
*account
,
325 GAsyncReadyCallback callback
,
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
);
332 g_async_initable_new_async (EMPATHY_TYPE_SERVER_SASL_HANDLER
,
333 G_PRIORITY_DEFAULT
, NULL
, callback
, user_data
,
340 auth_cb (GObject
*source
,
341 GAsyncResult
*result
,
344 EmpathyServerSASLHandler
*self
= user_data
;
345 EmpathyServerSASLHandlerPriv
*priv
= self
->priv
;
346 GError
*error
= NULL
;
348 if (!empathy_sasl_auth_finish (priv
->channel
, result
, &error
))
350 if (g_error_matches (error
, TP_ERROR
, TP_ERROR_AUTHENTICATION_FAILED
))
352 g_signal_emit (self
, signals
[AUTH_PASSWORD_FAILED
], 0, priv
->password
);
354 g_clear_error (&error
);
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
,
365 tp_channel_close_async (priv
->channel
, NULL
, NULL
);
366 g_object_unref (self
);
370 channel_has_may_save_response (TpChannel
*channel
)
372 /* determine if we are permitted to save the password locally */
374 gboolean may_save_response
;
376 props
= tp_channel_dup_immutable_properties (channel
);
378 if (!g_variant_lookup (props
,
379 TP_PROP_CHANNEL_INTERFACE_SASL_AUTHENTICATION_MAY_SAVE_RESPONSE
,
380 "b", &may_save_response
))
382 DEBUG ("MaySaveResponse unknown, assuming TRUE");
383 may_save_response
= TRUE
;
386 g_variant_unref (props
);
387 return may_save_response
;
391 empathy_server_sasl_handler_provide_password (
392 EmpathyServerSASLHandler
*handler
,
393 const gchar
*password
,
396 EmpathyServerSASLHandlerPriv
*priv
;
397 gboolean may_save_response
;
399 g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler
));
401 priv
= handler
->priv
;
403 empathy_sasl_auth_password_async (priv
->channel
, password
,
404 auth_cb
, g_object_ref (handler
));
406 DEBUG ("%sremembering the password", remember
? "" : "not ");
408 may_save_response
= channel_has_may_save_response (priv
->channel
);
412 if (may_save_response
)
414 g_free (priv
->password
);
416 /* We'll save the password if we manage to connect */
417 priv
->password
= g_strdup (password
);
418 priv
->save_password
= TRUE
;
420 else if (tp_proxy_has_interface_by_id (priv
->channel
,
421 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE
))
423 DEBUG ("Channel implements Ch.I.CredentialsStorage");
427 DEBUG ("Asked to remember password, but doing so is not permitted");
431 if (!may_save_response
)
433 /* delete any password present, it shouldn't be there */
434 tpaw_keyring_delete_account_password_async (priv
->account
, NULL
, NULL
);
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
))
442 emp_cli_channel_interface_credentials_storage_call_store_credentials (
443 TP_PROXY (priv
->channel
), -1, remember
, NULL
, NULL
, NULL
, NULL
);
448 empathy_server_sasl_handler_cancel (EmpathyServerSASLHandler
*handler
)
450 EmpathyServerSASLHandlerPriv
*priv
;
452 g_return_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler
));
454 priv
= handler
->priv
;
456 DEBUG ("Cancelling SASL mechanism...");
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
);
465 empathy_server_sasl_handler_get_account (EmpathyServerSASLHandler
*handler
)
467 EmpathyServerSASLHandlerPriv
*priv
;
469 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler
), NULL
);
471 priv
= handler
->priv
;
473 return priv
->account
;
477 empathy_server_sasl_handler_get_channel (EmpathyServerSASLHandler
*handler
)
479 EmpathyServerSASLHandlerPriv
*priv
;
481 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler
), NULL
);
483 priv
= handler
->priv
;
485 return priv
->channel
;
489 empathy_server_sasl_handler_has_password (EmpathyServerSASLHandler
*handler
)
491 EmpathyServerSASLHandlerPriv
*priv
;
493 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (handler
), FALSE
);
495 priv
= handler
->priv
;
497 return (priv
->password
!= NULL
);
501 * empathy_server_sasl_handler_can_save_response_somewhere:
504 * Returns: %TRUE if the response can be saved somewhere, either the keyring
505 * or via Ch.I.CredentialsStorage
508 empathy_server_sasl_handler_can_save_response_somewhere (
509 EmpathyServerSASLHandler
*self
)
511 EmpathyServerSASLHandlerPriv
*priv
;
512 gboolean may_save_response
;
513 gboolean has_storage_iface
;
515 g_return_val_if_fail (EMPATHY_IS_SERVER_SASL_HANDLER (self
), FALSE
);
519 may_save_response
= channel_has_may_save_response (priv
->channel
);
521 has_storage_iface
= tp_proxy_has_interface_by_id (priv
->channel
,
522 EMP_IFACE_QUARK_CHANNEL_INTERFACE_CREDENTIALS_STORAGE
);
524 return may_save_response
|| has_storage_iface
;