2 * Copyright (C) 2010 Collabora Ltd.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <glib/gi18n-lib.h>
23 #include "empathy-keyring.h"
25 #include <libsecret/secret.h>
28 #include <libaccounts-glib/ag-account.h>
29 #include <libaccounts-glib/ag-account-service.h>
30 #include <libaccounts-glib/ag-auth-data.h>
31 #include <libaccounts-glib/ag-manager.h>
32 #include <libaccounts-glib/ag-service.h>
33 #include <libsignon-glib/signon-identity.h>
34 #include "empathy-uoa-utils.h"
37 #include "empathy-utils.h"
39 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
40 #include "empathy-debug.h"
42 static const SecretSchema account_keyring_schema =
43 { "org.gnome.Empathy.Account", SECRET_SCHEMA_DONT_MATCH_NAME,
44 { { "account-id", SECRET_SCHEMA_ATTRIBUTE_STRING },
45 { "param-name", SECRET_SCHEMA_ATTRIBUTE_STRING },
48 static const SecretSchema room_keyring_schema =
49 { "org.gnome.Empathy.Room", SECRET_SCHEMA_DONT_MATCH_NAME,
50 { { "account-id", SECRET_SCHEMA_ATTRIBUTE_STRING },
51 { "room-id", SECRET_SCHEMA_ATTRIBUTE_STRING },
55 empathy_keyring_is_available (void)
63 lookup_item_cb (GObject *source,
67 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
71 password = secret_password_lookup_finish (result, &error);
74 g_simple_async_result_set_error (simple, TP_ERROR,
75 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
76 g_clear_error (&error);
82 g_simple_async_result_set_error (simple, TP_ERROR,
83 TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
87 g_simple_async_result_set_op_res_gpointer (simple, password,
88 (GDestroyNotify) secret_password_free);
91 g_simple_async_result_complete (simple);
92 g_object_unref (simple);
96 static AgAccountService *
97 uoa_password_common (TpAccount *tp_account,
98 GSimpleAsyncResult *result,
99 AgAuthData **ret_auth_data)
101 const GValue *storage_id;
102 AgAccountId account_id;
103 AgManager *manager = NULL;
104 AgAccount *account = NULL;
106 AgAccountService *service = NULL;
107 AgAuthData *auth_data = NULL;
109 g_assert (ret_auth_data != NULL);
110 *ret_auth_data = NULL;
112 storage_id = tp_account_get_storage_identifier (tp_account);
113 account_id = g_value_get_uint (storage_id);
116 g_simple_async_result_set_error (result,
117 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
118 "StorageId is invalid, cannot get the AgAccount for this TpAccount");
119 g_simple_async_result_complete_in_idle (result);
123 manager = empathy_uoa_manager_dup ();
124 account = ag_manager_get_account (manager, account_id);
126 /* Assuming there is only one IM service */
127 l = ag_account_list_services_by_type (account, EMPATHY_UOA_SERVICE_TYPE);
130 g_simple_async_result_set_error (result,
131 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
132 "AgAccount has no IM service");
133 g_simple_async_result_complete_in_idle (result);
136 service = ag_account_service_new (account, l->data);
137 ag_service_list_free (l);
139 auth_data = ag_account_service_get_auth_data (service);
140 if (auth_data == NULL)
142 g_simple_async_result_set_error (result,
143 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
144 "Service has no AgAuthData");
145 g_simple_async_result_complete_in_idle (result);
149 if (tp_strdiff (ag_auth_data_get_mechanism (auth_data), "password") ||
150 tp_strdiff (ag_auth_data_get_method (auth_data), "password"))
152 g_simple_async_result_set_error (result,
153 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
154 "Service does not use password authentication");
155 g_simple_async_result_complete_in_idle (result);
159 g_object_unref (manager);
160 g_object_unref (account);
162 *ret_auth_data = auth_data;
166 g_clear_object (&manager);
167 g_clear_object (&account);
168 g_clear_object (&service);
169 tp_clear_pointer (&auth_data, ag_auth_data_unref);
174 uoa_session_process_cb (SignonAuthSession *session,
175 GHashTable *session_data,
179 GSimpleAsyncResult *result = user_data;
180 const gchar *password;
184 g_simple_async_result_set_from_error (result, error);
188 password = tp_asv_get_string (session_data, "Secret");
189 if (tp_str_empty (password))
191 g_simple_async_result_set_error (result, TP_ERROR,
192 TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
196 g_simple_async_result_set_op_res_gpointer (result, g_strdup (password),
200 /* libaccounts-glib API does not guarantee the callback happens after
201 * reentering mainloop */
202 g_simple_async_result_complete_in_idle (result);
203 g_object_unref (result);
204 g_object_unref (session);
208 uoa_get_account_password (TpAccount *tp_account,
209 GSimpleAsyncResult *result)
211 AgAccountService *service;
212 AgAuthData *auth_data;
214 SignonIdentity *identity;
215 SignonAuthSession *session;
216 GError *error = NULL;
218 DEBUG ("Store password for %s in signond",
219 tp_account_get_path_suffix (tp_account));
221 service = uoa_password_common (tp_account, result, &auth_data);
225 cred_id = ag_auth_data_get_credentials_id (auth_data);
228 g_simple_async_result_set_error (result,
229 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
230 "AgAccount has no CredentialsId");
231 g_simple_async_result_complete_in_idle (result);
235 identity = signon_identity_new_from_db (cred_id);
236 session = signon_identity_create_session (identity,
237 ag_auth_data_get_method (auth_data), &error);
238 g_object_unref (identity);
242 g_simple_async_result_set_from_error (result, error);
243 g_simple_async_result_complete_in_idle (result);
247 signon_auth_session_process (session,
248 ag_auth_data_get_parameters (auth_data),
249 ag_auth_data_get_mechanism (auth_data),
250 uoa_session_process_cb,
251 g_object_ref (result));
254 ag_auth_data_unref (auth_data);
255 g_object_unref (service);
260 empathy_keyring_get_account_password_async (TpAccount *account,
261 GAsyncReadyCallback callback,
264 GSimpleAsyncResult *simple;
265 const gchar *account_id;
267 g_return_if_fail (TP_IS_ACCOUNT (account));
268 g_return_if_fail (callback != NULL);
270 simple = g_simple_async_result_new (G_OBJECT (account), callback,
271 user_data, empathy_keyring_get_account_password_async);
273 account_id = tp_proxy_get_object_path (account) +
274 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
276 DEBUG ("Trying to get password for: %s", account_id);
280 const gchar *provider;
282 provider = tp_account_get_storage_provider (account);
283 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
285 uoa_get_account_password (account, simple);
286 g_object_unref (simple);
292 secret_password_lookup (&account_keyring_schema, NULL,
293 lookup_item_cb, simple,
294 "account-id", account_id,
295 "param-name", "password",
300 empathy_keyring_get_room_password_async (TpAccount *account,
302 GAsyncReadyCallback callback,
305 GSimpleAsyncResult *simple;
306 const gchar *account_id;
308 g_return_if_fail (TP_IS_ACCOUNT (account));
309 g_return_if_fail (id != NULL);
310 g_return_if_fail (callback != NULL);
312 simple = g_simple_async_result_new (G_OBJECT (account), callback,
313 user_data, empathy_keyring_get_room_password_async);
315 account_id = tp_proxy_get_object_path (account) +
316 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
318 DEBUG ("Trying to get password for room '%s' on account '%s'",
321 secret_password_lookup (&room_keyring_schema, NULL,
322 lookup_item_cb, simple,
323 "account-id", account_id,
329 empathy_keyring_get_account_password_finish (TpAccount *account,
330 GAsyncResult *result,
333 empathy_implement_finish_return_pointer (account,
334 empathy_keyring_get_account_password_async);
338 empathy_keyring_get_room_password_finish (TpAccount *account,
339 GAsyncResult *result,
342 empathy_implement_finish_return_pointer (account,
343 empathy_keyring_get_room_password_async);
349 store_password_cb (GObject *source,
350 GAsyncResult *result,
353 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
354 GError *error = NULL;
356 if (!secret_password_store_finish (result, &error))
358 g_simple_async_result_set_error (simple, TP_ERROR,
359 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
360 g_error_free (error);
363 g_simple_async_result_complete (simple);
364 g_object_unref (simple);
370 AgAccountService *service;
373 GSimpleAsyncResult *result;
374 } UoaChangePasswordData;
376 static UoaChangePasswordData *
377 uoa_change_password_data_new (AgAccountService *service,
378 const gchar *password,
380 GSimpleAsyncResult *result)
382 UoaChangePasswordData *data;
384 data = g_slice_new0 (UoaChangePasswordData);
385 data->service = g_object_ref (service);
386 data->password = g_strdup (password);
387 data->remember = remember;
388 data->result = g_object_ref (result);
394 uoa_change_password_data_free (UoaChangePasswordData *data)
396 g_object_unref (data->service);
397 g_free (data->password);
398 g_object_unref (data->result);
399 g_slice_free (UoaChangePasswordData, data);
403 uoa_identity_store_cb (SignonIdentity *identity,
408 UoaChangePasswordData *data = user_data;
411 g_simple_async_result_set_from_error (data->result, error);
413 g_simple_async_result_complete (data->result);
414 uoa_change_password_data_free (data);
415 g_object_unref (identity);
419 uoa_identity_query_info_cb (SignonIdentity *identity,
420 const SignonIdentityInfo *info,
424 UoaChangePasswordData *data = user_data;
428 g_simple_async_result_set_from_error (data->result, error);
429 /* libaccounts-glib API does not guarantee the callback happens after
430 * reentering mainloop */
431 g_simple_async_result_complete_in_idle (data->result);
432 uoa_change_password_data_free (data);
433 g_object_unref (identity);
437 /* const SignonIdentityInfo is a lie, cast it! - Mardy */
438 signon_identity_info_set_secret ((SignonIdentityInfo *) info,
439 data->password, data->remember);
441 signon_identity_store_credentials_with_info (identity, info,
442 uoa_identity_store_cb, data);
446 uoa_initial_account_store_cb (AgAccount *account,
450 UoaChangePasswordData *data = user_data;
453 g_simple_async_result_set_from_error (data->result, error);
455 /* libaccounts-glib API does not guarantee the callback happens after
456 * reentering mainloop */
457 g_simple_async_result_complete_in_idle (data->result);
458 uoa_change_password_data_free (data);
462 uoa_initial_identity_store_cb (SignonIdentity *identity,
467 UoaChangePasswordData *data = user_data;
468 AgAccount *account = ag_account_service_get_account (data->service);
469 GValue value = G_VALUE_INIT;
473 g_simple_async_result_set_from_error (data->result, error);
474 /* libaccounts-glib API does not guarantee the callback happens after
475 * reentering mainloop */
476 g_simple_async_result_complete_in_idle (data->result);
477 uoa_change_password_data_free (data);
478 g_object_unref (identity);
482 g_value_init (&value, G_TYPE_UINT);
483 g_value_set_uint (&value, id);
484 ag_account_select_service (account, NULL);
485 ag_account_set_value (account, "CredentialsId", &value);
486 g_value_unset (&value);
488 ag_account_store (account, uoa_initial_account_store_cb, data);
490 g_object_unref (identity);
494 uoa_set_account_password (TpAccount *tp_account,
495 const gchar *password,
497 GSimpleAsyncResult *result)
499 AgAccountService *service;
500 AgAuthData *auth_data;
502 UoaChangePasswordData *data;
503 SignonIdentity *identity;
505 DEBUG ("Store password for %s in signond",
506 tp_account_get_path_suffix (tp_account));
508 service = uoa_password_common (tp_account, result, &auth_data);
512 data = uoa_change_password_data_new (service, password, remember, result);
514 cred_id = ag_auth_data_get_credentials_id (auth_data);
517 SignonIdentityInfo *info;
518 const GHashTable *params;
519 const gchar *username;
520 const gchar *acl_all[] = { "*", NULL };
522 /* This is the first time we store password for this account.
523 * First check if we have an 'username' param as this is more accurate
524 * in the tp-idle case. */
525 params = tp_account_get_parameters (tp_account);
526 username = tp_asv_get_string (params, "username");
527 if (username == NULL)
528 username = tp_asv_get_string (params, "account");
530 identity = signon_identity_new ();
531 info = signon_identity_info_new ();
532 signon_identity_info_set_username (info, username);
533 signon_identity_info_set_secret (info, password, remember);
534 signon_identity_info_set_access_control_list (info, acl_all);
536 /* Give identity and data ownership to the callback */
537 signon_identity_store_credentials_with_info (identity, info,
538 uoa_initial_identity_store_cb, data);
540 signon_identity_info_free (info);
544 /* There is already a password stored, query info to update it.
545 * Give identity and data ownership to the callback */
546 identity = signon_identity_new_from_db (cred_id);
547 signon_identity_query_info (identity,
548 uoa_identity_query_info_cb, data);
551 g_object_unref (service);
552 ag_auth_data_unref (auth_data);
557 empathy_keyring_set_account_password_async (TpAccount *account,
558 const gchar *password,
560 GAsyncReadyCallback callback,
563 GSimpleAsyncResult *simple;
564 const gchar *account_id;
567 g_return_if_fail (TP_IS_ACCOUNT (account));
568 g_return_if_fail (password != NULL);
570 simple = g_simple_async_result_new (G_OBJECT (account), callback,
571 user_data, empathy_keyring_set_account_password_async);
573 account_id = tp_proxy_get_object_path (account) +
574 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
576 DEBUG ("Remembering password for %s", account_id);
580 const gchar *provider;
582 provider = tp_account_get_storage_provider (account);
583 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
585 uoa_set_account_password (account, password, remember, simple);
586 g_object_unref (simple);
592 name = g_strdup_printf (_("IM account password for %s (%s)"),
593 tp_account_get_display_name (account), account_id);
595 secret_password_store (&account_keyring_schema,
596 remember ? NULL : SECRET_COLLECTION_SESSION,
598 NULL, store_password_cb, simple,
599 "account-id", account_id,
600 "param-name", "password",
607 empathy_keyring_set_room_password_async (TpAccount *account,
609 const gchar *password,
610 GAsyncReadyCallback callback,
613 GSimpleAsyncResult *simple;
614 const gchar *account_id;
617 g_return_if_fail (TP_IS_ACCOUNT (account));
618 g_return_if_fail (id != NULL);
619 g_return_if_fail (password != NULL);
621 simple = g_simple_async_result_new (G_OBJECT (account), callback,
622 user_data, empathy_keyring_set_room_password_async);
624 account_id = tp_proxy_get_object_path (account) +
625 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
627 DEBUG ("Remembering password for room '%s' on account '%s'", id, account_id);
629 name = g_strdup_printf (_("Password for chatroom '%s' on account %s (%s)"),
630 id, tp_account_get_display_name (account), account_id);
632 secret_password_store (&room_keyring_schema, NULL, name, password,
633 NULL, store_password_cb, simple,
634 "account-id", account_id,
642 empathy_keyring_set_account_password_finish (TpAccount *account,
643 GAsyncResult *result,
646 empathy_implement_finish_void (account, empathy_keyring_set_account_password_async);
650 empathy_keyring_set_room_password_finish (TpAccount *account,
651 GAsyncResult *result,
654 empathy_implement_finish_void (account, empathy_keyring_set_room_password_async);
660 items_delete_cb (GObject *source,
661 GAsyncResult *result,
664 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
665 GError *error = NULL;
667 if (!secret_password_clear_finish (result, &error))
669 g_simple_async_result_set_error (simple, TP_ERROR,
670 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
671 g_error_free (error);
674 g_simple_async_result_complete (simple);
675 g_object_unref (simple);
679 empathy_keyring_delete_account_password_async (TpAccount *account,
680 GAsyncReadyCallback callback,
683 GSimpleAsyncResult *simple;
684 const gchar *account_id;
686 g_return_if_fail (TP_IS_ACCOUNT (account));
688 simple = g_simple_async_result_new (G_OBJECT (account), callback,
689 user_data, empathy_keyring_delete_account_password_async);
691 account_id = tp_proxy_get_object_path (account) +
692 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
694 DEBUG ("Deleting password for %s", account_id);
698 const gchar *provider;
700 provider = tp_account_get_storage_provider (account);
701 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
703 /* I see no other way to forget the stored password than overwriting
704 * with an empty one. */
705 uoa_set_account_password (account, "", FALSE, simple);
706 g_object_unref (simple);
712 secret_password_clear (&account_keyring_schema, NULL,
713 items_delete_cb, simple,
714 "account-id", account_id,
715 "param-name", "password",
720 empathy_keyring_delete_account_password_finish (TpAccount *account,
721 GAsyncResult *result,
724 empathy_implement_finish_void (account, empathy_keyring_delete_account_password_async);