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
20 #include "tpaw-keyring.h"
22 #include <glib/gi18n-lib.h>
23 #include <libsecret/secret.h>
26 #include <libaccounts-glib/ag-account.h>
27 #include <libaccounts-glib/ag-account-service.h>
28 #include <libaccounts-glib/ag-auth-data.h>
29 #include <libaccounts-glib/ag-manager.h>
30 #include <libaccounts-glib/ag-service.h>
31 #include <libsignon-glib/signon-identity.h>
32 #include "tpaw-uoa-utils.h"
35 #include "empathy-utils.h"
37 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
38 #include "empathy-debug.h"
40 /* We cannot change the schema name for compatibility, so it's still
41 * referring to Empathy */
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 },
57 lookup_item_cb (GObject *source,
61 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
65 password = secret_password_lookup_finish (result, &error);
68 g_simple_async_result_set_error (simple, TP_ERROR,
69 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
70 g_clear_error (&error);
76 g_simple_async_result_set_error (simple, TP_ERROR,
77 TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
81 g_simple_async_result_set_op_res_gpointer (simple, password,
82 (GDestroyNotify) secret_password_free);
85 g_simple_async_result_complete (simple);
86 g_object_unref (simple);
90 static AgAccountService *
91 uoa_password_common (TpAccount *tp_account,
92 GSimpleAsyncResult *result,
93 AgAuthData **ret_auth_data)
95 const GValue *storage_id;
96 AgAccountId account_id;
97 AgManager *manager = NULL;
98 AgAccount *account = NULL;
100 AgAccountService *service = NULL;
101 AgAuthData *auth_data = NULL;
103 g_assert (ret_auth_data != NULL);
104 *ret_auth_data = NULL;
106 storage_id = tp_account_get_storage_identifier (tp_account);
107 account_id = g_value_get_uint (storage_id);
110 g_simple_async_result_set_error (result,
111 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
112 "StorageId is invalid, cannot get the AgAccount for this TpAccount");
113 g_simple_async_result_complete_in_idle (result);
117 manager = tpaw_uoa_manager_dup ();
118 account = ag_manager_get_account (manager, account_id);
120 /* Assuming there is only one IM service */
121 l = ag_account_list_services_by_type (account, TPAW_UOA_SERVICE_TYPE);
124 g_simple_async_result_set_error (result,
125 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
126 "AgAccount has no IM service");
127 g_simple_async_result_complete_in_idle (result);
130 service = ag_account_service_new (account, l->data);
131 ag_service_list_free (l);
133 auth_data = ag_account_service_get_auth_data (service);
134 if (auth_data == NULL)
136 g_simple_async_result_set_error (result,
137 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
138 "Service has no AgAuthData");
139 g_simple_async_result_complete_in_idle (result);
143 if (tp_strdiff (ag_auth_data_get_mechanism (auth_data), "password") ||
144 tp_strdiff (ag_auth_data_get_method (auth_data), "password"))
146 g_simple_async_result_set_error (result,
147 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
148 "Service does not use password authentication");
149 g_simple_async_result_complete_in_idle (result);
153 g_object_unref (manager);
154 g_object_unref (account);
156 *ret_auth_data = auth_data;
160 g_clear_object (&manager);
161 g_clear_object (&account);
162 g_clear_object (&service);
163 tp_clear_pointer (&auth_data, ag_auth_data_unref);
168 uoa_session_process_cb (SignonAuthSession *session,
169 GHashTable *session_data,
173 GSimpleAsyncResult *result = user_data;
174 const gchar *password;
178 g_simple_async_result_set_from_error (result, error);
182 password = tp_asv_get_string (session_data, "Secret");
183 if (tp_str_empty (password))
185 g_simple_async_result_set_error (result, TP_ERROR,
186 TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
190 g_simple_async_result_set_op_res_gpointer (result, g_strdup (password),
194 /* libaccounts-glib API does not guarantee the callback happens after
195 * reentering mainloop */
196 g_simple_async_result_complete_in_idle (result);
197 g_object_unref (result);
198 g_object_unref (session);
202 uoa_get_account_password (TpAccount *tp_account,
203 GSimpleAsyncResult *result)
205 AgAccountService *service;
206 AgAuthData *auth_data;
208 SignonIdentity *identity;
209 SignonAuthSession *session;
210 GError *error = NULL;
212 DEBUG ("Store password for %s in signond",
213 tp_account_get_path_suffix (tp_account));
215 service = uoa_password_common (tp_account, result, &auth_data);
219 cred_id = ag_auth_data_get_credentials_id (auth_data);
222 g_simple_async_result_set_error (result,
223 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
224 "AgAccount has no CredentialsId");
225 g_simple_async_result_complete_in_idle (result);
229 identity = signon_identity_new_from_db (cred_id);
230 session = signon_identity_create_session (identity,
231 ag_auth_data_get_method (auth_data), &error);
232 g_object_unref (identity);
236 g_simple_async_result_set_from_error (result, error);
237 g_simple_async_result_complete_in_idle (result);
241 signon_auth_session_process (session,
242 ag_auth_data_get_parameters (auth_data),
243 ag_auth_data_get_mechanism (auth_data),
244 uoa_session_process_cb,
245 g_object_ref (result));
248 ag_auth_data_unref (auth_data);
249 g_object_unref (service);
254 tpaw_keyring_get_account_password_async (TpAccount *account,
255 GAsyncReadyCallback callback,
258 GSimpleAsyncResult *simple;
259 const gchar *account_id;
261 g_return_if_fail (TP_IS_ACCOUNT (account));
262 g_return_if_fail (callback != NULL);
264 simple = g_simple_async_result_new (G_OBJECT (account), callback,
265 user_data, tpaw_keyring_get_account_password_async);
267 account_id = tp_proxy_get_object_path (account) +
268 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
270 DEBUG ("Trying to get password for: %s", account_id);
274 const gchar *provider;
276 provider = tp_account_get_storage_provider (account);
277 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
279 uoa_get_account_password (account, simple);
280 g_object_unref (simple);
286 secret_password_lookup (&account_keyring_schema, NULL,
287 lookup_item_cb, simple,
288 "account-id", account_id,
289 "param-name", "password",
294 tpaw_keyring_get_room_password_async (TpAccount *account,
296 GAsyncReadyCallback callback,
299 GSimpleAsyncResult *simple;
300 const gchar *account_id;
302 g_return_if_fail (TP_IS_ACCOUNT (account));
303 g_return_if_fail (id != NULL);
304 g_return_if_fail (callback != NULL);
306 simple = g_simple_async_result_new (G_OBJECT (account), callback,
307 user_data, tpaw_keyring_get_room_password_async);
309 account_id = tp_proxy_get_object_path (account) +
310 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
312 DEBUG ("Trying to get password for room '%s' on account '%s'",
315 secret_password_lookup (&room_keyring_schema, NULL,
316 lookup_item_cb, simple,
317 "account-id", account_id,
323 tpaw_keyring_get_account_password_finish (TpAccount *account,
324 GAsyncResult *result,
327 empathy_implement_finish_return_pointer (account,
328 tpaw_keyring_get_account_password_async);
332 tpaw_keyring_get_room_password_finish (TpAccount *account,
333 GAsyncResult *result,
336 empathy_implement_finish_return_pointer (account,
337 tpaw_keyring_get_room_password_async);
343 store_password_cb (GObject *source,
344 GAsyncResult *result,
347 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
348 GError *error = NULL;
350 if (!secret_password_store_finish (result, &error))
352 g_simple_async_result_set_error (simple, TP_ERROR,
353 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
354 g_error_free (error);
357 g_simple_async_result_complete (simple);
358 g_object_unref (simple);
364 AgAccountService *service;
367 GSimpleAsyncResult *result;
368 } UoaChangePasswordData;
370 static UoaChangePasswordData *
371 uoa_change_password_data_new (AgAccountService *service,
372 const gchar *password,
374 GSimpleAsyncResult *result)
376 UoaChangePasswordData *data;
378 data = g_slice_new0 (UoaChangePasswordData);
379 data->service = g_object_ref (service);
380 data->password = g_strdup (password);
381 data->remember = remember;
382 data->result = g_object_ref (result);
388 uoa_change_password_data_free (UoaChangePasswordData *data)
390 g_object_unref (data->service);
391 g_free (data->password);
392 g_object_unref (data->result);
393 g_slice_free (UoaChangePasswordData, data);
397 uoa_identity_store_cb (SignonIdentity *identity,
402 UoaChangePasswordData *data = user_data;
405 g_simple_async_result_set_from_error (data->result, error);
407 g_simple_async_result_complete (data->result);
408 uoa_change_password_data_free (data);
409 g_object_unref (identity);
413 uoa_identity_query_info_cb (SignonIdentity *identity,
414 const SignonIdentityInfo *info,
418 UoaChangePasswordData *data = user_data;
422 g_simple_async_result_set_from_error (data->result, error);
423 /* libaccounts-glib API does not guarantee the callback happens after
424 * reentering mainloop */
425 g_simple_async_result_complete_in_idle (data->result);
426 uoa_change_password_data_free (data);
427 g_object_unref (identity);
431 /* const SignonIdentityInfo is a lie, cast it! - Mardy */
432 signon_identity_info_set_secret ((SignonIdentityInfo *) info,
433 data->password, data->remember);
435 signon_identity_store_credentials_with_info (identity, info,
436 uoa_identity_store_cb, data);
440 uoa_initial_account_store_cb (AgAccount *account,
444 UoaChangePasswordData *data = user_data;
447 g_simple_async_result_set_from_error (data->result, error);
449 /* libaccounts-glib API does not guarantee the callback happens after
450 * reentering mainloop */
451 g_simple_async_result_complete_in_idle (data->result);
452 uoa_change_password_data_free (data);
456 uoa_initial_identity_store_cb (SignonIdentity *identity,
461 UoaChangePasswordData *data = user_data;
462 AgAccount *account = ag_account_service_get_account (data->service);
463 GValue value = G_VALUE_INIT;
467 g_simple_async_result_set_from_error (data->result, error);
468 /* libaccounts-glib API does not guarantee the callback happens after
469 * reentering mainloop */
470 g_simple_async_result_complete_in_idle (data->result);
471 uoa_change_password_data_free (data);
472 g_object_unref (identity);
476 g_value_init (&value, G_TYPE_UINT);
477 g_value_set_uint (&value, id);
478 ag_account_select_service (account, NULL);
479 ag_account_set_value (account, "CredentialsId", &value);
480 g_value_unset (&value);
482 ag_account_store (account, uoa_initial_account_store_cb, data);
484 g_object_unref (identity);
488 uoa_set_account_password (TpAccount *tp_account,
489 const gchar *password,
491 GSimpleAsyncResult *result)
493 AgAccountService *service;
494 AgAuthData *auth_data;
496 UoaChangePasswordData *data;
497 SignonIdentity *identity;
499 DEBUG ("Store password for %s in signond",
500 tp_account_get_path_suffix (tp_account));
502 service = uoa_password_common (tp_account, result, &auth_data);
506 data = uoa_change_password_data_new (service, password, remember, result);
508 cred_id = ag_auth_data_get_credentials_id (auth_data);
511 SignonIdentityInfo *info;
512 const GHashTable *params;
513 const gchar *username;
514 const gchar *acl_all[] = { "*", NULL };
516 /* This is the first time we store password for this account.
517 * First check if we have an 'username' param as this is more accurate
518 * in the tp-idle case. */
519 params = tp_account_get_parameters (tp_account);
520 username = tp_asv_get_string (params, "username");
521 if (username == NULL)
522 username = tp_asv_get_string (params, "account");
524 identity = signon_identity_new ();
525 info = signon_identity_info_new ();
526 signon_identity_info_set_username (info, username);
527 signon_identity_info_set_secret (info, password, remember);
528 signon_identity_info_set_access_control_list (info, acl_all);
530 /* Give identity and data ownership to the callback */
531 signon_identity_store_credentials_with_info (identity, info,
532 uoa_initial_identity_store_cb, data);
534 signon_identity_info_free (info);
538 /* There is already a password stored, query info to update it.
539 * Give identity and data ownership to the callback */
540 identity = signon_identity_new_from_db (cred_id);
541 signon_identity_query_info (identity,
542 uoa_identity_query_info_cb, data);
545 g_object_unref (service);
546 ag_auth_data_unref (auth_data);
551 tpaw_keyring_set_account_password_async (TpAccount *account,
552 const gchar *password,
554 GAsyncReadyCallback callback,
557 GSimpleAsyncResult *simple;
558 const gchar *account_id;
561 g_return_if_fail (TP_IS_ACCOUNT (account));
562 g_return_if_fail (password != NULL);
564 simple = g_simple_async_result_new (G_OBJECT (account), callback,
565 user_data, tpaw_keyring_set_account_password_async);
567 account_id = tp_proxy_get_object_path (account) +
568 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
570 DEBUG ("Remembering password for %s", account_id);
574 const gchar *provider;
576 provider = tp_account_get_storage_provider (account);
577 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
579 uoa_set_account_password (account, password, remember, simple);
580 g_object_unref (simple);
586 name = g_strdup_printf (_("IM account password for %s (%s)"),
587 tp_account_get_display_name (account), account_id);
589 secret_password_store (&account_keyring_schema,
590 remember ? NULL : SECRET_COLLECTION_SESSION,
592 NULL, store_password_cb, simple,
593 "account-id", account_id,
594 "param-name", "password",
601 tpaw_keyring_set_room_password_async (TpAccount *account,
603 const gchar *password,
604 GAsyncReadyCallback callback,
607 GSimpleAsyncResult *simple;
608 const gchar *account_id;
611 g_return_if_fail (TP_IS_ACCOUNT (account));
612 g_return_if_fail (id != NULL);
613 g_return_if_fail (password != NULL);
615 simple = g_simple_async_result_new (G_OBJECT (account), callback,
616 user_data, tpaw_keyring_set_room_password_async);
618 account_id = tp_proxy_get_object_path (account) +
619 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
621 DEBUG ("Remembering password for room '%s' on account '%s'", id, account_id);
623 name = g_strdup_printf (_("Password for chatroom '%s' on account %s (%s)"),
624 id, tp_account_get_display_name (account), account_id);
626 secret_password_store (&room_keyring_schema, NULL, name, password,
627 NULL, store_password_cb, simple,
628 "account-id", account_id,
636 tpaw_keyring_set_account_password_finish (TpAccount *account,
637 GAsyncResult *result,
640 empathy_implement_finish_void (account, tpaw_keyring_set_account_password_async);
644 tpaw_keyring_set_room_password_finish (TpAccount *account,
645 GAsyncResult *result,
648 empathy_implement_finish_void (account, tpaw_keyring_set_room_password_async);
654 items_delete_cb (GObject *source,
655 GAsyncResult *result,
658 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
659 GError *error = NULL;
661 secret_password_clear_finish (result, &error);
664 g_simple_async_result_set_error (simple, TP_ERROR,
665 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
666 g_error_free (error);
669 g_simple_async_result_complete (simple);
670 g_object_unref (simple);
674 tpaw_keyring_delete_account_password_async (TpAccount *account,
675 GAsyncReadyCallback callback,
678 GSimpleAsyncResult *simple;
679 const gchar *account_id;
681 g_return_if_fail (TP_IS_ACCOUNT (account));
683 simple = g_simple_async_result_new (G_OBJECT (account), callback,
684 user_data, tpaw_keyring_delete_account_password_async);
686 account_id = tp_proxy_get_object_path (account) +
687 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
689 DEBUG ("Deleting password for %s", account_id);
693 const gchar *provider;
695 provider = tp_account_get_storage_provider (account);
696 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
698 /* I see no other way to forget the stored password than overwriting
699 * with an empty one. */
700 uoa_set_account_password (account, "", FALSE, simple);
701 g_object_unref (simple);
707 secret_password_clear (&account_keyring_schema, NULL,
708 items_delete_cb, simple,
709 "account-id", account_id,
710 "param-name", "password",
715 tpaw_keyring_delete_account_password_finish (TpAccount *account,
716 GAsyncResult *result,
719 empathy_implement_finish_void (account, tpaw_keyring_delete_account_password_async);