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 <libsoup/soup.h>
27 #include <libebackend/libebackend.h>
29 #include "goaewsclient.h"
30 #include "e-goa-password-based.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_IMAP_PROVIDER_NAME "imapx"
40 #define CAMEL_SMTP_PROVIDER_NAME "smtp"
42 #define CAMEL_OAUTH_MECHANISM_NAME "XOAUTH"
43 #define CAMEL_OAUTH2_MECHANISM_NAME "XOAUTH2"
45 typedef struct _EGnomeOnlineAccounts EGnomeOnlineAccounts;
46 typedef struct _EGnomeOnlineAccountsClass EGnomeOnlineAccountsClass;
48 struct _EGnomeOnlineAccounts {
51 GoaClient *goa_client;
52 GCancellable *create_client;
54 /* GoaAccount ID -> ESource UID */
55 GHashTable *goa_to_eds;
58 struct _EGnomeOnlineAccountsClass {
59 EExtensionClass parent_class;
62 /* Module Entry Points */
63 void e_module_load (GTypeModule *type_module);
64 void e_module_unload (GTypeModule *type_module);
66 /* Forward Declarations */
67 GType e_gnome_online_accounts_get_type (void);
68 static void e_gnome_online_accounts_oauth2_support_init
69 (EOAuth2SupportInterface *interface);
71 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
73 e_gnome_online_accounts,
76 G_IMPLEMENT_INTERFACE_DYNAMIC (
77 E_TYPE_OAUTH2_SUPPORT,
78 e_gnome_online_accounts_oauth2_support_init))
81 gnome_online_accounts_get_backend_name (const gchar *goa_provider_type)
83 const gchar *eds_backend_name = NULL;
85 g_return_val_if_fail (goa_provider_type != NULL, NULL);
87 /* This is a mapping between GoaAccount provider types and
88 * ESourceCollection backend names. It requires knowledge
89 * of other registry modules, possibly even from 3rd party
90 * packages. No way around it. */
92 if (g_str_equal (goa_provider_type, "exchange"))
93 eds_backend_name = "ews";
95 if (g_str_equal (goa_provider_type, "google"))
96 eds_backend_name = "google";
98 if (g_str_equal (goa_provider_type, "imap_smtp"))
99 eds_backend_name = "none";
101 if (g_str_equal (goa_provider_type, "yahoo"))
102 eds_backend_name = "yahoo";
104 if (g_str_equal (goa_provider_type, "owncloud"))
105 eds_backend_name = "owncloud";
107 return eds_backend_name;
110 static ESourceRegistryServer *
111 gnome_online_accounts_get_server (EGnomeOnlineAccounts *extension)
113 EExtensible *extensible;
115 extensible = e_extension_get_extensible (E_EXTENSION (extension));
117 return E_SOURCE_REGISTRY_SERVER (extensible);
121 gnome_online_accounts_provider_type_to_backend_name (GBinding *binding,
122 const GValue *source_value,
123 GValue *target_value,
126 const gchar *provider_type;
127 const gchar *backend_name;
129 provider_type = g_value_get_string (source_value);
130 backend_name = gnome_online_accounts_get_backend_name (provider_type);
131 g_return_val_if_fail (backend_name != NULL, FALSE);
132 g_value_set_string (target_value, backend_name);
138 gnome_online_accounts_object_is_non_null (GBinding *binding,
139 const GValue *source_value,
140 GValue *target_value,
145 v_object = g_value_get_object (source_value);
146 g_value_set_boolean (target_value, v_object != NULL);
152 gnome_online_accounts_ref_account (EGnomeOnlineAccounts *extension,
155 ESourceRegistryServer *server;
156 GoaObject *match = NULL;
158 const gchar *extension_name;
159 gchar *account_id = NULL;
161 extension_name = E_SOURCE_EXTENSION_GOA;
162 server = gnome_online_accounts_get_server (extension);
164 source = e_source_registry_server_find_extension (
165 server, source, extension_name);
167 if (source != NULL) {
170 goa_ext = e_source_get_extension (source, extension_name);
171 account_id = e_source_goa_dup_account_id (goa_ext);
173 g_object_unref (source);
176 if (account_id == NULL)
179 /* FIXME Use goa_client_lookup_by_id() once we require GOA 3.6. */
181 list = goa_client_get_accounts (extension->goa_client);
183 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
184 GoaObject *goa_object;
185 GoaAccount *goa_account;
186 const gchar *candidate_id;
188 goa_object = GOA_OBJECT (iter->data);
189 goa_account = goa_object_get_account (goa_object);
190 candidate_id = goa_account_get_id (goa_account);
192 if (g_strcmp0 (account_id, candidate_id) == 0)
193 match = g_object_ref (goa_object);
195 g_object_unref (goa_account);
201 g_list_free_full (list, (GDestroyNotify) g_object_unref);
209 gnome_online_accounts_new_source (EGnomeOnlineAccounts *extension)
211 ESourceRegistryServer *server;
214 GError *error = NULL;
216 /* This being a brand new data source, creating the instance
217 * should never fail but we'll check for errors just the same. */
218 server = gnome_online_accounts_get_server (extension);
219 file = e_server_side_source_new_user_file (NULL);
220 source = e_server_side_source_new (server, file, &error);
221 g_object_unref (file);
224 g_warn_if_fail (source == NULL);
225 g_warning ("%s: %s", G_STRFUNC, error->message);
226 g_error_free (error);
232 #ifdef HAVE_GOA_PASSWORD_BASED
234 replace_host (gchar **url,
239 uri = soup_uri_new (*url);
243 soup_uri_set_host (uri, host);
246 *url = soup_uri_to_string (uri, FALSE);
250 #endif /* HAVE_GOA_PASSWORD_BASED */
253 gnome_online_accounts_config_exchange (EGnomeOnlineAccounts *extension,
255 GoaObject *goa_object)
257 #ifdef HAVE_GOA_PASSWORD_BASED
258 GoaExchange *goa_exchange;
259 ESourceExtension *source_extension;
260 const gchar *extension_name;
261 gchar *as_url = NULL;
262 gchar *oab_url = NULL;
264 GError *error = NULL;
266 goa_exchange = goa_object_peek_exchange (goa_object);
267 if (goa_exchange == NULL)
270 /* This should force the ESourceCamelEws type to be registered.
271 * It will also tell us if Evolution-EWS is even installed. */
272 class = g_type_class_ref (g_type_from_name ("EEwsBackend"));
274 g_type_class_unref (class);
277 "%s: Could not locate EEwsBackendClass. "
278 "Is Evolution-EWS installed?", G_STRFUNC);
282 /* XXX GNOME Online Accounts already runs autodiscover to test
283 * the user-entered values but doesn't share the discovered
284 * URLs. It only provides us a host name and expects us to
285 * re-run autodiscover for ourselves.
287 * So I've copied a slab of code from GOA which was in turn
288 * copied from Evolution-EWS which does the autodiscovery.
290 * I've already complained to Debarshi Ray about the lack
291 * of useful info in GOA's Exchange interface so hopefully
292 * it will someday publish discovered URLs and then we can
293 * remove this hack. */
295 goa_ews_autodiscover_sync (
296 goa_object, &as_url, &oab_url, NULL, &error);
299 g_warning ("%s: %s", G_STRFUNC, error->message);
300 g_error_free (error);
304 g_return_if_fail (as_url != NULL);
305 g_return_if_fail (oab_url != NULL);
307 /* XXX We don't have direct access to CamelEwsSettings from here
308 * since it's defined in Evolution-EWS. But we can find out
309 * its extension name and set properties by name. */
311 extension_name = e_source_camel_get_extension_name ("ews");
312 source_extension = e_source_get_extension (source, extension_name);
314 /* This will be NULL if Evolution-EWS is not installed. */
315 if (source_extension != NULL) {
316 GoaAccount *goa_account;
317 CamelSettings *settings;
318 gchar *host, *user, *email;
320 goa_account = goa_object_peek_account (goa_object);
321 host = goa_exchange_dup_host (goa_exchange);
322 user = goa_account_dup_identity (goa_account);
323 email = goa_account_dup_presentation_identity (goa_account);
326 replace_host (&as_url, host);
327 replace_host (&oab_url, host);
337 settings = e_source_camel_get_settings (
338 E_SOURCE_CAMEL (source_extension));
352 "%s: Failed to create [%s] extension",
353 G_STRFUNC, extension_name);
358 #endif /* HAVE_GOA_PASSWORD_BASED */
362 gnome_online_accounts_config_imap (EGnomeOnlineAccounts *extension,
364 GoaObject *goa_object)
366 #ifdef HAVE_GOA_IMAP_SMTP
368 ESourceCamel *camel_extension;
369 ESourceBackend *backend_extension;
370 GSocketConnectable *network_address;
371 CamelSettings *settings;
372 const gchar *extension_name;
373 const gchar *provider_name;
376 GError *error = NULL;
378 goa_mail = goa_object_peek_mail (goa_object);
380 if (goa_mail == NULL)
383 if (!goa_mail_get_imap_supported (goa_mail))
386 use_ssl = goa_mail_get_imap_use_ssl (goa_mail);
387 use_tls = goa_mail_get_imap_use_tls (goa_mail);
389 /* Check that the host string is parsable. */
390 network_address = g_network_address_parse (
391 goa_mail_get_imap_host (goa_mail),
392 use_ssl ? 993 : 143, &error);
396 ((network_address != NULL) && (error == NULL)) ||
397 ((network_address == NULL) && (error != NULL)));
400 /* XXX Mail account will be broken if we fail. */
401 g_critical ("%s: %s", G_STRFUNC, error->message);
402 g_error_free (error);
406 provider_name = CAMEL_IMAP_PROVIDER_NAME;
408 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
409 backend_extension = e_source_get_extension (source, extension_name);
411 e_source_backend_set_backend_name (backend_extension, provider_name);
413 extension_name = e_source_camel_get_extension_name (provider_name);
414 camel_extension = e_source_get_extension (source, extension_name);
415 settings = e_source_camel_get_settings (camel_extension);
417 camel_network_settings_set_host (
418 CAMEL_NETWORK_SETTINGS (settings),
419 g_network_address_get_hostname (
420 G_NETWORK_ADDRESS (network_address)));
422 camel_network_settings_set_port (
423 CAMEL_NETWORK_SETTINGS (settings),
424 g_network_address_get_port (
425 G_NETWORK_ADDRESS (network_address)));
427 camel_network_settings_set_user (
428 CAMEL_NETWORK_SETTINGS (settings),
429 goa_mail_get_imap_user_name (goa_mail));
431 /* Prefer "use_tls" over "use_ssl" if both are set. */
432 camel_network_settings_set_security_method (
433 CAMEL_NETWORK_SETTINGS (settings),
435 CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT :
437 CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT :
438 CAMEL_NETWORK_SECURITY_METHOD_NONE);
440 g_object_unref (network_address);
445 gnome_online_accounts_config_smtp (EGnomeOnlineAccounts *extension,
447 GoaObject *goa_object)
449 #ifdef HAVE_GOA_IMAP_SMTP
451 ESourceCamel *camel_extension;
452 ESourceBackend *backend_extension;
453 GSocketConnectable *network_address;
454 CamelSettings *settings;
455 const gchar *extension_name;
456 const gchar *provider_name;
459 GError *error = NULL;
461 goa_mail = goa_object_peek_mail (goa_object);
463 if (goa_mail == NULL)
466 if (!goa_mail_get_smtp_supported (goa_mail))
469 use_ssl = goa_mail_get_smtp_use_ssl (goa_mail);
470 use_tls = goa_mail_get_smtp_use_tls (goa_mail);
472 /* Check that the host string is parsable. */
473 network_address = g_network_address_parse (
474 goa_mail_get_smtp_host (goa_mail),
475 use_ssl ? 465 : 587, &error);
479 ((network_address != NULL) && (error == NULL)) ||
480 ((network_address == NULL) && (error != NULL)));
483 /* XXX Mail account will be broken if we fail. */
484 g_critical ("%s: %s", G_STRFUNC, error->message);
485 g_error_free (error);
489 provider_name = CAMEL_SMTP_PROVIDER_NAME;
491 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
492 backend_extension = e_source_get_extension (source, extension_name);
494 e_source_backend_set_backend_name (backend_extension, provider_name);
496 extension_name = e_source_camel_get_extension_name (provider_name);
497 camel_extension = e_source_get_extension (source, extension_name);
498 settings = e_source_camel_get_settings (camel_extension);
500 camel_network_settings_set_host (
501 CAMEL_NETWORK_SETTINGS (settings),
502 g_network_address_get_hostname (
503 G_NETWORK_ADDRESS (network_address)));
505 camel_network_settings_set_port (
506 CAMEL_NETWORK_SETTINGS (settings),
507 g_network_address_get_port (
508 G_NETWORK_ADDRESS (network_address)));
510 camel_network_settings_set_user (
511 CAMEL_NETWORK_SETTINGS (settings),
512 goa_mail_get_smtp_user_name (goa_mail));
514 /* Prefer "use_tls" over "use_ssl" if both are set. */
515 camel_network_settings_set_security_method (
516 CAMEL_NETWORK_SETTINGS (settings),
518 CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT :
520 CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT :
521 CAMEL_NETWORK_SECURITY_METHOD_NONE);
523 g_object_unref (network_address);
528 gnome_online_accounts_config_oauth (EGnomeOnlineAccounts *extension,
530 GoaObject *goa_object)
532 ESourceExtension *source_extension;
533 const gchar *extension_name;
535 if (goa_object_peek_oauth_based (goa_object) == NULL)
538 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
539 source_extension = e_source_get_extension (source, extension_name);
541 e_source_authentication_set_method (
542 E_SOURCE_AUTHENTICATION (source_extension),
543 CAMEL_OAUTH_MECHANISM_NAME);
547 gnome_online_accounts_config_oauth2 (EGnomeOnlineAccounts *extension,
549 GoaObject *goa_object)
551 ESourceExtension *source_extension;
552 const gchar *extension_name;
554 if (goa_object_peek_oauth2_based (goa_object) == NULL)
557 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
558 source_extension = e_source_get_extension (source, extension_name);
560 e_source_authentication_set_method (
561 E_SOURCE_AUTHENTICATION (source_extension),
562 CAMEL_OAUTH2_MECHANISM_NAME);
566 gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension,
568 GoaObject *goa_object)
570 GoaAccount *goa_account;
571 ESourceExtension *source_extension;
572 const gchar *extension_name;
573 const gchar *provider_type;
574 const gchar *backend_name;
576 goa_account = goa_object_get_account (goa_object);
577 provider_type = goa_account_get_provider_type (goa_account);
578 backend_name = gnome_online_accounts_get_backend_name (provider_type);
580 g_object_bind_property (
581 goa_account, "presentation-identity",
582 source, "display-name",
583 G_BINDING_SYNC_CREATE);
585 extension_name = E_SOURCE_EXTENSION_GOA;
586 source_extension = e_source_get_extension (source, extension_name);
588 g_object_bind_property (
590 source_extension, "account-id",
591 G_BINDING_SYNC_CREATE);
593 /* Requires more properties from ownCloud, but these are not
594 * available before ownCloud was introduced, thus workaround
595 * it with the backend_name check. */
596 if (g_strcmp0 (backend_name, "owncloud") == 0) {
597 GoaCalendar *goa_calendar;
598 GoaContacts *goa_contacts;
600 goa_calendar = goa_object_get_calendar (goa_object);
602 g_object_bind_property (
604 source_extension, "calendar-url",
605 G_BINDING_SYNC_CREATE);
606 g_object_unref (goa_calendar);
609 goa_contacts = goa_object_get_contacts (goa_object);
611 g_object_bind_property (
613 source_extension, "contacts-url",
614 G_BINDING_SYNC_CREATE);
615 g_object_unref (goa_contacts);
619 extension_name = E_SOURCE_EXTENSION_COLLECTION;
620 source_extension = e_source_get_extension (source, extension_name);
622 g_object_bind_property_full (
623 goa_account, "provider-type",
624 source_extension, "backend-name",
625 G_BINDING_SYNC_CREATE,
626 gnome_online_accounts_provider_type_to_backend_name,
628 NULL, (GDestroyNotify) NULL);
630 g_object_bind_property (
631 goa_account, "identity",
632 source_extension, "identity",
633 G_BINDING_SYNC_CREATE);
635 g_object_bind_property_full (
636 goa_object, "calendar",
637 source_extension, "calendar-enabled",
638 G_BINDING_SYNC_CREATE,
639 gnome_online_accounts_object_is_non_null,
641 NULL, (GDestroyNotify) NULL);
643 g_object_bind_property_full (
644 goa_object, "contacts",
645 source_extension, "contacts-enabled",
646 G_BINDING_SYNC_CREATE,
647 gnome_online_accounts_object_is_non_null,
649 NULL, (GDestroyNotify) NULL);
651 g_object_bind_property_full (
653 source_extension, "mail-enabled",
654 G_BINDING_SYNC_CREATE,
655 gnome_online_accounts_object_is_non_null,
657 NULL, (GDestroyNotify) NULL);
659 g_object_unref (goa_account);
661 /* Handle optional GOA interfaces. */
662 gnome_online_accounts_config_exchange (extension, source, goa_object);
664 /* The data source should not be removable by clients. */
665 e_server_side_source_set_removable (
666 E_SERVER_SIDE_SOURCE (source), FALSE);
668 #ifdef HAVE_GOA_PASSWORD_BASED
669 if (goa_object_peek_password_based (goa_object) != NULL) {
670 /* Obtain passwords from the OnlineAccounts service. */
671 e_server_side_source_set_auth_session_type (
672 E_SERVER_SIDE_SOURCE (source),
673 E_TYPE_GOA_PASSWORD_BASED);
675 #endif /* HAVE_GOA_PASSWORD_BASED */
677 if (goa_object_peek_oauth2_based (goa_object) != NULL) {
678 /* This module provides OAuth 2.0 support to the collection.
679 * Note, children of the collection source will automatically
680 * inherit our EOAuth2Support through the property binding in
681 * collection_backend_child_added(). */
682 e_server_side_source_set_oauth2_support (
683 E_SERVER_SIDE_SOURCE (source),
684 E_OAUTH2_SUPPORT (extension));
689 gnome_online_accounts_config_mail_account (EGnomeOnlineAccounts *extension,
691 GoaObject *goa_object)
693 EServerSideSource *server_side_source;
695 /* Only one or the other should be present, not both. */
696 gnome_online_accounts_config_oauth (extension, source, goa_object);
697 gnome_online_accounts_config_oauth2 (extension, source, goa_object);
699 /* XXX Need to defer the network security settings to the
700 * provider-specific module since "imap-use-tls" tells
701 * us neither the port number, nor whether to use IMAP
702 * over SSL versus STARTTLS. The module will know.
704 * Addendum: This got fixed in GOA 3.8. There's now both
705 * "imap-use-tls" and "imap-use-ssl" properties.
706 * Go ahead and set up IMAP details here if we
707 * have GOA 3.8, otherwise continue deferring
708 * to provider-specific modules. */
709 gnome_online_accounts_config_imap (extension, source, goa_object);
711 /* Clients may change the source by may not remove it. */
712 server_side_source = E_SERVER_SIDE_SOURCE (source);
713 e_server_side_source_set_writable (server_side_source, TRUE);
714 e_server_side_source_set_removable (server_side_source, FALSE);
718 gnome_online_accounts_config_mail_identity (EGnomeOnlineAccounts *extension,
720 GoaObject *goa_object)
723 ESourceExtension *source_extension;
724 EServerSideSource *server_side_source;
725 const gchar *extension_name;
727 goa_mail = goa_object_get_mail (goa_object);
728 g_return_if_fail (goa_mail != NULL);
730 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
731 source_extension = e_source_get_extension (source, extension_name);
733 g_object_bind_property (
734 goa_mail, "email-address",
735 source_extension, "address",
736 G_BINDING_SYNC_CREATE);
738 g_object_unref (goa_mail);
740 /* Clients may change the source by may not remove it. */
741 server_side_source = E_SERVER_SIDE_SOURCE (source);
742 e_server_side_source_set_writable (server_side_source, TRUE);
743 e_server_side_source_set_removable (server_side_source, FALSE);
747 gnome_online_accounts_config_mail_transport (EGnomeOnlineAccounts *extension,
749 GoaObject *goa_object)
751 EServerSideSource *server_side_source;
753 /* Only one or the other should be present, not both. */
754 gnome_online_accounts_config_oauth (extension, source, goa_object);
755 gnome_online_accounts_config_oauth2 (extension, source, goa_object);
757 /* XXX Need to defer the network security settings to the
758 * provider-specific module since "smtp-use-tls" tells
759 * us neither the port number, nor whether to use SMTP
760 * over SSL versus STARTTLS. The module will know.
762 * Addendum: This got fixed in GOA 3.8. There's now both
763 * "smtp-use-tls" and "smtp-use-ssl" properties.
764 * Go ahead and set up SMTP details here if we
765 * have GOA 3.8, otherwise continue deferring
766 * to provider-specific modules. */
767 gnome_online_accounts_config_smtp (extension, source, goa_object);
769 /* Clients may change the source by may not remove it. */
770 server_side_source = E_SERVER_SIDE_SOURCE (source);
771 e_server_side_source_set_writable (server_side_source, TRUE);
772 e_server_side_source_set_removable (server_side_source, FALSE);
776 gnome_online_accounts_config_sources (EGnomeOnlineAccounts *extension,
778 GoaObject *goa_object)
780 ESourceRegistryServer *server;
781 ECollectionBackend *backend;
784 /* XXX This function was primarily intended to smooth the
785 * transition of mail accounts from XOAUTH to XOAUTH2,
786 * but it may be useful for other types of migration. */
788 gnome_online_accounts_config_collection (extension, source, goa_object);
790 server = gnome_online_accounts_get_server (extension);
791 backend = e_source_registry_server_ref_backend (server, source);
792 g_return_if_fail (backend != NULL);
794 list = e_collection_backend_list_mail_sources (backend);
796 for (link = list; link != NULL; link = g_list_next (link)) {
797 const gchar *extension_name;
799 source = E_SOURCE (link->data);
801 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
802 if (e_source_has_extension (source, extension_name))
803 gnome_online_accounts_config_mail_account (
804 extension, source, goa_object);
806 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
807 if (e_source_has_extension (source, extension_name))
808 gnome_online_accounts_config_mail_identity (
809 extension, source, goa_object);
811 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
812 if (e_source_has_extension (source, extension_name))
813 gnome_online_accounts_config_mail_transport (
814 extension, source, goa_object);
817 g_list_free_full (list, (GDestroyNotify) g_object_unref);
819 g_object_unref (backend);
823 gnome_online_accounts_create_collection (EGnomeOnlineAccounts *extension,
824 EBackendFactory *backend_factory,
825 GoaObject *goa_object)
827 GoaAccount *goa_account;
828 ESourceRegistryServer *server;
829 ESource *collection_source;
830 ESource *mail_account_source = NULL;
831 ESource *mail_identity_source = NULL;
832 ESource *mail_transport_source = NULL;
833 const gchar *account_id;
834 const gchar *parent_uid;
836 server = gnome_online_accounts_get_server (extension);
838 collection_source = gnome_online_accounts_new_source (extension);
839 g_return_if_fail (E_IS_SOURCE (collection_source));
841 gnome_online_accounts_config_collection (
842 extension, collection_source, goa_object);
843 parent_uid = e_source_get_uid (collection_source);
845 if (goa_object_peek_mail (goa_object)) {
846 mail_account_source =
847 gnome_online_accounts_new_source (extension);
848 g_return_if_fail (E_IS_SOURCE (mail_account_source));
850 mail_identity_source =
851 gnome_online_accounts_new_source (extension);
852 g_return_if_fail (E_IS_SOURCE (mail_identity_source));
854 mail_transport_source =
855 gnome_online_accounts_new_source (extension);
856 g_return_if_fail (E_IS_SOURCE (mail_transport_source));
858 /* Configure parent/child relationships. */
859 e_source_set_parent (mail_account_source, parent_uid);
860 e_source_set_parent (mail_identity_source, parent_uid);
861 e_source_set_parent (mail_transport_source, parent_uid);
863 /* Give the factory first crack at mail configuration. */
864 e_collection_backend_factory_prepare_mail (
865 E_COLLECTION_BACKEND_FACTORY (backend_factory),
867 mail_identity_source,
868 mail_transport_source);
870 gnome_online_accounts_config_mail_account (
871 extension, mail_account_source, goa_object);
872 gnome_online_accounts_config_mail_identity (
873 extension, mail_identity_source, goa_object);
874 gnome_online_accounts_config_mail_transport (
875 extension, mail_transport_source, goa_object);
878 /* Export the new source collection. */
879 e_source_registry_server_add_source (server, collection_source);
881 if (mail_account_source != NULL) {
882 e_source_registry_server_add_source (
883 server, mail_account_source);
884 g_object_unref (mail_account_source);
887 if (mail_identity_source != NULL) {
888 e_source_registry_server_add_source (
889 server, mail_identity_source);
890 g_object_unref (mail_identity_source);
893 if (mail_transport_source != NULL) {
894 e_source_registry_server_add_source (
895 server, mail_transport_source);
896 g_object_unref (mail_transport_source);
899 goa_account = goa_object_get_account (goa_object);
900 account_id = goa_account_get_id (goa_account);
902 g_hash_table_insert (
903 extension->goa_to_eds,
904 g_strdup (account_id),
905 g_strdup (parent_uid));
907 g_object_unref (goa_account);
908 g_object_unref (collection_source);
912 gnome_online_accounts_remove_collection (EGnomeOnlineAccounts *extension,
915 GError *error = NULL;
917 /* This removes the entire subtree rooted at source.
918 * Deletes the corresponding on-disk key files too. */
919 e_source_remove_sync (source, NULL, &error);
922 g_warning ("%s: %s", G_STRFUNC, error->message);
923 g_error_free (error);
928 gnome_online_accounts_account_added_cb (GoaClient *goa_client,
929 GoaObject *goa_object,
930 EGnomeOnlineAccounts *extension)
932 GoaAccount *goa_account;
933 ESourceRegistryServer *server;
934 EBackendFactory *backend_factory = NULL;
935 const gchar *provider_type;
936 const gchar *backend_name;
937 const gchar *account_id;
938 const gchar *source_uid;
940 server = gnome_online_accounts_get_server (extension);
942 goa_account = goa_object_get_account (goa_object);
943 provider_type = goa_account_get_provider_type (goa_account);
944 backend_name = gnome_online_accounts_get_backend_name (provider_type);
946 account_id = goa_account_get_id (goa_account);
947 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
949 if (source_uid == NULL && backend_name != NULL)
950 backend_factory = e_data_factory_ref_backend_factory (
951 E_DATA_FACTORY (server), backend_name);
953 if (backend_factory != NULL) {
954 gnome_online_accounts_create_collection (
955 extension, backend_factory, goa_object);
956 g_object_unref (backend_factory);
959 g_object_unref (goa_account);
963 gnome_online_accounts_account_removed_cb (GoaClient *goa_client,
964 GoaObject *goa_object,
965 EGnomeOnlineAccounts *extension)
967 ESource *source = NULL;
968 ESourceRegistryServer *server;
969 GoaAccount *goa_account;
970 const gchar *account_id;
971 const gchar *source_uid;
973 server = gnome_online_accounts_get_server (extension);
975 goa_account = goa_object_get_account (goa_object);
977 account_id = goa_account_get_id (goa_account);
978 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
980 if (source_uid != NULL)
981 source = e_source_registry_server_ref_source (
984 if (source != NULL) {
985 gnome_online_accounts_remove_collection (extension, source);
986 g_object_unref (source);
989 g_object_unref (goa_account);
993 gnome_online_accounts_compare_id (GoaObject *goa_object,
994 const gchar *target_id)
996 GoaAccount *goa_account;
997 const gchar *account_id;
1000 goa_account = goa_object_get_account (goa_object);
1001 account_id = goa_account_get_id (goa_account);
1002 result = g_strcmp0 (account_id, target_id);
1003 g_object_unref (goa_account);
1009 gnome_online_accounts_populate_accounts_table (EGnomeOnlineAccounts *extension,
1012 ESourceRegistryServer *server;
1013 GQueue trash = G_QUEUE_INIT;
1015 const gchar *extension_name;
1017 server = gnome_online_accounts_get_server (extension);
1019 extension_name = E_SOURCE_EXTENSION_GOA;
1020 list = e_source_registry_server_list_sources (server, extension_name);
1022 for (link = list; link != NULL; link = g_list_next (link)) {
1024 ESourceGoa *goa_ext;
1025 const gchar *account_id;
1026 const gchar *source_uid;
1029 source = E_SOURCE (link->data);
1030 source_uid = e_source_get_uid (source);
1032 extension_name = E_SOURCE_EXTENSION_GOA;
1033 goa_ext = e_source_get_extension (source, extension_name);
1034 account_id = e_source_goa_get_account_id (goa_ext);
1036 if (account_id == NULL)
1039 /* Verify the GOA account still exists. */
1040 match = g_list_find_custom (
1041 goa_objects, account_id,
1042 (GCompareFunc) gnome_online_accounts_compare_id);
1044 /* If a matching GoaObject was found, add its ID
1045 * to our accounts hash table. Otherwise remove
1046 * the ESource after we finish looping. */
1047 if (match != NULL) {
1048 GoaObject *goa_object;
1050 g_hash_table_insert (
1051 extension->goa_to_eds,
1052 g_strdup (account_id),
1053 g_strdup (source_uid));
1055 goa_object = GOA_OBJECT (match->data);
1056 gnome_online_accounts_config_sources (
1057 extension, source, goa_object);
1059 g_queue_push_tail (&trash, source);
1063 /* Empty the trash. */
1064 while (!g_queue_is_empty (&trash)) {
1065 ESource *source = g_queue_pop_head (&trash);
1066 gnome_online_accounts_remove_collection (extension, source);
1069 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1073 gnome_online_accounts_create_client_cb (GObject *source_object,
1074 GAsyncResult *result,
1077 EGnomeOnlineAccounts *extension;
1078 GoaClient *goa_client;
1080 GError *error = NULL;
1082 /* If we get back a G_IO_ERROR_CANCELLED then it means the
1083 * EGnomeOnlineAccounts is already finalized, so be careful
1084 * not to touch it until after we have a valid GoaClient. */
1086 goa_client = goa_client_new_finish (result, &error);
1088 if (error != NULL) {
1089 g_warn_if_fail (goa_client == NULL);
1091 "Unable to connect to the GNOME Online "
1092 "Accounts service: %s", error->message);
1093 g_error_free (error);
1097 g_return_if_fail (GOA_IS_CLIENT (goa_client));
1099 /* Should be safe to dereference the EGnomeOnlineAccounts now. */
1101 extension = E_GNOME_ONLINE_ACCOUNTS (user_data);
1102 extension->goa_client = goa_client; /* takes ownership */
1104 /* Don't need the GCancellable anymore. */
1105 g_object_unref (extension->create_client);
1106 extension->create_client = NULL;
1108 list = goa_client_get_accounts (extension->goa_client);
1110 /* This populates a hash table of GOA ID -> ESource UID strings by
1111 * searching through available data sources for ones with a "GNOME
1112 * Online Accounts" extension. If such an extension is found, but
1113 * no corresponding GoaAccount (presumably meaning the GOA account
1114 * was somehow deleted between E-D-S sessions) then the ESource in
1115 * which the extension was found gets deleted. */
1116 gnome_online_accounts_populate_accounts_table (extension, list);
1118 for (link = list; link != NULL; link = g_list_next (link))
1119 gnome_online_accounts_account_added_cb (
1120 extension->goa_client,
1121 GOA_OBJECT (link->data),
1124 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1126 /* Listen for Online Account changes. */
1128 extension->goa_client, "account-added",
1129 G_CALLBACK (gnome_online_accounts_account_added_cb),
1132 extension->goa_client, "account-removed",
1133 G_CALLBACK (gnome_online_accounts_account_removed_cb),
1138 gnome_online_accounts_bus_acquired_cb (EDBusServer *server,
1139 GDBusConnection *connection,
1140 EGnomeOnlineAccounts *extension)
1142 /* Connect to the GNOME Online Accounts service. */
1144 /* Note we don't reference the extension. If the
1145 * extension gets destroyed before this completes
1146 * we cancel the operation from dispose(). */
1148 extension->create_client,
1149 gnome_online_accounts_create_client_cb,
1154 gnome_online_accounts_dispose (GObject *object)
1156 EGnomeOnlineAccounts *extension;
1158 extension = E_GNOME_ONLINE_ACCOUNTS (object);
1160 if (extension->goa_client != NULL) {
1161 g_signal_handlers_disconnect_matched (
1162 extension->goa_client,
1163 G_SIGNAL_MATCH_DATA,
1164 0, 0, NULL, NULL, object);
1165 g_object_unref (extension->goa_client);
1166 extension->goa_client = NULL;
1169 /* This cancels goa_client_new() in case it still
1170 * hasn't completed. We're no longer interested. */
1171 if (extension->create_client != NULL) {
1172 g_cancellable_cancel (extension->create_client);
1173 g_object_unref (extension->create_client);
1174 extension->create_client = NULL;
1177 /* Chain up to parent's dispose() method. */
1178 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1183 gnome_online_accounts_finalize (GObject *object)
1185 EGnomeOnlineAccounts *extension;
1187 extension = E_GNOME_ONLINE_ACCOUNTS (object);
1189 g_hash_table_destroy (extension->goa_to_eds);
1191 /* Chain up to parent's finalize() method. */
1192 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1197 gnome_online_accounts_constructed (GObject *object)
1199 EExtension *extension;
1200 EExtensible *extensible;
1202 extension = E_EXTENSION (object);
1203 extensible = e_extension_get_extensible (extension);
1205 /* Wait for the registry service to acquire its well-known
1206 * bus name so we don't do anything destructive beforehand. */
1209 extensible, "bus-acquired",
1210 G_CALLBACK (gnome_online_accounts_bus_acquired_cb),
1213 /* Chain up to parent's constructed() method. */
1214 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1215 constructed (object);
1219 gnome_online_accounts_get_access_token_sync (EOAuth2Support *support,
1221 GCancellable *cancellable,
1222 gchar **out_access_token,
1223 gint *out_expires_in,
1226 GoaObject *goa_object;
1227 GoaAccount *goa_account;
1228 GoaOAuth2Based *goa_oauth2_based;
1231 goa_object = gnome_online_accounts_ref_account (
1232 E_GNOME_ONLINE_ACCOUNTS (support), source);
1234 if (goa_object == NULL) {
1236 error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1237 _("Cannot find a corresponding account in "
1238 "the org.gnome.OnlineAccounts service from "
1239 "which to obtain an access token for '%s'"),
1240 e_source_get_display_name (source));
1244 goa_account = goa_object_get_account (goa_object);
1245 g_return_val_if_fail (goa_account != NULL, FALSE);
1247 goa_oauth2_based = goa_object_get_oauth2_based (goa_object);
1248 g_return_val_if_fail (goa_oauth2_based != NULL, FALSE);
1250 success = goa_account_call_ensure_credentials_sync (
1251 goa_account, NULL, cancellable, error);
1254 success = goa_oauth2_based_call_get_access_token_sync (
1255 goa_oauth2_based, out_access_token,
1256 out_expires_in, cancellable, error);
1258 g_object_unref (goa_oauth2_based);
1259 g_object_unref (goa_account);
1260 g_object_unref (goa_object);
1264 _("Failed to obtain an access token for '%s': "),
1265 e_source_get_display_name (source));
1271 e_gnome_online_accounts_class_init (EGnomeOnlineAccountsClass *class)
1273 GObjectClass *object_class;
1274 EExtensionClass *extension_class;
1276 object_class = G_OBJECT_CLASS (class);
1277 object_class->dispose = gnome_online_accounts_dispose;
1278 object_class->finalize = gnome_online_accounts_finalize;
1279 object_class->constructed = gnome_online_accounts_constructed;
1281 extension_class = E_EXTENSION_CLASS (class);
1282 extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
1286 e_gnome_online_accounts_class_finalize (EGnomeOnlineAccountsClass *class)
1291 e_gnome_online_accounts_oauth2_support_init (EOAuth2SupportInterface *interface)
1293 interface->get_access_token_sync =
1294 gnome_online_accounts_get_access_token_sync;
1298 e_gnome_online_accounts_init (EGnomeOnlineAccounts *extension)
1300 /* Used to cancel unfinished goa_client_new(). */
1301 extension->create_client = g_cancellable_new ();
1303 extension->goa_to_eds = g_hash_table_new_full (
1304 (GHashFunc) g_str_hash,
1305 (GEqualFunc) g_str_equal,
1306 (GDestroyNotify) g_free,
1307 (GDestroyNotify) g_free);
1310 G_MODULE_EXPORT void
1311 e_module_load (GTypeModule *type_module)
1313 e_goa_password_based_type_register (type_module);
1314 e_gnome_online_accounts_register_type (type_module);
1317 G_MODULE_EXPORT void
1318 e_module_unload (GTypeModule *type_module)