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 <libsecret/secret.h>
25 #include <libsoup/soup.h>
27 #include <libebackend/libebackend.h>
29 #include "goaewsclient.h"
31 /* Standard GObject macros */
32 #define E_TYPE_GNOME_ONLINE_ACCOUNTS \
33 (e_gnome_online_accounts_get_type ())
34 #define E_GNOME_ONLINE_ACCOUNTS(obj) \
35 (G_TYPE_CHECK_INSTANCE_CAST \
36 ((obj), E_TYPE_GNOME_ONLINE_ACCOUNTS, EGnomeOnlineAccounts))
38 #define CAMEL_OAUTH_MECHANISM_NAME "XOAUTH"
39 #define CAMEL_OAUTH2_MECHANISM_NAME "XOAUTH2"
41 typedef struct _EGnomeOnlineAccounts EGnomeOnlineAccounts;
42 typedef struct _EGnomeOnlineAccountsClass EGnomeOnlineAccountsClass;
44 struct _EGnomeOnlineAccounts {
47 GoaClient *goa_client;
48 GCancellable *create_client;
50 /* GoaAccount ID -> ESource UID */
51 GHashTable *goa_to_eds;
54 struct _EGnomeOnlineAccountsClass {
55 EExtensionClass parent_class;
58 /* The keyring definintions are copied from e-authentication-session.c */
60 #define KEYRING_ITEM_ATTRIBUTE_NAME "e-source-uid"
61 #define KEYRING_ITEM_DISPLAY_FORMAT "Evolution Data Source %s"
63 #ifdef HAVE_GOA_PASSWORD_BASED
64 /* XXX Probably want to share this with
65 * evolution-source-registry-migrate-sources.c */
66 static SecretSchema schema = {
67 "org.gnome.Evolution.DataSource",
68 SECRET_SCHEMA_DONT_MATCH_NAME,
70 { KEYRING_ITEM_ATTRIBUTE_NAME,
71 SECRET_SCHEMA_ATTRIBUTE_STRING },
75 #endif /* HAVE_GOA_PASSWORD_BASED */
77 /* Module Entry Points */
78 void e_module_load (GTypeModule *type_module);
79 void e_module_unload (GTypeModule *type_module);
81 /* Forward Declarations */
82 GType e_gnome_online_accounts_get_type (void);
84 G_DEFINE_DYNAMIC_TYPE (
86 e_gnome_online_accounts,
90 gnome_online_accounts_get_backend_name (const gchar *goa_provider_type)
92 const gchar *eds_backend_name = NULL;
94 /* This is a mapping between GoaAccount provider types and
95 * ESourceCollection backend names. It requires knowledge
96 * of other registry modules, possibly even from 3rd party
97 * packages. No way around it. */
99 if (g_strcmp0 (goa_provider_type, "exchange") == 0)
100 eds_backend_name = "ews";
102 if (g_strcmp0 (goa_provider_type, "google") == 0)
103 eds_backend_name = "google";
105 else if (g_strcmp0 (goa_provider_type, "yahoo") == 0)
106 eds_backend_name = "yahoo";
108 return eds_backend_name;
111 static ESourceRegistryServer *
112 gnome_online_accounts_get_server (EGnomeOnlineAccounts *extension)
114 EExtensible *extensible;
116 extensible = e_extension_get_extensible (E_EXTENSION (extension));
118 return E_SOURCE_REGISTRY_SERVER (extensible);
122 gnome_online_accounts_provider_type_to_backend_name (GBinding *binding,
123 const GValue *source_value,
124 GValue *target_value,
127 const gchar *provider_type;
128 const gchar *backend_name;
130 provider_type = g_value_get_string (source_value);
131 backend_name = gnome_online_accounts_get_backend_name (provider_type);
132 g_return_val_if_fail (backend_name != NULL, FALSE);
133 g_value_set_string (target_value, backend_name);
139 gnome_online_accounts_object_is_non_null (GBinding *binding,
140 const GValue *source_value,
141 GValue *target_value,
146 v_object = g_value_get_object (source_value);
147 g_value_set_boolean (target_value, v_object != NULL);
153 gnome_online_accounts_new_source (EGnomeOnlineAccounts *extension)
155 ESourceRegistryServer *server;
158 GError *error = NULL;
160 /* This being a brand new data source, creating the instance
161 * should never fail but we'll check for errors just the same. */
162 server = gnome_online_accounts_get_server (extension);
163 file = e_server_side_source_new_user_file (NULL);
164 source = e_server_side_source_new (server, file, &error);
165 g_object_unref (file);
168 g_warn_if_fail (source == NULL);
169 g_warning ("%s: %s", G_STRFUNC, error->message);
170 g_error_free (error);
176 #ifdef HAVE_GOA_PASSWORD_BASED
178 replace_host (gchar **url,
183 uri = soup_uri_new (*url);
187 soup_uri_set_host (uri, host);
190 *url = soup_uri_to_string (uri, FALSE);
194 #endif /* HAVE_GOA_PASSWORD_BASED */
197 gnome_online_accounts_config_exchange (EGnomeOnlineAccounts *extension,
199 GoaObject *goa_object)
201 #ifdef HAVE_GOA_PASSWORD_BASED
202 GoaExchange *goa_exchange;
203 ESourceExtension *source_extension;
204 const gchar *extension_name;
205 gchar *as_url = NULL;
206 gchar *oab_url = NULL;
208 GError *error = NULL;
210 goa_exchange = goa_object_peek_exchange (goa_object);
211 if (goa_exchange == NULL)
214 /* This should force the ESourceCamelEws type to be registered.
215 * It will also tell us if Evolution-EWS is even installed. */
216 class = g_type_class_ref (g_type_from_name ("EEwsBackend"));
218 g_type_class_unref (class);
221 "%s: Could not locate EEwsBackendClass. "
222 "Is Evolution-EWS installed?", G_STRFUNC);
226 /* XXX GNOME Online Accounts already runs autodiscover to test
227 * the user-entered values but doesn't share the discovered
228 * URLs. It only provides us a host name and expects us to
229 * re-run autodiscover for ourselves.
231 * So I've copied a slab of code from GOA which was in turn
232 * copied from Evolution-EWS which does the autodiscovery.
234 * I've already complained to Debarshi Ray about the lack
235 * of useful info in GOA's Exchange interface so hopefully
236 * it will someday publish discovered URLs and then we can
237 * remove this hack. */
239 goa_ews_autodiscover_sync (
240 goa_object, &as_url, &oab_url, NULL, &error);
243 g_warning ("%s: %s", G_STRFUNC, error->message);
244 g_error_free (error);
248 g_return_if_fail (as_url != NULL);
249 g_return_if_fail (oab_url != NULL);
251 /* XXX We don't have direct access to CamelEwsSettings from here
252 * since it's defined in Evolution-EWS. But we can find out
253 * its extension name and set properties by name. */
255 extension_name = e_source_camel_get_extension_name ("ews");
256 source_extension = e_source_get_extension (source, extension_name);
258 /* This will be NULL if Evolution-EWS is not installed. */
259 if (source_extension != NULL) {
260 GoaAccount *goa_account;
261 gchar *host, *user, *email;
263 goa_account = goa_object_peek_account (goa_object);
264 host = goa_exchange_dup_host (goa_exchange);
265 user = goa_account_dup_identity (goa_account);
266 email = goa_account_dup_presentation_identity (goa_account);
269 replace_host (&as_url, host);
270 replace_host (&oab_url, host);
280 g_object_set (e_source_camel_get_settings (E_SOURCE_CAMEL (source_extension)),
291 "%s: Failed to create [%s] extension",
292 G_STRFUNC, extension_name);
297 #endif /* HAVE_GOA_PASSWORD_BASED */
301 gnome_online_accounts_config_oauth (EGnomeOnlineAccounts *extension,
303 GoaObject *goa_object)
305 ESourceExtension *source_extension;
306 const gchar *extension_name;
308 if (goa_object_peek_oauth_based (goa_object) == NULL)
311 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
312 source_extension = e_source_get_extension (source, extension_name);
314 e_source_authentication_set_method (
315 E_SOURCE_AUTHENTICATION (source_extension),
316 CAMEL_OAUTH_MECHANISM_NAME);
320 gnome_online_accounts_config_oauth2 (EGnomeOnlineAccounts *extension,
322 GoaObject *goa_object)
324 ESourceExtension *source_extension;
325 const gchar *extension_name;
327 if (goa_object_peek_oauth2_based (goa_object) == NULL)
330 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
331 source_extension = e_source_get_extension (source, extension_name);
333 e_source_authentication_set_method (
334 E_SOURCE_AUTHENTICATION (source_extension),
335 CAMEL_OAUTH2_MECHANISM_NAME);
339 gnome_online_accounts_config_password (EGnomeOnlineAccounts *extension,
341 GoaObject *goa_object)
343 #ifdef HAVE_GOA_PASSWORD_BASED
344 GoaAccount *goa_account;
345 GoaPasswordBased *goa_password_based;
346 EAsyncClosure *closure;
347 GAsyncResult *result;
351 gchar *password = NULL;
352 GError *error = NULL;
354 /* If the GNOME Online Account is password-based, we use its
355 * password to seed our own keyring entry for the collection
356 * source which avoids having to special-case authentication
357 * like we do for OAuth. Plus, if the stored password is no
358 * good we'll prompt for a new one instead of just giving up. */
360 goa_password_based = goa_object_get_password_based (goa_object);
362 if (goa_password_based == NULL)
365 closure = e_async_closure_new ();
367 /* XXX The GOA documentation doesn't explain the string
368 * argument in goa_password_based_get_password() so
369 * we'll pass in the identity and hope for the best. */
370 goa_account = goa_object_get_account (goa_object);
371 arg_id = goa_account_dup_identity (goa_account);
372 g_object_unref (goa_account);
374 goa_password_based_call_get_password (
375 goa_password_based, arg_id, NULL,
376 e_async_closure_callback, closure);
380 result = e_async_closure_wait (closure);
382 goa_password_based_call_get_password_finish (
383 goa_password_based, &password, result, &error);
386 g_warning ("%s: %s", G_STRFUNC, error->message);
387 g_error_free (error);
391 uid = e_source_get_uid (source);
392 display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
394 secret_password_store (
395 &schema, SECRET_COLLECTION_DEFAULT,
396 display_name, password, NULL,
397 e_async_closure_callback, closure,
398 KEYRING_ITEM_ATTRIBUTE_NAME, uid,
401 result = e_async_closure_wait (closure);
403 secret_password_store_finish (result, &error);
405 g_free (display_name);
407 /* If we fail to store the password, we'll just end up prompting
408 * for a password like normal. Annoying, maybe, but not the end
409 * of the world. Still leave a breadcrumb for debugging though. */
411 g_warning ("%s: %s", G_STRFUNC, error->message);
412 g_error_free (error);
416 e_async_closure_free (closure);
417 g_object_unref (goa_password_based);
418 #endif /* HAVE_GOA_PASSWORD_BASED */
422 gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension,
424 GoaObject *goa_object)
426 GoaAccount *goa_account;
427 ESourceExtension *source_extension;
428 const gchar *extension_name;
430 goa_account = goa_object_get_account (goa_object);
432 g_object_bind_property (
433 goa_account, "presentation-identity",
434 source, "display-name",
435 G_BINDING_SYNC_CREATE);
437 extension_name = E_SOURCE_EXTENSION_GOA;
438 source_extension = e_source_get_extension (source, extension_name);
440 g_object_bind_property (
442 source_extension, "account-id",
443 G_BINDING_SYNC_CREATE);
445 extension_name = E_SOURCE_EXTENSION_COLLECTION;
446 source_extension = e_source_get_extension (source, extension_name);
448 g_object_bind_property_full (
449 goa_account, "provider-type",
450 source_extension, "backend-name",
451 G_BINDING_SYNC_CREATE,
452 gnome_online_accounts_provider_type_to_backend_name,
454 NULL, (GDestroyNotify) NULL);
456 g_object_bind_property (
457 goa_account, "identity",
458 source_extension, "identity",
459 G_BINDING_SYNC_CREATE);
461 g_object_bind_property_full (
462 goa_object, "calendar",
463 source_extension, "calendar-enabled",
464 G_BINDING_SYNC_CREATE,
465 gnome_online_accounts_object_is_non_null,
467 NULL, (GDestroyNotify) NULL);
469 g_object_bind_property_full (
470 goa_object, "contacts",
471 source_extension, "contacts-enabled",
472 G_BINDING_SYNC_CREATE,
473 gnome_online_accounts_object_is_non_null,
475 NULL, (GDestroyNotify) NULL);
477 g_object_bind_property_full (
479 source_extension, "mail-enabled",
480 G_BINDING_SYNC_CREATE,
481 gnome_online_accounts_object_is_non_null,
483 NULL, (GDestroyNotify) NULL);
485 g_object_unref (goa_account);
487 /* Handle optional GOA interfaces. */
488 gnome_online_accounts_config_exchange (extension, source, goa_object);
489 gnome_online_accounts_config_password (extension, source, goa_object);
491 /* The data source should not be removable by clients. */
492 e_server_side_source_set_removable (
493 E_SERVER_SIDE_SOURCE (source), FALSE);
497 gnome_online_accounts_config_mail_account (EGnomeOnlineAccounts *extension,
499 GoaObject *goa_object)
501 EServerSideSource *server_side_source;
503 /* Only one or the other should be present, not both. */
504 gnome_online_accounts_config_oauth (extension, source, goa_object);
505 gnome_online_accounts_config_oauth2 (extension, source, goa_object);
507 /* XXX Need to defer the network security settings to the
508 * provider-specific module since "imap-use-tls" tells
509 * us neither the port number, nor whether to use IMAP
510 * over SSL versus STARTTLS. The module will know. */
512 /* Clients may change the source by may not remove it. */
513 server_side_source = E_SERVER_SIDE_SOURCE (source);
514 e_server_side_source_set_writable (server_side_source, TRUE);
515 e_server_side_source_set_removable (server_side_source, FALSE);
519 gnome_online_accounts_config_mail_identity (EGnomeOnlineAccounts *extension,
521 GoaObject *goa_object)
524 ESourceExtension *source_extension;
525 EServerSideSource *server_side_source;
526 const gchar *extension_name;
528 goa_mail = goa_object_get_mail (goa_object);
529 g_return_if_fail (goa_mail != NULL);
531 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
532 source_extension = e_source_get_extension (source, extension_name);
534 g_object_bind_property (
535 goa_mail, "email-address",
536 source_extension, "address",
537 G_BINDING_SYNC_CREATE);
539 g_object_unref (goa_mail);
541 /* Clients may change the source by may not remove it. */
542 server_side_source = E_SERVER_SIDE_SOURCE (source);
543 e_server_side_source_set_writable (server_side_source, TRUE);
544 e_server_side_source_set_removable (server_side_source, FALSE);
548 gnome_online_accounts_config_mail_transport (EGnomeOnlineAccounts *extension,
550 GoaObject *goa_object)
552 EServerSideSource *server_side_source;
554 /* Only one or the other should be present, not both. */
555 gnome_online_accounts_config_oauth (extension, source, goa_object);
556 gnome_online_accounts_config_oauth2 (extension, source, goa_object);
558 /* XXX Need to defer the network security settings to the
559 * provider-specific module since "smtp-use-tls" tells
560 * us neither the port number, nor whether to use SMTP
561 * over SSL versus STARTTLS. The module will know. */
563 /* Clients may change the source by may not remove it. */
564 server_side_source = E_SERVER_SIDE_SOURCE (source);
565 e_server_side_source_set_writable (server_side_source, TRUE);
566 e_server_side_source_set_removable (server_side_source, FALSE);
570 gnome_online_accounts_config_sources (EGnomeOnlineAccounts *extension,
572 GoaObject *goa_object)
574 ESourceRegistryServer *server;
575 ECollectionBackend *backend;
578 /* XXX This function was primarily intended to smooth the
579 * transition of mail accounts from XOAUTH to XOAUTH2,
580 * but it may be useful for other types of migration. */
582 gnome_online_accounts_config_collection (extension, source, goa_object);
584 server = gnome_online_accounts_get_server (extension);
585 backend = e_source_registry_server_ref_backend (server, source);
586 g_return_if_fail (backend != NULL);
588 list = e_collection_backend_list_mail_sources (backend);
590 for (link = list; link != NULL; link = g_list_next (link)) {
591 const gchar *extension_name;
593 source = E_SOURCE (link->data);
595 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
596 if (e_source_has_extension (source, extension_name))
597 gnome_online_accounts_config_mail_account (
598 extension, source, goa_object);
600 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
601 if (e_source_has_extension (source, extension_name))
602 gnome_online_accounts_config_mail_identity (
603 extension, source, goa_object);
605 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
606 if (e_source_has_extension (source, extension_name))
607 gnome_online_accounts_config_mail_transport (
608 extension, source, goa_object);
611 g_list_free_full (list, (GDestroyNotify) g_object_unref);
613 g_object_unref (backend);
617 gnome_online_accounts_create_collection (EGnomeOnlineAccounts *extension,
618 EBackendFactory *backend_factory,
619 GoaObject *goa_object)
621 GoaAccount *goa_account;
622 ESourceRegistryServer *server;
623 ESource *collection_source;
624 ESource *mail_account_source;
625 ESource *mail_identity_source;
626 ESource *mail_transport_source;
627 const gchar *account_id;
628 const gchar *parent_uid;
630 server = gnome_online_accounts_get_server (extension);
632 collection_source = gnome_online_accounts_new_source (extension);
633 g_return_if_fail (E_IS_SOURCE (collection_source));
635 mail_account_source = gnome_online_accounts_new_source (extension);
636 g_return_if_fail (E_IS_SOURCE (mail_account_source));
638 mail_identity_source = gnome_online_accounts_new_source (extension);
639 g_return_if_fail (E_IS_SOURCE (mail_identity_source));
641 mail_transport_source = gnome_online_accounts_new_source (extension);
642 g_return_if_fail (E_IS_SOURCE (mail_transport_source));
644 /* Configure parent/child relationships. */
645 parent_uid = e_source_get_uid (collection_source);
646 e_source_set_parent (mail_account_source, parent_uid);
647 e_source_set_parent (mail_identity_source, parent_uid);
648 e_source_set_parent (mail_transport_source, parent_uid);
650 /* Give the factory first crack at mail configuration. */
651 e_collection_backend_factory_prepare_mail (
652 E_COLLECTION_BACKEND_FACTORY (backend_factory),
654 mail_identity_source,
655 mail_transport_source);
657 /* Now it's our turn. */
658 gnome_online_accounts_config_collection (
659 extension, collection_source, goa_object);
660 gnome_online_accounts_config_mail_account (
661 extension, mail_account_source, goa_object);
662 gnome_online_accounts_config_mail_identity (
663 extension, mail_identity_source, goa_object);
664 gnome_online_accounts_config_mail_transport (
665 extension, mail_transport_source, goa_object);
667 /* Export the new source collection. */
668 e_source_registry_server_add_source (server, collection_source);
669 e_source_registry_server_add_source (server, mail_account_source);
670 e_source_registry_server_add_source (server, mail_identity_source);
671 e_source_registry_server_add_source (server, mail_transport_source);
673 goa_account = goa_object_get_account (goa_object);
674 account_id = goa_account_get_id (goa_account);
676 g_hash_table_insert (
677 extension->goa_to_eds,
678 g_strdup (account_id),
679 g_strdup (parent_uid));
681 g_object_unref (goa_account);
683 g_object_unref (collection_source);
684 g_object_unref (mail_account_source);
685 g_object_unref (mail_identity_source);
686 g_object_unref (mail_transport_source);
690 gnome_online_accounts_remove_collection (EGnomeOnlineAccounts *extension,
693 GError *error = NULL;
695 /* This removes the entire subtree rooted at source.
696 * Deletes the corresponding on-disk key files too. */
697 e_source_remove_sync (source, NULL, &error);
700 g_warning ("%s: %s", G_STRFUNC, error->message);
701 g_error_free (error);
706 gnome_online_accounts_account_added_cb (GoaClient *goa_client,
707 GoaObject *goa_object,
708 EGnomeOnlineAccounts *extension)
710 GoaAccount *goa_account;
711 ESourceRegistryServer *server;
712 EBackendFactory *backend_factory = NULL;
713 const gchar *provider_type;
714 const gchar *backend_name;
715 const gchar *account_id;
716 const gchar *source_uid;
718 server = gnome_online_accounts_get_server (extension);
720 goa_account = goa_object_get_account (goa_object);
721 provider_type = goa_account_get_provider_type (goa_account);
722 backend_name = gnome_online_accounts_get_backend_name (provider_type);
724 account_id = goa_account_get_id (goa_account);
725 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
727 if (source_uid == NULL && backend_name != NULL)
728 backend_factory = e_data_factory_ref_backend_factory (
729 E_DATA_FACTORY (server), backend_name);
731 if (backend_factory != NULL) {
732 gnome_online_accounts_create_collection (
733 extension, backend_factory, goa_object);
734 g_object_unref (backend_factory);
737 g_object_unref (goa_account);
741 gnome_online_accounts_account_removed_cb (GoaClient *goa_client,
742 GoaObject *goa_object,
743 EGnomeOnlineAccounts *extension)
745 ESource *source = NULL;
746 ESourceRegistryServer *server;
747 GoaAccount *goa_account;
748 const gchar *account_id;
749 const gchar *source_uid;
751 server = gnome_online_accounts_get_server (extension);
753 goa_account = goa_object_get_account (goa_object);
755 account_id = goa_account_get_id (goa_account);
756 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
758 if (source_uid != NULL)
759 source = e_source_registry_server_ref_source (
762 if (source != NULL) {
763 gnome_online_accounts_remove_collection (extension, source);
764 g_object_unref (source);
767 g_object_unref (goa_account);
771 gnome_online_accounts_compare_id (GoaObject *goa_object,
772 const gchar *target_id)
774 GoaAccount *goa_account;
775 const gchar *account_id;
778 goa_account = goa_object_get_account (goa_object);
779 account_id = goa_account_get_id (goa_account);
780 result = g_strcmp0 (account_id, target_id);
781 g_object_unref (goa_account);
787 gnome_online_accounts_populate_accounts_table (EGnomeOnlineAccounts *extension,
790 ESourceRegistryServer *server;
791 GQueue trash = G_QUEUE_INIT;
793 const gchar *extension_name;
795 server = gnome_online_accounts_get_server (extension);
797 extension_name = E_SOURCE_EXTENSION_GOA;
798 list = e_source_registry_server_list_sources (server, extension_name);
800 for (link = list; link != NULL; link = g_list_next (link)) {
803 const gchar *account_id;
804 const gchar *source_uid;
807 source = E_SOURCE (link->data);
808 source_uid = e_source_get_uid (source);
810 extension_name = E_SOURCE_EXTENSION_GOA;
811 goa_ext = e_source_get_extension (source, extension_name);
812 account_id = e_source_goa_get_account_id (goa_ext);
814 if (account_id == NULL)
817 /* Verify the GOA account still exists. */
818 match = g_list_find_custom (
819 goa_objects, account_id,
820 (GCompareFunc) gnome_online_accounts_compare_id);
822 /* If a matching GoaObject was found, add its ID
823 * to our accounts hash table. Otherwise remove
824 * the ESource after we finish looping. */
826 GoaObject *goa_object;
828 g_hash_table_insert (
829 extension->goa_to_eds,
830 g_strdup (account_id),
831 g_strdup (source_uid));
833 goa_object = GOA_OBJECT (match->data);
834 gnome_online_accounts_config_sources (
835 extension, source, goa_object);
837 g_queue_push_tail (&trash, source);
841 /* Empty the trash. */
842 while (!g_queue_is_empty (&trash)) {
843 ESource *source = g_queue_pop_head (&trash);
844 gnome_online_accounts_remove_collection (extension, source);
847 g_list_free_full (list, (GDestroyNotify) g_object_unref);
851 gnome_online_accounts_create_client_cb (GObject *source_object,
852 GAsyncResult *result,
855 EGnomeOnlineAccounts *extension;
856 GoaClient *goa_client;
858 GError *error = NULL;
860 /* If we get back a G_IO_ERROR_CANCELLED then it means the
861 * EGnomeOnlineAccounts is already finalized, so be careful
862 * not to touch it until after we have a valid GoaClient. */
864 goa_client = goa_client_new_finish (result, &error);
867 g_warn_if_fail (goa_client == NULL);
869 "Unable to connect to the GNOME Online "
870 "Accounts service: %s", error->message);
871 g_error_free (error);
875 g_return_if_fail (GOA_IS_CLIENT (goa_client));
877 /* Should be safe to dereference the EGnomeOnlineAccounts now. */
879 extension = E_GNOME_ONLINE_ACCOUNTS (user_data);
880 extension->goa_client = goa_client; /* takes ownership */
882 /* Don't need the GCancellable anymore. */
883 g_object_unref (extension->create_client);
884 extension->create_client = NULL;
886 list = goa_client_get_accounts (extension->goa_client);
888 /* This populates a hash table of GOA ID -> ESource UID strings by
889 * searching through available data sources for ones with a "GNOME
890 * Online Accounts" extension. If such an extension is found, but
891 * no corresponding GoaAccount (presumably meaning the GOA account
892 * was somehow deleted between E-D-S sessions) then the ESource in
893 * which the extension was found gets deleted. */
894 gnome_online_accounts_populate_accounts_table (extension, list);
896 for (link = list; link != NULL; link = g_list_next (link))
897 gnome_online_accounts_account_added_cb (
898 extension->goa_client,
899 GOA_OBJECT (link->data),
902 g_list_free_full (list, (GDestroyNotify) g_object_unref);
904 /* Listen for Online Account changes. */
906 extension->goa_client, "account-added",
907 G_CALLBACK (gnome_online_accounts_account_added_cb),
910 extension->goa_client, "account-removed",
911 G_CALLBACK (gnome_online_accounts_account_removed_cb),
916 gnome_online_accounts_bus_acquired_cb (EDBusServer *server,
917 GDBusConnection *connection,
918 EGnomeOnlineAccounts *extension)
920 /* Connect to the GNOME Online Accounts service. */
922 /* Note we don't reference the extension. If the
923 * extension gets destroyed before this completes
924 * we cancel the operation from dispose(). */
926 extension->create_client,
927 gnome_online_accounts_create_client_cb,
932 gnome_online_accounts_dispose (GObject *object)
934 EGnomeOnlineAccounts *extension;
936 extension = E_GNOME_ONLINE_ACCOUNTS (object);
938 if (extension->goa_client != NULL) {
939 g_signal_handlers_disconnect_matched (
940 extension->goa_client,
942 0, 0, NULL, NULL, object);
943 g_object_unref (extension->goa_client);
944 extension->goa_client = NULL;
947 /* This cancels goa_client_new() in case it still
948 * hasn't completed. We're no longer interested. */
949 if (extension->create_client != NULL) {
950 g_cancellable_cancel (extension->create_client);
951 g_object_unref (extension->create_client);
952 extension->create_client = NULL;
955 /* Chain up to parent's dispose() method. */
956 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
961 gnome_online_accounts_finalize (GObject *object)
963 EGnomeOnlineAccounts *extension;
965 extension = E_GNOME_ONLINE_ACCOUNTS (object);
967 g_hash_table_destroy (extension->goa_to_eds);
969 /* Chain up to parent's finalize() method. */
970 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
975 gnome_online_accounts_constructed (GObject *object)
977 EExtension *extension;
978 EExtensible *extensible;
980 extension = E_EXTENSION (object);
981 extensible = e_extension_get_extensible (extension);
983 /* Wait for the registry service to acquire its well-known
984 * bus name so we don't do anything destructive beforehand. */
987 extensible, "bus-acquired",
988 G_CALLBACK (gnome_online_accounts_bus_acquired_cb),
991 /* Chain up to parent's constructed() method. */
992 G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
993 constructed (object);
997 e_gnome_online_accounts_class_init (EGnomeOnlineAccountsClass *class)
999 GObjectClass *object_class;
1000 EExtensionClass *extension_class;
1002 object_class = G_OBJECT_CLASS (class);
1003 object_class->dispose = gnome_online_accounts_dispose;
1004 object_class->finalize = gnome_online_accounts_finalize;
1005 object_class->constructed = gnome_online_accounts_constructed;
1007 extension_class = E_EXTENSION_CLASS (class);
1008 extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
1012 e_gnome_online_accounts_class_finalize (EGnomeOnlineAccountsClass *class)
1017 e_gnome_online_accounts_init (EGnomeOnlineAccounts *extension)
1019 /* Used to cancel unfinished goa_client_new(). */
1020 extension->create_client = g_cancellable_new ();
1022 extension->goa_to_eds = g_hash_table_new_full (
1023 (GHashFunc) g_str_hash,
1024 (GEqualFunc) g_str_equal,
1025 (GDestroyNotify) g_free,
1026 (GDestroyNotify) g_free);
1029 G_MODULE_EXPORT void
1030 e_module_load (GTypeModule *type_module)
1032 e_gnome_online_accounts_register_type (type_module);
1035 G_MODULE_EXPORT void
1036 e_module_unload (GTypeModule *type_module)