2 * module-ubuntu-online-accounts.c
4 * This program 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 of the License, or (at your option) version 3.
9 * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
20 #include <glib/gi18n-lib.h>
21 #include <libsignon-glib/signon-glib.h>
22 #include <libaccounts-glib/accounts-glib.h>
24 /* XXX accounts-glib.h should include this */
25 #include <libaccounts-glib/ag-auth-data.h>
27 #include <libebackend/libebackend.h>
29 #include "uoa-utils.h"
31 /* Standard GObject macros */
32 #define E_TYPE_UBUNTU_ONLINE_ACCOUNTS \
33 (e_ubuntu_online_accounts_get_type ())
34 #define E_UBUNTU_ONLINE_ACCOUNTS(obj) \
35 (G_TYPE_CHECK_INSTANCE_CAST \
36 ((obj), E_TYPE_UBUNTU_ONLINE_ACCOUNTS, EUbuntuOnlineAccounts))
38 /* Service types we support. */
39 #define SERVICE_TYPE_MAIL "mail"
40 #define SERVICE_TYPE_CALENDAR "calendar"
41 #define SERVICE_TYPE_CONTACTS "contacts"
43 #define CAMEL_OAUTH2_MECHANISM_NAME "XOAUTH2"
45 typedef struct _EUbuntuOnlineAccounts EUbuntuOnlineAccounts;
46 typedef struct _EUbuntuOnlineAccountsClass EUbuntuOnlineAccountsClass;
47 typedef struct _AsyncContext AsyncContext;
49 struct _EUbuntuOnlineAccounts {
52 AgManager *ag_manager;
54 /* AgAccountId -> ESource UID */
55 GHashTable *uoa_to_eds;
58 struct _EUbuntuOnlineAccountsClass {
59 EExtensionClass parent_class;
62 struct _AsyncContext {
63 EUbuntuOnlineAccounts *extension;
64 EBackendFactory *backend_factory;
69 /* Module Entry Points */
70 void e_module_load (GTypeModule *type_module);
71 void e_module_unload (GTypeModule *type_module);
73 /* Forward Declarations */
74 GType e_ubuntu_online_accounts_get_type (void);
75 static void e_ubuntu_online_accounts_oauth2_support_init
76 (EOAuth2SupportInterface *interface);
78 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
79 EUbuntuOnlineAccounts,
80 e_ubuntu_online_accounts,
83 G_IMPLEMENT_INTERFACE_DYNAMIC (
84 E_TYPE_OAUTH2_SUPPORT,
85 e_ubuntu_online_accounts_oauth2_support_init))
88 async_context_free (AsyncContext *async_context)
90 if (async_context->extension != NULL)
91 g_object_unref (async_context->extension);
93 if (async_context->backend_factory != NULL)
94 g_object_unref (async_context->backend_factory);
96 g_free (async_context->access_token);
98 g_slice_free (AsyncContext, async_context);
102 ubuntu_online_accounts_get_backend_name (const gchar *uoa_provider_name)
104 const gchar *eds_backend_name = NULL;
106 /* This is a mapping between AgAccount provider names and
107 * ESourceCollection backend names. It requires knowledge
108 * of other registry modules, possibly even from 3rd party
111 * FIXME Put the EDS backend name in the .service config
112 * files so we're not hard-coding the providers we
113 * support. This isn't GNOME Online Accounts. */
115 if (g_strcmp0 (uoa_provider_name, "google") == 0)
116 eds_backend_name = "google";
118 if (g_strcmp0 (uoa_provider_name, "yahoo") == 0)
119 eds_backend_name = "yahoo";
121 return eds_backend_name;
124 static ESourceRegistryServer *
125 ubuntu_online_accounts_get_server (EUbuntuOnlineAccounts *extension)
127 EExtensible *extensible;
129 extensible = e_extension_get_extensible (E_EXTENSION (extension));
131 return E_SOURCE_REGISTRY_SERVER (extensible);
135 ubuntu_online_accounts_provider_name_to_backend_name (GBinding *binding,
136 const GValue *source_value,
137 GValue *target_value,
140 const gchar *provider_name;
141 const gchar *backend_name;
143 provider_name = g_value_get_string (source_value);
144 backend_name = ubuntu_online_accounts_get_backend_name (provider_name);
145 g_return_val_if_fail (backend_name != NULL, FALSE);
146 g_value_set_string (target_value, backend_name);
151 static AgAccountService *
152 ubuntu_online_accounts_ref_account_service (EUbuntuOnlineAccounts *extension,
155 GHashTable *account_services;
156 ESourceRegistryServer *server;
157 AgAccountService *ag_account_service = NULL;
158 const gchar *extension_name;
159 const gchar *service_name = NULL;
161 /* Figure out which AgAccountService to use based
162 * on which extensions are present in the ESource. */
164 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
165 if (e_source_has_extension (source, extension_name))
166 service_name = SERVICE_TYPE_CONTACTS;
168 extension_name = E_SOURCE_EXTENSION_CALENDAR;
169 if (e_source_has_extension (source, extension_name))
170 service_name = SERVICE_TYPE_CALENDAR;
172 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
173 if (e_source_has_extension (source, extension_name))
174 service_name = SERVICE_TYPE_CALENDAR;
176 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
177 if (e_source_has_extension (source, extension_name))
178 service_name = SERVICE_TYPE_CALENDAR;
180 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
181 if (e_source_has_extension (source, extension_name))
182 service_name = SERVICE_TYPE_MAIL;
184 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
185 if (e_source_has_extension (source, extension_name))
186 service_name = SERVICE_TYPE_MAIL;
188 g_return_val_if_fail (service_name != NULL, NULL);
190 extension_name = E_SOURCE_EXTENSION_UOA;
191 server = ubuntu_online_accounts_get_server (extension);
193 source = e_source_registry_server_find_extension (
194 server, source, extension_name);
196 if (source != NULL) {
197 account_services = g_object_get_data (
198 G_OBJECT (source), "ag-account-services");
199 g_warn_if_fail (account_services != NULL);
201 if (account_services != NULL) {
202 ag_account_service = g_hash_table_lookup (
203 account_services, service_name);
204 if (ag_account_service != NULL)
205 g_object_ref (ag_account_service);
208 g_object_unref (source);
211 return ag_account_service;
215 ubuntu_online_accounts_supports_oauth2 (AgAccountService *ag_account_service)
217 AgAuthData *ag_auth_data;
218 gboolean supports_oauth2 = FALSE;
221 ag_auth_data = ag_account_service_get_auth_data (ag_account_service);
222 method = ag_auth_data_get_method (ag_auth_data);
223 supports_oauth2 = (g_strcmp0 (method, "oauth2") == 0);
224 ag_auth_data_unref (ag_auth_data);
226 return supports_oauth2;
230 ubuntu_online_accounts_new_source (EUbuntuOnlineAccounts *extension)
232 ESourceRegistryServer *server;
235 GError *error = NULL;
237 /* This being a brand new data source, creating the instance
238 * should never fail but we'll check for errors just the same. */
239 server = ubuntu_online_accounts_get_server (extension);
240 file = e_server_side_source_new_user_file (NULL);
241 source = e_server_side_source_new (server, file, &error);
242 g_object_unref (file);
245 g_warn_if_fail (source == NULL);
246 g_warning ("%s: %s", G_STRFUNC, error->message);
247 g_error_free (error);
254 ubuntu_online_accounts_new_account_services (EUbuntuOnlineAccounts *extension,
255 AgAccount *ag_account)
257 GHashTable *account_services;
260 account_services = g_hash_table_new_full (
261 (GHashFunc) g_str_hash,
262 (GEqualFunc) g_str_equal,
263 (GDestroyNotify) g_free,
264 (GDestroyNotify) g_object_unref);
266 /* Populate the hash table with AgAccountService instances by
267 * service type. There should only be one AgService per type.
269 * XXX We really should not have to create AgAccountService
270 * instances ourselves. The AgAccount itself should own
271 * them and provide functions for listing them. Instead
272 * it only provides functions for listing its AgServices,
273 * which is decidedly less useful. */
274 list = ag_account_list_services (ag_account);
275 for (link = list; link != NULL; link = g_list_next (link)) {
276 AgService *ag_service = link->data;
277 const gchar *service_type;
279 service_type = ag_service_get_service_type (ag_service);
281 g_hash_table_insert (
283 g_strdup (service_type),
284 ag_account_service_new (ag_account, ag_service));
286 ag_service_list_free (list);
288 return account_services;
292 ubuntu_online_accounts_config_oauth2 (EUbuntuOnlineAccounts *extension,
294 GHashTable *account_services)
296 AgAccountService *ag_account_service;
297 ESourceExtension *source_extension;
298 const gchar *extension_name;
300 ag_account_service = g_hash_table_lookup (
301 account_services, SERVICE_TYPE_MAIL);
302 if (ag_account_service == NULL)
305 if (!ubuntu_online_accounts_supports_oauth2 (ag_account_service))
308 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
309 source_extension = e_source_get_extension (source, extension_name);
311 e_source_authentication_set_method (
312 E_SOURCE_AUTHENTICATION (source_extension),
313 CAMEL_OAUTH2_MECHANISM_NAME);
317 ubuntu_online_accounts_config_collection (EUbuntuOnlineAccounts *extension,
319 AgAccount *ag_account,
320 GHashTable *account_services,
321 const gchar *user_identity)
323 AgAccountService *ag_account_service;
324 ESourceExtension *source_extension;
325 gboolean supports_oauth2 = FALSE;
326 const gchar *extension_name;
328 g_object_bind_property (
329 ag_account, "display-name",
330 source, "display-name",
331 G_BINDING_SYNC_CREATE);
333 g_object_bind_property (
334 ag_account, "enabled",
336 G_BINDING_SYNC_CREATE);
338 extension_name = E_SOURCE_EXTENSION_UOA;
339 source_extension = e_source_get_extension (source, extension_name);
341 g_object_bind_property (
343 source_extension, "account-id",
344 G_BINDING_SYNC_CREATE);
346 extension_name = E_SOURCE_EXTENSION_COLLECTION;
347 source_extension = e_source_get_extension (source, extension_name);
349 g_object_bind_property_full (
350 ag_account, "provider",
351 source_extension, "backend-name",
352 G_BINDING_SYNC_CREATE,
353 ubuntu_online_accounts_provider_name_to_backend_name,
355 NULL, (GDestroyNotify) NULL);
357 if (user_identity != NULL)
358 e_source_collection_set_identity (
359 E_SOURCE_COLLECTION (source_extension),
362 ag_account_service = g_hash_table_lookup (
363 account_services, SERVICE_TYPE_MAIL);
364 if (ag_account_service != NULL) {
365 g_object_bind_property (
366 ag_account_service , "enabled",
367 source_extension, "mail-enabled",
368 G_BINDING_SYNC_CREATE);
370 ubuntu_online_accounts_supports_oauth2 (
374 ag_account_service = g_hash_table_lookup (
375 account_services, SERVICE_TYPE_CALENDAR);
376 if (ag_account_service != NULL) {
377 g_object_bind_property (
378 ag_account_service, "enabled",
379 source_extension, "calendar-enabled",
380 G_BINDING_SYNC_CREATE);
382 ubuntu_online_accounts_supports_oauth2 (
386 ag_account_service = g_hash_table_lookup (
387 account_services, SERVICE_TYPE_CONTACTS);
388 if (ag_account_service != NULL) {
389 g_object_bind_property (
390 ag_account_service, "enabled",
391 source_extension, "contacts-enabled",
392 G_BINDING_SYNC_CREATE);
394 ubuntu_online_accounts_supports_oauth2 (
398 /* Stash the AgAccountService hash table in the ESource
399 * to keep the property bindings alive. The hash table
400 * will be destroyed along with the ESource. */
401 g_object_set_data_full (
403 "ag-account-services",
404 g_hash_table_ref (account_services),
405 (GDestroyNotify) g_hash_table_unref);
407 /* The data source should not be removable by clients. */
408 e_server_side_source_set_removable (
409 E_SERVER_SIDE_SOURCE (source), FALSE);
411 if (supports_oauth2) {
412 /* This module provides OAuth 2.0 support to the collection.
413 * Note, children of the collection source will automatically
414 * inherit our EOAuth2Support through the property binding in
415 * collection_backend_child_added(). */
416 e_server_side_source_set_oauth2_support (
417 E_SERVER_SIDE_SOURCE (source),
418 E_OAUTH2_SUPPORT (extension));
423 ubuntu_online_accounts_config_mail_account (EUbuntuOnlineAccounts *extension,
425 GHashTable *account_services)
427 EServerSideSource *server_side_source;
429 ubuntu_online_accounts_config_oauth2 (
430 extension, source, account_services);
432 /* Clients may change the source but may not remove it. */
433 server_side_source = E_SERVER_SIDE_SOURCE (source);
434 e_server_side_source_set_writable (server_side_source, TRUE);
435 e_server_side_source_set_removable (server_side_source, FALSE);
439 ubuntu_online_accounts_config_mail_identity (EUbuntuOnlineAccounts *extension,
441 GHashTable *account_services,
442 const gchar *email_address)
444 EServerSideSource *server_side_source;
446 if (email_address != NULL) {
447 ESourceMailIdentity *source_extension;
449 source_extension = e_source_get_extension (
450 source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
451 e_source_mail_identity_set_address (
452 source_extension, email_address);
455 /* Clients may change the source but may not remove it. */
456 server_side_source = E_SERVER_SIDE_SOURCE (source);
457 e_server_side_source_set_writable (server_side_source, TRUE);
458 e_server_side_source_set_removable (server_side_source, FALSE);
462 ubuntu_online_accounts_config_mail_transport (EUbuntuOnlineAccounts *extension,
464 GHashTable *account_services)
466 EServerSideSource *server_side_source;
468 ubuntu_online_accounts_config_oauth2 (
469 extension, source, account_services);
471 /* Clients may change the source but may not remove it. */
472 server_side_source = E_SERVER_SIDE_SOURCE (source);
473 e_server_side_source_set_writable (server_side_source, TRUE);
474 e_server_side_source_set_removable (server_side_source, FALSE);
478 ubuntu_online_accounts_config_sources (EUbuntuOnlineAccounts *extension,
480 AgAccount *ag_account)
482 ESourceRegistryServer *server;
483 ECollectionBackend *backend;
484 GHashTable *account_services;
487 account_services = ubuntu_online_accounts_new_account_services (
488 extension, ag_account);
490 ubuntu_online_accounts_config_collection (
491 extension, source, ag_account, account_services, NULL);
493 server = ubuntu_online_accounts_get_server (extension);
494 backend = e_source_registry_server_ref_backend (server, source);
495 g_return_if_fail (backend != NULL);
497 list = e_collection_backend_list_mail_sources (backend);
499 for (link = list; link != NULL; link = g_list_next (link)) {
500 const gchar *extension_name;
502 source = E_SOURCE (link->data);
504 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
505 if (e_source_has_extension (source, extension_name))
506 ubuntu_online_accounts_config_mail_account (
507 extension, source, account_services);
509 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
510 if (e_source_has_extension (source, extension_name))
511 ubuntu_online_accounts_config_mail_identity (
512 extension, source, account_services, NULL);
514 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
515 if (e_source_has_extension (source, extension_name))
516 ubuntu_online_accounts_config_mail_transport (
517 extension, source, account_services);
520 g_list_free_full (list, (GDestroyNotify) g_object_unref);
522 g_object_unref (backend);
524 g_hash_table_unref (account_services);
528 ubuntu_online_accounts_create_collection (EUbuntuOnlineAccounts *extension,
529 EBackendFactory *backend_factory,
530 AgAccount *ag_account,
531 const gchar *user_identity,
532 const gchar *email_address)
534 ESourceRegistryServer *server;
535 ESource *collection_source;
536 ESource *mail_account_source;
537 ESource *mail_identity_source;
538 ESource *mail_transport_source;
539 GHashTable *account_services;
540 const gchar *parent_uid;
542 server = ubuntu_online_accounts_get_server (extension);
544 collection_source = ubuntu_online_accounts_new_source (extension);
545 g_return_if_fail (E_IS_SOURCE (collection_source));
547 mail_account_source = ubuntu_online_accounts_new_source (extension);
548 g_return_if_fail (E_IS_SOURCE (mail_account_source));
550 mail_identity_source = ubuntu_online_accounts_new_source (extension);
551 g_return_if_fail (E_IS_SOURCE (mail_identity_source));
553 mail_transport_source = ubuntu_online_accounts_new_source (extension);
554 g_return_if_fail (E_IS_SOURCE (mail_transport_source));
556 /* Configure parent/child relationships. */
557 parent_uid = e_source_get_uid (collection_source);
558 e_source_set_parent (mail_account_source, parent_uid);
559 e_source_set_parent (mail_identity_source, parent_uid);
560 e_source_set_parent (mail_transport_source, parent_uid);
562 /* Give the factory first crack at mail configuration. */
563 e_collection_backend_factory_prepare_mail (
564 E_COLLECTION_BACKEND_FACTORY (backend_factory),
566 mail_identity_source,
567 mail_transport_source);
569 /* Now it's our turn. */
570 account_services = ubuntu_online_accounts_new_account_services (
571 extension, ag_account);
572 ubuntu_online_accounts_config_collection (
573 extension, collection_source, ag_account,
574 account_services, user_identity);
575 ubuntu_online_accounts_config_mail_account (
576 extension, mail_account_source, account_services);
577 ubuntu_online_accounts_config_mail_identity (
578 extension, mail_identity_source,
579 account_services, email_address);
580 ubuntu_online_accounts_config_mail_transport (
581 extension, mail_transport_source, account_services);
582 g_hash_table_unref (account_services);
584 /* Export the new source collection. */
585 e_source_registry_server_add_source (server, collection_source);
586 e_source_registry_server_add_source (server, mail_account_source);
587 e_source_registry_server_add_source (server, mail_identity_source);
588 e_source_registry_server_add_source (server, mail_transport_source);
590 g_hash_table_insert (
591 extension->uoa_to_eds,
592 GUINT_TO_POINTER (ag_account->id),
593 g_strdup (parent_uid));
595 g_object_unref (collection_source);
596 g_object_unref (mail_account_source);
597 g_object_unref (mail_identity_source);
598 g_object_unref (mail_transport_source);
602 ubuntu_online_accounts_got_userinfo_cb (GObject *source_object,
603 GAsyncResult *result,
606 AgAccount *ag_account;
607 AsyncContext *async_context = user_data;
608 gchar *user_identity = NULL;
609 gchar *email_address = NULL;
610 GError *error = NULL;
612 ag_account = AG_ACCOUNT (source_object);
614 e_ag_account_collect_userinfo_finish (
615 ag_account, result, &user_identity, &email_address, &error);
618 ubuntu_online_accounts_create_collection (
619 async_context->extension,
620 async_context->backend_factory,
626 "%s: Failed to create ESource "
627 "collection for AgAccount '%s': %s",
629 ag_account_get_display_name (ag_account),
631 g_error_free (error);
634 g_free (user_identity);
635 g_free (email_address);
637 async_context_free (async_context);
641 ubuntu_online_accounts_collect_userinfo (EUbuntuOnlineAccounts *extension,
642 EBackendFactory *backend_factory,
643 AgAccount *ag_account)
645 AsyncContext *async_context;
647 /* Before we create a collection we need to collect user info from
648 * the online service. GNOME Online Accounts does this for us, but
649 * no such luck with libaccounts-glib or libsignon-glib. */
651 async_context = g_slice_new0 (AsyncContext);
652 async_context->extension = g_object_ref (extension);
653 async_context->backend_factory = g_object_ref (backend_factory);
655 e_ag_account_collect_userinfo (
657 ubuntu_online_accounts_got_userinfo_cb,
662 ubuntu_online_accounts_remove_collection (EUbuntuOnlineAccounts *extension,
665 GError *error = NULL;
667 /* This removes the entire subtree rooted at source.
668 * Deletes the corresponding on-disk key files too. */
669 e_source_remove_sync (source, NULL, &error);
672 g_warning ("%s: %s", G_STRFUNC, error->message);
673 g_error_free (error);
678 ubuntu_online_accounts_account_created_cb (AgManager *ag_manager,
679 AgAccountId ag_account_id,
680 EUbuntuOnlineAccounts *extension)
682 AgAccount *ag_account;
683 ESourceRegistryServer *server;
684 EBackendFactory *backend_factory = NULL;
685 const gchar *provider_name;
686 const gchar *backend_name;
687 const gchar *source_uid;
689 server = ubuntu_online_accounts_get_server (extension);
691 ag_account = ag_manager_get_account (ag_manager, ag_account_id);
692 g_return_if_fail (ag_account != NULL);
694 provider_name = ag_account_get_provider_name (ag_account);
695 backend_name = ubuntu_online_accounts_get_backend_name (provider_name);
697 source_uid = g_hash_table_lookup (
698 extension->uoa_to_eds,
699 GUINT_TO_POINTER (ag_account_id));
701 if (source_uid == NULL && backend_name != NULL)
702 backend_factory = e_data_factory_ref_backend_factory (
703 E_DATA_FACTORY (server), backend_name);
705 if (backend_factory != NULL) {
706 ubuntu_online_accounts_collect_userinfo (
707 extension, backend_factory, ag_account);
708 g_object_unref (backend_factory);
711 g_object_unref (ag_account);
715 ubuntu_online_accounts_account_deleted_cb (AgManager *ag_manager,
716 AgAccountId ag_account_id,
717 EUbuntuOnlineAccounts *extension)
719 ESource *source = NULL;
720 ESourceRegistryServer *server;
721 const gchar *source_uid;
723 server = ubuntu_online_accounts_get_server (extension);
725 source_uid = g_hash_table_lookup (
726 extension->uoa_to_eds,
727 GUINT_TO_POINTER (ag_account_id));
729 if (source_uid != NULL)
730 source = e_source_registry_server_ref_source (
733 if (source != NULL) {
734 ubuntu_online_accounts_remove_collection (extension, source);
735 g_object_unref (source);
740 ubuntu_online_accounts_populate_accounts_table (EUbuntuOnlineAccounts *extension,
741 GList *ag_account_ids)
743 ESourceRegistryServer *server;
744 GQueue trash = G_QUEUE_INIT;
746 const gchar *extension_name;
748 server = ubuntu_online_accounts_get_server (extension);
750 extension_name = E_SOURCE_EXTENSION_UOA;
751 list = e_source_registry_server_list_sources (server, extension_name);
753 for (link = list; link != NULL; link = g_list_next (link)) {
756 AgAccount *ag_account = NULL;
757 AgAccountId ag_account_id;
758 const gchar *source_uid;
761 source = E_SOURCE (link->data);
762 source_uid = e_source_get_uid (source);
764 extension_name = E_SOURCE_EXTENSION_UOA;
765 uoa_ext = e_source_get_extension (source, extension_name);
766 ag_account_id = e_source_uoa_get_account_id (uoa_ext);
768 if (ag_account_id == 0)
771 /* Verify the UOA account still exists. */
772 match = g_list_find (
774 GUINT_TO_POINTER (ag_account_id));
776 ag_account = ag_manager_get_account (
777 extension->ag_manager, ag_account_id);
779 /* If a matching AgAccountId was found, add it
780 * to our accounts hash table. Otherwise remove
781 * the ESource after we finish looping. */
782 if (ag_account != NULL) {
783 g_hash_table_insert (
784 extension->uoa_to_eds,
785 GUINT_TO_POINTER (ag_account_id),
786 g_strdup (source_uid));
788 ubuntu_online_accounts_config_sources (
789 extension, source, ag_account);
791 g_queue_push_tail (&trash, source);
795 /* Empty the trash. */
796 while (!g_queue_is_empty (&trash)) {
797 ESource *source = g_queue_pop_head (&trash);
798 ubuntu_online_accounts_remove_collection (extension, source);
801 g_list_free_full (list, (GDestroyNotify) g_object_unref);
805 ubuntu_online_accounts_bus_acquired_cb (EDBusServer *server,
806 GDBusConnection *connection,
807 EUbuntuOnlineAccounts *extension)
811 extension->ag_manager = ag_manager_new ();
813 list = ag_manager_list (extension->ag_manager);
815 /* This populates a hash table of UOA ID -> ESource UID strings by
816 * searching through available data sources for ones with a "Ubuntu
817 * Online Accounts" extension. If such an extension is found, but
818 * no corresponding AgAccount (presumably meaning the UOA account
819 * was somehow deleted between E-D-S sessions) then the ESource in
820 * which the extension was found gets deleted. */
821 ubuntu_online_accounts_populate_accounts_table (extension, list);
823 for (link = list; link != NULL; link = g_list_next (link))
824 ubuntu_online_accounts_account_created_cb (
825 extension->ag_manager,
826 GPOINTER_TO_UINT (link->data),
829 ag_manager_list_free (list);
831 /* Listen for Online Account changes. */
833 extension->ag_manager, "account-created",
834 G_CALLBACK (ubuntu_online_accounts_account_created_cb),
837 extension->ag_manager, "account-deleted",
838 G_CALLBACK (ubuntu_online_accounts_account_deleted_cb),
843 ubuntu_online_accounts_dispose (GObject *object)
845 EUbuntuOnlineAccounts *extension;
847 extension = E_UBUNTU_ONLINE_ACCOUNTS (object);
849 if (extension->ag_manager != NULL) {
850 g_signal_handlers_disconnect_matched (
851 extension->ag_manager,
853 0, 0, NULL, NULL, object);
854 g_object_unref (extension->ag_manager);
855 extension->ag_manager = NULL;
858 /* Chain up to parent's dispose() method. */
859 G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)->
864 ubuntu_online_accounts_finalize (GObject *object)
866 EUbuntuOnlineAccounts *extension;
868 extension = E_UBUNTU_ONLINE_ACCOUNTS (object);
870 g_hash_table_destroy (extension->uoa_to_eds);
872 /* Chain up to parent's finalize() method. */
873 G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)->
878 ubuntu_online_accounts_constructed (GObject *object)
880 EExtension *extension;
881 EExtensible *extensible;
883 extension = E_EXTENSION (object);
884 extensible = e_extension_get_extensible (extension);
886 /* Wait for the registry service to acquire its well-known
887 * bus name so we don't do anything destructive beforehand. */
890 extensible, "bus-acquired",
891 G_CALLBACK (ubuntu_online_accounts_bus_acquired_cb),
894 /* Chain up to parent's constructed() method. */
895 G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)->
896 constructed (object);
900 ubuntu_online_accounts_get_access_token_sync (EOAuth2Support *support,
902 GCancellable *cancellable,
903 gchar **out_access_token,
904 gint *out_expires_in,
907 EAsyncClosure *closure;
908 GAsyncResult *result;
911 closure = e_async_closure_new ();
913 e_oauth2_support_get_access_token (
914 support, source, cancellable,
915 e_async_closure_callback, closure);
917 result = e_async_closure_wait (closure);
919 success = e_oauth2_support_get_access_token_finish (
920 support, result, out_access_token, out_expires_in, error);
922 e_async_closure_free (closure);
927 /* Helper for ubuntu_online_accounts_get_access_token() */
929 ubuntu_online_accounts_session_process_cb (GObject *source_object,
930 GAsyncResult *result,
933 GSimpleAsyncResult *simple;
934 AsyncContext *async_context;
935 GVariant *session_data;
936 GError *error = NULL;
938 simple = G_SIMPLE_ASYNC_RESULT (user_data);
939 async_context = g_simple_async_result_get_op_res_gpointer (simple);
941 session_data = signon_auth_session_process_finish (
942 SIGNON_AUTH_SESSION (source_object), result, &error);
946 ((session_data != NULL) && (error == NULL)) ||
947 ((session_data == NULL) && (error != NULL)));
949 if (session_data != NULL) {
951 session_data, "AccessToken", "s",
952 &async_context->access_token);
955 session_data, "ExpiresIn", "i",
956 &async_context->expires_in);
958 g_warn_if_fail (async_context->access_token != NULL);
959 g_variant_unref (session_data);
963 g_simple_async_result_take_error (simple, error);
965 g_simple_async_result_complete (simple);
967 g_object_unref (simple);
971 ubuntu_online_accounts_get_access_token (EOAuth2Support *support,
973 GCancellable *cancellable,
974 GAsyncReadyCallback callback,
977 GSimpleAsyncResult *simple;
978 AsyncContext *async_context;
979 SignonAuthSession *session;
980 AgAccountService *ag_account_service;
981 AgAuthData *ag_auth_data;
982 GError *error = NULL;
984 async_context = g_slice_new0 (AsyncContext);
986 simple = g_simple_async_result_new (
987 G_OBJECT (support), callback, user_data,
988 ubuntu_online_accounts_get_access_token);
990 g_simple_async_result_set_check_cancellable (simple, cancellable);
992 g_simple_async_result_set_op_res_gpointer (
993 simple, async_context, (GDestroyNotify) async_context_free);
995 ag_account_service = ubuntu_online_accounts_ref_account_service (
996 E_UBUNTU_ONLINE_ACCOUNTS (support), source);
998 if (ag_account_service == NULL) {
999 g_simple_async_result_set_error (
1000 simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1001 _("Cannot find a corresponding account "
1002 "service in the accounts database from "
1003 "which to obtain an access token for '%s'"),
1004 e_source_get_display_name (source));
1005 g_simple_async_result_complete_in_idle (simple);
1006 g_object_unref (simple);
1010 /* XXX This should never happen. But because libaccounts-glib
1011 * splits authentication method by service-type instead of
1012 * by provider, and because we broadcast OAuth 2.0 support
1013 * across the entire collection (spanning multiple service
1014 * types), it's conceivable that not all service-types for
1015 * a provider use OAuth 2.0, and an ESource for one of the
1016 * ones that DOESN'T could mistakenly request the token. */
1017 if (!ubuntu_online_accounts_supports_oauth2 (ag_account_service)) {
1018 g_simple_async_result_set_error (
1019 simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1020 _("Data source '%s' does not "
1021 "support OAuth 2.0 authentication"),
1022 e_source_get_display_name (source));
1023 g_simple_async_result_complete_in_idle (simple);
1024 g_object_unref (simple);
1028 ag_auth_data = ag_account_service_get_auth_data (ag_account_service);
1030 session = signon_auth_session_new (
1031 ag_auth_data_get_credentials_id (ag_auth_data),
1032 ag_auth_data_get_method (ag_auth_data), &error);
1036 ((session != NULL) && (error == NULL)) ||
1037 ((session == NULL) && (error != NULL)));
1039 if (session != NULL) {
1040 signon_auth_session_process_async (
1042 ag_auth_data_get_login_parameters (ag_auth_data, NULL),
1043 ag_auth_data_get_mechanism (ag_auth_data),
1045 ubuntu_online_accounts_session_process_cb,
1046 g_object_ref (simple));
1047 g_object_unref (session);
1049 g_simple_async_result_take_error (simple, error);
1050 g_simple_async_result_complete_in_idle (simple);
1053 ag_auth_data_unref (ag_auth_data);
1055 g_object_unref (ag_account_service);
1056 g_object_unref (simple);
1060 ubuntu_online_accounts_get_access_token_finish (EOAuth2Support *support,
1061 GAsyncResult *result,
1062 gchar **out_access_token,
1063 gint *out_expires_in,
1066 GSimpleAsyncResult *simple;
1067 AsyncContext *async_context;
1069 g_return_val_if_fail (
1070 g_simple_async_result_is_valid (
1071 result, G_OBJECT (support),
1072 ubuntu_online_accounts_get_access_token), FALSE);
1074 simple = G_SIMPLE_ASYNC_RESULT (result);
1075 async_context = g_simple_async_result_get_op_res_gpointer (simple);
1077 if (g_simple_async_result_propagate_error (simple, error))
1080 g_return_val_if_fail (async_context->access_token != NULL, FALSE);
1082 if (out_access_token != NULL) {
1083 *out_access_token = async_context->access_token;
1084 async_context->access_token = NULL;
1087 if (out_expires_in != NULL)
1088 *out_expires_in = async_context->expires_in;
1094 e_ubuntu_online_accounts_class_init (EUbuntuOnlineAccountsClass *class)
1096 GObjectClass *object_class;
1097 EExtensionClass *extension_class;
1099 object_class = G_OBJECT_CLASS (class);
1100 object_class->dispose = ubuntu_online_accounts_dispose;
1101 object_class->finalize = ubuntu_online_accounts_finalize;
1102 object_class->constructed = ubuntu_online_accounts_constructed;
1104 extension_class = E_EXTENSION_CLASS (class);
1105 extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
1109 e_ubuntu_online_accounts_class_finalize (EUbuntuOnlineAccountsClass *class)
1114 e_ubuntu_online_accounts_oauth2_support_init (EOAuth2SupportInterface *interface)
1116 interface->get_access_token_sync = ubuntu_online_accounts_get_access_token_sync;
1117 interface->get_access_token = ubuntu_online_accounts_get_access_token;
1118 interface->get_access_token_finish = ubuntu_online_accounts_get_access_token_finish;
1122 e_ubuntu_online_accounts_init (EUbuntuOnlineAccounts *extension)
1124 extension->uoa_to_eds = g_hash_table_new_full (
1125 (GHashFunc) g_direct_hash,
1126 (GEqualFunc) g_direct_equal,
1127 (GDestroyNotify) NULL,
1128 (GDestroyNotify) g_free);
1131 G_MODULE_EXPORT void
1132 e_module_load (GTypeModule *type_module)
1134 e_ubuntu_online_accounts_register_type (type_module);
1137 G_MODULE_EXPORT void
1138 e_module_unload (GTypeModule *type_module)