]> git.0d.be Git - empathy.git/blob - tp-account-widgets/tpaw-keyring.c
uoa-utils: move from Empathy to tp-aw
[empathy.git] / tp-account-widgets / tpaw-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 #include "tpaw-keyring.h"
21
22 #include <glib/gi18n-lib.h>
23 #include <libsecret/secret.h>
24
25 #ifdef HAVE_UOA
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"
33 #endif
34
35 #include "empathy-utils.h"
36
37 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
38 #include "empathy-debug.h"
39
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 },
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 /* get */
55
56 static void
57 lookup_item_cb (GObject *source,
58     GAsyncResult *result,
59     gpointer user_data)
60 {
61   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
62   GError *error = NULL;
63   gchar *password;
64
65   password = secret_password_lookup_finish (result, &error);
66   if (error != NULL)
67     {
68       g_simple_async_result_set_error (simple, TP_ERROR,
69           TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
70       g_clear_error (&error);
71       goto out;
72     }
73
74   if (password == NULL)
75     {
76       g_simple_async_result_set_error (simple, TP_ERROR,
77           TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
78       goto out;
79     }
80
81   g_simple_async_result_set_op_res_gpointer (simple, password,
82       (GDestroyNotify) secret_password_free);
83
84 out:
85   g_simple_async_result_complete (simple);
86   g_object_unref (simple);
87 }
88
89 #ifdef HAVE_UOA
90 static AgAccountService *
91 uoa_password_common (TpAccount *tp_account,
92     GSimpleAsyncResult *result,
93     AgAuthData **ret_auth_data)
94 {
95   const GValue *storage_id;
96   AgAccountId account_id;
97   AgManager *manager = NULL;
98   AgAccount *account = NULL;
99   GList *l;
100   AgAccountService *service = NULL;
101   AgAuthData *auth_data = NULL;
102
103   g_assert (ret_auth_data != NULL);
104   *ret_auth_data = NULL;
105
106   storage_id = tp_account_get_storage_identifier (tp_account);
107   account_id = g_value_get_uint (storage_id);
108   if (account_id == 0)
109     {
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);
114       goto error;
115     }
116
117   manager = tpaw_uoa_manager_dup ();
118   account = ag_manager_get_account (manager, account_id);
119
120   /* Assuming there is only one IM service */
121   l = ag_account_list_services_by_type (account, TPAW_UOA_SERVICE_TYPE);
122   if (l == NULL)
123     {
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);
128       goto error;
129     }
130   service = ag_account_service_new (account, l->data);
131   ag_service_list_free (l);
132
133   auth_data = ag_account_service_get_auth_data (service);
134   if (auth_data == NULL)
135     {
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);
140       goto error;
141     }
142
143   if (tp_strdiff (ag_auth_data_get_mechanism (auth_data), "password") ||
144       tp_strdiff (ag_auth_data_get_method (auth_data), "password"))
145     {
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);
150       goto error;
151     }
152
153   g_object_unref (manager);
154   g_object_unref (account);
155
156   *ret_auth_data = auth_data;
157   return service;
158
159 error:
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);
164   return NULL;
165 }
166
167 static void
168 uoa_session_process_cb (SignonAuthSession *session,
169     GHashTable *session_data,
170     const GError *error,
171     gpointer user_data)
172 {
173   GSimpleAsyncResult *result = user_data;
174   const gchar *password;
175
176   if (error != NULL)
177     {
178       g_simple_async_result_set_from_error (result, error);
179       goto out;
180     }
181
182   password = tp_asv_get_string (session_data, "Secret");
183   if (tp_str_empty (password))
184     {
185       g_simple_async_result_set_error (result, TP_ERROR,
186           TP_ERROR_DOES_NOT_EXIST, _("Password not found"));
187       goto out;
188     }
189
190   g_simple_async_result_set_op_res_gpointer (result, g_strdup (password),
191       g_free);
192
193 out:
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);
199 }
200
201 static void
202 uoa_get_account_password (TpAccount *tp_account,
203     GSimpleAsyncResult *result)
204 {
205   AgAccountService *service;
206   AgAuthData *auth_data;
207   guint cred_id;
208   SignonIdentity *identity;
209   SignonAuthSession *session;
210   GError *error = NULL;
211
212   DEBUG ("Store password for %s in signond",
213       tp_account_get_path_suffix (tp_account));
214
215   service = uoa_password_common (tp_account, result, &auth_data);
216   if (service == NULL)
217     return;
218
219   cred_id = ag_auth_data_get_credentials_id (auth_data);
220   if (cred_id == 0)
221     {
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);
226       goto out;
227     }
228
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);
233
234   if (session == NULL)
235     {
236       g_simple_async_result_set_from_error (result, error);
237       g_simple_async_result_complete_in_idle (result);
238       goto out;
239     }
240
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));
246
247 out:
248   ag_auth_data_unref (auth_data);
249   g_object_unref (service);
250 }
251 #endif
252
253 void
254 tpaw_keyring_get_account_password_async (TpAccount *account,
255     GAsyncReadyCallback callback,
256     gpointer user_data)
257 {
258   GSimpleAsyncResult *simple;
259   const gchar *account_id;
260
261   g_return_if_fail (TP_IS_ACCOUNT (account));
262   g_return_if_fail (callback != NULL);
263
264   simple = g_simple_async_result_new (G_OBJECT (account), callback,
265       user_data, tpaw_keyring_get_account_password_async);
266
267   account_id = tp_proxy_get_object_path (account) +
268     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
269
270   DEBUG ("Trying to get password for: %s", account_id);
271
272 #ifdef HAVE_UOA
273     {
274       const gchar *provider;
275
276       provider = tp_account_get_storage_provider (account);
277       if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
278         {
279           uoa_get_account_password (account, simple);
280           g_object_unref (simple);
281           return;
282         }
283     }
284 #endif
285
286   secret_password_lookup (&account_keyring_schema, NULL,
287           lookup_item_cb, simple,
288           "account-id", account_id,
289           "param-name", "password",
290           NULL);
291 }
292
293 void
294 tpaw_keyring_get_room_password_async (TpAccount *account,
295     const gchar *id,
296     GAsyncReadyCallback callback,
297     gpointer user_data)
298 {
299   GSimpleAsyncResult *simple;
300   const gchar *account_id;
301
302   g_return_if_fail (TP_IS_ACCOUNT (account));
303   g_return_if_fail (id != NULL);
304   g_return_if_fail (callback != NULL);
305
306   simple = g_simple_async_result_new (G_OBJECT (account), callback,
307       user_data, tpaw_keyring_get_room_password_async);
308
309   account_id = tp_proxy_get_object_path (account) +
310     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
311
312   DEBUG ("Trying to get password for room '%s' on account '%s'",
313       id, account_id);
314
315   secret_password_lookup (&room_keyring_schema, NULL,
316           lookup_item_cb, simple,
317           "account-id", account_id,
318           "room-id", id,
319           NULL);
320 }
321
322 const gchar *
323 tpaw_keyring_get_account_password_finish (TpAccount *account,
324     GAsyncResult *result,
325     GError **error)
326 {
327   empathy_implement_finish_return_pointer (account,
328       tpaw_keyring_get_account_password_async);
329 }
330
331 const gchar *
332 tpaw_keyring_get_room_password_finish (TpAccount *account,
333     GAsyncResult *result,
334     GError **error)
335 {
336   empathy_implement_finish_return_pointer (account,
337       tpaw_keyring_get_room_password_async);
338 }
339
340 /* set */
341
342 static void
343 store_password_cb (GObject *source,
344     GAsyncResult *result,
345     gpointer user_data)
346 {
347   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
348   GError *error = NULL;
349
350   if (!secret_password_store_finish (result, &error))
351     {
352       g_simple_async_result_set_error (simple, TP_ERROR,
353           TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
354       g_error_free (error);
355     }
356
357   g_simple_async_result_complete (simple);
358   g_object_unref (simple);
359 }
360
361 #ifdef HAVE_UOA
362 typedef struct
363 {
364   AgAccountService *service;
365   gchar *password;
366   gboolean remember;
367   GSimpleAsyncResult *result;
368 } UoaChangePasswordData;
369
370 static UoaChangePasswordData *
371 uoa_change_password_data_new (AgAccountService *service,
372     const gchar *password,
373     gboolean remember,
374     GSimpleAsyncResult *result)
375 {
376   UoaChangePasswordData *data;
377
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);
383
384   return data;
385 }
386
387 static void
388 uoa_change_password_data_free (UoaChangePasswordData *data)
389 {
390   g_object_unref (data->service);
391   g_free (data->password);
392   g_object_unref (data->result);
393   g_slice_free (UoaChangePasswordData, data);
394 }
395
396 static void
397 uoa_identity_store_cb (SignonIdentity *identity,
398     guint32 id,
399     const GError *error,
400     gpointer user_data)
401 {
402   UoaChangePasswordData *data = user_data;
403
404   if (error != NULL)
405     g_simple_async_result_set_from_error (data->result, error);
406
407   g_simple_async_result_complete (data->result);
408   uoa_change_password_data_free (data);
409   g_object_unref (identity);
410 }
411
412 static void
413 uoa_identity_query_info_cb (SignonIdentity *identity,
414     const SignonIdentityInfo *info,
415     const GError *error,
416     gpointer user_data)
417 {
418   UoaChangePasswordData *data = user_data;
419
420   if (error != NULL)
421     {
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);
428       return;
429     }
430
431   /* const SignonIdentityInfo is a lie, cast it! - Mardy */
432   signon_identity_info_set_secret ((SignonIdentityInfo *) info,
433       data->password, data->remember);
434
435   signon_identity_store_credentials_with_info (identity, info,
436       uoa_identity_store_cb, data);
437 }
438
439 static void
440 uoa_initial_account_store_cb (AgAccount *account,
441     const GError *error,
442     gpointer user_data)
443 {
444   UoaChangePasswordData *data = user_data;
445
446   if (error != NULL)
447     g_simple_async_result_set_from_error (data->result, error);
448
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);
453 }
454
455 static void
456 uoa_initial_identity_store_cb (SignonIdentity *identity,
457     guint32 id,
458     const GError *error,
459     gpointer user_data)
460 {
461   UoaChangePasswordData *data = user_data;
462   AgAccount *account = ag_account_service_get_account (data->service);
463   GValue value = G_VALUE_INIT;
464
465   if (error != NULL)
466     {
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);
473       return;
474     }
475
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);
481
482   ag_account_store (account, uoa_initial_account_store_cb, data);
483
484   g_object_unref (identity);
485 }
486
487 static void
488 uoa_set_account_password (TpAccount *tp_account,
489     const gchar *password,
490     gboolean remember,
491     GSimpleAsyncResult *result)
492 {
493   AgAccountService *service;
494   AgAuthData *auth_data;
495   guint cred_id;
496   UoaChangePasswordData *data;
497   SignonIdentity *identity;
498
499   DEBUG ("Store password for %s in signond",
500       tp_account_get_path_suffix (tp_account));
501
502   service = uoa_password_common (tp_account, result, &auth_data);
503   if (service == NULL)
504     return;
505
506   data = uoa_change_password_data_new (service, password, remember, result);
507
508   cred_id = ag_auth_data_get_credentials_id (auth_data);
509   if (cred_id == 0)
510     {
511       SignonIdentityInfo *info;
512       const GHashTable *params;
513       const gchar *username;
514       const gchar *acl_all[] = { "*", NULL };
515
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");
523
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);
529
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);
533
534       signon_identity_info_free (info);
535     }
536   else
537     {
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);
543     }
544
545   g_object_unref (service);
546   ag_auth_data_unref (auth_data);
547 }
548 #endif
549
550 void
551 tpaw_keyring_set_account_password_async (TpAccount *account,
552     const gchar *password,
553     gboolean remember,
554     GAsyncReadyCallback callback,
555     gpointer user_data)
556 {
557   GSimpleAsyncResult *simple;
558   const gchar *account_id;
559   gchar *name;
560
561   g_return_if_fail (TP_IS_ACCOUNT (account));
562   g_return_if_fail (password != NULL);
563
564   simple = g_simple_async_result_new (G_OBJECT (account), callback,
565       user_data, tpaw_keyring_set_account_password_async);
566
567   account_id = tp_proxy_get_object_path (account) +
568     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
569
570   DEBUG ("Remembering password for %s", account_id);
571
572 #ifdef HAVE_UOA
573     {
574       const gchar *provider;
575
576       provider = tp_account_get_storage_provider (account);
577       if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
578         {
579           uoa_set_account_password (account, password, remember, simple);
580           g_object_unref (simple);
581           return;
582         }
583     }
584 #endif
585
586   name = g_strdup_printf (_("IM account password for %s (%s)"),
587       tp_account_get_display_name (account), account_id);
588
589   secret_password_store (&account_keyring_schema,
590       remember ? NULL : SECRET_COLLECTION_SESSION,
591       name, password,
592       NULL, store_password_cb, simple,
593       "account-id", account_id,
594       "param-name", "password",
595       NULL);
596
597   g_free (name);
598 }
599
600 void
601 tpaw_keyring_set_room_password_async (TpAccount *account,
602     const gchar *id,
603     const gchar *password,
604     GAsyncReadyCallback callback,
605     gpointer user_data)
606 {
607   GSimpleAsyncResult *simple;
608   const gchar *account_id;
609   gchar *name;
610
611   g_return_if_fail (TP_IS_ACCOUNT (account));
612   g_return_if_fail (id != NULL);
613   g_return_if_fail (password != NULL);
614
615   simple = g_simple_async_result_new (G_OBJECT (account), callback,
616       user_data, tpaw_keyring_set_room_password_async);
617
618   account_id = tp_proxy_get_object_path (account) +
619     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
620
621   DEBUG ("Remembering password for room '%s' on account '%s'", id, account_id);
622
623   name = g_strdup_printf (_("Password for chatroom '%s' on account %s (%s)"),
624       id, tp_account_get_display_name (account), account_id);
625
626   secret_password_store (&room_keyring_schema, NULL, name, password,
627       NULL, store_password_cb, simple,
628       "account-id", account_id,
629       "room-id", id,
630       NULL);
631
632   g_free (name);
633 }
634
635 gboolean
636 tpaw_keyring_set_account_password_finish (TpAccount *account,
637     GAsyncResult *result,
638     GError **error)
639 {
640   empathy_implement_finish_void (account, tpaw_keyring_set_account_password_async);
641 }
642
643 gboolean
644 tpaw_keyring_set_room_password_finish (TpAccount *account,
645     GAsyncResult *result,
646     GError **error)
647 {
648   empathy_implement_finish_void (account, tpaw_keyring_set_room_password_async);
649 }
650
651 /* delete */
652
653 static void
654 items_delete_cb (GObject *source,
655     GAsyncResult *result,
656     gpointer user_data)
657 {
658   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
659   GError *error = NULL;
660
661   secret_password_clear_finish (result, &error);
662   if (error != NULL)
663     {
664       g_simple_async_result_set_error (simple, TP_ERROR,
665               TP_ERROR_DOES_NOT_EXIST, "%s", error->message);
666       g_error_free (error);
667     }
668
669   g_simple_async_result_complete (simple);
670   g_object_unref (simple);
671 }
672
673 void
674 tpaw_keyring_delete_account_password_async (TpAccount *account,
675     GAsyncReadyCallback callback,
676     gpointer user_data)
677 {
678   GSimpleAsyncResult *simple;
679   const gchar *account_id;
680
681   g_return_if_fail (TP_IS_ACCOUNT (account));
682
683   simple = g_simple_async_result_new (G_OBJECT (account), callback,
684       user_data, tpaw_keyring_delete_account_password_async);
685
686   account_id = tp_proxy_get_object_path (account) +
687     strlen (TP_ACCOUNT_OBJECT_PATH_BASE);
688
689   DEBUG ("Deleting password for %s", account_id);
690
691 #ifdef HAVE_UOA
692     {
693       const gchar *provider;
694
695       provider = tp_account_get_storage_provider (account);
696       if (!tp_strdiff (provider, EMPATHY_UOA_PROVIDER))
697         {
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);
702           return;
703         }
704     }
705 #endif
706
707   secret_password_clear (&account_keyring_schema, NULL,
708           items_delete_cb, simple,
709           "account-id", account_id,
710           "param-name", "password",
711           NULL);
712 }
713
714 gboolean
715 tpaw_keyring_delete_account_password_finish (TpAccount *account,
716     GAsyncResult *result,
717     GError **error)
718 {
719   empathy_implement_finish_void (account, tpaw_keyring_delete_account_password_async);
720 }