2 * module-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>
26 #include <libebackend/libebackend.h>
28 #include "goaewsclient.h"
30 /* Standard GObject macros */
31 #define E_TYPE_ONLINE_ACCOUNTS \
32 (e_online_accounts_get_type ())
33 #define E_ONLINE_ACCOUNTS(obj) \
34 (G_TYPE_CHECK_INSTANCE_CAST \
35 ((obj), E_TYPE_ONLINE_ACCOUNTS, EOnlineAccounts))
37 #define CAMEL_OAUTH_MECHANISM_NAME "XOAUTH"
38 #define CAMEL_OAUTH2_MECHANISM_NAME "XOAUTH2"
40 typedef struct _EOnlineAccounts EOnlineAccounts;
41 typedef struct _EOnlineAccountsClass EOnlineAccountsClass;
43 struct _EOnlineAccounts {
46 GoaClient *goa_client;
47 GCancellable *create_client;
49 /* GoaAccount ID -> ESource UID */
50 GHashTable *goa_to_eds;
53 struct _EOnlineAccountsClass {
54 EExtensionClass parent_class;
57 /* The keyring definintions are copied from e-authentication-session.c */
59 #define KEYRING_ITEM_ATTRIBUTE_NAME "e-source-uid"
60 #define KEYRING_ITEM_DISPLAY_FORMAT "Evolution Data Source %s"
62 #ifdef HAVE_GOA_PASSWORD_BASED
63 /* XXX Probably want to share this with
64 * evolution-source-registry-migrate-sources.c */
65 static SecretSchema schema = {
66 "org.gnome.Evolution.DataSource",
67 SECRET_SCHEMA_DONT_MATCH_NAME,
69 { KEYRING_ITEM_ATTRIBUTE_NAME,
70 SECRET_SCHEMA_ATTRIBUTE_STRING },
74 #endif /* HAVE_GOA_PASSWORD_BASED */
76 /* Module Entry Points */
77 void e_module_load (GTypeModule *type_module);
78 void e_module_unload (GTypeModule *type_module);
80 /* Forward Declarations */
81 GType e_online_accounts_get_type (void);
83 G_DEFINE_DYNAMIC_TYPE (
89 online_accounts_get_backend_name (const gchar *goa_provider_type)
91 const gchar *eds_backend_name = NULL;
93 /* This is a mapping between GoaAccount provider types and
94 * ESourceCollection backend names. It requires knowledge
95 * of other registry modules, possibly even from 3rd party
96 * packages. No way around it. */
98 if (g_strcmp0 (goa_provider_type, "exchange") == 0)
99 eds_backend_name = "ews";
101 if (g_strcmp0 (goa_provider_type, "google") == 0)
102 eds_backend_name = "google";
104 else if (g_strcmp0 (goa_provider_type, "yahoo") == 0)
105 eds_backend_name = "yahoo";
107 return eds_backend_name;
110 static ESourceRegistryServer *
111 online_accounts_get_server (EOnlineAccounts *extension)
113 EExtensible *extensible;
115 extensible = e_extension_get_extensible (E_EXTENSION (extension));
117 return E_SOURCE_REGISTRY_SERVER (extensible);
121 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 = 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 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 online_accounts_new_source (EOnlineAccounts *extension)
154 ESourceRegistryServer *server;
157 GError *error = NULL;
159 /* This being a brand new data source, creating the instance
160 * should never fail but we'll check for errors just the same. */
161 server = online_accounts_get_server (extension);
162 file = e_server_side_source_new_user_file (NULL);
163 source = e_server_side_source_new (server, file, &error);
164 g_object_unref (file);
167 g_warn_if_fail (source == NULL);
168 g_warning ("%s: %s", G_STRFUNC, error->message);
169 g_error_free (error);
176 online_accounts_config_exchange (EOnlineAccounts *extension,
178 GoaObject *goa_object)
180 #ifdef HAVE_GOA_PASSWORD_BASED
181 ESourceExtension *source_extension;
182 const gchar *extension_name;
183 gchar *as_url = NULL;
184 gchar *oab_url = NULL;
186 GError *error = NULL;
188 if (goa_object_peek_exchange (goa_object) == NULL)
191 /* This should force the ESourceCamelEws type to be registered.
192 * It will also tell us if Evolution-EWS is even installed. */
193 class = g_type_class_ref (g_type_from_name ("EEwsBackend"));
195 g_type_class_unref (class);
198 "%s: Could not locate EEwsBackendClass. "
199 "Is Evolution-EWS installed?", G_STRFUNC);
203 /* XXX GNOME Online Accounts already runs autodiscover to test
204 * the user-entered values but doesn't share the discovered
205 * URLs. It only provides us a host name and expects us to
206 * re-run autodiscover for ourselves.
208 * So I've copied a slab of code from GOA which was in turn
209 * copied from Evolution-EWS which does the autodiscovery.
211 * I've already complained to Debarshi Ray about the lack
212 * of useful info in GOA's Exchange interface so hopefully
213 * it will someday publish discovered URLs and then we can
214 * remove this hack. */
216 goa_ews_autodiscover_sync (
217 goa_object, &as_url, &oab_url, NULL, &error);
220 g_warning ("%s: %s", G_STRFUNC, error->message);
221 g_error_free (error);
225 g_return_if_fail (as_url != NULL);
226 g_return_if_fail (oab_url != NULL);
228 /* XXX We don't have direct access to CamelEwsSettings from here
229 * since it's defined in Evolution-EWS. But we can find out
230 * its extension name and set properties by name. */
232 extension_name = e_source_camel_get_extension_name ("ews");
233 source_extension = e_source_get_extension (source, extension_name);
235 /* This will be NULL if Evolution-EWS is not installed. */
236 if (source_extension != NULL) {
244 "%s: Failed to create [%s] extension",
245 G_STRFUNC, extension_name);
250 #endif /* HAVE_GOA_PASSWORD_BASED */
254 online_accounts_config_oauth (EOnlineAccounts *extension,
256 GoaObject *goa_object)
258 ESourceExtension *source_extension;
259 const gchar *extension_name;
261 if (goa_object_peek_oauth_based (goa_object) == NULL)
264 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
265 source_extension = e_source_get_extension (source, extension_name);
267 e_source_authentication_set_method (
268 E_SOURCE_AUTHENTICATION (source_extension),
269 CAMEL_OAUTH_MECHANISM_NAME);
273 online_accounts_config_oauth2 (EOnlineAccounts *extension,
275 GoaObject *goa_object)
277 ESourceExtension *source_extension;
278 const gchar *extension_name;
280 if (goa_object_peek_oauth2_based (goa_object) == NULL)
283 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
284 source_extension = e_source_get_extension (source, extension_name);
286 e_source_authentication_set_method (
287 E_SOURCE_AUTHENTICATION (source_extension),
288 CAMEL_OAUTH2_MECHANISM_NAME);
292 online_accounts_config_password (EOnlineAccounts *extension,
294 GoaObject *goa_object)
296 #ifdef HAVE_GOA_PASSWORD_BASED
297 GoaAccount *goa_account;
298 GoaPasswordBased *goa_password_based;
299 EAsyncClosure *closure;
300 GAsyncResult *result;
304 gchar *password = NULL;
305 GError *error = NULL;
307 /* If the GNOME Online Account is password-based, we use its
308 * password to seed our own keyring entry for the collection
309 * source which avoids having to special-case authentication
310 * like we do for OAuth. Plus, if the stored password is no
311 * good we'll prompt for a new one instead of just giving up. */
313 goa_password_based = goa_object_get_password_based (goa_object);
315 if (goa_password_based == NULL)
318 closure = e_async_closure_new ();
320 /* XXX The GOA documentation doesn't explain the string
321 * argument in goa_password_based_get_password() so
322 * we'll pass in the identity and hope for the best. */
323 goa_account = goa_object_get_account (goa_object);
324 arg_id = goa_account_dup_identity (goa_account);
325 g_object_unref (goa_account);
327 goa_password_based_call_get_password (
328 goa_password_based, arg_id, NULL,
329 e_async_closure_callback, closure);
333 result = e_async_closure_wait (closure);
335 goa_password_based_call_get_password_finish (
336 goa_password_based, &password, result, &error);
339 g_warning ("%s: %s", G_STRFUNC, error->message);
340 g_error_free (error);
344 uid = e_source_get_uid (source);
345 display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
347 secret_password_store (
348 &schema, SECRET_COLLECTION_DEFAULT,
349 display_name, password, NULL,
350 e_async_closure_callback, closure,
351 KEYRING_ITEM_ATTRIBUTE_NAME, uid,
354 result = e_async_closure_wait (closure);
356 secret_password_store_finish (result, &error);
358 g_free (display_name);
360 /* If we fail to store the password, we'll just end up prompting
361 * for a password like normal. Annoying, maybe, but not the end
362 * of the world. Still leave a breadcrumb for debugging though. */
364 g_warning ("%s: %s", G_STRFUNC, error->message);
365 g_error_free (error);
369 e_async_closure_free (closure);
370 g_object_unref (goa_password_based);
371 #endif /* HAVE_GOA_PASSWORD_BASED */
375 online_accounts_config_collection (EOnlineAccounts *extension,
377 GoaObject *goa_object)
379 GoaAccount *goa_account;
380 ESourceExtension *source_extension;
381 const gchar *extension_name;
383 goa_account = goa_object_get_account (goa_object);
385 g_object_bind_property (
386 goa_account, "presentation-identity",
387 source, "display-name",
388 G_BINDING_SYNC_CREATE);
390 extension_name = E_SOURCE_EXTENSION_GOA;
391 source_extension = e_source_get_extension (source, extension_name);
393 g_object_bind_property (
395 source_extension, "account-id",
396 G_BINDING_SYNC_CREATE);
398 extension_name = E_SOURCE_EXTENSION_COLLECTION;
399 source_extension = e_source_get_extension (source, extension_name);
401 g_object_bind_property_full (
402 goa_account, "provider-type",
403 source_extension, "backend-name",
404 G_BINDING_SYNC_CREATE,
405 online_accounts_provider_type_to_backend_name,
407 NULL, (GDestroyNotify) NULL);
409 g_object_bind_property (
410 goa_account, "identity",
411 source_extension, "identity",
412 G_BINDING_SYNC_CREATE);
414 g_object_bind_property_full (
415 goa_object, "calendar",
416 source_extension, "calendar-enabled",
417 G_BINDING_SYNC_CREATE,
418 online_accounts_object_is_non_null,
420 NULL, (GDestroyNotify) NULL);
422 g_object_bind_property_full (
423 goa_object, "contacts",
424 source_extension, "contacts-enabled",
425 G_BINDING_SYNC_CREATE,
426 online_accounts_object_is_non_null,
428 NULL, (GDestroyNotify) NULL);
430 g_object_bind_property_full (
432 source_extension, "mail-enabled",
433 G_BINDING_SYNC_CREATE,
434 online_accounts_object_is_non_null,
436 NULL, (GDestroyNotify) NULL);
438 g_object_unref (goa_account);
440 /* Handle optional GOA interfaces. */
441 online_accounts_config_exchange (extension, source, goa_object);
442 online_accounts_config_password (extension, source, goa_object);
444 /* The data source should not be removable by clients. */
445 e_server_side_source_set_removable (
446 E_SERVER_SIDE_SOURCE (source), FALSE);
450 online_accounts_config_mail_account (EOnlineAccounts *extension,
452 GoaObject *goa_object)
454 EServerSideSource *server_side_source;
456 /* Only one or the other should be present, not both. */
457 online_accounts_config_oauth (extension, source, goa_object);
458 online_accounts_config_oauth2 (extension, source, goa_object);
460 /* XXX Need to defer the network security settings to the
461 * provider-specific module since "imap-use-tls" tells
462 * us neither the port number, nor whether to use IMAP
463 * over SSL versus STARTTLS. The module will know. */
465 /* Clients may change the source by may not remove it. */
466 server_side_source = E_SERVER_SIDE_SOURCE (source);
467 e_server_side_source_set_writable (server_side_source, TRUE);
468 e_server_side_source_set_removable (server_side_source, FALSE);
472 online_accounts_config_mail_identity (EOnlineAccounts *extension,
474 GoaObject *goa_object)
477 ESourceExtension *source_extension;
478 EServerSideSource *server_side_source;
479 const gchar *extension_name;
481 goa_mail = goa_object_get_mail (goa_object);
482 g_return_if_fail (goa_mail != NULL);
484 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
485 source_extension = e_source_get_extension (source, extension_name);
487 g_object_bind_property (
488 goa_mail, "email-address",
489 source_extension, "address",
490 G_BINDING_SYNC_CREATE);
492 g_object_unref (goa_mail);
494 /* Clients may change the source by may not remove it. */
495 server_side_source = E_SERVER_SIDE_SOURCE (source);
496 e_server_side_source_set_writable (server_side_source, TRUE);
497 e_server_side_source_set_removable (server_side_source, FALSE);
501 online_accounts_config_mail_transport (EOnlineAccounts *extension,
503 GoaObject *goa_object)
505 EServerSideSource *server_side_source;
507 /* Only one or the other should be present, not both. */
508 online_accounts_config_oauth (extension, source, goa_object);
509 online_accounts_config_oauth2 (extension, source, goa_object);
511 /* XXX Need to defer the network security settings to the
512 * provider-specific module since "smtp-use-tls" tells
513 * us neither the port number, nor whether to use SMTP
514 * over SSL versus STARTTLS. The module will know. */
516 /* Clients may change the source by may not remove it. */
517 server_side_source = E_SERVER_SIDE_SOURCE (source);
518 e_server_side_source_set_writable (server_side_source, TRUE);
519 e_server_side_source_set_removable (server_side_source, FALSE);
523 online_accounts_config_sources (EOnlineAccounts *extension,
525 GoaObject *goa_object)
527 ESourceRegistryServer *server;
528 ECollectionBackend *backend;
531 /* XXX This function was primarily intended to smooth the
532 * transition of mail accounts from XOAUTH to XOAUTH2,
533 * but it may be useful for other types of migration. */
535 online_accounts_config_collection (extension, source, goa_object);
537 server = online_accounts_get_server (extension);
538 backend = e_source_registry_server_ref_backend (server, source);
539 g_return_if_fail (backend != NULL);
541 list = e_collection_backend_list_mail_sources (backend);
543 for (link = list; link != NULL; link = g_list_next (link)) {
544 const gchar *extension_name;
546 source = E_SOURCE (link->data);
548 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
549 if (e_source_has_extension (source, extension_name))
550 online_accounts_config_mail_account (
551 extension, source, goa_object);
553 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
554 if (e_source_has_extension (source, extension_name))
555 online_accounts_config_mail_identity (
556 extension, source, goa_object);
558 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
559 if (e_source_has_extension (source, extension_name))
560 online_accounts_config_mail_transport (
561 extension, source, goa_object);
564 g_list_free_full (list, (GDestroyNotify) g_object_unref);
566 g_object_unref (backend);
570 online_accounts_create_collection (EOnlineAccounts *extension,
571 EBackendFactory *backend_factory,
572 GoaObject *goa_object)
574 GoaAccount *goa_account;
575 ESourceRegistryServer *server;
576 ESource *collection_source;
577 ESource *mail_account_source;
578 ESource *mail_identity_source;
579 ESource *mail_transport_source;
580 const gchar *account_id;
581 const gchar *parent_uid;
583 server = online_accounts_get_server (extension);
585 collection_source = online_accounts_new_source (extension);
586 g_return_if_fail (E_IS_SOURCE (collection_source));
588 mail_account_source = online_accounts_new_source (extension);
589 g_return_if_fail (E_IS_SOURCE (mail_account_source));
591 mail_identity_source = online_accounts_new_source (extension);
592 g_return_if_fail (E_IS_SOURCE (mail_identity_source));
594 mail_transport_source = online_accounts_new_source (extension);
595 g_return_if_fail (E_IS_SOURCE (mail_transport_source));
597 /* Configure parent/child relationships. */
598 parent_uid = e_source_get_uid (collection_source);
599 e_source_set_parent (mail_account_source, parent_uid);
600 e_source_set_parent (mail_identity_source, parent_uid);
601 e_source_set_parent (mail_transport_source, parent_uid);
603 /* Give the factory first crack at mail configuration. */
604 e_collection_backend_factory_prepare_mail (
605 E_COLLECTION_BACKEND_FACTORY (backend_factory),
607 mail_identity_source,
608 mail_transport_source);
610 /* Now it's our turn. */
611 online_accounts_config_collection (
612 extension, collection_source, goa_object);
613 online_accounts_config_mail_account (
614 extension, mail_account_source, goa_object);
615 online_accounts_config_mail_identity (
616 extension, mail_identity_source, goa_object);
617 online_accounts_config_mail_transport (
618 extension, mail_transport_source, goa_object);
620 /* Export the new source collection. */
621 e_source_registry_server_add_source (server, collection_source);
622 e_source_registry_server_add_source (server, mail_account_source);
623 e_source_registry_server_add_source (server, mail_identity_source);
624 e_source_registry_server_add_source (server, mail_transport_source);
626 goa_account = goa_object_get_account (goa_object);
627 account_id = goa_account_get_id (goa_account);
629 g_hash_table_insert (
630 extension->goa_to_eds,
631 g_strdup (account_id),
632 g_strdup (parent_uid));
634 g_object_unref (goa_account);
636 g_object_unref (collection_source);
637 g_object_unref (mail_account_source);
638 g_object_unref (mail_identity_source);
639 g_object_unref (mail_transport_source);
643 online_accounts_remove_collection (EOnlineAccounts *extension,
646 GError *error = NULL;
648 /* This removes the entire subtree rooted at source.
649 * Deletes the corresponding on-disk key files too. */
650 e_source_remove_sync (source, NULL, &error);
653 g_warning ("%s: %s", G_STRFUNC, error->message);
654 g_error_free (error);
659 online_accounts_account_added_cb (GoaClient *goa_client,
660 GoaObject *goa_object,
661 EOnlineAccounts *extension)
663 GoaAccount *goa_account;
664 ESourceRegistryServer *server;
665 EBackendFactory *backend_factory = NULL;
666 const gchar *provider_type;
667 const gchar *backend_name;
668 const gchar *account_id;
669 const gchar *source_uid;
671 server = online_accounts_get_server (extension);
673 goa_account = goa_object_get_account (goa_object);
674 provider_type = goa_account_get_provider_type (goa_account);
675 backend_name = online_accounts_get_backend_name (provider_type);
677 account_id = goa_account_get_id (goa_account);
678 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
680 if (source_uid == NULL && backend_name != NULL)
681 backend_factory = e_data_factory_ref_backend_factory (
682 E_DATA_FACTORY (server), backend_name);
684 if (backend_factory != NULL) {
685 online_accounts_create_collection (
686 extension, backend_factory, goa_object);
687 g_object_unref (backend_factory);
690 g_object_unref (goa_account);
694 online_accounts_account_removed_cb (GoaClient *goa_client,
695 GoaObject *goa_object,
696 EOnlineAccounts *extension)
698 ESource *source = NULL;
699 ESourceRegistryServer *server;
700 GoaAccount *goa_account;
701 const gchar *account_id;
702 const gchar *source_uid;
704 server = online_accounts_get_server (extension);
706 goa_account = goa_object_get_account (goa_object);
708 account_id = goa_account_get_id (goa_account);
709 source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
711 if (source_uid != NULL)
712 source = e_source_registry_server_ref_source (
715 if (source != NULL) {
716 online_accounts_remove_collection (extension, source);
717 g_object_unref (source);
720 g_object_unref (goa_account);
724 online_accounts_compare_id (GoaObject *goa_object,
725 const gchar *target_id)
727 GoaAccount *goa_account;
728 const gchar *account_id;
731 goa_account = goa_object_get_account (goa_object);
732 account_id = goa_account_get_id (goa_account);
733 result = g_strcmp0 (account_id, target_id);
734 g_object_unref (goa_account);
740 online_accounts_populate_accounts_table (EOnlineAccounts *extension,
743 ESourceRegistryServer *server;
744 GQueue trash = G_QUEUE_INIT;
746 const gchar *extension_name;
748 server = online_accounts_get_server (extension);
750 extension_name = E_SOURCE_EXTENSION_GOA;
751 list = e_source_registry_server_list_sources (server, extension_name);
753 for (link = list; link != NULL; link = g_list_next (link)) {
756 const gchar *account_id;
757 const gchar *source_uid;
760 source = E_SOURCE (link->data);
761 source_uid = e_source_get_uid (source);
763 extension_name = E_SOURCE_EXTENSION_GOA;
764 goa_ext = e_source_get_extension (source, extension_name);
765 account_id = e_source_goa_get_account_id (goa_ext);
767 if (account_id == NULL)
770 /* Verify the GOA account still exists. */
771 match = g_list_find_custom (
772 goa_objects, account_id,
773 (GCompareFunc) online_accounts_compare_id);
775 /* If a matching GoaObject was found, add its ID
776 * to our accounts hash table. Otherwise remove
777 * the ESource after we finish looping. */
779 GoaObject *goa_object;
781 g_hash_table_insert (
782 extension->goa_to_eds,
783 g_strdup (account_id),
784 g_strdup (source_uid));
786 goa_object = GOA_OBJECT (match->data);
787 online_accounts_config_sources (
788 extension, source, goa_object);
790 g_queue_push_tail (&trash, source);
794 /* Empty the trash. */
795 while (!g_queue_is_empty (&trash)) {
796 ESource *source = g_queue_pop_head (&trash);
797 online_accounts_remove_collection (extension, source);
800 g_list_free_full (list, (GDestroyNotify) g_object_unref);
804 online_accounts_create_client_cb (GObject *source_object,
805 GAsyncResult *result,
808 EOnlineAccounts *extension;
809 GoaClient *goa_client;
811 GError *error = NULL;
813 /* If we get back a G_IO_ERROR_CANCELLED then it means the
814 * EOnlineAccounts is already finalized, so be careful not
815 * to touch it until after we have a valid GoaClient. */
817 goa_client = goa_client_new_finish (result, &error);
820 g_warn_if_fail (goa_client == NULL);
822 "Unable to connect to the GNOME Online "
823 "Accounts service: %s", error->message);
824 g_error_free (error);
828 g_return_if_fail (GOA_IS_CLIENT (goa_client));
830 /* Should be safe to dereference the EOnlineAccounts now. */
832 extension = E_ONLINE_ACCOUNTS (user_data);
833 extension->goa_client = goa_client; /* takes ownership */
835 /* Don't need the GCancellable anymore. */
836 g_object_unref (extension->create_client);
837 extension->create_client = NULL;
839 list = goa_client_get_accounts (extension->goa_client);
841 /* This populates a hash table of GOA ID -> ESource UID strings by
842 * searching through available data sources for ones with a "GNOME
843 * Online Accounts" extension. If such an extension is found, but
844 * no corresponding GoaAccount (presumably meaning the GOA account
845 * was somehow deleted between E-D-S sessions) then the ESource in
846 * which the extension was found gets deleted. */
847 online_accounts_populate_accounts_table (extension, list);
849 for (link = list; link != NULL; link = g_list_next (link))
850 online_accounts_account_added_cb (
851 extension->goa_client,
852 GOA_OBJECT (link->data),
855 g_list_free_full (list, (GDestroyNotify) g_object_unref);
857 /* Listen for Online Account changes. */
859 extension->goa_client, "account-added",
860 G_CALLBACK (online_accounts_account_added_cb), extension);
862 extension->goa_client, "account-removed",
863 G_CALLBACK (online_accounts_account_removed_cb), extension);
867 online_accounts_bus_acquired_cb (EDBusServer *server,
868 GDBusConnection *connection,
869 EOnlineAccounts *extension)
871 /* Connect to the GNOME Online Accounts service. */
873 /* Note we don't reference the extension. If the
874 * extension gets destroyed before this completes
875 * we cancel the operation from dispose(). */
877 extension->create_client,
878 online_accounts_create_client_cb,
883 online_accounts_dispose (GObject *object)
885 EOnlineAccounts *extension;
887 extension = E_ONLINE_ACCOUNTS (object);
889 if (extension->goa_client != NULL) {
890 g_signal_handlers_disconnect_matched (
891 extension->goa_client,
893 0, 0, NULL, NULL, object);
894 g_object_unref (extension->goa_client);
895 extension->goa_client = NULL;
898 /* This cancels goa_client_new() in case it still
899 * hasn't completed. We're no longer interested. */
900 if (extension->create_client != NULL) {
901 g_cancellable_cancel (extension->create_client);
902 g_object_unref (extension->create_client);
903 extension->create_client = NULL;
906 /* Chain up to parent's dispose() method. */
907 G_OBJECT_CLASS (e_online_accounts_parent_class)->dispose (object);
911 online_accounts_finalize (GObject *object)
913 EOnlineAccounts *extension;
915 extension = E_ONLINE_ACCOUNTS (object);
917 g_hash_table_destroy (extension->goa_to_eds);
919 /* Chain up to parent's finalize() method. */
920 G_OBJECT_CLASS (e_online_accounts_parent_class)->finalize (object);
924 online_accounts_constructed (GObject *object)
926 EExtension *extension;
927 EExtensible *extensible;
929 extension = E_EXTENSION (object);
930 extensible = e_extension_get_extensible (extension);
932 /* Wait for the registry service to acquire its well-known
933 * bus name so we don't do anything destructive beforehand. */
936 extensible, "bus-acquired",
937 G_CALLBACK (online_accounts_bus_acquired_cb), extension);
939 /* Chain up to parent's constructed() method. */
940 G_OBJECT_CLASS (e_online_accounts_parent_class)->constructed (object);
944 e_online_accounts_class_init (EOnlineAccountsClass *class)
946 GObjectClass *object_class;
947 EExtensionClass *extension_class;
949 object_class = G_OBJECT_CLASS (class);
950 object_class->dispose = online_accounts_dispose;
951 object_class->finalize = online_accounts_finalize;
952 object_class->constructed = online_accounts_constructed;
954 extension_class = E_EXTENSION_CLASS (class);
955 extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
959 e_online_accounts_class_finalize (EOnlineAccountsClass *class)
964 e_online_accounts_init (EOnlineAccounts *extension)
966 /* Used to cancel unfinished goa_client_new(). */
967 extension->create_client = g_cancellable_new ();
969 extension->goa_to_eds = g_hash_table_new_full (
970 (GHashFunc) g_str_hash,
971 (GEqualFunc) g_str_equal,
972 (GDestroyNotify) g_free,
973 (GDestroyNotify) g_free);
977 e_module_load (GTypeModule *type_module)
979 e_online_accounts_register_type (type_module);
983 e_module_unload (GTypeModule *type_module)