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 #include <libebackend/libebackend.h>
26 #include "uoa-utils.h"
28 /* Standard GObject macros */
29 #define E_TYPE_UBUNTU_ONLINE_ACCOUNTS \
30 (e_ubuntu_online_accounts_get_type ())
31 #define E_UBUNTU_ONLINE_ACCOUNTS(obj) \
32 (G_TYPE_CHECK_INSTANCE_CAST \
33 ((obj), E_TYPE_UBUNTU_ONLINE_ACCOUNTS, EUbuntuOnlineAccounts))
35 /* Service types we support. */
36 #define SERVICE_TYPE_MAIL "mail"
37 #define SERVICE_TYPE_CALENDAR "calendar"
38 #define SERVICE_TYPE_CONTACTS "contacts"
40 #define CAMEL_OAUTH2_MECHANISM_NAME "XOAUTH2"
42 typedef struct _EUbuntuOnlineAccounts EUbuntuOnlineAccounts;
43 typedef struct _EUbuntuOnlineAccountsClass EUbuntuOnlineAccountsClass;
44 typedef struct _AsyncContext AsyncContext;
46 struct _EUbuntuOnlineAccounts {
49 AgManager *ag_manager;
51 /* AgAccountId -> ESource UID */
52 GHashTable *uoa_to_eds;
55 struct _EUbuntuOnlineAccountsClass {
56 EExtensionClass parent_class;
59 struct _AsyncContext {
60 EUbuntuOnlineAccounts *extension;
61 EBackendFactory *backend_factory;
66 /* Module Entry Points */
67 void e_module_load (GTypeModule *type_module);
68 void e_module_unload (GTypeModule *type_module);
70 /* Forward Declarations */
71 GType e_ubuntu_online_accounts_get_type (void);
72 static void e_ubuntu_online_accounts_oauth2_support_init
73 (EOAuth2SupportInterface *interface);
75 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
76 EUbuntuOnlineAccounts,
77 e_ubuntu_online_accounts,
80 G_IMPLEMENT_INTERFACE_DYNAMIC (
81 E_TYPE_OAUTH2_SUPPORT,
82 e_ubuntu_online_accounts_oauth2_support_init))
85 async_context_free (AsyncContext *async_context)
87 if (async_context->extension != NULL)
88 g_object_unref (async_context->extension);
90 if (async_context->backend_factory != NULL)
91 g_object_unref (async_context->backend_factory);
93 g_free (async_context->access_token);
95 g_slice_free (AsyncContext, async_context);
99 ubuntu_online_accounts_get_backend_name (const gchar *uoa_provider_name)
101 const gchar *eds_backend_name = NULL;
103 /* This is a mapping between AgAccount provider names and
104 * ESourceCollection backend names. It requires knowledge
105 * of other registry modules, possibly even from 3rd party
108 * FIXME Put the EDS backend name in the .service config
109 * files so we're not hard-coding the providers we
110 * support. This isn't GNOME Online Accounts. */
112 if (g_strcmp0 (uoa_provider_name, "google") == 0)
113 eds_backend_name = "google";
115 if (g_strcmp0 (uoa_provider_name, "yahoo") == 0)
116 eds_backend_name = "yahoo";
118 return eds_backend_name;
121 static ESourceRegistryServer *
122 ubuntu_online_accounts_get_server (EUbuntuOnlineAccounts *extension)
124 EExtensible *extensible;
126 extensible = e_extension_get_extensible (E_EXTENSION (extension));
128 return E_SOURCE_REGISTRY_SERVER (extensible);
132 ubuntu_online_accounts_provider_name_to_backend_name (GBinding *binding,
133 const GValue *source_value,
134 GValue *target_value,
137 const gchar *provider_name;
138 const gchar *backend_name;
140 provider_name = g_value_get_string (source_value);
141 backend_name = ubuntu_online_accounts_get_backend_name (provider_name);
142 g_return_val_if_fail (backend_name != NULL, FALSE);
143 g_value_set_string (target_value, backend_name);
148 static AgAccountService *
149 ubuntu_online_accounts_ref_account_service (EUbuntuOnlineAccounts *extension,
152 GHashTable *account_services;
153 ESourceRegistryServer *server;
154 AgAccountService *ag_account_service = NULL;
155 const gchar *extension_name;
156 const gchar *service_name = NULL;
158 /* Figure out which AgAccountService to use based
159 * on which extensions are present in the ESource. */
161 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
162 if (e_source_has_extension (source, extension_name))
163 service_name = SERVICE_TYPE_CONTACTS;
165 extension_name = E_SOURCE_EXTENSION_CALENDAR;
166 if (e_source_has_extension (source, extension_name))
167 service_name = SERVICE_TYPE_CALENDAR;
169 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
170 if (e_source_has_extension (source, extension_name))
171 service_name = SERVICE_TYPE_CALENDAR;
173 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
174 if (e_source_has_extension (source, extension_name))
175 service_name = SERVICE_TYPE_CALENDAR;
177 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
178 if (e_source_has_extension (source, extension_name))
179 service_name = SERVICE_TYPE_MAIL;
181 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
182 if (e_source_has_extension (source, extension_name))
183 service_name = SERVICE_TYPE_MAIL;
185 g_return_val_if_fail (service_name != NULL, NULL);
187 extension_name = E_SOURCE_EXTENSION_UOA;
188 server = ubuntu_online_accounts_get_server (extension);
190 source = e_source_registry_server_find_extension (
191 server, source, extension_name);
193 if (source != NULL) {
194 account_services = g_object_get_data (
195 G_OBJECT (source), "ag-account-services");
196 g_warn_if_fail (account_services != NULL);
198 if (account_services != NULL) {
199 ag_account_service = g_hash_table_lookup (
200 account_services, service_name);
201 if (ag_account_service != NULL)
202 g_object_ref (ag_account_service);
205 g_object_unref (source);
208 return ag_account_service;
212 ubuntu_online_accounts_supports_oauth2 (AgAccountService *ag_account_service)
214 AgAuthData *ag_auth_data;
215 gboolean supports_oauth2 = FALSE;
218 ag_auth_data = ag_account_service_get_auth_data (ag_account_service);
219 method = ag_auth_data_get_method (ag_auth_data);
220 supports_oauth2 = (g_strcmp0 (method, "oauth2") == 0);
221 ag_auth_data_unref (ag_auth_data);
223 return supports_oauth2;
227 ubuntu_online_accounts_new_source (EUbuntuOnlineAccounts *extension)
229 ESourceRegistryServer *server;
232 GError *error = NULL;
234 /* This being a brand new data source, creating the instance
235 * should never fail but we'll check for errors just the same. */
236 server = ubuntu_online_accounts_get_server (extension);
237 file = e_server_side_source_new_user_file (NULL);
238 source = e_server_side_source_new (server, file, &error);
239 g_object_unref (file);
242 g_warn_if_fail (source == NULL);
243 g_warning ("%s: %s", G_STRFUNC, error->message);
244 g_error_free (error);
251 ubuntu_online_accounts_new_account_services (EUbuntuOnlineAccounts *extension,
252 AgAccount *ag_account)
254 GHashTable *account_services;
257 account_services = g_hash_table_new_full (
258 (GHashFunc) g_str_hash,
259 (GEqualFunc) g_str_equal,
260 (GDestroyNotify) g_free,
261 (GDestroyNotify) g_object_unref);
263 /* Populate the hash table with AgAccountService instances by
264 * service type. There should only be one AgService per type.
266 * XXX We really should not have to create AgAccountService
267 * instances ourselves. The AgAccount itself should own
268 * them and provide functions for listing them. Instead
269 * it only provides functions for listing its AgServices,
270 * which is decidedly less useful. */
271 list = ag_account_list_services (ag_account);
272 for (link = list; link != NULL; link = g_list_next (link)) {
273 AgService *ag_service = link->data;
274 const gchar *service_type;
276 service_type = ag_service_get_service_type (ag_service);
278 g_hash_table_insert (
280 g_strdup (service_type),
281 ag_account_service_new (ag_account, ag_service));
283 ag_service_list_free (list);
285 return account_services;
289 ubuntu_online_accounts_config_oauth2 (EUbuntuOnlineAccounts *extension,
291 GHashTable *account_services)
293 AgAccountService *ag_account_service;
294 ESourceExtension *source_extension;
295 const gchar *extension_name;
297 ag_account_service = g_hash_table_lookup (
298 account_services, SERVICE_TYPE_MAIL);
299 if (ag_account_service == NULL)
302 if (!ubuntu_online_accounts_supports_oauth2 (ag_account_service))
305 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
306 source_extension = e_source_get_extension (source, extension_name);
308 e_source_authentication_set_method (
309 E_SOURCE_AUTHENTICATION (source_extension),
310 CAMEL_OAUTH2_MECHANISM_NAME);
314 ubuntu_online_accounts_config_collection (EUbuntuOnlineAccounts *extension,
316 AgAccount *ag_account,
317 GHashTable *account_services,
318 const gchar *user_identity)
320 AgAccountService *ag_account_service;
321 ESourceExtension *source_extension;
322 gboolean supports_oauth2 = FALSE;
323 const gchar *extension_name;
325 g_object_bind_property (
326 ag_account, "display-name",
327 source, "display-name",
328 G_BINDING_SYNC_CREATE);
330 g_object_bind_property (
331 ag_account, "enabled",
333 G_BINDING_SYNC_CREATE);
335 extension_name = E_SOURCE_EXTENSION_UOA;
336 source_extension = e_source_get_extension (source, extension_name);
338 g_object_bind_property (
340 source_extension, "account-id",
341 G_BINDING_SYNC_CREATE);
343 extension_name = E_SOURCE_EXTENSION_COLLECTION;
344 source_extension = e_source_get_extension (source, extension_name);
346 g_object_bind_property_full (
347 ag_account, "provider",
348 source_extension, "backend-name",
349 G_BINDING_SYNC_CREATE,
350 ubuntu_online_accounts_provider_name_to_backend_name,
352 NULL, (GDestroyNotify) NULL);
354 if (user_identity != NULL)
355 e_source_collection_set_identity (
356 E_SOURCE_COLLECTION (source_extension),
359 ag_account_service = g_hash_table_lookup (
360 account_services, SERVICE_TYPE_MAIL);
361 if (ag_account_service != NULL) {
362 g_object_bind_property (
363 ag_account_service , "enabled",
364 source_extension, "mail-enabled",
365 G_BINDING_SYNC_CREATE);
367 ubuntu_online_accounts_supports_oauth2 (
371 ag_account_service = g_hash_table_lookup (
372 account_services, SERVICE_TYPE_CALENDAR);
373 if (ag_account_service != NULL) {
374 g_object_bind_property (
375 ag_account_service, "enabled",
376 source_extension, "calendar-enabled",
377 G_BINDING_SYNC_CREATE);
379 ubuntu_online_accounts_supports_oauth2 (
383 ag_account_service = g_hash_table_lookup (
384 account_services, SERVICE_TYPE_CONTACTS);
385 if (ag_account_service != NULL) {
386 g_object_bind_property (
387 ag_account_service, "enabled",
388 source_extension, "contacts-enabled",
389 G_BINDING_SYNC_CREATE);
391 ubuntu_online_accounts_supports_oauth2 (
395 /* Stash the AgAccountService hash table in the ESource
396 * to keep the property bindings alive. The hash table
397 * will be destroyed along with the ESource. */
398 g_object_set_data_full (
400 "ag-account-services",
401 g_hash_table_ref (account_services),
402 (GDestroyNotify) g_hash_table_unref);
404 /* The data source should not be removable by clients. */
405 e_server_side_source_set_removable (
406 E_SERVER_SIDE_SOURCE (source), FALSE);
408 if (supports_oauth2) {
409 /* This module provides OAuth 2.0 support to the collection.
410 * Note, children of the collection source will automatically
411 * inherit our EOAuth2Support through the property binding in
412 * collection_backend_child_added(). */
413 e_server_side_source_set_oauth2_support (
414 E_SERVER_SIDE_SOURCE (source),
415 E_OAUTH2_SUPPORT (extension));
420 ubuntu_online_accounts_config_mail_account (EUbuntuOnlineAccounts *extension,
422 GHashTable *account_services)
424 EServerSideSource *server_side_source;
426 ubuntu_online_accounts_config_oauth2 (
427 extension, source, account_services);
429 /* Clients may change the source but may not remove it. */
430 server_side_source = E_SERVER_SIDE_SOURCE (source);
431 e_server_side_source_set_writable (server_side_source, TRUE);
432 e_server_side_source_set_removable (server_side_source, FALSE);
436 ubuntu_online_accounts_config_mail_identity (EUbuntuOnlineAccounts *extension,
438 GHashTable *account_services,
439 const gchar *email_address)
441 EServerSideSource *server_side_source;
443 if (email_address != NULL) {
444 ESourceMailIdentity *source_extension;
446 source_extension = e_source_get_extension (
447 source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
448 e_source_mail_identity_set_address (
449 source_extension, email_address);
452 /* Clients may change the source but may not remove it. */
453 server_side_source = E_SERVER_SIDE_SOURCE (source);
454 e_server_side_source_set_writable (server_side_source, TRUE);
455 e_server_side_source_set_removable (server_side_source, FALSE);
459 ubuntu_online_accounts_config_mail_transport (EUbuntuOnlineAccounts *extension,
461 GHashTable *account_services)
463 EServerSideSource *server_side_source;
465 ubuntu_online_accounts_config_oauth2 (
466 extension, source, account_services);
468 /* Clients may change the source but may not remove it. */
469 server_side_source = E_SERVER_SIDE_SOURCE (source);
470 e_server_side_source_set_writable (server_side_source, TRUE);
471 e_server_side_source_set_removable (server_side_source, FALSE);
475 ubuntu_online_accounts_config_sources (EUbuntuOnlineAccounts *extension,
477 AgAccount *ag_account)
479 ESourceRegistryServer *server;
480 ECollectionBackend *backend;
481 GHashTable *account_services;
484 account_services = ubuntu_online_accounts_new_account_services (
485 extension, ag_account);
487 ubuntu_online_accounts_config_collection (
488 extension, source, ag_account, account_services, NULL);
490 server = ubuntu_online_accounts_get_server (extension);
491 backend = e_source_registry_server_ref_backend (server, source);
492 g_return_if_fail (backend != NULL);
494 list = e_collection_backend_list_mail_sources (backend);
496 for (link = list; link != NULL; link = g_list_next (link)) {
497 const gchar *extension_name;
499 source = E_SOURCE (link->data);
501 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
502 if (e_source_has_extension (source, extension_name))
503 ubuntu_online_accounts_config_mail_account (
504 extension, source, account_services);
506 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
507 if (e_source_has_extension (source, extension_name))
508 ubuntu_online_accounts_config_mail_identity (
509 extension, source, account_services, NULL);
511 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
512 if (e_source_has_extension (source, extension_name))
513 ubuntu_online_accounts_config_mail_transport (
514 extension, source, account_services);
517 g_list_free_full (list, (GDestroyNotify) g_object_unref);
519 g_object_unref (backend);
521 g_hash_table_unref (account_services);
525 ubuntu_online_accounts_create_collection (EUbuntuOnlineAccounts *extension,
526 EBackendFactory *backend_factory,
527 AgAccount *ag_account,
528 const gchar *user_identity,
529 const gchar *email_address)
531 ESourceRegistryServer *server;
532 ESource *collection_source;
533 ESource *mail_account_source;
534 ESource *mail_identity_source;
535 ESource *mail_transport_source;
536 GHashTable *account_services;
537 const gchar *parent_uid;
539 server = ubuntu_online_accounts_get_server (extension);
541 collection_source = ubuntu_online_accounts_new_source (extension);
542 g_return_if_fail (E_IS_SOURCE (collection_source));
544 mail_account_source = ubuntu_online_accounts_new_source (extension);
545 g_return_if_fail (E_IS_SOURCE (mail_account_source));
547 mail_identity_source = ubuntu_online_accounts_new_source (extension);
548 g_return_if_fail (E_IS_SOURCE (mail_identity_source));
550 mail_transport_source = ubuntu_online_accounts_new_source (extension);
551 g_return_if_fail (E_IS_SOURCE (mail_transport_source));
553 /* Configure parent/child relationships. */
554 parent_uid = e_source_get_uid (collection_source);
555 e_source_set_parent (mail_account_source, parent_uid);
556 e_source_set_parent (mail_identity_source, parent_uid);
557 e_source_set_parent (mail_transport_source, parent_uid);
559 /* Give the factory first crack at mail configuration. */
560 e_collection_backend_factory_prepare_mail (
561 E_COLLECTION_BACKEND_FACTORY (backend_factory),
563 mail_identity_source,
564 mail_transport_source);
566 /* Now it's our turn. */
567 account_services = ubuntu_online_accounts_new_account_services (
568 extension, ag_account);
569 ubuntu_online_accounts_config_collection (
570 extension, collection_source, ag_account,
571 account_services, user_identity);
572 ubuntu_online_accounts_config_mail_account (
573 extension, mail_account_source, account_services);
574 ubuntu_online_accounts_config_mail_identity (
575 extension, mail_identity_source,
576 account_services, email_address);
577 ubuntu_online_accounts_config_mail_transport (
578 extension, mail_transport_source, account_services);
579 g_hash_table_unref (account_services);
581 /* Export the new source collection. */
582 e_source_registry_server_add_source (server, collection_source);
583 e_source_registry_server_add_source (server, mail_account_source);
584 e_source_registry_server_add_source (server, mail_identity_source);
585 e_source_registry_server_add_source (server, mail_transport_source);
587 g_hash_table_insert (
588 extension->uoa_to_eds,
589 GUINT_TO_POINTER (ag_account->id),
590 g_strdup (parent_uid));
592 g_object_unref (collection_source);
593 g_object_unref (mail_account_source);
594 g_object_unref (mail_identity_source);
595 g_object_unref (mail_transport_source);
599 ubuntu_online_accounts_got_userinfo_cb (GObject *source_object,
600 GAsyncResult *result,
603 AgAccount *ag_account;
604 AsyncContext *async_context = user_data;
605 gchar *user_identity = NULL;
606 gchar *email_address = NULL;
607 GError *error = NULL;
609 ag_account = AG_ACCOUNT (source_object);
611 e_ag_account_collect_userinfo_finish (
612 ag_account, result, &user_identity, &email_address, &error);
615 ubuntu_online_accounts_create_collection (
616 async_context->extension,
617 async_context->backend_factory,
623 "%s: Failed to create ESource "
624 "collection for AgAccount '%s': %s",
626 ag_account_get_display_name (ag_account),
628 g_error_free (error);
631 g_free (user_identity);
632 g_free (email_address);
634 async_context_free (async_context);
638 ubuntu_online_accounts_collect_userinfo (EUbuntuOnlineAccounts *extension,
639 EBackendFactory *backend_factory,
640 AgAccount *ag_account)
642 AsyncContext *async_context;
644 /* Before we create a collection we need to collect user info from
645 * the online service. GNOME Online Accounts does this for us, but
646 * no such luck with libaccounts-glib or libsignon-glib. */
648 async_context = g_slice_new0 (AsyncContext);
649 async_context->extension = g_object_ref (extension);
650 async_context->backend_factory = g_object_ref (backend_factory);
652 e_ag_account_collect_userinfo (
654 ubuntu_online_accounts_got_userinfo_cb,
659 ubuntu_online_accounts_remove_collection (EUbuntuOnlineAccounts *extension,
662 GError *error = NULL;
664 /* This removes the entire subtree rooted at source.
665 * Deletes the corresponding on-disk key files too. */
666 e_source_remove_sync (source, NULL, &error);
669 g_warning ("%s: %s", G_STRFUNC, error->message);
670 g_error_free (error);
675 ubuntu_online_accounts_account_created_cb (AgManager *ag_manager,
676 AgAccountId ag_account_id,
677 EUbuntuOnlineAccounts *extension)
679 AgAccount *ag_account;
680 ESourceRegistryServer *server;
681 EBackendFactory *backend_factory = NULL;
682 const gchar *provider_name;
683 const gchar *backend_name;
684 const gchar *source_uid;
686 server = ubuntu_online_accounts_get_server (extension);
688 ag_account = ag_manager_get_account (ag_manager, ag_account_id);
689 g_return_if_fail (ag_account != NULL);
691 provider_name = ag_account_get_provider_name (ag_account);
692 backend_name = ubuntu_online_accounts_get_backend_name (provider_name);
694 source_uid = g_hash_table_lookup (
695 extension->uoa_to_eds,
696 GUINT_TO_POINTER (ag_account_id));
698 if (source_uid == NULL && backend_name != NULL)
699 backend_factory = e_data_factory_ref_backend_factory (
700 E_DATA_FACTORY (server), backend_name);
702 if (backend_factory != NULL) {
703 ubuntu_online_accounts_collect_userinfo (
704 extension, backend_factory, ag_account);
705 g_object_unref (backend_factory);
708 g_object_unref (ag_account);
712 ubuntu_online_accounts_account_deleted_cb (AgManager *ag_manager,
713 AgAccountId ag_account_id,
714 EUbuntuOnlineAccounts *extension)
716 ESource *source = NULL;
717 ESourceRegistryServer *server;
718 const gchar *source_uid;
720 server = ubuntu_online_accounts_get_server (extension);
722 source_uid = g_hash_table_lookup (
723 extension->uoa_to_eds,
724 GUINT_TO_POINTER (ag_account_id));
726 if (source_uid != NULL)
727 source = e_source_registry_server_ref_source (
730 if (source != NULL) {
731 ubuntu_online_accounts_remove_collection (extension, source);
732 g_object_unref (source);
737 ubuntu_online_accounts_populate_accounts_table (EUbuntuOnlineAccounts *extension,
738 GList *ag_account_ids)
740 ESourceRegistryServer *server;
741 GQueue trash = G_QUEUE_INIT;
743 const gchar *extension_name;
745 server = ubuntu_online_accounts_get_server (extension);
747 extension_name = E_SOURCE_EXTENSION_UOA;
748 list = e_source_registry_server_list_sources (server, extension_name);
750 for (link = list; link != NULL; link = g_list_next (link)) {
753 AgAccount *ag_account = NULL;
754 AgAccountId ag_account_id;
755 const gchar *source_uid;
758 source = E_SOURCE (link->data);
759 source_uid = e_source_get_uid (source);
761 extension_name = E_SOURCE_EXTENSION_UOA;
762 uoa_ext = e_source_get_extension (source, extension_name);
763 ag_account_id = e_source_uoa_get_account_id (uoa_ext);
765 if (ag_account_id == 0)
768 /* Verify the UOA account still exists. */
769 match = g_list_find (
771 GUINT_TO_POINTER (ag_account_id));
773 ag_account = ag_manager_get_account (
774 extension->ag_manager, ag_account_id);
776 /* If a matching AgAccountId was found, add it
777 * to our accounts hash table. Otherwise remove
778 * the ESource after we finish looping. */
779 if (ag_account != NULL) {
780 g_hash_table_insert (
781 extension->uoa_to_eds,
782 GUINT_TO_POINTER (ag_account_id),
783 g_strdup (source_uid));
785 ubuntu_online_accounts_config_sources (
786 extension, source, ag_account);
788 g_queue_push_tail (&trash, source);
792 /* Empty the trash. */
793 while (!g_queue_is_empty (&trash)) {
794 ESource *source = g_queue_pop_head (&trash);
795 ubuntu_online_accounts_remove_collection (extension, source);
798 g_list_free_full (list, (GDestroyNotify) g_object_unref);
802 ubuntu_online_accounts_bus_acquired_cb (EDBusServer *server,
803 GDBusConnection *connection,
804 EUbuntuOnlineAccounts *extension)
808 extension->ag_manager = ag_manager_new ();
810 list = ag_manager_list (extension->ag_manager);
812 /* This populates a hash table of UOA ID -> ESource UID strings by
813 * searching through available data sources for ones with a "Ubuntu
814 * Online Accounts" extension. If such an extension is found, but
815 * no corresponding AgAccount (presumably meaning the UOA account
816 * was somehow deleted between E-D-S sessions) then the ESource in
817 * which the extension was found gets deleted. */
818 ubuntu_online_accounts_populate_accounts_table (extension, list);
820 for (link = list; link != NULL; link = g_list_next (link))
821 ubuntu_online_accounts_account_created_cb (
822 extension->ag_manager,
823 GPOINTER_TO_UINT (link->data),
826 ag_manager_list_free (list);
828 /* Listen for Online Account changes. */
830 extension->ag_manager, "account-created",
831 G_CALLBACK (ubuntu_online_accounts_account_created_cb),
834 extension->ag_manager, "account-deleted",
835 G_CALLBACK (ubuntu_online_accounts_account_deleted_cb),
840 ubuntu_online_accounts_dispose (GObject *object)
842 EUbuntuOnlineAccounts *extension;
844 extension = E_UBUNTU_ONLINE_ACCOUNTS (object);
846 if (extension->ag_manager != NULL) {
847 g_signal_handlers_disconnect_matched (
848 extension->ag_manager,
850 0, 0, NULL, NULL, object);
851 g_object_unref (extension->ag_manager);
852 extension->ag_manager = NULL;
855 /* Chain up to parent's dispose() method. */
856 G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)->
861 ubuntu_online_accounts_finalize (GObject *object)
863 EUbuntuOnlineAccounts *extension;
865 extension = E_UBUNTU_ONLINE_ACCOUNTS (object);
867 g_hash_table_destroy (extension->uoa_to_eds);
869 /* Chain up to parent's finalize() method. */
870 G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)->
875 ubuntu_online_accounts_constructed (GObject *object)
877 EExtension *extension;
878 EExtensible *extensible;
880 extension = E_EXTENSION (object);
881 extensible = e_extension_get_extensible (extension);
883 /* Wait for the registry service to acquire its well-known
884 * bus name so we don't do anything destructive beforehand. */
887 extensible, "bus-acquired",
888 G_CALLBACK (ubuntu_online_accounts_bus_acquired_cb),
891 /* Chain up to parent's constructed() method. */
892 G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)->
893 constructed (object);
897 ubuntu_online_accounts_get_access_token_sync (EOAuth2Support *support,
899 GCancellable *cancellable,
900 gchar **out_access_token,
901 gint *out_expires_in,
904 EAsyncClosure *closure;
905 GAsyncResult *result;
908 closure = e_async_closure_new ();
910 e_oauth2_support_get_access_token (
911 support, source, cancellable,
912 e_async_closure_callback, closure);
914 result = e_async_closure_wait (closure);
916 success = e_oauth2_support_get_access_token_finish (
917 support, result, out_access_token, out_expires_in, error);
919 e_async_closure_free (closure);
924 /* Helper for ubuntu_online_accounts_get_access_token() */
926 ubuntu_online_accounts_session_process_cb (GObject *source_object,
927 GAsyncResult *result,
930 GSimpleAsyncResult *simple;
931 AsyncContext *async_context;
932 GVariant *session_data;
933 GError *error = NULL;
935 simple = G_SIMPLE_ASYNC_RESULT (user_data);
936 async_context = g_simple_async_result_get_op_res_gpointer (simple);
938 session_data = signon_auth_session_process_finish (
939 SIGNON_AUTH_SESSION (source_object), result, &error);
943 ((session_data != NULL) && (error == NULL)) ||
944 ((session_data == NULL) && (error != NULL)));
946 if (session_data != NULL) {
948 session_data, "AccessToken", "s",
949 &async_context->access_token);
952 session_data, "ExpiresIn", "i",
953 &async_context->expires_in);
955 g_warn_if_fail (async_context->access_token != NULL);
956 g_variant_unref (session_data);
960 g_simple_async_result_take_error (simple, error);
962 g_simple_async_result_complete (simple);
964 g_object_unref (simple);
968 ubuntu_online_accounts_get_access_token (EOAuth2Support *support,
970 GCancellable *cancellable,
971 GAsyncReadyCallback callback,
974 GSimpleAsyncResult *simple;
975 AsyncContext *async_context;
976 SignonAuthSession *session;
977 AgAccountService *ag_account_service;
978 AgAuthData *ag_auth_data;
979 GError *error = NULL;
981 async_context = g_slice_new0 (AsyncContext);
983 simple = g_simple_async_result_new (
984 G_OBJECT (support), callback, user_data,
985 ubuntu_online_accounts_get_access_token);
987 g_simple_async_result_set_check_cancellable (simple, cancellable);
989 g_simple_async_result_set_op_res_gpointer (
990 simple, async_context, (GDestroyNotify) async_context_free);
992 ag_account_service = ubuntu_online_accounts_ref_account_service (
993 E_UBUNTU_ONLINE_ACCOUNTS (support), source);
995 if (ag_account_service == NULL) {
996 g_simple_async_result_set_error (
997 simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
998 _("Cannot find a corresponding account "
999 "service in the accounts database from "
1000 "which to obtain an access token for '%s'"),
1001 e_source_get_display_name (source));
1002 g_simple_async_result_complete_in_idle (simple);
1003 g_object_unref (simple);
1007 /* XXX This should never happen. But because libaccounts-glib
1008 * splits authentication method by service-type instead of
1009 * by provider, and because we broadcast OAuth 2.0 support
1010 * across the entire collection (spanning multiple service
1011 * types), it's conceivable that not all service-types for
1012 * a provider use OAuth 2.0, and an ESource for one of the
1013 * ones that DOESN'T could mistakenly request the token. */
1014 if (!ubuntu_online_accounts_supports_oauth2 (ag_account_service)) {
1015 g_simple_async_result_set_error (
1016 simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1017 _("Data source '%s' does not "
1018 "support OAuth 2.0 authentication"),
1019 e_source_get_display_name (source));
1020 g_simple_async_result_complete_in_idle (simple);
1021 g_object_unref (simple);
1025 ag_auth_data = ag_account_service_get_auth_data (ag_account_service);
1027 session = signon_auth_session_new (
1028 ag_auth_data_get_credentials_id (ag_auth_data),
1029 ag_auth_data_get_method (ag_auth_data), &error);
1033 ((session != NULL) && (error == NULL)) ||
1034 ((session == NULL) && (error != NULL)));
1036 if (session != NULL) {
1037 signon_auth_session_process_async (
1039 ag_auth_data_get_login_parameters (ag_auth_data, NULL),
1040 ag_auth_data_get_mechanism (ag_auth_data),
1042 ubuntu_online_accounts_session_process_cb,
1043 g_object_ref (simple));
1044 g_object_unref (session);
1046 g_simple_async_result_take_error (simple, error);
1047 g_simple_async_result_complete_in_idle (simple);
1050 ag_auth_data_unref (ag_auth_data);
1052 g_object_unref (ag_account_service);
1053 g_object_unref (simple);
1057 ubuntu_online_accounts_get_access_token_finish (EOAuth2Support *support,
1058 GAsyncResult *result,
1059 gchar **out_access_token,
1060 gint *out_expires_in,
1063 GSimpleAsyncResult *simple;
1064 AsyncContext *async_context;
1066 g_return_val_if_fail (
1067 g_simple_async_result_is_valid (
1068 result, G_OBJECT (support),
1069 ubuntu_online_accounts_get_access_token), FALSE);
1071 simple = G_SIMPLE_ASYNC_RESULT (result);
1072 async_context = g_simple_async_result_get_op_res_gpointer (simple);
1074 if (g_simple_async_result_propagate_error (simple, error))
1077 g_return_val_if_fail (async_context->access_token != NULL, FALSE);
1079 if (out_access_token != NULL) {
1080 *out_access_token = async_context->access_token;
1081 async_context->access_token = NULL;
1084 if (out_expires_in != NULL)
1085 *out_expires_in = async_context->expires_in;
1091 e_ubuntu_online_accounts_class_init (EUbuntuOnlineAccountsClass *class)
1093 GObjectClass *object_class;
1094 EExtensionClass *extension_class;
1096 object_class = G_OBJECT_CLASS (class);
1097 object_class->dispose = ubuntu_online_accounts_dispose;
1098 object_class->finalize = ubuntu_online_accounts_finalize;
1099 object_class->constructed = ubuntu_online_accounts_constructed;
1101 extension_class = E_EXTENSION_CLASS (class);
1102 extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
1106 e_ubuntu_online_accounts_class_finalize (EUbuntuOnlineAccountsClass *class)
1111 e_ubuntu_online_accounts_oauth2_support_init (EOAuth2SupportInterface *interface)
1113 interface->get_access_token_sync = ubuntu_online_accounts_get_access_token_sync;
1114 interface->get_access_token = ubuntu_online_accounts_get_access_token;
1115 interface->get_access_token_finish = ubuntu_online_accounts_get_access_token_finish;
1119 e_ubuntu_online_accounts_init (EUbuntuOnlineAccounts *extension)
1121 extension->uoa_to_eds = g_hash_table_new_full (
1122 (GHashFunc) g_direct_hash,
1123 (GEqualFunc) g_direct_equal,
1124 (GDestroyNotify) NULL,
1125 (GDestroyNotify) g_free);
1128 G_MODULE_EXPORT void
1129 e_module_load (GTypeModule *type_module)
1131 e_ubuntu_online_accounts_register_type (type_module);
1134 G_MODULE_EXPORT void
1135 e_module_unload (GTypeModule *type_module)