goa: Populate imap/smtp details if using GOA >= 3.8.
[platform/upstream/evolution-data-server.git] / modules / gnome-online-accounts / module-gnome-online-accounts.c
1 /*
2  * module-gnome-online-accounts.c
3  *
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.
8  *
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.
13  *
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/>
16  *
17  */
18
19 /* XXX Yeah, yeah... */
20 #define GOA_API_IS_SUBJECT_TO_CHANGE
21
22 #include <config.h>
23 #include <goa/goa.h>
24 #include <glib/gi18n-lib.h>
25 #include <libsecret/secret.h>
26 #include <libsoup/soup.h>
27
28 #include <libebackend/libebackend.h>
29
30 #include "goaewsclient.h"
31
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))
38
39 #define CAMEL_IMAP_PROVIDER_NAME    "imapx"
40 #define CAMEL_SMTP_PROVIDER_NAME    "smtp"
41
42 #define CAMEL_OAUTH_MECHANISM_NAME  "XOAUTH"
43 #define CAMEL_OAUTH2_MECHANISM_NAME "XOAUTH2"
44
45 typedef struct _EGnomeOnlineAccounts EGnomeOnlineAccounts;
46 typedef struct _EGnomeOnlineAccountsClass EGnomeOnlineAccountsClass;
47
48 struct _EGnomeOnlineAccounts {
49         EExtension parent;
50
51         GoaClient *goa_client;
52         GCancellable *create_client;
53
54         /* GoaAccount ID -> ESource UID */
55         GHashTable *goa_to_eds;
56 };
57
58 struct _EGnomeOnlineAccountsClass {
59         EExtensionClass parent_class;
60 };
61
62 /* The keyring definintions are copied from e-authentication-session.c */
63
64 #define KEYRING_ITEM_ATTRIBUTE_NAME     "e-source-uid"
65 #define KEYRING_ITEM_DISPLAY_FORMAT     "Evolution Data Source %s"
66
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,
73         {
74                 { KEYRING_ITEM_ATTRIBUTE_NAME,
75                   SECRET_SCHEMA_ATTRIBUTE_STRING },
76                 { NULL, 0 }
77         }
78 };
79 #endif /* HAVE_GOA_PASSWORD_BASED */
80
81 /* Module Entry Points */
82 void e_module_load (GTypeModule *type_module);
83 void e_module_unload (GTypeModule *type_module);
84
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);
89
90 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
91         EGnomeOnlineAccounts,
92         e_gnome_online_accounts,
93         E_TYPE_EXTENSION,
94         0,
95         G_IMPLEMENT_INTERFACE_DYNAMIC (
96                 E_TYPE_OAUTH2_SUPPORT,
97                 e_gnome_online_accounts_oauth2_support_init))
98
99 static const gchar *
100 gnome_online_accounts_get_backend_name (const gchar *goa_provider_type)
101 {
102         const gchar *eds_backend_name = NULL;
103
104         g_return_val_if_fail (goa_provider_type != NULL, NULL);
105
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. */
110
111         if (g_str_equal (goa_provider_type, "exchange"))
112                 eds_backend_name = "ews";
113
114         if (g_str_equal (goa_provider_type, "google"))
115                 eds_backend_name = "google";
116
117         if (g_str_equal (goa_provider_type, "imap_smtp"))
118                 eds_backend_name = "none";
119
120         if (g_str_equal (goa_provider_type, "yahoo"))
121                 eds_backend_name = "yahoo";
122
123         if (g_str_equal (goa_provider_type, "owncloud"))
124                 eds_backend_name = "owncloud";
125
126         return eds_backend_name;
127 }
128
129 static ESourceRegistryServer *
130 gnome_online_accounts_get_server (EGnomeOnlineAccounts *extension)
131 {
132         EExtensible *extensible;
133
134         extensible = e_extension_get_extensible (E_EXTENSION (extension));
135
136         return E_SOURCE_REGISTRY_SERVER (extensible);
137 }
138
139 static gboolean
140 gnome_online_accounts_provider_type_to_backend_name (GBinding *binding,
141                                                      const GValue *source_value,
142                                                      GValue *target_value,
143                                                      gpointer unused)
144 {
145         const gchar *provider_type;
146         const gchar *backend_name;
147
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);
152
153         return TRUE;
154 }
155
156 static gboolean
157 gnome_online_accounts_object_is_non_null (GBinding *binding,
158                                           const GValue *source_value,
159                                           GValue *target_value,
160                                           gpointer unused)
161 {
162         gpointer v_object;
163
164         v_object = g_value_get_object (source_value);
165         g_value_set_boolean (target_value, v_object != NULL);
166
167         return TRUE;
168 }
169
170 static GoaObject *
171 gnome_online_accounts_ref_account (EGnomeOnlineAccounts *extension,
172                                    ESource *source)
173 {
174         ESourceRegistryServer *server;
175         GoaObject *match = NULL;
176         GList *list, *iter;
177         const gchar *extension_name;
178         gchar *account_id = NULL;
179
180         extension_name = E_SOURCE_EXTENSION_GOA;
181         server = gnome_online_accounts_get_server (extension);
182
183         source = e_source_registry_server_find_extension (
184                 server, source, extension_name);
185
186         if (source != NULL) {
187                 ESourceGoa *goa_ext;
188
189                 goa_ext = e_source_get_extension (source, extension_name);
190                 account_id = e_source_goa_dup_account_id (goa_ext);
191
192                 g_object_unref (source);
193         }
194
195         if (account_id == NULL)
196                 return NULL;
197
198         /* FIXME Use goa_client_lookup_by_id() once we require GOA 3.6. */
199
200         list = goa_client_get_accounts (extension->goa_client);
201
202         for (iter = list; iter != NULL; iter = g_list_next (iter)) {
203                 GoaObject *goa_object;
204                 GoaAccount *goa_account;
205                 const gchar *candidate_id;
206
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);
210
211                 if (g_strcmp0 (account_id, candidate_id) == 0)
212                         match = g_object_ref (goa_object);
213
214                 g_object_unref (goa_account);
215
216                 if (match != NULL)
217                         break;
218         }
219
220         g_list_free_full (list, (GDestroyNotify) g_object_unref);
221
222         g_free (account_id);
223
224         return match;
225 }
226
227 static ESource *
228 gnome_online_accounts_new_source (EGnomeOnlineAccounts *extension)
229 {
230         ESourceRegistryServer *server;
231         ESource *source;
232         GFile *file;
233         GError *error = NULL;
234
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);
241
242         if (error != NULL) {
243                 g_warn_if_fail (source == NULL);
244                 g_warning ("%s: %s", G_STRFUNC, error->message);
245                 g_error_free (error);
246         }
247
248         return source;
249 }
250
251 #ifdef HAVE_GOA_PASSWORD_BASED
252 static void
253 replace_host (gchar **url,
254               const gchar *host)
255 {
256         SoupURI *uri;
257
258         uri = soup_uri_new (*url);
259         if (!uri)
260                 return;
261
262         soup_uri_set_host (uri, host);
263
264         g_free (*url);
265         *url = soup_uri_to_string (uri, FALSE);
266
267         soup_uri_free (uri);
268 }
269 #endif /* HAVE_GOA_PASSWORD_BASED */
270
271 static void
272 gnome_online_accounts_config_exchange (EGnomeOnlineAccounts *extension,
273                                        ESource *source,
274                                        GoaObject *goa_object)
275 {
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;
282         gpointer class;
283         GError *error = NULL;
284
285         goa_exchange = goa_object_peek_exchange (goa_object);
286         if (goa_exchange == NULL)
287                 return;
288
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"));
292         if (class != NULL) {
293                 g_type_class_unref (class);
294         } else {
295                 g_critical (
296                         "%s: Could not locate EEwsBackendClass. "
297                         "Is Evolution-EWS installed?", G_STRFUNC);
298                 return;
299         }
300
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.
305          *
306          *     So I've copied a slab of code from GOA which was in turn
307          *     copied from Evolution-EWS which does the autodiscovery.
308          *
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. */
313
314         goa_ews_autodiscover_sync (
315                 goa_object, &as_url, &oab_url, NULL, &error);
316
317         if (error != NULL) {
318                 g_warning ("%s: %s", G_STRFUNC, error->message);
319                 g_error_free (error);
320                 return;
321         }
322
323         g_return_if_fail (as_url != NULL);
324         g_return_if_fail (oab_url != NULL);
325
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. */
329
330         extension_name = e_source_camel_get_extension_name ("ews");
331         source_extension = e_source_get_extension (source, extension_name);
332
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;
338
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);
343
344                 if (host && *host) {
345                         replace_host (&as_url, host);
346                         replace_host (&oab_url, host);
347                 }
348
349                 g_object_set (
350                         source_extension,
351                         "hosturl", as_url,
352                         "oaburl", oab_url,
353                         "email", email,
354                         NULL);
355
356                 settings = e_source_camel_get_settings (
357                         E_SOURCE_CAMEL (source_extension));
358
359                 g_object_set (
360                         settings,
361                         "host", host,
362                         "user", user,
363                         "email", email,
364                         NULL);
365
366                 g_free (host);
367                 g_free (user);
368                 g_free (email);
369         } else {
370                 g_critical (
371                         "%s: Failed to create [%s] extension",
372                         G_STRFUNC, extension_name);
373         }
374
375         g_free (as_url);
376         g_free (oab_url);
377 #endif /* HAVE_GOA_PASSWORD_BASED */
378 }
379
380 static void
381 gnome_online_accounts_config_imap (EGnomeOnlineAccounts *extension,
382                                    ESource *source,
383                                    GoaObject *goa_object)
384 {
385 #ifdef HAVE_GOA_IMAP_SMTP
386         GoaMail *goa_mail;
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;
393         gboolean use_ssl;
394         gboolean use_tls;
395         GError *error = NULL;
396
397         goa_mail = goa_object_peek_mail (goa_object);
398
399         if (goa_mail == NULL)
400                 return;
401
402         if (!goa_mail_get_imap_supported (goa_mail))
403                 return;
404
405         use_ssl = goa_mail_get_imap_use_ssl (goa_mail);
406         use_tls = goa_mail_get_imap_use_tls (goa_mail);
407
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);
412
413         /* Sanity check. */
414         g_return_if_fail (
415                 ((network_address != NULL) && (error == NULL)) ||
416                 ((network_address == NULL) && (error != NULL)));
417
418         if (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);
422                 return;
423         }
424
425         provider_name = CAMEL_IMAP_PROVIDER_NAME;
426
427         extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
428         backend_extension = e_source_get_extension (source, extension_name);
429
430         e_source_backend_set_backend_name (backend_extension, provider_name);
431
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);
435
436         camel_network_settings_set_host (
437                 CAMEL_NETWORK_SETTINGS (settings),
438                 g_network_address_get_hostname (
439                 G_NETWORK_ADDRESS (network_address)));
440
441         camel_network_settings_set_port (
442                 CAMEL_NETWORK_SETTINGS (settings),
443                 g_network_address_get_port (
444                 G_NETWORK_ADDRESS (network_address)));
445
446         camel_network_settings_set_user (
447                 CAMEL_NETWORK_SETTINGS (settings),
448                 goa_mail_get_imap_user_name (goa_mail));
449
450         /* Prefer "use_tls" over "use_ssl" if both are set. */
451         camel_network_settings_set_security_method (
452                 CAMEL_NETWORK_SETTINGS (settings),
453                 use_tls ?
454                 CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT :
455                 use_ssl ?
456                 CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT :
457                 CAMEL_NETWORK_SECURITY_METHOD_NONE);
458
459         g_object_unref (network_address);
460 #endif
461 }
462
463 static void
464 gnome_online_accounts_config_smtp (EGnomeOnlineAccounts *extension,
465                                    ESource *source,
466                                    GoaObject *goa_object)
467 {
468 #ifdef HAVE_GOA_IMAP_SMTP
469         GoaMail *goa_mail;
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;
476         gboolean use_ssl;
477         gboolean use_tls;
478         GError *error = NULL;
479
480         goa_mail = goa_object_peek_mail (goa_object);
481
482         if (goa_mail == NULL)
483                 return;
484
485         if (!goa_mail_get_smtp_supported (goa_mail))
486                 return;
487
488         use_ssl = goa_mail_get_smtp_use_ssl (goa_mail);
489         use_tls = goa_mail_get_smtp_use_tls (goa_mail);
490
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);
495
496         /* Sanity check. */
497         g_return_if_fail (
498                 ((network_address != NULL) && (error == NULL)) ||
499                 ((network_address == NULL) && (error != NULL)));
500
501         if (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);
505                 return;
506         }
507
508         provider_name = CAMEL_SMTP_PROVIDER_NAME;
509
510         extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
511         backend_extension = e_source_get_extension (source, extension_name);
512
513         e_source_backend_set_backend_name (backend_extension, provider_name);
514
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);
518
519         camel_network_settings_set_host (
520                 CAMEL_NETWORK_SETTINGS (settings),
521                 g_network_address_get_hostname (
522                 G_NETWORK_ADDRESS (network_address)));
523
524         camel_network_settings_set_port (
525                 CAMEL_NETWORK_SETTINGS (settings),
526                 g_network_address_get_port (
527                 G_NETWORK_ADDRESS (network_address)));
528
529         camel_network_settings_set_user (
530                 CAMEL_NETWORK_SETTINGS (settings),
531                 goa_mail_get_smtp_user_name (goa_mail));
532
533         /* Prefer "use_tls" over "use_ssl" if both are set. */
534         camel_network_settings_set_security_method (
535                 CAMEL_NETWORK_SETTINGS (settings),
536                 use_tls ?
537                 CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT :
538                 use_ssl ?
539                 CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT :
540                 CAMEL_NETWORK_SECURITY_METHOD_NONE);
541
542         g_object_unref (network_address);
543 #endif
544 }
545
546 static void
547 gnome_online_accounts_config_oauth (EGnomeOnlineAccounts *extension,
548                                     ESource *source,
549                                     GoaObject *goa_object)
550 {
551         ESourceExtension *source_extension;
552         const gchar *extension_name;
553
554         if (goa_object_peek_oauth_based (goa_object) == NULL)
555                 return;
556
557         extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
558         source_extension = e_source_get_extension (source, extension_name);
559
560         e_source_authentication_set_method (
561                 E_SOURCE_AUTHENTICATION (source_extension),
562                 CAMEL_OAUTH_MECHANISM_NAME);
563 }
564
565 static void
566 gnome_online_accounts_config_oauth2 (EGnomeOnlineAccounts *extension,
567                                      ESource *source,
568                                      GoaObject *goa_object)
569 {
570         ESourceExtension *source_extension;
571         const gchar *extension_name;
572
573         if (goa_object_peek_oauth2_based (goa_object) == NULL)
574                 return;
575
576         extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
577         source_extension = e_source_get_extension (source, extension_name);
578
579         e_source_authentication_set_method (
580                 E_SOURCE_AUTHENTICATION (source_extension),
581                 CAMEL_OAUTH2_MECHANISM_NAME);
582 }
583
584 static void
585 gnome_online_accounts_config_password (EGnomeOnlineAccounts *extension,
586                                        ESource *source,
587                                        GoaObject *goa_object)
588 {
589 #ifdef HAVE_GOA_PASSWORD_BASED
590         GoaAccount *goa_account;
591         GoaPasswordBased *goa_password_based;
592         EAsyncClosure *closure;
593         GAsyncResult *result;
594         const gchar *uid;
595         gchar *arg_id;
596         gchar *display_name;
597         gchar *password = NULL;
598         GError *error = NULL;
599
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. */
605
606         goa_password_based = goa_object_get_password_based (goa_object);
607
608         if (goa_password_based == NULL)
609                 return;
610
611         closure = e_async_closure_new ();
612
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);
619
620         goa_password_based_call_get_password (
621                 goa_password_based, arg_id, NULL,
622                 e_async_closure_callback, closure);
623
624         g_free (arg_id);
625
626         result = e_async_closure_wait (closure);
627
628         goa_password_based_call_get_password_finish (
629                 goa_password_based, &password, result, &error);
630
631         if (error != NULL) {
632                 g_warning ("%s: %s", G_STRFUNC, error->message);
633                 g_error_free (error);
634                 goto exit;
635         }
636
637         uid = e_source_get_uid (source);
638         display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
639
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,
645                 NULL);
646
647         result = e_async_closure_wait (closure);
648
649         secret_password_store_finish (result, &error);
650
651         g_free (display_name);
652         g_free (password);
653
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. */
657         if (error != NULL) {
658                 g_warning ("%s: %s", G_STRFUNC, error->message);
659                 g_error_free (error);
660         }
661
662 exit:
663         e_async_closure_free (closure);
664         g_object_unref (goa_password_based);
665 #endif /* HAVE_GOA_PASSWORD_BASED */
666 }
667
668 static void
669 gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension,
670                                          ESource *source,
671                                          GoaObject *goa_object)
672 {
673         GoaAccount *goa_account;
674         ESourceExtension *source_extension;
675         const gchar *extension_name;
676         const gchar *provider_type;
677         const gchar *backend_name;
678
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);
682
683         g_object_bind_property (
684                 goa_account, "presentation-identity",
685                 source, "display-name",
686                 G_BINDING_SYNC_CREATE);
687
688         extension_name = E_SOURCE_EXTENSION_GOA;
689         source_extension = e_source_get_extension (source, extension_name);
690
691         g_object_bind_property (
692                 goa_account, "id",
693                 source_extension, "account-id",
694                 G_BINDING_SYNC_CREATE);
695
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
699         */
700         if (g_strcmp0 (backend_name, "owncloud") == 0) {
701                 GoaCalendar *goa_calendar;
702                 GoaContacts *goa_contacts;
703
704                 goa_calendar = goa_object_get_calendar (goa_object);
705                 if (goa_calendar) {
706                         g_object_bind_property (
707                                 goa_calendar, "uri",
708                                 source_extension, "calendar-url",
709                                 G_BINDING_SYNC_CREATE);
710                         g_object_unref (goa_calendar);
711                 }
712
713                 goa_contacts = goa_object_get_contacts (goa_object);
714                 if (goa_contacts) {
715                         g_object_bind_property (
716                                 goa_contacts, "uri",
717                                 source_extension, "contacts-url",
718                                 G_BINDING_SYNC_CREATE);
719                         g_object_unref (goa_contacts);
720                 }
721         }
722
723         extension_name = E_SOURCE_EXTENSION_COLLECTION;
724         source_extension = e_source_get_extension (source, extension_name);
725
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,
731                 NULL,
732                 NULL, (GDestroyNotify) NULL);
733
734         g_object_bind_property (
735                 goa_account, "identity",
736                 source_extension, "identity",
737                 G_BINDING_SYNC_CREATE);
738
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,
744                 NULL,
745                 NULL, (GDestroyNotify) NULL);
746
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,
752                 NULL,
753                 NULL, (GDestroyNotify) NULL);
754
755         g_object_bind_property_full (
756                 goa_object, "mail",
757                 source_extension, "mail-enabled",
758                 G_BINDING_SYNC_CREATE,
759                 gnome_online_accounts_object_is_non_null,
760                 NULL,
761                 NULL, (GDestroyNotify) NULL);
762
763         g_object_unref (goa_account);
764
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);
768
769         /* The data source should not be removable by clients. */
770         e_server_side_source_set_removable (
771                 E_SERVER_SIDE_SOURCE (source), FALSE);
772
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));
781         }
782 }
783
784 static void
785 gnome_online_accounts_config_mail_account (EGnomeOnlineAccounts *extension,
786                                            ESource *source,
787                                            GoaObject *goa_object)
788 {
789         EServerSideSource *server_side_source;
790
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);
794
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.
799          *
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);
806
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);
811 }
812
813 static void
814 gnome_online_accounts_config_mail_identity (EGnomeOnlineAccounts *extension,
815                                             ESource *source,
816                                             GoaObject *goa_object)
817 {
818         GoaMail *goa_mail;
819         ESourceExtension *source_extension;
820         EServerSideSource *server_side_source;
821         const gchar *extension_name;
822
823         goa_mail = goa_object_get_mail (goa_object);
824         g_return_if_fail (goa_mail != NULL);
825
826         extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
827         source_extension = e_source_get_extension (source, extension_name);
828
829         g_object_bind_property (
830                 goa_mail, "email-address",
831                 source_extension, "address",
832                 G_BINDING_SYNC_CREATE);
833
834         g_object_unref (goa_mail);
835
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);
840 }
841
842 static void
843 gnome_online_accounts_config_mail_transport (EGnomeOnlineAccounts *extension,
844                                              ESource *source,
845                                              GoaObject *goa_object)
846 {
847         EServerSideSource *server_side_source;
848
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);
852
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.
857          *
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);
864
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);
869 }
870
871 static void
872 gnome_online_accounts_config_sources (EGnomeOnlineAccounts *extension,
873                                       ESource *source,
874                                       GoaObject *goa_object)
875 {
876         ESourceRegistryServer *server;
877         ECollectionBackend *backend;
878         GList *list, *link;
879
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. */
883
884         gnome_online_accounts_config_collection (extension, source, goa_object);
885
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);
889
890         list = e_collection_backend_list_mail_sources (backend);
891
892         for (link = list; link != NULL; link = g_list_next (link)) {
893                 const gchar *extension_name;
894
895                 source = E_SOURCE (link->data);
896
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);
901
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);
906
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);
911         }
912
913         g_list_free_full (list, (GDestroyNotify) g_object_unref);
914
915         g_object_unref (backend);
916 }
917
918 static void
919 gnome_online_accounts_create_collection (EGnomeOnlineAccounts *extension,
920                                          EBackendFactory *backend_factory,
921                                          GoaObject *goa_object)
922 {
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;
931
932         server = gnome_online_accounts_get_server (extension);
933
934         collection_source = gnome_online_accounts_new_source (extension);
935         g_return_if_fail (E_IS_SOURCE (collection_source));
936
937         gnome_online_accounts_config_collection (
938                 extension, collection_source, goa_object);
939         parent_uid = e_source_get_uid (collection_source);
940
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));
945
946                 mail_identity_source =
947                         gnome_online_accounts_new_source (extension);
948                 g_return_if_fail (E_IS_SOURCE (mail_identity_source));
949
950                 mail_transport_source =
951                         gnome_online_accounts_new_source (extension);
952                 g_return_if_fail (E_IS_SOURCE (mail_transport_source));
953
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);
958
959                 /* Give the factory first crack at mail configuration. */
960                 e_collection_backend_factory_prepare_mail (
961                         E_COLLECTION_BACKEND_FACTORY (backend_factory),
962                         mail_account_source,
963                         mail_identity_source,
964                         mail_transport_source);
965
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);
972         }
973
974         /* Export the new source collection. */
975         e_source_registry_server_add_source (server, collection_source);
976
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);
981         }
982
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);
987         }
988
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);
993         }
994
995         goa_account = goa_object_get_account (goa_object);
996         account_id = goa_account_get_id (goa_account);
997
998         g_hash_table_insert (
999                 extension->goa_to_eds,
1000                 g_strdup (account_id),
1001                 g_strdup (parent_uid));
1002
1003         g_object_unref (goa_account);
1004         g_object_unref (collection_source);
1005 }
1006
1007 static void
1008 gnome_online_accounts_remove_collection (EGnomeOnlineAccounts *extension,
1009                                          ESource *source)
1010 {
1011         GError *error = NULL;
1012
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);
1016
1017         if (error != NULL) {
1018                 g_warning ("%s: %s", G_STRFUNC, error->message);
1019                 g_error_free (error);
1020         }
1021 }
1022
1023 static void
1024 gnome_online_accounts_account_added_cb (GoaClient *goa_client,
1025                                         GoaObject *goa_object,
1026                                         EGnomeOnlineAccounts *extension)
1027 {
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;
1035
1036         server = gnome_online_accounts_get_server (extension);
1037
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);
1041
1042         account_id = goa_account_get_id (goa_account);
1043         source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
1044
1045         if (source_uid == NULL && backend_name != NULL)
1046                 backend_factory = e_data_factory_ref_backend_factory (
1047                         E_DATA_FACTORY (server), backend_name);
1048
1049         if (backend_factory != NULL) {
1050                 gnome_online_accounts_create_collection (
1051                         extension, backend_factory, goa_object);
1052                 g_object_unref (backend_factory);
1053         }
1054
1055         g_object_unref (goa_account);
1056 }
1057
1058 static void
1059 gnome_online_accounts_account_removed_cb (GoaClient *goa_client,
1060                                           GoaObject *goa_object,
1061                                           EGnomeOnlineAccounts *extension)
1062 {
1063         ESource *source = NULL;
1064         ESourceRegistryServer *server;
1065         GoaAccount *goa_account;
1066         const gchar *account_id;
1067         const gchar *source_uid;
1068
1069         server = gnome_online_accounts_get_server (extension);
1070
1071         goa_account = goa_object_get_account (goa_object);
1072
1073         account_id = goa_account_get_id (goa_account);
1074         source_uid = g_hash_table_lookup (extension->goa_to_eds, account_id);
1075
1076         if (source_uid != NULL)
1077                 source = e_source_registry_server_ref_source (
1078                         server, source_uid);
1079
1080         if (source != NULL) {
1081                 gnome_online_accounts_remove_collection (extension, source);
1082                 g_object_unref (source);
1083         }
1084
1085         g_object_unref (goa_account);
1086 }
1087
1088 static gint
1089 gnome_online_accounts_compare_id (GoaObject *goa_object,
1090                                   const gchar *target_id)
1091 {
1092         GoaAccount *goa_account;
1093         const gchar *account_id;
1094         gint result;
1095
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);
1100
1101         return result;
1102 }
1103
1104 static void
1105 gnome_online_accounts_populate_accounts_table (EGnomeOnlineAccounts *extension,
1106                                                GList *goa_objects)
1107 {
1108         ESourceRegistryServer *server;
1109         GQueue trash = G_QUEUE_INIT;
1110         GList *list, *link;
1111         const gchar *extension_name;
1112
1113         server = gnome_online_accounts_get_server (extension);
1114
1115         extension_name = E_SOURCE_EXTENSION_GOA;
1116         list = e_source_registry_server_list_sources (server, extension_name);
1117
1118         for (link = list; link != NULL; link = g_list_next (link)) {
1119                 ESource *source;
1120                 ESourceGoa *goa_ext;
1121                 const gchar *account_id;
1122                 const gchar *source_uid;
1123                 GList *match;
1124
1125                 source = E_SOURCE (link->data);
1126                 source_uid = e_source_get_uid (source);
1127
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);
1131
1132                 if (account_id == NULL)
1133                         continue;
1134
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);
1139
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;
1145
1146                         g_hash_table_insert (
1147                                 extension->goa_to_eds,
1148                                 g_strdup (account_id),
1149                                 g_strdup (source_uid));
1150
1151                         goa_object = GOA_OBJECT (match->data);
1152                         gnome_online_accounts_config_sources (
1153                                 extension, source, goa_object);
1154                 } else {
1155                         g_queue_push_tail (&trash, source);
1156                 }
1157         }
1158
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);
1163         }
1164
1165         g_list_free_full (list, (GDestroyNotify) g_object_unref);
1166 }
1167
1168 static void
1169 gnome_online_accounts_create_client_cb (GObject *source_object,
1170                                         GAsyncResult *result,
1171                                         gpointer user_data)
1172 {
1173         EGnomeOnlineAccounts *extension;
1174         GoaClient *goa_client;
1175         GList *list, *link;
1176         GError *error = NULL;
1177
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. */
1181
1182         goa_client = goa_client_new_finish (result, &error);
1183
1184         if (error != NULL) {
1185                 g_warn_if_fail (goa_client == NULL);
1186                 g_warning (
1187                         "Unable to connect to the GNOME Online "
1188                         "Accounts service: %s", error->message);
1189                 g_error_free (error);
1190                 return;
1191         }
1192
1193         g_return_if_fail (GOA_IS_CLIENT (goa_client));
1194
1195         /* Should be safe to dereference the EGnomeOnlineAccounts now. */
1196
1197         extension = E_GNOME_ONLINE_ACCOUNTS (user_data);
1198         extension->goa_client = goa_client;  /* takes ownership */
1199
1200         /* Don't need the GCancellable anymore. */
1201         g_object_unref (extension->create_client);
1202         extension->create_client = NULL;
1203
1204         list = goa_client_get_accounts (extension->goa_client);
1205
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);
1213
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),
1218                         extension);
1219
1220         g_list_free_full (list, (GDestroyNotify) g_object_unref);
1221
1222         /* Listen for Online Account changes. */
1223         g_signal_connect (
1224                 extension->goa_client, "account-added",
1225                 G_CALLBACK (gnome_online_accounts_account_added_cb),
1226                 extension);
1227         g_signal_connect (
1228                 extension->goa_client, "account-removed",
1229                 G_CALLBACK (gnome_online_accounts_account_removed_cb),
1230                 extension);
1231 }
1232
1233 static void
1234 gnome_online_accounts_bus_acquired_cb (EDBusServer *server,
1235                                        GDBusConnection *connection,
1236                                        EGnomeOnlineAccounts *extension)
1237 {
1238         /* Connect to the GNOME Online Accounts service. */
1239
1240         /* Note we don't reference the extension.  If the
1241          * extension gets destroyed before this completes
1242          * we cancel the operation from dispose(). */
1243         goa_client_new (
1244                 extension->create_client,
1245                 gnome_online_accounts_create_client_cb,
1246                 extension);
1247 }
1248
1249 static void
1250 gnome_online_accounts_dispose (GObject *object)
1251 {
1252         EGnomeOnlineAccounts *extension;
1253
1254         extension = E_GNOME_ONLINE_ACCOUNTS (object);
1255
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;
1263         }
1264
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;
1271         }
1272
1273         /* Chain up to parent's dispose() method. */
1274         G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1275                 dispose (object);
1276 }
1277
1278 static void
1279 gnome_online_accounts_finalize (GObject *object)
1280 {
1281         EGnomeOnlineAccounts *extension;
1282
1283         extension = E_GNOME_ONLINE_ACCOUNTS (object);
1284
1285         g_hash_table_destroy (extension->goa_to_eds);
1286
1287         /* Chain up to parent's finalize() method. */
1288         G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1289                 finalize (object);
1290 }
1291
1292 static void
1293 gnome_online_accounts_constructed (GObject *object)
1294 {
1295         EExtension *extension;
1296         EExtensible *extensible;
1297
1298         extension = E_EXTENSION (object);
1299         extensible = e_extension_get_extensible (extension);
1300
1301         /* Wait for the registry service to acquire its well-known
1302          * bus name so we don't do anything destructive beforehand. */
1303
1304         g_signal_connect (
1305                 extensible, "bus-acquired",
1306                 G_CALLBACK (gnome_online_accounts_bus_acquired_cb),
1307                 extension);
1308
1309         /* Chain up to parent's constructed() method. */
1310         G_OBJECT_CLASS (e_gnome_online_accounts_parent_class)->
1311                 constructed (object);
1312 }
1313
1314 static gboolean
1315 gnome_online_accounts_get_access_token_sync (EOAuth2Support *support,
1316                                              ESource *source,
1317                                              GCancellable *cancellable,
1318                                              gchar **out_access_token,
1319                                              gint *out_expires_in,
1320                                              GError **error)
1321 {
1322         GoaObject *goa_object;
1323         GoaAccount *goa_account;
1324         GoaOAuth2Based *goa_oauth2_based;
1325         gboolean success;
1326
1327         goa_object = gnome_online_accounts_ref_account (
1328                 E_GNOME_ONLINE_ACCOUNTS (support), source);
1329
1330         if (goa_object == NULL) {
1331                 g_set_error (
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));
1337                 return FALSE;
1338         }
1339
1340         goa_account = goa_object_get_account (goa_object);
1341         g_return_val_if_fail (goa_account != NULL, FALSE);
1342
1343         goa_oauth2_based = goa_object_get_oauth2_based (goa_object);
1344         g_return_val_if_fail (goa_oauth2_based != NULL, FALSE);
1345
1346         success = goa_account_call_ensure_credentials_sync (
1347                 goa_account, NULL, cancellable, error);
1348
1349         if (success)
1350                 success = goa_oauth2_based_call_get_access_token_sync (
1351                         goa_oauth2_based, out_access_token,
1352                         out_expires_in, cancellable, error);
1353
1354         g_object_unref (goa_oauth2_based);
1355         g_object_unref (goa_account);
1356         g_object_unref (goa_object);
1357
1358         g_prefix_error (
1359                 error,
1360                 _("Failed to obtain an access token for '%s': "),
1361                 e_source_get_display_name (source));
1362
1363         return success;
1364 }
1365
1366 static void
1367 e_gnome_online_accounts_class_init (EGnomeOnlineAccountsClass *class)
1368 {
1369         GObjectClass *object_class;
1370         EExtensionClass *extension_class;
1371
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;
1376
1377         extension_class = E_EXTENSION_CLASS (class);
1378         extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER;
1379 }
1380
1381 static void
1382 e_gnome_online_accounts_class_finalize (EGnomeOnlineAccountsClass *class)
1383 {
1384 }
1385
1386 static void
1387 e_gnome_online_accounts_oauth2_support_init (EOAuth2SupportInterface *interface)
1388 {
1389         interface->get_access_token_sync =
1390                 gnome_online_accounts_get_access_token_sync;
1391 }
1392
1393 static void
1394 e_gnome_online_accounts_init (EGnomeOnlineAccounts *extension)
1395 {
1396         /* Used to cancel unfinished goa_client_new(). */
1397         extension->create_client = g_cancellable_new ();
1398
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);
1404 }
1405
1406 G_MODULE_EXPORT void
1407 e_module_load (GTypeModule *type_module)
1408 {
1409         e_gnome_online_accounts_register_type (type_module);
1410 }
1411
1412 G_MODULE_EXPORT void
1413 e_module_unload (GTypeModule *type_module)
1414 {
1415 }
1416