2 * module-gnome-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/>
19 /* XXX Yeah, yeah... */
20 #define GOA_API_IS_SUBJECT_TO_CHANGE
24 #include <glib/gi18n-lib.h>
25 #include <libsecret/secret.h>
26 #include <libsoup/soup.h>
28 #include <libebackend/libebackend.h>
30 #include "goaewsclient.h"
32 /* Standard GObject macros */
33 #define E_TYPE_GNOME_ONLINE_ACCOUNTS \
34 (e_gnome_online_accounts_get_type ())
35 #define E_GNOME_ONLINE_ACCOUNTS(obj) \
36 (G_TYPE_CHECK_INSTANCE_CAST \
37 ((obj), E_TYPE_GNOME_ONLINE_ACCOUNTS, EGnomeOnlineAccounts))
39 #define CAMEL_OAUTH_MECHANISM_NAME "XOAUTH"
40 #define CAMEL_OAUTH2_MECHANISM_NAME "XOAUTH2"
42 typedef struct _EGnomeOnlineAccounts EGnomeOnlineAccounts;
43 typedef struct _EGnomeOnlineAccountsClass EGnomeOnlineAccountsClass;
45 struct _EGnomeOnlineAccounts {
48 GoaClient *goa_client;
49 GCancellable *create_client;
51 /* GoaAccount ID -> ESource UID */
52 GHashTable *goa_to_eds;
55 struct _EGnomeOnlineAccountsClass {
56 EExtensionClass parent_class;
59 /* The keyring definintions are copied from e-authentication-session.c */
61 #define KEYRING_ITEM_ATTRIBUTE_NAME "e-source-uid"
62 #define KEYRING_ITEM_DISPLAY_FORMAT "Evolution Data Source %s"
64 #ifdef HAVE_GOA_PASSWORD_BASED
65 /* XXX Probably want to share this with
66 * evolution-source-registry-migrate-sources.c */
67 static SecretSchema schema = {
68 "org.gnome.Evolution.DataSource",
69 SECRET_SCHEMA_DONT_MATCH_NAME,
71 { KEYRING_ITEM_ATTRIBUTE_NAME,
72 SECRET_SCHEMA_ATTRIBUTE_STRING },
76 #endif /* HAVE_GOA_PASSWORD_BASED */
78 /* Module Entry Points */
79 void e_module_load (GTypeModule *type_module);
80 void e_module_unload (GTypeModule *type_module);
82 /* Forward Declarations */
83 GType e_gnome_online_accounts_get_type (void);
84 static void e_gnome_online_accounts_oauth2_support_init
85 (EOAuth2SupportInterface *interface);
87 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
89 e_gnome_online_accounts,
92 G_IMPLEMENT_INTERFACE_DYNAMIC (
93 E_TYPE_OAUTH2_SUPPORT,
94 e_gnome_online_accounts_oauth2_support_init))
97 gnome_online_accounts_get_backend_name (const gchar *goa_provider_type)
99 const gchar *eds_backend_name = NULL;
101 g_return_val_if_fail (goa_provider_type != NULL, NULL);
103 /* This is a mapping between GoaAccount provider types and
104 * ESourceCollection backend names. It requires knowledge
105 * of other registry modules, possibly even from 3rd party
106 * packages. No way around it. */
108 if (g_str_equal (goa_provider_type, "exchange"))
109 eds_backend_name = "ews";
111 if (g_str_equal (goa_provider_type, "google"))
112 eds_backend_name = "google";
114 if (g_str_equal (goa_provider_type, "imap_smtp"))
115 eds_backend_name = "none";
117 if (g_str_equal (goa_provider_type, "yahoo"))
118 eds_backend_name = "yahoo";
120 if (g_str_equal (goa_provider_type, "owncloud"))
121 eds_backend_name = "owncloud";
123 return eds_backend_name;
126 static ESourceRegistryServer *
127 gnome_online_accounts_get_server (EGnomeOnlineAccounts *extension)
129 EExtensible *extensible;
131 extensible = e_extension_get_extensible (E_EXTENSION (extension));
133 return E_SOURCE_REGISTRY_SERVER (extensible);
137 gnome_online_accounts_provider_type_to_backend_name (GBinding *binding,
138 const GValue *source_value,
139 GValue *target_value,
142 const gchar *provider_type;
143 const gchar *backend_name;
145 provider_type = g_value_get_string (source_value);
146 backend_name = gnome_online_accounts_get_backend_name (provider_type);
147 g_return_val_if_fail (backend_name != NULL, FALSE);
148 g_value_set_string (target_value, backend_name);
154 gnome_online_accounts_object_is_non_null (GBinding *binding,
155 const GValue *source_value,
156 GValue *target_value,
161 v_object = g_value_get_object (source_value);
162 g_value_set_boolean (target_value, v_object != NULL);
168 gnome_online_accounts_ref_account (EGnomeOnlineAccounts *extension,
171 ESourceRegistryServer *server;
172 GoaObject *match = NULL;
174 const gchar *extension_name;
175 gchar *account_id = NULL;
177 extension_name = E_SOURCE_EXTENSION_GOA;
178 server = gnome_online_accounts_get_server (extension);
180 source = e_source_registry_server_find_extension (
181 server, source, extension_name);
183 if (source != NULL) {
186 goa_ext = e_source_get_extension (source, extension_name);
187 account_id = e_source_goa_dup_account_id (goa_ext);
189 g_object_unref (source);
192 if (account_id == NULL)
195 /* FIXME Use goa_client_lookup_by_id() once we require GOA 3.6. */
197 list = goa_client_get_accounts (extension->goa_client);
199 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
200 GoaObject *goa_object;
201 GoaAccount *goa_account;
202 const gchar *candidate_id;
204 goa_object = GOA_OBJECT (iter->data);
205 goa_account = goa_object_get_account (goa_object);
206 candidate_id = goa_account_get_id (goa_account);
208 if (g_strcmp0 (account_id, candidate_id) == 0)
209 match = g_object_ref (goa_object);
211 g_object_unref (goa_account);
217 g_list_free_full (list, (GDestroyNotify) g_object_unref);
225 gnome_online_accounts_new_source (EGnomeOnlineAccounts *extension)
227 ESourceRegistryServer *server;
230 GError *error = NULL;
232 /* This being a brand new data source, creating the instance
233 * should never fail but we'll check for errors just the same. */
234 server = gnome_online_accounts_get_server (extension);
235 file = e_server_side_source_new_user_file (NULL);
236 source = e_server_side_source_new (server, file, &error);
237 g_object_unref (file);
240 g_warn_if_fail (source == NULL);
241 g_warning ("%s: %s", G_STRFUNC, error->message);
242 g_error_free (error);
248 #ifdef HAVE_GOA_PASSWORD_BASED
250 replace_host (gchar **url,
255 uri = soup_uri_new (*url);
259 soup_uri_set_host (uri, host);
262 *url = soup_uri_to_string (uri, FALSE);
266 #endif /* HAVE_GOA_PASSWORD_BASED */
269 gnome_online_accounts_config_exchange (EGnomeOnlineAccounts *extension,
271 GoaObject *goa_object)
273 #ifdef HAVE_GOA_PASSWORD_BASED
274 GoaExchange *goa_exchange;
275 ESourceExtension *source_extension;
276 const gchar *extension_name;
277 gchar *as_url = NULL;
278 gchar *oab_url = NULL;
280 GError *error = NULL;
282 goa_exchange = goa_object_peek_exchange (goa_object);
283 if (goa_exchange == NULL)
286 /* This should force the ESourceCamelEws type to be registered.
287 * It will also tell us if Evolution-EWS is even installed. */
288 class = g_type_class_ref (g_type_from_name ("EEwsBackend"));
290 g_type_class_unref (class);
293 "%s: Could not locate EEwsBackendClass. "
294 "Is Evolution-EWS installed?", G_STRFUNC);
298 /* XXX GNOME Online Accounts already runs autodiscover to test
299 * the user-entered values but doesn't share the discovered
300 * URLs. It only provides us a host name and expects us to
301 * re-run autodiscover for ourselves.
303 * So I've copied a slab of code from GOA which was in turn
304 * copied from Evolution-EWS which does the autodiscovery.
306 * I've already complained to Debarshi Ray about the lack
307 * of useful info in GOA's Exchange interface so hopefully
308 * it will someday publish discovered URLs and then we can
309 * remove this hack. */
311 goa_ews_autodiscover_sync (
312 goa_object, &as_url, &oab_url, NULL, &error);
315 g_warning ("%s: %s", G_STRFUNC, error->message);
316 g_error_free (error);
320 g_return_if_fail (as_url != NULL);
321 g_return_if_fail (oab_url != NULL);
323 /* XXX We don't have direct access to CamelEwsSettings from here
324 * since it's defined in Evolution-EWS. But we can find out
325 * its extension name and set properties by name. */
327 extension_name = e_source_camel_get_extension_name ("ews");
328 source_extension = e_source_get_extension (source, extension_name);
330 /* This will be NULL if Evolution-EWS is not installed. */
331 if (source_extension != NULL) {
332 GoaAccount *goa_account;
333 CamelSettings *settings;
334 gchar *host, *user, *email;
336 goa_account = goa_object_peek_account (goa_object);
337 host = goa_exchange_dup_host (goa_exchange);
338 user = goa_account_dup_identity (goa_account);
339 email = goa_account_dup_presentation_identity (goa_account);
342 replace_host (&as_url, host);
343 replace_host (&oab_url, host);
353 settings = e_source_camel_get_settings (
354 E_SOURCE_CAMEL (source_extension));
368 "%s: Failed to create [%s] extension",
369 G_STRFUNC, extension_name);
374 #endif /* HAVE_GOA_PASSWORD_BASED */
378 gnome_online_accounts_config_oauth (EGnomeOnlineAccounts *extension,
380 GoaObject *goa_object)
382 ESourceExtension *source_extension;
383 const gchar *extension_name;
385 if (goa_object_peek_oauth_based (goa_object) == NULL)
388 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
389 source_extension = e_source_get_extension (source, extension_name);
391 e_source_authentication_set_method (
392 E_SOURCE_AUTHENTICATION (source_extension),
393 CAMEL_OAUTH_MECHANISM_NAME);
397 gnome_online_accounts_config_oauth2 (EGnomeOnlineAccounts *extension,
399 GoaObject *goa_object)
401 ESourceExtension *source_extension;
402 const gchar *extension_name;
404 if (goa_object_peek_oauth2_based (goa_object) == NULL)
407 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
408 source_extension = e_source_get_extension (source, extension_name);
410 e_source_authentication_set_method (
411 E_SOURCE_AUTHENTICATION (source_extension),
412 CAMEL_OAUTH2_MECHANISM_NAME);
416 gnome_online_accounts_config_password (EGnomeOnlineAccounts *extension,
418 GoaObject *goa_object)
420 #ifdef HAVE_GOA_PASSWORD_BASED
421 GoaAccount *goa_account;
422 GoaPasswordBased *goa_password_based;
423 EAsyncClosure *closure;
424 GAsyncResult *result;
428 gchar *password = NULL;
429 GError *error = NULL;
431 /* If the GNOME Online Account is password-based, we use its
432 * password to seed our own keyring entry for the collection
433 * source which avoids having to special-case authentication
434 * like we do for OAuth. Plus, if the stored password is no
435 * good we'll prompt for a new one instead of just giving up. */
437 goa_password_based = goa_object_get_password_based (goa_object);
439 if (goa_password_based == NULL)
442 closure = e_async_closure_new ();
444 /* XXX The GOA documentation doesn't explain the string
445 * argument in goa_password_based_get_password() so
446 * we'll pass in the identity and hope for the best. */
447 goa_account = goa_object_get_account (goa_object);
448 arg_id = goa_account_dup_identity (goa_account);
449 g_object_unref (goa_account);
451 goa_password_based_call_get_password (
452 goa_password_based, arg_id, NULL,
453 e_async_closure_callback, closure);
457 result = e_async_closure_wait (closure);
459 goa_password_based_call_get_password_finish (
460 goa_password_based, &password, result, &error);
463 g_warning ("%s: %s", G_STRFUNC, error->message);
464 g_error_free (error);
468 uid = e_source_get_uid (source);
469 display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
471 secret_password_store (
472 &schema, SECRET_COLLECTION_DEFAULT,
473 display_name, password, NULL,
474 e_async_closure_callback, closure,
475 KEYRING_ITEM_ATTRIBUTE_NAME, uid,
478 result = e_async_closure_wait (closure);
480 secret_password_store_finish (result, &error);
482 g_free (display_name);
485 /* If we fail to store the password, we'll just end up prompting
486 * for a password like normal. Annoying, maybe, but not the end
487 * of the world. Still leave a breadcrumb for debugging though. */
489 g_warning ("%s: %s", G_STRFUNC, error->message);
490 g_error_free (error);
494 e_async_closure_free (closure);
495 g_object_unref (goa_password_based);
496 #endif /* HAVE_GOA_PASSWORD_BASED */
500 gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension,
502 GoaObject *goa_object)
504 GoaAccount *goa_account;
505 ESourceExtension *source_extension;
506 const gchar *extension_name;
507 const gchar *provider_type;
508 const gchar *backend_name;
510 goa_account = goa_object_get_account (goa_object);
511 provider_type = goa_account_get_provider_type (goa_account);
512 backend_name = gnome_online_accounts_get_backend_name (provider_type);
514 g_object_bind_property (
515 goa_account, "presentation-identity",
516 source, "display-name",
517 G_BINDING_SYNC_CREATE);
519 extension_name = E_SOURCE_EXTENSION_GOA;
520 source_extension = e_source_get_extension (source, extension_name);
522 g_object_bind_property (
524 source_extension, "account-id",
525 G_BINDING_SYNC_CREATE);
527 /* requires more properties from ownCould, but these are not
528 available before ownCloud was introduced, thus workaround
529 it with the backend_name check
531 if (g_strcmp0 (backend_name, "owncloud") == 0) {
532 GoaCalendar *goa_calendar;
533 GoaContacts *goa_contacts;
535 goa_calendar = goa_object_get_calendar (goa_object);
537 g_object_bind_property (
539 source_extension, "calendar-url",
540 G_BINDING_SYNC_CREATE);
541 g_object_unref (goa_calendar);
544 goa_contacts = goa_object_get_contacts (goa_object);
546 g_object_bind_property (
548 source_extension, "contacts-url",
549 G_BINDING_SYNC_CREATE);
550 g_object_unref (goa_contacts);
554 extension_name = E_SOURCE_EXTENSION_COLLECTION;
555 source_extension = e_source_get_extension (source, extension_name);
557 g_object_bind_property_full (
558 goa_account, "provider-type",
559 source_extension, "backend-name",
560 G_BINDING_SYNC_CREATE,
561 gnome_online_accounts_provider_type_to_backend_name,
563 NULL, (GDestroyNotify) NULL);
565 g_object_bind_property (
566 goa_account, "identity",
567 source_extension, "identity",
568 G_BINDING_SYNC_CREATE);
570 g_object_bind_property_full (
571 goa_object, "calendar",
572 source_extension, "calendar-enabled",
573 G_BINDING_SYNC_CREATE,
574 gnome_online_accounts_object_is_non_null,
576 NULL, (GDestroyNotify) NULL);
578 g_object_bind_property_full (
579 goa_object, "contacts",
580 source_extension, "contacts-enabled",
581 G_BINDING_SYNC_CREATE,
582 gnome_online_accounts_object_is_non_null,
584 NULL, (GDestroyNotify) NULL);
586 g_object_bind_property_full (
588 source_extension, "mail-enabled",
589 G_BINDING_SYNC_CREATE,
590 gnome_online_accounts_object_is_non_null,
592 NULL, (GDestroyNotify) NULL);
594 g_object_unref (goa_account);
596 /* Handle optional GOA interfaces. */
597 gnome_online_accounts_config_exchange (extension, source, goa_object);
598 gnome_online_accounts_config_password (extension, source, goa_object);
600 /* The data source should not be removable by clients. */
601 e_server_side_source_set_removable (
602 E_SERVER_SIDE_SOURCE (source), FALSE);
604 if (goa_object_peek_oauth2_based (goa_object) != NULL) {
605 /* This module provides OAuth 2.0 support to the collection.
606 * Note, children of the collection source will automatically
607 * inherit our EOAuth2Support through the property binding in
608 * collection_backend_child_added(). */
609 e_server_side_source_set_oauth2_support (
610 E_SERVER_SIDE_SOURCE (source),
611 E_OAUTH2_SUPPORT (extension));
616 gnome_online_accounts_config_mail_account (EGnomeOnlineAccounts *extension,
618 GoaObject *goa_object)
620 EServerSideSource *server_side_source;
622 /* Only one or the other should be present, not both. */
623 gnome_online_accounts_config_oauth (extension, source, goa_object);
624 gnome_online_accounts_config_oauth2 (extension, source, goa_object);
626 /* XXX Need to defer the network security settings to the
627 * provider-specific module since "imap-use-tls" tells
628 * us neither the port number, nor whether to use IMAP
629 * over SSL versus STARTTLS. The module will know. */
631 /* Clients may change the source by may not remove it. */
632 server_side_source = E_SERVER_SIDE_SOURCE (source);
633 e_server_side_source_set_writable (server_side_source, TRUE);
634 e_server_side_source_set_removable (server_side_source, FALSE);
638 gnome_online_accounts_config_mail_identity (EGnomeOnlineAccounts *extension,
640 GoaObject *goa_object)
643 ESourceExtension *source_extension;
644 EServerSideSource *server_side_source;
645 const gchar *extension_name;
647 goa_mail = goa_object_get_mail (goa_object);
648 g_return_if_fail (goa_mail != NULL);
650 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
651 source_extension = e_source_get_extension (source, extension_name);
653 g_object_bind_property (
654 goa_mail, "email-address",
655 source_extension, "address",
656 G_BINDING_SYNC_CREATE);
658 g_object_unref (goa_mail);
660 /* Clients may change the source by may not remove it. */
661 server_side_source = E_SERVER_SIDE_SOURCE (source);
662 e_server_side_source_set_writable (server_side_source, TRUE);
663 e_server_side_source_set_removable (server_side_source, FALSE);
667 gnome_online_accounts_config_mail_transport (EGnomeOnlineAccounts *extension,
669 GoaObject *goa_object)
671 EServerSideSource *server_side_source;
673 /* Only one or the other should be present, not both. */
674 gnome_online_accounts_config_oauth (extension, source, goa_object);
675 gnome_online_accounts_config_oauth2 (extension, source, goa_object);
677 /* XXX Need to defer the network security settings to the
678 * provider-specific module since "smtp-use-tls" tells
679 * us neither the port number, nor whether to use SMTP
680 * over SSL versus STARTTLS. The module will know. */
682 /* Clients may change the source by may not remove it. */
683 server_side_source = E_SERVER_SIDE_SOURCE (source);
684 e_server_side_source_set_writable (server_side_source, TRUE);
685 e_server_side_source_set_removable (server_side_source, FALSE);
689 gnome_online_accounts_config_sources (EGnomeOnlineAccounts *extension,
691 GoaObject *goa_object)
693 ESourceRegistryServer *server;
694 ECollectionBackend *backend;
697 /* XXX This function was primarily intended to smooth the
698 * transition of mail accounts from XOAUTH to XOAUTH2,
699 * but it may be useful for other types of migration. */
701 gnome_online_accounts_config_collection (extension, source, goa_object);
703 server = gnome_online_accounts_get_server (extension);
704 backend = e_source_registry_server_ref_backend (server, source);
705 g_return_if_fail (backend != NULL);
707 list = e_collection_backend_list_mail_sources (backend);
709 for (link = list; link != NULL; link = g_list_next (link)) {
710 const gchar *extension_name;
712 source = E_SOURCE (link->data);
714 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
715 if (e_source_has_extension (source, extension_name))
716 gnome_online_accounts_config_mail_account (
717 extension, source, goa_object);
719 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
720 if (e_source_has_extension (source, extension_name))
721 gnome_online_accounts_config_mail_identity (
722 extension, source, goa_object);
724 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
725 if (e_source_has_extension (source, extension_name))
726 gnome_online_accounts_config_mail_transport (
727 extension, source, goa_object);
730 g_list_free_full (list, (GDestroyNotify) g_object_unref);
732 g_object_unref (backend);
736 gnome_online_accounts_create_collection (EGnomeOnlineAccounts *extension,
737 EBackendFactory *backend_factory,
738 GoaObject *goa_object)
740 GoaAccount *goa_account;
741 ESourceRegistryServer *server;
742 ESource *collection_source;
743 ESource *mail_account_source = NULL;
744 ESource *mail_identity_source = NULL;
745 ESource *mail_transport_source = NULL;
746 const gchar *account_id;
747 const gchar *parent_uid;
749 server = gnome_online_accounts_get_server (extension);
751 collection_source = gnome_online_accounts_new_source (extension);
752 g_return_if_fail (E_IS_SOURCE (collection_source));
754 gnome_online_accounts_config_collection (extension, collection_source, goa_object);
755 parent_uid = e_source_get_uid (collection_source);
757 if (goa_object_peek_mail (goa_object)) {
758 mail_account_source = gnome_online_accounts_new_source (extension);
759 g_return_if_fail (E_IS_SOURCE (mail_account_source));
761 mail_identity_source = gnome_online_accounts_new_source (extension);
762 g_return_if_fail (E_IS_SOURCE (mail_identity_source));
764 mail_transport_source = gnome_online_accounts_new_source (extension);
765 g_return_if_fail (E_IS_SOURCE (mail_transport_source));
767 /* Configure parent/child relationships. */
768 e_source_set_parent (mail_account_source, parent_uid);
769 e_source_set_parent (mail_identity_source, parent_uid);
770 e_source_set_parent (mail_transport_source, parent_uid);
772 /* Give the factory first crack at mail configuration. */
773 e_collection_backend_factory_prepare_mail (
774 E_COLLECTION_BACKEND_FACTORY (backend_factory),
776 mail_identity_source,
777 mail_transport_source);
779 gnome_online_accounts_config_mail_account (extension, mail_account_source, goa_object);
780 gnome_online_accounts_config_mail_identity (extension, mail_identity_source, goa_object);
781 gnome_online_accounts_config_mail_transport (extension, mail_transport_source, goa_object);
784 /* Export the new source collection. */
785 e_source_registry_server_add_source (server, collection_source);
787 if (mail_account_source) {
788 e_source_registry_server_add_source (server, mail_account_source);
789 g_object_unref (mail_account_source);
792 if (mail_identity_source) {
793 e_source_registry_server_add_source (server, mail_identity_source);
794 g_object_unref (mail_identity_source);
797 if (mail_transport_source) {
798 e_source_registry_server_add_source (server, mail_transport_source);
799 g_object_unref (mail_transport_source);
802 goa_account = goa_object_get_account (goa_object);
803 account_id = goa_account_get_id (goa_account);
805 g_hash_table_insert (
806 extension->goa_to_eds,
807 g_strdup (account_id),
808 g_strdup (parent_uid));
810 g_object_unref (goa_account);
811 g_object_unref (collection_source);
815 gnome_online_accounts_remove_collection (EGnomeOnlineAccounts *extension,
818 GError *error = NULL;
820 /* This removes the entire subtree rooted at source.
821 * Deletes the corresponding on-disk key files too. */
822 e_source_remove_sync (source, NULL, &error);
825 g_warning ("%s: %s", G_STRFUNC, error->message);
826 g_error_free (error);
831 gnome_online_accounts_account_added_cb (GoaClient *goa_client,
832 GoaObject *goa_object,
833 EGnomeOnlineAccounts *extension)
835 GoaAccount *goa_account;
836 ESourceRegistryServer *server;
837 EBackendFactory *backend_factory = NULL;
838 const gchar *provider_type;
839 const gchar *backend_name;
840 const gchar *account_id;
841 const gchar *source_uid;
843 server = gnome_online_accounts_get_server (extension);
845 goa_account = goa_object_get_account (goa_object);
846 provider_type = goa_account_get_provider_type (goa_account);
847 backend_name = gnome_online_accounts_get_backend_name (provider_type);
849 account_id = goa_account_get_id (goa_account);
850 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
852 if (source_uid == NULL && backend_name != NULL)
853 backend_factory = e_data_factory_ref_backend_factory (
854 E_DATA_FACTORY (server), backend_name);
856 if (backend_factory != NULL) {
857 gnome_online_accounts_create_collection (
858 extension, backend_factory, goa_object);
859 g_object_unref (backend_factory);
862 g_object_unref (goa_account);
866 gnome_online_accounts_account_removed_cb (GoaClient *goa_client,
867 GoaObject *goa_object,
868 EGnomeOnlineAccounts *extension)
870 ESource *source = NULL;
871 ESourceRegistryServer *server;
872 GoaAccount *goa_account;
873 const gchar *account_id;
874 const gchar *source_uid;
876 server = gnome_online_accounts_get_server (extension);
878 goa_account = goa_object_get_account (goa_object);
880 account_id = goa_account_get_id (goa_account);
881 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
883 if (source_uid != NULL)
884 source = e_source_registry_server_ref_source (
887 if (source != NULL) {
888 gnome_online_accounts_remove_collection (extension, source);
889 g_object_unref (source);
892 g_object_unref (goa_account);
896 gnome_online_accounts_compare_id (GoaObject *goa_object,
897 const gchar *target_id)
899 GoaAccount *goa_account;
900 const gchar *account_id;
903 goa_account = goa_object_get_account (goa_object);
904 account_id = goa_account_get_id (goa_account);
905 result = g_strcmp0 (account_id, target_id);
906 g_object_unref (goa_account);
912 gnome_online_accounts_populate_accounts_table (EGnomeOnlineAccounts *extension,
915 ESourceRegistryServer *server;
916 GQueue trash = G_QUEUE_INIT;
918 const gchar *extension_name;
920 server = gnome_online_accounts_get_server (extension);
922 extension_name = E_SOURCE_EXTENSION_GOA;
923 list = e_source_registry_server_list_sources (server, extension_name);
925 for (link = list; link != NULL; link = g_list_next (link)) {
928 const gchar *account_id;
929 const gchar *source_uid;
932 source = E_SOURCE (link->data);
933 source_uid = e_source_get_uid (source);
935 extension_name = E_SOURCE_EXTENSION_GOA;
936 goa_ext = e_source_get_extension (source, extension_name);
937 account_id = e_source_goa_get_account_id (goa_ext);
939 if (account_id == NULL)
942 /* Verify the GOA account still exists. */
943 match = g_list_find_custom (
944 goa_objects, account_id,
945 (GCompareFunc) gnome_online_accounts_compare_id);
947 /* If a matching GoaObject was found, add its ID
948 * to our accounts hash table. Otherwise remove
949 * the ESource after we finish looping. */
951 GoaObject *goa_object;
953 g_hash_table_insert (
954 extension->goa_to_eds,
955 g_strdup (account_id),
956 g_strdup (source_uid));
958 goa_object = GOA_OBJECT (match->data);
959 gnome_online_accounts_config_sources (
960 extension, source, goa_object);
962 g_queue_push_tail (&trash, source);
966 /* Empty the trash. */
967 while (!g_queue_is_empty (&trash)) {
968 ESource *source = g_queue_pop_head (&trash);
969 gnome_online_accounts_remove_collection (extension, source);
972 g_list_free_full (list, (GDestroyNotify) g_object_unref);
976 gnome_online_accounts_create_client_cb (GObject *source_object,
977 GAsyncResult *result,
980 EGnomeOnlineAccounts *extension;
981 GoaClient *goa_client;
983 GError *error = NULL;
985 /* If we get back a G_IO_ERROR_CANCELLED then it means the
986 * EGnomeOnlineAccounts is already finalized, so be careful
987 * not to touch it until after we have a valid GoaClient. */
989 goa_client = goa_client_new_finish (result, &error);
992 g_warn_if_fail (goa_client == NULL);
994 "Unable to connect to the GNOME Online "
995 "Accounts service: %s", error->message);
996 g_error_free (error);
1000 g_return_if_fail (GOA_IS_CLIENT (goa_client));
1002 /* Should be safe to dereference the EGnomeOnlineAccounts now. */
1004 extension = E_GNOME_ONLINE_ACCOUNTS (user_data);
1005 extension->goa_client = goa_client; /* takes ownership */
1007 /* Don't need the GCancellable anymore. */
1008 g_object_unref (extension->create_client);
1009 extension->create_client = NULL;
1011 list = goa_client_get_accounts (extension->goa_client);
1013 /* This populates a hash table of GOA ID -> ESource UID strings by
1014 * searching through available data sources for ones with a "GNOME
1015 * Online Accounts" extension. If such an extension is found, but
1016 * no corresponding GoaAccount (presumably meaning the GOA account
1017 * was somehow deleted between E-D-S sessions) then the ESource in
1018 * which the extension was found gets deleted. */
1019 gnome_online_accounts_populate_accounts_table (extension, list);
1021 for (link = list; link != NULL; link = g_list_next (link))
1022 gnome_online_accounts_account_added_cb (
1023 extension->goa_client,
1024 GOA_OBJECT (link->data),
1027 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1029 /* Listen for Online Account changes. */
1031 extension->goa_client, "account-added",
1032 G_CALLBACK (gnome_online_accounts_account_added_cb),
1035 extension->goa_client, "account-removed",
1036 G_CALLBACK (gnome_online_accounts_account_removed_cb),
1041 gnome_online_accounts_bus_acquired_cb (EDBusServer *server,
1042 GDBusConnection *connection,
1043 EGnomeOnlineAccounts *extension)
1045 /* Connect to the GNOME Online Accounts service. */
1047 /* Note we don't reference the extension. If the
1048 * extension gets destroyed before this completes
1049 * we cancel the operation from dispose(). */
1051 extension->create_client,
1052 gnome_online_accounts_create_client_cb,
1057 gnome_online_accounts_dispose (GObject *object)
1059 EGnomeOnlineAccounts *extension;
1061 extension = E_GNOME_ONLINE_ACCOUNTS (object);
1063 if (extension->goa_client != NULL) {
1064 g_signal_handlers_disconnect_matched (
1065 extension->goa_client,
1066 G_SIGNAL_MATCH_DATA,
1067 0, 0, NULL, NULL, object);
1068 g_object_unref (extension->goa_client);
1069 extension->goa_client = NULL;
1072 /* This cancels goa_client_new() in case it still
1073 * hasn't completed. We're no longer interested. */
1074 if (extension->create_client != NULL) {
1075 g_cancellable_cancel (extension->create_client);
1076 g_object_unref (extension->create_client);
1077 extension->create_client = NULL;
1080 /* Chain up to parent's dispose() method. */
1081 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1086 gnome_online_accounts_finalize (GObject *object)
1088 EGnomeOnlineAccounts *extension;
1090 extension = E_GNOME_ONLINE_ACCOUNTS (object);
1092 g_hash_table_destroy (extension->goa_to_eds);
1094 /* Chain up to parent's finalize() method. */
1095 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1100 gnome_online_accounts_constructed (GObject *object)
1102 EExtension *extension;
1103 EExtensible *extensible;
1105 extension = E_EXTENSION (object);
1106 extensible = e_extension_get_extensible (extension);
1108 /* Wait for the registry service to acquire its well-known
1109 * bus name so we don't do anything destructive beforehand. */
1112 extensible, "bus-acquired",
1113 G_CALLBACK (gnome_online_accounts_bus_acquired_cb),
1116 /* Chain up to parent's constructed() method. */
1117 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1118 constructed (object);
1122 gnome_online_accounts_get_access_token_sync (EOAuth2Support *support,
1124 GCancellable *cancellable,
1125 gchar **out_access_token,
1126 gint *out_expires_in,
1129 GoaObject *goa_object;
1130 GoaAccount *goa_account;
1131 GoaOAuth2Based *goa_oauth2_based;
1134 goa_object = gnome_online_accounts_ref_account (
1135 E_GNOME_ONLINE_ACCOUNTS (support), source);
1137 if (goa_object == NULL) {
1139 error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1140 _("Cannot find a corresponding account in "
1141 "the org.gnome.OnlineAccounts service from "
1142 "which to obtain an access token for '%s'"),
1143 e_source_get_display_name (source));
1147 goa_account = goa_object_get_account (goa_object);
1148 g_return_val_if_fail (goa_account != NULL, FALSE);
1150 goa_oauth2_based = goa_object_get_oauth2_based (goa_object);
1151 g_return_val_if_fail (goa_oauth2_based != NULL, FALSE);
1153 success = goa_account_call_ensure_credentials_sync (
1154 goa_account, NULL, cancellable, error);
1157 success = goa_oauth2_based_call_get_access_token_sync (
1158 goa_oauth2_based, out_access_token,
1159 out_expires_in, cancellable, error);
1161 g_object_unref (goa_oauth2_based);
1162 g_object_unref (goa_account);
1163 g_object_unref (goa_object);
1167 _("Failed to obtain an access token for '%s': "),
1168 e_source_get_display_name (source));
1174 e_gnome_online_accounts_class_init (EGnomeOnlineAccountsClass *class)
1176 GObjectClass *object_class;
1177 EExtensionClass *extension_class;
1179 object_class = G_OBJECT_CLASS (class);
1180 object_class->dispose = gnome_online_accounts_dispose;
1181 object_class->finalize = gnome_online_accounts_finalize;
1182 object_class->constructed = gnome_online_accounts_constructed;
1184 extension_class = E_EXTENSION_CLASS (class);
1185 extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
1189 e_gnome_online_accounts_class_finalize (EGnomeOnlineAccountsClass *class)
1194 e_gnome_online_accounts_oauth2_support_init (EOAuth2SupportInterface *interface)
1196 interface->get_access_token_sync = gnome_online_accounts_get_access_token_sync;
1200 e_gnome_online_accounts_init (EGnomeOnlineAccounts *extension)
1202 /* Used to cancel unfinished goa_client_new(). */
1203 extension->create_client = g_cancellable_new ();
1205 extension->goa_to_eds = g_hash_table_new_full (
1206 (GHashFunc) g_str_hash,
1207 (GEqualFunc) g_str_equal,
1208 (GDestroyNotify) g_free,
1209 (GDestroyNotify) g_free);
1212 G_MODULE_EXPORT void
1213 e_module_load (GTypeModule *type_module)
1215 e_gnome_online_accounts_register_type (type_module);
1218 G_MODULE_EXPORT void
1219 e_module_unload (GTypeModule *type_module)