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_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 /* The keyring definintions are copied from e-authentication-session.c */
64 #define KEYRING_ITEM_ATTRIBUTE_NAME "e-source-uid"
65 #define KEYRING_ITEM_DISPLAY_FORMAT "Evolution Data Source %s"
67 #ifdef HAVE_GOA_PASSWORD_BASED
68 /* XXX Probably want to share this with
69 * evolution-source-registry-migrate-sources.c */
70 static SecretSchema schema = {
71 "org.gnome.Evolution.DataSource",
72 SECRET_SCHEMA_DONT_MATCH_NAME,
74 { KEYRING_ITEM_ATTRIBUTE_NAME,
75 SECRET_SCHEMA_ATTRIBUTE_STRING },
79 #endif /* HAVE_GOA_PASSWORD_BASED */
81 /* Module Entry Points */
82 void e_module_load (GTypeModule *type_module);
83 void e_module_unload (GTypeModule *type_module);
85 /* Forward Declarations */
86 GType e_gnome_online_accounts_get_type (void);
87 static void e_gnome_online_accounts_oauth2_support_init
88 (EOAuth2SupportInterface *interface);
90 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
92 e_gnome_online_accounts,
95 G_IMPLEMENT_INTERFACE_DYNAMIC (
96 E_TYPE_OAUTH2_SUPPORT,
97 e_gnome_online_accounts_oauth2_support_init))
100 gnome_online_accounts_get_backend_name (const gchar *goa_provider_type)
102 const gchar *eds_backend_name = NULL;
104 g_return_val_if_fail (goa_provider_type != NULL, NULL);
106 /* This is a mapping between GoaAccount provider types and
107 * ESourceCollection backend names. It requires knowledge
108 * of other registry modules, possibly even from 3rd party
109 * packages. No way around it. */
111 if (g_str_equal (goa_provider_type, "exchange"))
112 eds_backend_name = "ews";
114 if (g_str_equal (goa_provider_type, "google"))
115 eds_backend_name = "google";
117 if (g_str_equal (goa_provider_type, "imap_smtp"))
118 eds_backend_name = "none";
120 if (g_str_equal (goa_provider_type, "yahoo"))
121 eds_backend_name = "yahoo";
123 if (g_str_equal (goa_provider_type, "owncloud"))
124 eds_backend_name = "owncloud";
126 return eds_backend_name;
129 static ESourceRegistryServer *
130 gnome_online_accounts_get_server (EGnomeOnlineAccounts *extension)
132 EExtensible *extensible;
134 extensible = e_extension_get_extensible (E_EXTENSION (extension));
136 return E_SOURCE_REGISTRY_SERVER (extensible);
140 gnome_online_accounts_provider_type_to_backend_name (GBinding *binding,
141 const GValue *source_value,
142 GValue *target_value,
145 const gchar *provider_type;
146 const gchar *backend_name;
148 provider_type = g_value_get_string (source_value);
149 backend_name = gnome_online_accounts_get_backend_name (provider_type);
150 g_return_val_if_fail (backend_name != NULL, FALSE);
151 g_value_set_string (target_value, backend_name);
157 gnome_online_accounts_object_is_non_null (GBinding *binding,
158 const GValue *source_value,
159 GValue *target_value,
164 v_object = g_value_get_object (source_value);
165 g_value_set_boolean (target_value, v_object != NULL);
171 gnome_online_accounts_ref_account (EGnomeOnlineAccounts *extension,
174 ESourceRegistryServer *server;
175 GoaObject *match = NULL;
177 const gchar *extension_name;
178 gchar *account_id = NULL;
180 extension_name = E_SOURCE_EXTENSION_GOA;
181 server = gnome_online_accounts_get_server (extension);
183 source = e_source_registry_server_find_extension (
184 server, source, extension_name);
186 if (source != NULL) {
189 goa_ext = e_source_get_extension (source, extension_name);
190 account_id = e_source_goa_dup_account_id (goa_ext);
192 g_object_unref (source);
195 if (account_id == NULL)
198 /* FIXME Use goa_client_lookup_by_id() once we require GOA 3.6. */
200 list = goa_client_get_accounts (extension->goa_client);
202 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
203 GoaObject *goa_object;
204 GoaAccount *goa_account;
205 const gchar *candidate_id;
207 goa_object = GOA_OBJECT (iter->data);
208 goa_account = goa_object_get_account (goa_object);
209 candidate_id = goa_account_get_id (goa_account);
211 if (g_strcmp0 (account_id, candidate_id) == 0)
212 match = g_object_ref (goa_object);
214 g_object_unref (goa_account);
220 g_list_free_full (list, (GDestroyNotify) g_object_unref);
228 gnome_online_accounts_new_source (EGnomeOnlineAccounts *extension)
230 ESourceRegistryServer *server;
233 GError *error = NULL;
235 /* This being a brand new data source, creating the instance
236 * should never fail but we'll check for errors just the same. */
237 server = gnome_online_accounts_get_server (extension);
238 file = e_server_side_source_new_user_file (NULL);
239 source = e_server_side_source_new (server, file, &error);
240 g_object_unref (file);
243 g_warn_if_fail (source == NULL);
244 g_warning ("%s: %s", G_STRFUNC, error->message);
245 g_error_free (error);
251 #ifdef HAVE_GOA_PASSWORD_BASED
253 replace_host (gchar **url,
258 uri = soup_uri_new (*url);
262 soup_uri_set_host (uri, host);
265 *url = soup_uri_to_string (uri, FALSE);
269 #endif /* HAVE_GOA_PASSWORD_BASED */
272 gnome_online_accounts_config_exchange (EGnomeOnlineAccounts *extension,
274 GoaObject *goa_object)
276 #ifdef HAVE_GOA_PASSWORD_BASED
277 GoaExchange *goa_exchange;
278 ESourceExtension *source_extension;
279 const gchar *extension_name;
280 gchar *as_url = NULL;
281 gchar *oab_url = NULL;
283 GError *error = NULL;
285 goa_exchange = goa_object_peek_exchange (goa_object);
286 if (goa_exchange == NULL)
289 /* This should force the ESourceCamelEws type to be registered.
290 * It will also tell us if Evolution-EWS is even installed. */
291 class = g_type_class_ref (g_type_from_name ("EEwsBackend"));
293 g_type_class_unref (class);
296 "%s: Could not locate EEwsBackendClass. "
297 "Is Evolution-EWS installed?", G_STRFUNC);
301 /* XXX GNOME Online Accounts already runs autodiscover to test
302 * the user-entered values but doesn't share the discovered
303 * URLs. It only provides us a host name and expects us to
304 * re-run autodiscover for ourselves.
306 * So I've copied a slab of code from GOA which was in turn
307 * copied from Evolution-EWS which does the autodiscovery.
309 * I've already complained to Debarshi Ray about the lack
310 * of useful info in GOA's Exchange interface so hopefully
311 * it will someday publish discovered URLs and then we can
312 * remove this hack. */
314 goa_ews_autodiscover_sync (
315 goa_object, &as_url, &oab_url, NULL, &error);
318 g_warning ("%s: %s", G_STRFUNC, error->message);
319 g_error_free (error);
323 g_return_if_fail (as_url != NULL);
324 g_return_if_fail (oab_url != NULL);
326 /* XXX We don't have direct access to CamelEwsSettings from here
327 * since it's defined in Evolution-EWS. But we can find out
328 * its extension name and set properties by name. */
330 extension_name = e_source_camel_get_extension_name ("ews");
331 source_extension = e_source_get_extension (source, extension_name);
333 /* This will be NULL if Evolution-EWS is not installed. */
334 if (source_extension != NULL) {
335 GoaAccount *goa_account;
336 CamelSettings *settings;
337 gchar *host, *user, *email;
339 goa_account = goa_object_peek_account (goa_object);
340 host = goa_exchange_dup_host (goa_exchange);
341 user = goa_account_dup_identity (goa_account);
342 email = goa_account_dup_presentation_identity (goa_account);
345 replace_host (&as_url, host);
346 replace_host (&oab_url, host);
356 settings = e_source_camel_get_settings (
357 E_SOURCE_CAMEL (source_extension));
371 "%s: Failed to create [%s] extension",
372 G_STRFUNC, extension_name);
377 #endif /* HAVE_GOA_PASSWORD_BASED */
381 gnome_online_accounts_config_imap (EGnomeOnlineAccounts *extension,
383 GoaObject *goa_object)
385 #ifdef HAVE_GOA_IMAP_SMTP
387 ESourceCamel *camel_extension;
388 ESourceBackend *backend_extension;
389 GSocketConnectable *network_address;
390 CamelSettings *settings;
391 const gchar *extension_name;
392 const gchar *provider_name;
395 GError *error = NULL;
397 goa_mail = goa_object_peek_mail (goa_object);
399 if (goa_mail == NULL)
402 if (!goa_mail_get_imap_supported (goa_mail))
405 use_ssl = goa_mail_get_imap_use_ssl (goa_mail);
406 use_tls = goa_mail_get_imap_use_tls (goa_mail);
408 /* Check that the host string is parsable. */
409 network_address = g_network_address_parse (
410 goa_mail_get_imap_host (goa_mail),
411 use_ssl ? 993 : 143, &error);
415 ((network_address != NULL) && (error == NULL)) ||
416 ((network_address == NULL) && (error != NULL)));
419 /* XXX Mail account will be broken if we fail. */
420 g_critical ("%s: %s", G_STRFUNC, error->message);
421 g_error_free (error);
425 provider_name = CAMEL_IMAP_PROVIDER_NAME;
427 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
428 backend_extension = e_source_get_extension (source, extension_name);
430 e_source_backend_set_backend_name (backend_extension, provider_name);
432 extension_name = e_source_camel_get_extension_name (provider_name);
433 camel_extension = e_source_get_extension (source, extension_name);
434 settings = e_source_camel_get_settings (camel_extension);
436 camel_network_settings_set_host (
437 CAMEL_NETWORK_SETTINGS (settings),
438 g_network_address_get_hostname (
439 G_NETWORK_ADDRESS (network_address)));
441 camel_network_settings_set_port (
442 CAMEL_NETWORK_SETTINGS (settings),
443 g_network_address_get_port (
444 G_NETWORK_ADDRESS (network_address)));
446 camel_network_settings_set_user (
447 CAMEL_NETWORK_SETTINGS (settings),
448 goa_mail_get_imap_user_name (goa_mail));
450 /* Prefer "use_tls" over "use_ssl" if both are set. */
451 camel_network_settings_set_security_method (
452 CAMEL_NETWORK_SETTINGS (settings),
454 CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT :
456 CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT :
457 CAMEL_NETWORK_SECURITY_METHOD_NONE);
459 g_object_unref (network_address);
464 gnome_online_accounts_config_smtp (EGnomeOnlineAccounts *extension,
466 GoaObject *goa_object)
468 #ifdef HAVE_GOA_IMAP_SMTP
470 ESourceCamel *camel_extension;
471 ESourceBackend *backend_extension;
472 GSocketConnectable *network_address;
473 CamelSettings *settings;
474 const gchar *extension_name;
475 const gchar *provider_name;
478 GError *error = NULL;
480 goa_mail = goa_object_peek_mail (goa_object);
482 if (goa_mail == NULL)
485 if (!goa_mail_get_smtp_supported (goa_mail))
488 use_ssl = goa_mail_get_smtp_use_ssl (goa_mail);
489 use_tls = goa_mail_get_smtp_use_tls (goa_mail);
491 /* Check that the host string is parsable. */
492 network_address = g_network_address_parse (
493 goa_mail_get_smtp_host (goa_mail),
494 use_ssl ? 465 : 587, &error);
498 ((network_address != NULL) && (error == NULL)) ||
499 ((network_address == NULL) && (error != NULL)));
502 /* XXX Mail account will be broken if we fail. */
503 g_critical ("%s: %s", G_STRFUNC, error->message);
504 g_error_free (error);
508 provider_name = CAMEL_SMTP_PROVIDER_NAME;
510 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
511 backend_extension = e_source_get_extension (source, extension_name);
513 e_source_backend_set_backend_name (backend_extension, provider_name);
515 extension_name = e_source_camel_get_extension_name (provider_name);
516 camel_extension = e_source_get_extension (source, extension_name);
517 settings = e_source_camel_get_settings (camel_extension);
519 camel_network_settings_set_host (
520 CAMEL_NETWORK_SETTINGS (settings),
521 g_network_address_get_hostname (
522 G_NETWORK_ADDRESS (network_address)));
524 camel_network_settings_set_port (
525 CAMEL_NETWORK_SETTINGS (settings),
526 g_network_address_get_port (
527 G_NETWORK_ADDRESS (network_address)));
529 camel_network_settings_set_user (
530 CAMEL_NETWORK_SETTINGS (settings),
531 goa_mail_get_smtp_user_name (goa_mail));
533 /* Prefer "use_tls" over "use_ssl" if both are set. */
534 camel_network_settings_set_security_method (
535 CAMEL_NETWORK_SETTINGS (settings),
537 CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT :
539 CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT :
540 CAMEL_NETWORK_SECURITY_METHOD_NONE);
542 g_object_unref (network_address);
547 gnome_online_accounts_config_oauth (EGnomeOnlineAccounts *extension,
549 GoaObject *goa_object)
551 ESourceExtension *source_extension;
552 const gchar *extension_name;
554 if (goa_object_peek_oauth_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_OAUTH_MECHANISM_NAME);
566 gnome_online_accounts_config_oauth2 (EGnomeOnlineAccounts *extension,
568 GoaObject *goa_object)
570 ESourceExtension *source_extension;
571 const gchar *extension_name;
573 if (goa_object_peek_oauth2_based (goa_object) == NULL)
576 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
577 source_extension = e_source_get_extension (source, extension_name);
579 e_source_authentication_set_method (
580 E_SOURCE_AUTHENTICATION (source_extension),
581 CAMEL_OAUTH2_MECHANISM_NAME);
585 gnome_online_accounts_config_password (EGnomeOnlineAccounts *extension,
587 GoaObject *goa_object)
589 #ifdef HAVE_GOA_PASSWORD_BASED
590 GoaAccount *goa_account;
591 GoaPasswordBased *goa_password_based;
592 EAsyncClosure *closure;
593 GAsyncResult *result;
597 gchar *password = NULL;
598 GError *error = NULL;
600 /* If the GNOME Online Account is password-based, we use its
601 * password to seed our own keyring entry for the collection
602 * source which avoids having to special-case authentication
603 * like we do for OAuth. Plus, if the stored password is no
604 * good we'll prompt for a new one instead of just giving up. */
606 goa_password_based = goa_object_get_password_based (goa_object);
608 if (goa_password_based == NULL)
611 closure = e_async_closure_new ();
613 /* XXX The GOA documentation doesn't explain the string
614 * argument in goa_password_based_get_password() so
615 * we'll pass in the identity and hope for the best. */
616 goa_account = goa_object_get_account (goa_object);
617 arg_id = goa_account_dup_identity (goa_account);
618 g_object_unref (goa_account);
620 goa_password_based_call_get_password (
621 goa_password_based, arg_id, NULL,
622 e_async_closure_callback, closure);
626 result = e_async_closure_wait (closure);
628 goa_password_based_call_get_password_finish (
629 goa_password_based, &password, result, &error);
632 g_warning ("%s: %s", G_STRFUNC, error->message);
633 g_error_free (error);
637 uid = e_source_get_uid (source);
638 display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
640 secret_password_store (
641 &schema, SECRET_COLLECTION_DEFAULT,
642 display_name, password, NULL,
643 e_async_closure_callback, closure,
644 KEYRING_ITEM_ATTRIBUTE_NAME, uid,
647 result = e_async_closure_wait (closure);
649 secret_password_store_finish (result, &error);
651 g_free (display_name);
654 /* If we fail to store the password, we'll just end up prompting
655 * for a password like normal. Annoying, maybe, but not the end
656 * of the world. Still leave a breadcrumb for debugging though. */
658 g_warning ("%s: %s", G_STRFUNC, error->message);
659 g_error_free (error);
663 e_async_closure_free (closure);
664 g_object_unref (goa_password_based);
665 #endif /* HAVE_GOA_PASSWORD_BASED */
669 gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension,
671 GoaObject *goa_object)
673 GoaAccount *goa_account;
674 ESourceExtension *source_extension;
675 const gchar *extension_name;
676 const gchar *provider_type;
677 const gchar *backend_name;
679 goa_account = goa_object_get_account (goa_object);
680 provider_type = goa_account_get_provider_type (goa_account);
681 backend_name = gnome_online_accounts_get_backend_name (provider_type);
683 g_object_bind_property (
684 goa_account, "presentation-identity",
685 source, "display-name",
686 G_BINDING_SYNC_CREATE);
688 extension_name = E_SOURCE_EXTENSION_GOA;
689 source_extension = e_source_get_extension (source, extension_name);
691 g_object_bind_property (
693 source_extension, "account-id",
694 G_BINDING_SYNC_CREATE);
696 /* requires more properties from ownCould, but these are not
697 * available before ownCloud was introduced, thus workaround
698 * it with the backend_name check
700 if (g_strcmp0 (backend_name, "owncloud") == 0) {
701 GoaCalendar *goa_calendar;
702 GoaContacts *goa_contacts;
704 goa_calendar = goa_object_get_calendar (goa_object);
706 g_object_bind_property (
708 source_extension, "calendar-url",
709 G_BINDING_SYNC_CREATE);
710 g_object_unref (goa_calendar);
713 goa_contacts = goa_object_get_contacts (goa_object);
715 g_object_bind_property (
717 source_extension, "contacts-url",
718 G_BINDING_SYNC_CREATE);
719 g_object_unref (goa_contacts);
723 extension_name = E_SOURCE_EXTENSION_COLLECTION;
724 source_extension = e_source_get_extension (source, extension_name);
726 g_object_bind_property_full (
727 goa_account, "provider-type",
728 source_extension, "backend-name",
729 G_BINDING_SYNC_CREATE,
730 gnome_online_accounts_provider_type_to_backend_name,
732 NULL, (GDestroyNotify) NULL);
734 g_object_bind_property (
735 goa_account, "identity",
736 source_extension, "identity",
737 G_BINDING_SYNC_CREATE);
739 g_object_bind_property_full (
740 goa_object, "calendar",
741 source_extension, "calendar-enabled",
742 G_BINDING_SYNC_CREATE,
743 gnome_online_accounts_object_is_non_null,
745 NULL, (GDestroyNotify) NULL);
747 g_object_bind_property_full (
748 goa_object, "contacts",
749 source_extension, "contacts-enabled",
750 G_BINDING_SYNC_CREATE,
751 gnome_online_accounts_object_is_non_null,
753 NULL, (GDestroyNotify) NULL);
755 g_object_bind_property_full (
757 source_extension, "mail-enabled",
758 G_BINDING_SYNC_CREATE,
759 gnome_online_accounts_object_is_non_null,
761 NULL, (GDestroyNotify) NULL);
763 g_object_unref (goa_account);
765 /* Handle optional GOA interfaces. */
766 gnome_online_accounts_config_exchange (extension, source, goa_object);
767 gnome_online_accounts_config_password (extension, source, goa_object);
769 /* The data source should not be removable by clients. */
770 e_server_side_source_set_removable (
771 E_SERVER_SIDE_SOURCE (source), FALSE);
773 if (goa_object_peek_oauth2_based (goa_object) != NULL) {
774 /* This module provides OAuth 2.0 support to the collection.
775 * Note, children of the collection source will automatically
776 * inherit our EOAuth2Support through the property binding in
777 * collection_backend_child_added(). */
778 e_server_side_source_set_oauth2_support (
779 E_SERVER_SIDE_SOURCE (source),
780 E_OAUTH2_SUPPORT (extension));
785 gnome_online_accounts_config_mail_account (EGnomeOnlineAccounts *extension,
787 GoaObject *goa_object)
789 EServerSideSource *server_side_source;
791 /* Only one or the other should be present, not both. */
792 gnome_online_accounts_config_oauth (extension, source, goa_object);
793 gnome_online_accounts_config_oauth2 (extension, source, goa_object);
795 /* XXX Need to defer the network security settings to the
796 * provider-specific module since "imap-use-tls" tells
797 * us neither the port number, nor whether to use IMAP
798 * over SSL versus STARTTLS. The module will know.
800 * Addendum: This got fixed in GOA 3.8. There's now both
801 * "imap-use-tls" and "imap-use-ssl" properties.
802 * Go ahead and set up IMAP details here if we
803 * have GOA 3.8, otherwise continue deferring
804 * to provider-specific modules. */
805 gnome_online_accounts_config_imap (extension, source, goa_object);
807 /* Clients may change the source by may not remove it. */
808 server_side_source = E_SERVER_SIDE_SOURCE (source);
809 e_server_side_source_set_writable (server_side_source, TRUE);
810 e_server_side_source_set_removable (server_side_source, FALSE);
814 gnome_online_accounts_config_mail_identity (EGnomeOnlineAccounts *extension,
816 GoaObject *goa_object)
819 ESourceExtension *source_extension;
820 EServerSideSource *server_side_source;
821 const gchar *extension_name;
823 goa_mail = goa_object_get_mail (goa_object);
824 g_return_if_fail (goa_mail != NULL);
826 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
827 source_extension = e_source_get_extension (source, extension_name);
829 g_object_bind_property (
830 goa_mail, "email-address",
831 source_extension, "address",
832 G_BINDING_SYNC_CREATE);
834 g_object_unref (goa_mail);
836 /* Clients may change the source by may not remove it. */
837 server_side_source = E_SERVER_SIDE_SOURCE (source);
838 e_server_side_source_set_writable (server_side_source, TRUE);
839 e_server_side_source_set_removable (server_side_source, FALSE);
843 gnome_online_accounts_config_mail_transport (EGnomeOnlineAccounts *extension,
845 GoaObject *goa_object)
847 EServerSideSource *server_side_source;
849 /* Only one or the other should be present, not both. */
850 gnome_online_accounts_config_oauth (extension, source, goa_object);
851 gnome_online_accounts_config_oauth2 (extension, source, goa_object);
853 /* XXX Need to defer the network security settings to the
854 * provider-specific module since "smtp-use-tls" tells
855 * us neither the port number, nor whether to use SMTP
856 * over SSL versus STARTTLS. The module will know.
858 * Addendum: This got fixed in GOA 3.8. There's now both
859 * "smtp-use-tls" and "smtp-use-ssl" properties.
860 * Go ahead and set up SMTP details here if we
861 * have GOA 3.8, otherwise continue deferring
862 * to provider-specific modules. */
863 gnome_online_accounts_config_smtp (extension, source, goa_object);
865 /* Clients may change the source by may not remove it. */
866 server_side_source = E_SERVER_SIDE_SOURCE (source);
867 e_server_side_source_set_writable (server_side_source, TRUE);
868 e_server_side_source_set_removable (server_side_source, FALSE);
872 gnome_online_accounts_config_sources (EGnomeOnlineAccounts *extension,
874 GoaObject *goa_object)
876 ESourceRegistryServer *server;
877 ECollectionBackend *backend;
880 /* XXX This function was primarily intended to smooth the
881 * transition of mail accounts from XOAUTH to XOAUTH2,
882 * but it may be useful for other types of migration. */
884 gnome_online_accounts_config_collection (extension, source, goa_object);
886 server = gnome_online_accounts_get_server (extension);
887 backend = e_source_registry_server_ref_backend (server, source);
888 g_return_if_fail (backend != NULL);
890 list = e_collection_backend_list_mail_sources (backend);
892 for (link = list; link != NULL; link = g_list_next (link)) {
893 const gchar *extension_name;
895 source = E_SOURCE (link->data);
897 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
898 if (e_source_has_extension (source, extension_name))
899 gnome_online_accounts_config_mail_account (
900 extension, source, goa_object);
902 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
903 if (e_source_has_extension (source, extension_name))
904 gnome_online_accounts_config_mail_identity (
905 extension, source, goa_object);
907 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
908 if (e_source_has_extension (source, extension_name))
909 gnome_online_accounts_config_mail_transport (
910 extension, source, goa_object);
913 g_list_free_full (list, (GDestroyNotify) g_object_unref);
915 g_object_unref (backend);
919 gnome_online_accounts_create_collection (EGnomeOnlineAccounts *extension,
920 EBackendFactory *backend_factory,
921 GoaObject *goa_object)
923 GoaAccount *goa_account;
924 ESourceRegistryServer *server;
925 ESource *collection_source;
926 ESource *mail_account_source = NULL;
927 ESource *mail_identity_source = NULL;
928 ESource *mail_transport_source = NULL;
929 const gchar *account_id;
930 const gchar *parent_uid;
932 server = gnome_online_accounts_get_server (extension);
934 collection_source = gnome_online_accounts_new_source (extension);
935 g_return_if_fail (E_IS_SOURCE (collection_source));
937 gnome_online_accounts_config_collection (
938 extension, collection_source, goa_object);
939 parent_uid = e_source_get_uid (collection_source);
941 if (goa_object_peek_mail (goa_object)) {
942 mail_account_source =
943 gnome_online_accounts_new_source (extension);
944 g_return_if_fail (E_IS_SOURCE (mail_account_source));
946 mail_identity_source =
947 gnome_online_accounts_new_source (extension);
948 g_return_if_fail (E_IS_SOURCE (mail_identity_source));
950 mail_transport_source =
951 gnome_online_accounts_new_source (extension);
952 g_return_if_fail (E_IS_SOURCE (mail_transport_source));
954 /* Configure parent/child relationships. */
955 e_source_set_parent (mail_account_source, parent_uid);
956 e_source_set_parent (mail_identity_source, parent_uid);
957 e_source_set_parent (mail_transport_source, parent_uid);
959 /* Give the factory first crack at mail configuration. */
960 e_collection_backend_factory_prepare_mail (
961 E_COLLECTION_BACKEND_FACTORY (backend_factory),
963 mail_identity_source,
964 mail_transport_source);
966 gnome_online_accounts_config_mail_account (
967 extension, mail_account_source, goa_object);
968 gnome_online_accounts_config_mail_identity (
969 extension, mail_identity_source, goa_object);
970 gnome_online_accounts_config_mail_transport (
971 extension, mail_transport_source, goa_object);
974 /* Export the new source collection. */
975 e_source_registry_server_add_source (server, collection_source);
977 if (mail_account_source != NULL) {
978 e_source_registry_server_add_source (
979 server, mail_account_source);
980 g_object_unref (mail_account_source);
983 if (mail_identity_source != NULL) {
984 e_source_registry_server_add_source (
985 server, mail_identity_source);
986 g_object_unref (mail_identity_source);
989 if (mail_transport_source != NULL) {
990 e_source_registry_server_add_source (
991 server, mail_transport_source);
992 g_object_unref (mail_transport_source);
995 goa_account = goa_object_get_account (goa_object);
996 account_id = goa_account_get_id (goa_account);
998 g_hash_table_insert (
999 extension->goa_to_eds,
1000 g_strdup (account_id),
1001 g_strdup (parent_uid));
1003 g_object_unref (goa_account);
1004 g_object_unref (collection_source);
1008 gnome_online_accounts_remove_collection (EGnomeOnlineAccounts *extension,
1011 GError *error = NULL;
1013 /* This removes the entire subtree rooted at source.
1014 * Deletes the corresponding on-disk key files too. */
1015 e_source_remove_sync (source, NULL, &error);
1017 if (error != NULL) {
1018 g_warning ("%s: %s", G_STRFUNC, error->message);
1019 g_error_free (error);
1024 gnome_online_accounts_account_added_cb (GoaClient *goa_client,
1025 GoaObject *goa_object,
1026 EGnomeOnlineAccounts *extension)
1028 GoaAccount *goa_account;
1029 ESourceRegistryServer *server;
1030 EBackendFactory *backend_factory = NULL;
1031 const gchar *provider_type;
1032 const gchar *backend_name;
1033 const gchar *account_id;
1034 const gchar *source_uid;
1036 server = gnome_online_accounts_get_server (extension);
1038 goa_account = goa_object_get_account (goa_object);
1039 provider_type = goa_account_get_provider_type (goa_account);
1040 backend_name = gnome_online_accounts_get_backend_name (provider_type);
1042 account_id = goa_account_get_id (goa_account);
1043 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
1045 if (source_uid == NULL && backend_name != NULL)
1046 backend_factory = e_data_factory_ref_backend_factory (
1047 E_DATA_FACTORY (server), backend_name);
1049 if (backend_factory != NULL) {
1050 gnome_online_accounts_create_collection (
1051 extension, backend_factory, goa_object);
1052 g_object_unref (backend_factory);
1055 g_object_unref (goa_account);
1059 gnome_online_accounts_account_removed_cb (GoaClient *goa_client,
1060 GoaObject *goa_object,
1061 EGnomeOnlineAccounts *extension)
1063 ESource *source = NULL;
1064 ESourceRegistryServer *server;
1065 GoaAccount *goa_account;
1066 const gchar *account_id;
1067 const gchar *source_uid;
1069 server = gnome_online_accounts_get_server (extension);
1071 goa_account = goa_object_get_account (goa_object);
1073 account_id = goa_account_get_id (goa_account);
1074 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
1076 if (source_uid != NULL)
1077 source = e_source_registry_server_ref_source (
1078 server, source_uid);
1080 if (source != NULL) {
1081 gnome_online_accounts_remove_collection (extension, source);
1082 g_object_unref (source);
1085 g_object_unref (goa_account);
1089 gnome_online_accounts_compare_id (GoaObject *goa_object,
1090 const gchar *target_id)
1092 GoaAccount *goa_account;
1093 const gchar *account_id;
1096 goa_account = goa_object_get_account (goa_object);
1097 account_id = goa_account_get_id (goa_account);
1098 result = g_strcmp0 (account_id, target_id);
1099 g_object_unref (goa_account);
1105 gnome_online_accounts_populate_accounts_table (EGnomeOnlineAccounts *extension,
1108 ESourceRegistryServer *server;
1109 GQueue trash = G_QUEUE_INIT;
1111 const gchar *extension_name;
1113 server = gnome_online_accounts_get_server (extension);
1115 extension_name = E_SOURCE_EXTENSION_GOA;
1116 list = e_source_registry_server_list_sources (server, extension_name);
1118 for (link = list; link != NULL; link = g_list_next (link)) {
1120 ESourceGoa *goa_ext;
1121 const gchar *account_id;
1122 const gchar *source_uid;
1125 source = E_SOURCE (link->data);
1126 source_uid = e_source_get_uid (source);
1128 extension_name = E_SOURCE_EXTENSION_GOA;
1129 goa_ext = e_source_get_extension (source, extension_name);
1130 account_id = e_source_goa_get_account_id (goa_ext);
1132 if (account_id == NULL)
1135 /* Verify the GOA account still exists. */
1136 match = g_list_find_custom (
1137 goa_objects, account_id,
1138 (GCompareFunc) gnome_online_accounts_compare_id);
1140 /* If a matching GoaObject was found, add its ID
1141 * to our accounts hash table. Otherwise remove
1142 * the ESource after we finish looping. */
1143 if (match != NULL) {
1144 GoaObject *goa_object;
1146 g_hash_table_insert (
1147 extension->goa_to_eds,
1148 g_strdup (account_id),
1149 g_strdup (source_uid));
1151 goa_object = GOA_OBJECT (match->data);
1152 gnome_online_accounts_config_sources (
1153 extension, source, goa_object);
1155 g_queue_push_tail (&trash, source);
1159 /* Empty the trash. */
1160 while (!g_queue_is_empty (&trash)) {
1161 ESource *source = g_queue_pop_head (&trash);
1162 gnome_online_accounts_remove_collection (extension, source);
1165 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1169 gnome_online_accounts_create_client_cb (GObject *source_object,
1170 GAsyncResult *result,
1173 EGnomeOnlineAccounts *extension;
1174 GoaClient *goa_client;
1176 GError *error = NULL;
1178 /* If we get back a G_IO_ERROR_CANCELLED then it means the
1179 * EGnomeOnlineAccounts is already finalized, so be careful
1180 * not to touch it until after we have a valid GoaClient. */
1182 goa_client = goa_client_new_finish (result, &error);
1184 if (error != NULL) {
1185 g_warn_if_fail (goa_client == NULL);
1187 "Unable to connect to the GNOME Online "
1188 "Accounts service: %s", error->message);
1189 g_error_free (error);
1193 g_return_if_fail (GOA_IS_CLIENT (goa_client));
1195 /* Should be safe to dereference the EGnomeOnlineAccounts now. */
1197 extension = E_GNOME_ONLINE_ACCOUNTS (user_data);
1198 extension->goa_client = goa_client; /* takes ownership */
1200 /* Don't need the GCancellable anymore. */
1201 g_object_unref (extension->create_client);
1202 extension->create_client = NULL;
1204 list = goa_client_get_accounts (extension->goa_client);
1206 /* This populates a hash table of GOA ID -> ESource UID strings by
1207 * searching through available data sources for ones with a "GNOME
1208 * Online Accounts" extension. If such an extension is found, but
1209 * no corresponding GoaAccount (presumably meaning the GOA account
1210 * was somehow deleted between E-D-S sessions) then the ESource in
1211 * which the extension was found gets deleted. */
1212 gnome_online_accounts_populate_accounts_table (extension, list);
1214 for (link = list; link != NULL; link = g_list_next (link))
1215 gnome_online_accounts_account_added_cb (
1216 extension->goa_client,
1217 GOA_OBJECT (link->data),
1220 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1222 /* Listen for Online Account changes. */
1224 extension->goa_client, "account-added",
1225 G_CALLBACK (gnome_online_accounts_account_added_cb),
1228 extension->goa_client, "account-removed",
1229 G_CALLBACK (gnome_online_accounts_account_removed_cb),
1234 gnome_online_accounts_bus_acquired_cb (EDBusServer *server,
1235 GDBusConnection *connection,
1236 EGnomeOnlineAccounts *extension)
1238 /* Connect to the GNOME Online Accounts service. */
1240 /* Note we don't reference the extension. If the
1241 * extension gets destroyed before this completes
1242 * we cancel the operation from dispose(). */
1244 extension->create_client,
1245 gnome_online_accounts_create_client_cb,
1250 gnome_online_accounts_dispose (GObject *object)
1252 EGnomeOnlineAccounts *extension;
1254 extension = E_GNOME_ONLINE_ACCOUNTS (object);
1256 if (extension->goa_client != NULL) {
1257 g_signal_handlers_disconnect_matched (
1258 extension->goa_client,
1259 G_SIGNAL_MATCH_DATA,
1260 0, 0, NULL, NULL, object);
1261 g_object_unref (extension->goa_client);
1262 extension->goa_client = NULL;
1265 /* This cancels goa_client_new() in case it still
1266 * hasn't completed. We're no longer interested. */
1267 if (extension->create_client != NULL) {
1268 g_cancellable_cancel (extension->create_client);
1269 g_object_unref (extension->create_client);
1270 extension->create_client = NULL;
1273 /* Chain up to parent's dispose() method. */
1274 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1279 gnome_online_accounts_finalize (GObject *object)
1281 EGnomeOnlineAccounts *extension;
1283 extension = E_GNOME_ONLINE_ACCOUNTS (object);
1285 g_hash_table_destroy (extension->goa_to_eds);
1287 /* Chain up to parent's finalize() method. */
1288 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1293 gnome_online_accounts_constructed (GObject *object)
1295 EExtension *extension;
1296 EExtensible *extensible;
1298 extension = E_EXTENSION (object);
1299 extensible = e_extension_get_extensible (extension);
1301 /* Wait for the registry service to acquire its well-known
1302 * bus name so we don't do anything destructive beforehand. */
1305 extensible, "bus-acquired",
1306 G_CALLBACK (gnome_online_accounts_bus_acquired_cb),
1309 /* Chain up to parent's constructed() method. */
1310 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1311 constructed (object);
1315 gnome_online_accounts_get_access_token_sync (EOAuth2Support *support,
1317 GCancellable *cancellable,
1318 gchar **out_access_token,
1319 gint *out_expires_in,
1322 GoaObject *goa_object;
1323 GoaAccount *goa_account;
1324 GoaOAuth2Based *goa_oauth2_based;
1327 goa_object = gnome_online_accounts_ref_account (
1328 E_GNOME_ONLINE_ACCOUNTS (support), source);
1330 if (goa_object == NULL) {
1332 error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1333 _("Cannot find a corresponding account in "
1334 "the org.gnome.OnlineAccounts service from "
1335 "which to obtain an access token for '%s'"),
1336 e_source_get_display_name (source));
1340 goa_account = goa_object_get_account (goa_object);
1341 g_return_val_if_fail (goa_account != NULL, FALSE);
1343 goa_oauth2_based = goa_object_get_oauth2_based (goa_object);
1344 g_return_val_if_fail (goa_oauth2_based != NULL, FALSE);
1346 success = goa_account_call_ensure_credentials_sync (
1347 goa_account, NULL, cancellable, error);
1350 success = goa_oauth2_based_call_get_access_token_sync (
1351 goa_oauth2_based, out_access_token,
1352 out_expires_in, cancellable, error);
1354 g_object_unref (goa_oauth2_based);
1355 g_object_unref (goa_account);
1356 g_object_unref (goa_object);
1360 _("Failed to obtain an access token for '%s': "),
1361 e_source_get_display_name (source));
1367 e_gnome_online_accounts_class_init (EGnomeOnlineAccountsClass *class)
1369 GObjectClass *object_class;
1370 EExtensionClass *extension_class;
1372 object_class = G_OBJECT_CLASS (class);
1373 object_class->dispose = gnome_online_accounts_dispose;
1374 object_class->finalize = gnome_online_accounts_finalize;
1375 object_class->constructed = gnome_online_accounts_constructed;
1377 extension_class = E_EXTENSION_CLASS (class);
1378 extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
1382 e_gnome_online_accounts_class_finalize (EGnomeOnlineAccountsClass *class)
1387 e_gnome_online_accounts_oauth2_support_init (EOAuth2SupportInterface *interface)
1389 interface->get_access_token_sync =
1390 gnome_online_accounts_get_access_token_sync;
1394 e_gnome_online_accounts_init (EGnomeOnlineAccounts *extension)
1396 /* Used to cancel unfinished goa_client_new(). */
1397 extension->create_client = g_cancellable_new ();
1399 extension->goa_to_eds = g_hash_table_new_full (
1400 (GHashFunc) g_str_hash,
1401 (GEqualFunc) g_str_equal,
1402 (GDestroyNotify) g_free,
1403 (GDestroyNotify) g_free);
1406 G_MODULE_EXPORT void
1407 e_module_load (GTypeModule *type_module)
1409 e_gnome_online_accounts_register_type (type_module);
1412 G_MODULE_EXPORT void
1413 e_module_unload (GTypeModule *type_module)