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 "empathy-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 "empathy-uoa-utils.h"
35 #include "empathy-utils.h"
37 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
38 #include "empathy-debug.h"
40 static const SecretSchema account_keyring_schema =
41 { "org.gnome.Empathy.Account", SECRET_SCHEMA_DONT_MATCH_NAME,
42 { { "account-id", SECRET_SCHEMA_ATTRIBUTE_STRING },
43 { "param-name", SECRET_SCHEMA_ATTRIBUTE_STRING },
46 static const SecretSchema room_keyring_schema =
47 { "org.gnome.Empathy.Room", SECRET_SCHEMA_DONT_MATCH_NAME,
48 { { "account-id", SECRET_SCHEMA_ATTRIBUTE_STRING },
49 { "room-id", SECRET_SCHEMA_ATTRIBUTE_STRING },
53 empathy_keyring_is_available (void)
61 lookup_item_cb (GObject *source,
65 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
69 password = secret_password_lookup_finish (result, &error);
72 g_simple_async_result_set_error (simple, TP_ERROR,
73 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
74 g_clear_error (&error);
80 g_simple_async_result_set_error (simple, TP_ERROR,
81 TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
85 g_simple_async_result_set_op_res_gpointer (simple, password,
86 (GDestroyNotify) secret_password_free);
89 g_simple_async_result_complete (simple);
90 g_object_unref (simple);
94 static AgAccountService *
95 uoa_password_common (TpAccount *tp_account,
96 GSimpleAsyncResult *result,
97 AgAuthData **ret_auth_data)
99 const GValue *storage_id;
100 AgAccountId account_id;
101 AgManager *manager = NULL;
102 AgAccount *account = NULL;
104 AgAccountService *service = NULL;
105 AgAuthData *auth_data = NULL;
107 g_assert (ret_auth_data != NULL);
108 *ret_auth_data = NULL;
110 storage_id = tp_account_get_storage_identifier (tp_account);
111 account_id = g_value_get_uint (storage_id);
114 g_simple_async_result_set_error (result,
115 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
116 "StorageId is invalid, cannot get the AgAccount for this TpAccount");
117 g_simple_async_result_complete_in_idle (result);
121 manager = empathy_uoa_manager_dup ();
122 account = ag_manager_get_account (manager, account_id);
124 /* Assuming there is only one IM service */
125 l = ag_account_list_services_by_type (account, EMPATHY_UOA_SERVICE_TYPE);
128 g_simple_async_result_set_error (result,
129 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
130 "AgAccount has no IM service");
131 g_simple_async_result_complete_in_idle (result);
134 service = ag_account_service_new (account, l->data);
135 ag_service_list_free (l);
137 auth_data = ag_account_service_get_auth_data (service);
138 if (auth_data == NULL)
140 g_simple_async_result_set_error (result,
141 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
142 "Service has no AgAuthData");
143 g_simple_async_result_complete_in_idle (result);
147 if (tp_strdiff (ag_auth_data_get_mechanism (auth_data), "password") ||
148 tp_strdiff (ag_auth_data_get_method (auth_data), "password"))
150 g_simple_async_result_set_error (result,
151 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
152 "Service does not use password authentication");
153 g_simple_async_result_complete_in_idle (result);
157 g_object_unref (manager);
158 g_object_unref (account);
160 *ret_auth_data = auth_data;
164 g_clear_object (&manager);
165 g_clear_object (&account);
166 g_clear_object (&service);
167 tp_clear_pointer (&auth_data, ag_auth_data_unref);
172 uoa_session_process_cb (SignonAuthSession *session,
173 GHashTable *session_data,
177 GSimpleAsyncResult *result = user_data;
178 const gchar *password;
182 g_simple_async_result_set_from_error (result, error);
186 password = tp_asv_get_string (session_data, "Secret");
187 if (tp_str_empty (password))
189 g_simple_async_result_set_error (result, TP_ERROR,
190 TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
194 g_simple_async_result_set_op_res_gpointer (result, g_strdup (password),
198 /* libaccounts-glib API does not guarantee the callback happens after
199 * reentering mainloop */
200 g_simple_async_result_complete_in_idle (result);
201 g_object_unref (result);
202 g_object_unref (session);
206 uoa_get_account_password (TpAccount *tp_account,
207 GSimpleAsyncResult *result)
209 AgAccountService *service;
210 AgAuthData *auth_data;
212 SignonIdentity *identity;
213 SignonAuthSession *session;
214 GError *error = NULL;
216 DEBUG ("Store password for %s in signond",
217 tp_account_get_path_suffix (tp_account));
219 service = uoa_password_common (tp_account, result, &auth_data);
223 cred_id = ag_auth_data_get_credentials_id (auth_data);
226 g_simple_async_result_set_error (result,
227 TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
228 "AgAccount has no CredentialsId");
229 g_simple_async_result_complete_in_idle (result);
233 identity = signon_identity_new_from_db (cred_id);
234 session = signon_identity_create_session (identity,
235 ag_auth_data_get_method (auth_data), &error);
236 g_object_unref (identity);
240 g_simple_async_result_set_from_error (result, error);
241 g_simple_async_result_complete_in_idle (result);
245 signon_auth_session_process (session,
246 ag_auth_data_get_parameters (auth_data),
247 ag_auth_data_get_mechanism (auth_data),
248 uoa_session_process_cb,
249 g_object_ref (result));
252 ag_auth_data_unref (auth_data);
253 g_object_unref (service);
258 empathy_keyring_get_account_password_async (TpAccount *account,
259 GAsyncReadyCallback callback,
262 GSimpleAsyncResult *simple;
263 const gchar *account_id;
265 g_return_if_fail (TP_IS_ACCOUNT (account));
266 g_return_if_fail (callback != NULL);
268 simple = g_simple_async_result_new (G_OBJECT (account), callback,
269 user_data, empathy_keyring_get_account_password_async);
271 account_id = tp_proxy_get_object_path (account) +
272 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
274 DEBUG ("Trying to get password for: %s", account_id);
278 const gchar *provider;
280 provider = tp_account_get_storage_provider (account);
281 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
283 uoa_get_account_password (account, simple);
284 g_object_unref (simple);
290 secret_password_lookup (&account_keyring_schema, NULL,
291 lookup_item_cb, simple,
292 "account-id", account_id,
293 "param-name", "password",
298 empathy_keyring_get_room_password_async (TpAccount *account,
300 GAsyncReadyCallback callback,
303 GSimpleAsyncResult *simple;
304 const gchar *account_id;
306 g_return_if_fail (TP_IS_ACCOUNT (account));
307 g_return_if_fail (id != NULL);
308 g_return_if_fail (callback != NULL);
310 simple = g_simple_async_result_new (G_OBJECT (account), callback,
311 user_data, empathy_keyring_get_room_password_async);
313 account_id = tp_proxy_get_object_path (account) +
314 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
316 DEBUG ("Trying to get password for room '%s' on account '%s'",
319 secret_password_lookup (&room_keyring_schema, NULL,
320 lookup_item_cb, simple,
321 "account-id", account_id,
327 empathy_keyring_get_account_password_finish (TpAccount *account,
328 GAsyncResult *result,
331 empathy_implement_finish_return_pointer (account,
332 empathy_keyring_get_account_password_async);
336 empathy_keyring_get_room_password_finish (TpAccount *account,
337 GAsyncResult *result,
340 empathy_implement_finish_return_pointer (account,
341 empathy_keyring_get_room_password_async);
347 store_password_cb (GObject *source,
348 GAsyncResult *result,
351 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
352 GError *error = NULL;
354 if (!secret_password_store_finish (result, &error))
356 g_simple_async_result_set_error (simple, TP_ERROR,
357 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
358 g_error_free (error);
361 g_simple_async_result_complete (simple);
362 g_object_unref (simple);
368 AgAccountService *service;
371 GSimpleAsyncResult *result;
372 } UoaChangePasswordData;
374 static UoaChangePasswordData *
375 uoa_change_password_data_new (AgAccountService *service,
376 const gchar *password,
378 GSimpleAsyncResult *result)
380 UoaChangePasswordData *data;
382 data = g_slice_new0 (UoaChangePasswordData);
383 data->service = g_object_ref (service);
384 data->password = g_strdup (password);
385 data->remember = remember;
386 data->result = g_object_ref (result);
392 uoa_change_password_data_free (UoaChangePasswordData *data)
394 g_object_unref (data->service);
395 g_free (data->password);
396 g_object_unref (data->result);
397 g_slice_free (UoaChangePasswordData, data);
401 uoa_identity_store_cb (SignonIdentity *identity,
406 UoaChangePasswordData *data = user_data;
409 g_simple_async_result_set_from_error (data->result, error);
411 g_simple_async_result_complete (data->result);
412 uoa_change_password_data_free (data);
413 g_object_unref (identity);
417 uoa_identity_query_info_cb (SignonIdentity *identity,
418 const SignonIdentityInfo *info,
422 UoaChangePasswordData *data = user_data;
426 g_simple_async_result_set_from_error (data->result, error);
427 /* libaccounts-glib API does not guarantee the callback happens after
428 * reentering mainloop */
429 g_simple_async_result_complete_in_idle (data->result);
430 uoa_change_password_data_free (data);
431 g_object_unref (identity);
435 /* const SignonIdentityInfo is a lie, cast it! - Mardy */
436 signon_identity_info_set_secret ((SignonIdentityInfo *) info,
437 data->password, data->remember);
439 signon_identity_store_credentials_with_info (identity, info,
440 uoa_identity_store_cb, data);
444 uoa_initial_account_store_cb (AgAccount *account,
448 UoaChangePasswordData *data = user_data;
451 g_simple_async_result_set_from_error (data->result, error);
453 /* libaccounts-glib API does not guarantee the callback happens after
454 * reentering mainloop */
455 g_simple_async_result_complete_in_idle (data->result);
456 uoa_change_password_data_free (data);
460 uoa_initial_identity_store_cb (SignonIdentity *identity,
465 UoaChangePasswordData *data = user_data;
466 AgAccount *account = ag_account_service_get_account (data->service);
467 GValue value = G_VALUE_INIT;
471 g_simple_async_result_set_from_error (data->result, error);
472 /* libaccounts-glib API does not guarantee the callback happens after
473 * reentering mainloop */
474 g_simple_async_result_complete_in_idle (data->result);
475 uoa_change_password_data_free (data);
476 g_object_unref (identity);
480 g_value_init (&value, G_TYPE_UINT);
481 g_value_set_uint (&value, id);
482 ag_account_select_service (account, NULL);
483 ag_account_set_value (account, "CredentialsId", &value);
484 g_value_unset (&value);
486 ag_account_store (account, uoa_initial_account_store_cb, data);
488 g_object_unref (identity);
492 uoa_set_account_password (TpAccount *tp_account,
493 const gchar *password,
495 GSimpleAsyncResult *result)
497 AgAccountService *service;
498 AgAuthData *auth_data;
500 UoaChangePasswordData *data;
501 SignonIdentity *identity;
503 DEBUG ("Store password for %s in signond",
504 tp_account_get_path_suffix (tp_account));
506 service = uoa_password_common (tp_account, result, &auth_data);
510 data = uoa_change_password_data_new (service, password, remember, result);
512 cred_id = ag_auth_data_get_credentials_id (auth_data);
515 SignonIdentityInfo *info;
516 const GHashTable *params;
517 const gchar *username;
518 const gchar *acl_all[] = { "*", NULL };
520 /* This is the first time we store password for this account.
521 * First check if we have an 'username' param as this is more accurate
522 * in the tp-idle case. */
523 params = tp_account_get_parameters (tp_account);
524 username = tp_asv_get_string (params, "username");
525 if (username == NULL)
526 username = tp_asv_get_string (params, "account");
528 identity = signon_identity_new ();
529 info = signon_identity_info_new ();
530 signon_identity_info_set_username (info, username);
531 signon_identity_info_set_secret (info, password, remember);
532 signon_identity_info_set_access_control_list (info, acl_all);
534 /* Give identity and data ownership to the callback */
535 signon_identity_store_credentials_with_info (identity, info,
536 uoa_initial_identity_store_cb, data);
538 signon_identity_info_free (info);
542 /* There is already a password stored, query info to update it.
543 * Give identity and data ownership to the callback */
544 identity = signon_identity_new_from_db (cred_id);
545 signon_identity_query_info (identity,
546 uoa_identity_query_info_cb, data);
549 g_object_unref (service);
550 ag_auth_data_unref (auth_data);
555 empathy_keyring_set_account_password_async (TpAccount *account,
556 const gchar *password,
558 GAsyncReadyCallback callback,
561 GSimpleAsyncResult *simple;
562 const gchar *account_id;
565 g_return_if_fail (TP_IS_ACCOUNT (account));
566 g_return_if_fail (password != NULL);
568 simple = g_simple_async_result_new (G_OBJECT (account), callback,
569 user_data, empathy_keyring_set_account_password_async);
571 account_id = tp_proxy_get_object_path (account) +
572 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
574 DEBUG ("Remembering password for %s", account_id);
578 const gchar *provider;
580 provider = tp_account_get_storage_provider (account);
581 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
583 uoa_set_account_password (account, password, remember, simple);
584 g_object_unref (simple);
590 name = g_strdup_printf (_("IM account password for %s (%s)"),
591 tp_account_get_display_name (account), account_id);
593 secret_password_store (&account_keyring_schema,
594 remember ? NULL : SECRET_COLLECTION_SESSION,
596 NULL, store_password_cb, simple,
597 "account-id", account_id,
598 "param-name", "password",
605 empathy_keyring_set_room_password_async (TpAccount *account,
607 const gchar *password,
608 GAsyncReadyCallback callback,
611 GSimpleAsyncResult *simple;
612 const gchar *account_id;
615 g_return_if_fail (TP_IS_ACCOUNT (account));
616 g_return_if_fail (id != NULL);
617 g_return_if_fail (password != NULL);
619 simple = g_simple_async_result_new (G_OBJECT (account), callback,
620 user_data, empathy_keyring_set_room_password_async);
622 account_id = tp_proxy_get_object_path (account) +
623 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
625 DEBUG ("Remembering password for room '%s' on account '%s'", id, account_id);
627 name = g_strdup_printf (_("Password for chatroom '%s' on account %s (%s)"),
628 id, tp_account_get_display_name (account), account_id);
630 secret_password_store (&room_keyring_schema, NULL, name, password,
631 NULL, store_password_cb, simple,
632 "account-id", account_id,
640 empathy_keyring_set_account_password_finish (TpAccount *account,
641 GAsyncResult *result,
644 empathy_implement_finish_void (account, empathy_keyring_set_account_password_async);
648 empathy_keyring_set_room_password_finish (TpAccount *account,
649 GAsyncResult *result,
652 empathy_implement_finish_void (account, empathy_keyring_set_room_password_async);
658 items_delete_cb (GObject *source,
659 GAsyncResult *result,
662 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
663 GError *error = NULL;
665 if (!secret_password_clear_finish (result, &error))
667 g_simple_async_result_set_error (simple, TP_ERROR,
668 TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
669 g_error_free (error);
672 g_simple_async_result_complete (simple);
673 g_object_unref (simple);
677 empathy_keyring_delete_account_password_async (TpAccount *account,
678 GAsyncReadyCallback callback,
681 GSimpleAsyncResult *simple;
682 const gchar *account_id;
684 g_return_if_fail (TP_IS_ACCOUNT (account));
686 simple = g_simple_async_result_new (G_OBJECT (account), callback,
687 user_data, empathy_keyring_delete_account_password_async);
689 account_id = tp_proxy_get_object_path (account) +
690 strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
692 DEBUG ("Deleting password for %s", account_id);
696 const gchar *provider;
698 provider = tp_account_get_storage_provider (account);
699 if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
701 /* I see no other way to forget the stored password than overwriting
702 * with an empty one. */
703 uoa_set_account_password (account, "", FALSE, simple);
704 g_object_unref (simple);
710 secret_password_clear (&account_keyring_schema, NULL,
711 items_delete_cb, simple,
712 "account-id", account_id,
713 "param-name", "password",
718 empathy_keyring_delete_account_password_finish (TpAccount *account,
719 GAsyncResult *result,
722 empathy_implement_finish_void (account, empathy_keyring_delete_account_password_async);