]> git.0d.be Git - empathy.git/blob - libempathy/empathy-keyring.c
debug-window: fix typo
[empathy.git] / libempathy / empathy-keyring.c
1 /*
2  * Copyright (C) 2010 Collabora Ltd.
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include "config.h"
20
21 #include <glib/gi18n-lib.h>
22
23 #include "empathy-keyring.h"
24
25 #include <libsecret/secret.h>
26
27 #ifdef HAVE_UOA
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"
35 #endif
36
37 #include "empathy-utils.h"
38
39 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
40 #include "empathy-debug.h"
41
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 },
46       { NULL } } };
47
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 },
52       { NULL } } };
53
54 gboolean
55 empathy_keyring_is_available (void)
56 {
57   return TRUE;
58 }
59
60 /* get */
61
62 static void
63 lookup_item_cb (GObject *source,
64     GAsyncResult *result,
65     gpointer user_data)
66 {
67   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
68   GError *error = NULL;
69   gchar *password;
70
71   password = secret_password_lookup_finish (result, &error);
72   if (error != NULL)
73     {
74       g_simple_async_result_set_error (simple, TP_ERROR,
75           TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
76       g_clear_error (&error);
77       goto out;
78     }
79
80   if (password == NULL)
81     {
82       g_simple_async_result_set_error (simple, TP_ERROR,
83           TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
84       goto out;
85     }
86
87   g_simple_async_result_set_op_res_gpointer (simple, password,
88       (GDestroyNotify) secret_password_free);
89
90 out:
91   g_simple_async_result_complete (simple);
92   g_object_unref (simple);
93 }
94
95 #ifdef HAVE_UOA
96 static AgAccountService *
97 uoa_password_common (TpAccount *tp_account,
98     GSimpleAsyncResult *result,
99     AgAuthData **ret_auth_data)
100 {
101   const GValue *storage_id;
102   AgAccountId account_id;
103   AgManager *manager = NULL;
104   AgAccount *account = NULL;
105   GList *l;
106   AgAccountService *service = NULL;
107   AgAuthData *auth_data = NULL;
108
109   g_assert (ret_auth_data != NULL);
110   *ret_auth_data = NULL;
111
112   storage_id = tp_account_get_storage_identifier (tp_account);
113   account_id = g_value_get_uint (storage_id);
114   if (account_id == 0)
115     {
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);
120       goto error;
121     }
122
123   manager = empathy_uoa_manager_dup ();
124   account = ag_manager_get_account (manager, account_id);
125
126   /* Assuming there is only one IM service */
127   l = ag_account_list_services_by_type (account, EMPATHY_UOA_SERVICE_TYPE);
128   if (l == NULL)
129     {
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);
134       goto error;
135     }
136   service = ag_account_service_new (account, l->data);
137   ag_service_list_free (l);
138
139   auth_data = ag_account_service_get_auth_data (service);
140   if (auth_data == NULL)
141     {
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);
146       goto error;
147     }
148
149   if (tp_strdiff (ag_auth_data_get_mechanism (auth_data), "password") ||
150       tp_strdiff (ag_auth_data_get_method (auth_data), "password"))
151     {
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);
156       goto error;
157     }
158
159   g_object_unref (manager);
160   g_object_unref (account);
161
162   *ret_auth_data = auth_data;
163   return service;
164
165 error:
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);
170   return NULL;
171 }
172
173 static void
174 uoa_session_process_cb (SignonAuthSession *session,
175     GHashTable *session_data,
176     const GError *error,
177     gpointer user_data)
178 {
179   GSimpleAsyncResult *result = user_data;
180   const gchar *password;
181
182   if (error != NULL)
183     {
184       g_simple_async_result_set_from_error (result, error);
185       goto out;
186     }
187
188   password = tp_asv_get_string (session_data, "Secret");
189   if (tp_str_empty (password))
190     {
191       g_simple_async_result_set_error (result, TP_ERROR,
192           TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
193       goto out;
194     }
195
196   g_simple_async_result_set_op_res_gpointer (result, g_strdup (password),
197       g_free);
198
199 out:
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);
205 }
206
207 static void
208 uoa_get_account_password (TpAccount *tp_account,
209     GSimpleAsyncResult *result)
210 {
211   AgAccountService *service;
212   AgAuthData *auth_data;
213   guint cred_id;
214   SignonIdentity *identity;
215   SignonAuthSession *session;
216   GError *error = NULL;
217
218   DEBUG ("Store password for %s in signond",
219       tp_account_get_path_suffix (tp_account));
220
221   service = uoa_password_common (tp_account, result, &auth_data);
222   if (service == NULL)
223     return;
224
225   cred_id = ag_auth_data_get_credentials_id (auth_data);
226   if (cred_id == 0)
227     {
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);
232       goto out;
233     }
234
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);
239
240   if (session == NULL)
241     {
242       g_simple_async_result_set_from_error (result, error);
243       g_simple_async_result_complete_in_idle (result);
244       goto out;
245     }
246
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));
252
253 out:
254   ag_auth_data_unref (auth_data);
255   g_object_unref (service);
256 }
257 #endif
258
259 void
260 empathy_keyring_get_account_password_async (TpAccount *account,
261     GAsyncReadyCallback callback,
262     gpointer user_data)
263 {
264   GSimpleAsyncResult *simple;
265   const gchar *account_id;
266
267   g_return_if_fail (TP_IS_ACCOUNT (account));
268   g_return_if_fail (callback != NULL);
269
270   simple = g_simple_async_result_new (G_OBJECT (account), callback,
271       user_data, empathy_keyring_get_account_password_async);
272
273   account_id = tp_proxy_get_object_path (account) +
274     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
275
276   DEBUG ("Trying to get password for: %s", account_id);
277
278 #ifdef HAVE_UOA
279     {
280       const gchar *provider;
281
282       provider = tp_account_get_storage_provider (account);
283       if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
284         {
285           uoa_get_account_password (account, simple);
286           g_object_unref (simple);
287           return;
288         }
289     }
290 #endif
291
292   secret_password_lookup (&account_keyring_schema, NULL,
293           lookup_item_cb, simple,
294           "account-id", account_id,
295           "param-name", "password",
296           NULL);
297 }
298
299 void
300 empathy_keyring_get_room_password_async (TpAccount *account,
301     const gchar *id,
302     GAsyncReadyCallback callback,
303     gpointer user_data)
304 {
305   GSimpleAsyncResult *simple;
306   const gchar *account_id;
307
308   g_return_if_fail (TP_IS_ACCOUNT (account));
309   g_return_if_fail (id != NULL);
310   g_return_if_fail (callback != NULL);
311
312   simple = g_simple_async_result_new (G_OBJECT (account), callback,
313       user_data, empathy_keyring_get_room_password_async);
314
315   account_id = tp_proxy_get_object_path (account) +
316     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
317
318   DEBUG ("Trying to get password for room '%s' on account '%s'",
319       id, account_id);
320
321   secret_password_lookup (&room_keyring_schema, NULL,
322           lookup_item_cb, simple,
323           "account-id", account_id,
324           "room-id", id,
325           NULL);
326 }
327
328 const gchar *
329 empathy_keyring_get_account_password_finish (TpAccount *account,
330     GAsyncResult *result,
331     GError **error)
332 {
333   empathy_implement_finish_return_pointer (account,
334       empathy_keyring_get_account_password_async);
335 }
336
337 const gchar *
338 empathy_keyring_get_room_password_finish (TpAccount *account,
339     GAsyncResult *result,
340     GError **error)
341 {
342   empathy_implement_finish_return_pointer (account,
343       empathy_keyring_get_room_password_async);
344 }
345
346 /* set */
347
348 static void
349 store_password_cb (GObject *source,
350     GAsyncResult *result,
351     gpointer user_data)
352 {
353   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
354   GError *error = NULL;
355
356   if (!secret_password_store_finish (result, &error))
357     {
358       g_simple_async_result_set_error (simple, TP_ERROR,
359           TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
360       g_error_free (error);
361     }
362
363   g_simple_async_result_complete (simple);
364   g_object_unref (simple);
365 }
366
367 #ifdef HAVE_UOA
368 typedef struct
369 {
370   AgAccountService *service;
371   gchar *password;
372   gboolean remember;
373   GSimpleAsyncResult *result;
374 } UoaChangePasswordData;
375
376 static UoaChangePasswordData *
377 uoa_change_password_data_new (AgAccountService *service,
378     const gchar *password,
379     gboolean remember,
380     GSimpleAsyncResult *result)
381 {
382   UoaChangePasswordData *data;
383
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);
389
390   return data;
391 }
392
393 static void
394 uoa_change_password_data_free (UoaChangePasswordData *data)
395 {
396   g_object_unref (data->service);
397   g_free (data->password);
398   g_object_unref (data->result);
399   g_slice_free (UoaChangePasswordData, data);
400 }
401
402 static void
403 uoa_identity_store_cb (SignonIdentity *identity,
404     guint32 id,
405     const GError *error,
406     gpointer user_data)
407 {
408   UoaChangePasswordData *data = user_data;
409
410   if (error != NULL)
411     g_simple_async_result_set_from_error (data->result, error);
412
413   g_simple_async_result_complete (data->result);
414   uoa_change_password_data_free (data);
415   g_object_unref (identity);
416 }
417
418 static void
419 uoa_identity_query_info_cb (SignonIdentity *identity,
420     const SignonIdentityInfo *info,
421     const GError *error,
422     gpointer user_data)
423 {
424   UoaChangePasswordData *data = user_data;
425
426   if (error != NULL)
427     {
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);
434       return;
435     }
436
437   /* const SignonIdentityInfo is a lie, cast it! - Mardy */
438   signon_identity_info_set_secret ((SignonIdentityInfo *) info,
439       data->password, data->remember);
440
441   signon_identity_store_credentials_with_info (identity, info,
442       uoa_identity_store_cb, data);
443 }
444
445 static void
446 uoa_initial_account_store_cb (AgAccount *account,
447     const GError *error,
448     gpointer user_data)
449 {
450   UoaChangePasswordData *data = user_data;
451
452   if (error != NULL)
453     g_simple_async_result_set_from_error (data->result, error);
454
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);
459 }
460
461 static void
462 uoa_initial_identity_store_cb (SignonIdentity *identity,
463     guint32 id,
464     const GError *error,
465     gpointer user_data)
466 {
467   UoaChangePasswordData *data = user_data;
468   AgAccount *account = ag_account_service_get_account (data->service);
469   GValue value = G_VALUE_INIT;
470
471   if (error != NULL)
472     {
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);
479       return;
480     }
481
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);
487
488   ag_account_store (account, uoa_initial_account_store_cb, data);
489
490   g_object_unref (identity);
491 }
492
493 static void
494 uoa_set_account_password (TpAccount *tp_account,
495     const gchar *password,
496     gboolean remember,
497     GSimpleAsyncResult *result)
498 {
499   AgAccountService *service;
500   AgAuthData *auth_data;
501   guint cred_id;
502   UoaChangePasswordData *data;
503   SignonIdentity *identity;
504
505   DEBUG ("Store password for %s in signond",
506       tp_account_get_path_suffix (tp_account));
507
508   service = uoa_password_common (tp_account, result, &auth_data);
509   if (service == NULL)
510     return;
511
512   data = uoa_change_password_data_new (service, password, remember, result);
513
514   cred_id = ag_auth_data_get_credentials_id (auth_data);
515   if (cred_id == 0)
516     {
517       SignonIdentityInfo *info;
518       const GHashTable *params;
519       const gchar *username;
520       const gchar *acl_all[] = { "*", NULL };
521
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");
529
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);
535
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);
539
540       signon_identity_info_free (info);
541     }
542   else
543     {
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);
549     }
550
551   g_object_unref (service);
552   ag_auth_data_unref (auth_data);
553 }
554 #endif
555
556 void
557 empathy_keyring_set_account_password_async (TpAccount *account,
558     const gchar *password,
559     gboolean remember,
560     GAsyncReadyCallback callback,
561     gpointer user_data)
562 {
563   GSimpleAsyncResult *simple;
564   const gchar *account_id;
565   gchar *name;
566
567   g_return_if_fail (TP_IS_ACCOUNT (account));
568   g_return_if_fail (password != NULL);
569
570   simple = g_simple_async_result_new (G_OBJECT (account), callback,
571       user_data, empathy_keyring_set_account_password_async);
572
573   account_id = tp_proxy_get_object_path (account) +
574     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
575
576   DEBUG ("Remembering password for %s", account_id);
577
578 #ifdef HAVE_UOA
579     {
580       const gchar *provider;
581
582       provider = tp_account_get_storage_provider (account);
583       if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
584         {
585           uoa_set_account_password (account, password, remember, simple);
586           g_object_unref (simple);
587           return;
588         }
589     }
590 #endif
591
592   name = g_strdup_printf (_("IM account password for %s (%s)"),
593       tp_account_get_display_name (account), account_id);
594
595   secret_password_store (&account_keyring_schema,
596       remember ? NULL : SECRET_COLLECTION_SESSION,
597       name, password,
598       NULL, store_password_cb, simple,
599       "account-id", account_id,
600       "param-name", "password",
601       NULL);
602
603   g_free (name);
604 }
605
606 void
607 empathy_keyring_set_room_password_async (TpAccount *account,
608     const gchar *id,
609     const gchar *password,
610     GAsyncReadyCallback callback,
611     gpointer user_data)
612 {
613   GSimpleAsyncResult *simple;
614   const gchar *account_id;
615   gchar *name;
616
617   g_return_if_fail (TP_IS_ACCOUNT (account));
618   g_return_if_fail (id != NULL);
619   g_return_if_fail (password != NULL);
620
621   simple = g_simple_async_result_new (G_OBJECT (account), callback,
622       user_data, empathy_keyring_set_room_password_async);
623
624   account_id = tp_proxy_get_object_path (account) +
625     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
626
627   DEBUG ("Remembering password for room '%s' on account '%s'", id, account_id);
628
629   name = g_strdup_printf (_("Password for chatroom '%s' on account %s (%s)"),
630       id, tp_account_get_display_name (account), account_id);
631
632   secret_password_store (&room_keyring_schema, NULL, name, password,
633       NULL, store_password_cb, simple,
634       "account-id", account_id,
635       "room-id", id,
636       NULL);
637
638   g_free (name);
639 }
640
641 gboolean
642 empathy_keyring_set_account_password_finish (TpAccount *account,
643     GAsyncResult *result,
644     GError **error)
645 {
646   empathy_implement_finish_void (account, empathy_keyring_set_account_password_async);
647 }
648
649 gboolean
650 empathy_keyring_set_room_password_finish (TpAccount *account,
651     GAsyncResult *result,
652     GError **error)
653 {
654   empathy_implement_finish_void (account, empathy_keyring_set_room_password_async);
655 }
656
657 /* delete */
658
659 static void
660 items_delete_cb (GObject *source,
661     GAsyncResult *result,
662     gpointer user_data)
663 {
664   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
665   GError *error = NULL;
666
667   secret_password_clear_finish (result, &error);
668   if (error != NULL)
669     {
670       g_simple_async_result_set_error (simple, TP_ERROR,
671               TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
672       g_error_free (error);
673     }
674
675   g_simple_async_result_complete (simple);
676   g_object_unref (simple);
677 }
678
679 void
680 empathy_keyring_delete_account_password_async (TpAccount *account,
681     GAsyncReadyCallback callback,
682     gpointer user_data)
683 {
684   GSimpleAsyncResult *simple;
685   const gchar *account_id;
686
687   g_return_if_fail (TP_IS_ACCOUNT (account));
688
689   simple = g_simple_async_result_new (G_OBJECT (account), callback,
690       user_data, empathy_keyring_delete_account_password_async);
691
692   account_id = tp_proxy_get_object_path (account) +
693     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
694
695   DEBUG ("Deleting password for %s", account_id);
696
697 #ifdef HAVE_UOA
698     {
699       const gchar *provider;
700
701       provider = tp_account_get_storage_provider (account);
702       if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
703         {
704           /* I see no other way to forget the stored password than overwriting
705            * with an empty one. */
706           uoa_set_account_password (account, "", FALSE, simple);
707           g_object_unref (simple);
708           return;
709         }
710     }
711 #endif
712
713   secret_password_clear (&account_keyring_schema, NULL,
714           items_delete_cb, simple,
715           "account-id", account_id,
716           "param-name", "password",
717           NULL);
718 }
719
720 gboolean
721 empathy_keyring_delete_account_password_finish (TpAccount *account,
722     GAsyncResult *result,
723     GError **error)
724 {
725   empathy_implement_finish_void (account, empathy_keyring_delete_account_password_async);
726 }