[evolution-ews] Bug 775690 - Support OAuth2 for outlook.office365.com (initial changes)



commit 6b7aff3563ca857546e879a18ecc5f8535d8e87b
Author: Milan Crha <mcrha redhat com>
Date:   Mon Feb 5 21:31:02 2018 +0100

    Bug 775690 - Support OAuth2 for outlook.office365.com (initial changes)
    
    The code is work in progress, only for testing at the moment,
    thus also disabled by default, but can be enabled in runtime with
      export EWS_WITH_OAUTH2=1
    then the OAuth2 is offered for outlook.office365.com account,
    where at least the Tenant and Application ID should be filled.

 CMakeLists.txt                                     |   22 +
 config.h.in                                        |    9 +
 po/POTFILES.in                                     |    3 +
 src/addressbook/e-book-backend-ews-factory.c       |    2 +
 src/addressbook/e-book-backend-ews.c               |    6 +-
 src/calendar/e-cal-backend-ews-factory.c           |    2 +
 src/calendar/e-cal-backend-ews.c                   |    2 +-
 src/camel/camel-ews-provider.c                     |    4 +
 src/camel/camel-ews-store.c                        |   22 +-
 src/camel/camel-ews-transport.c                    |    5 +-
 src/camel/camel-ews-utils.c                        |   46 ++
 src/camel/camel-ews-utils.h                        |    3 +
 src/collection/e-ews-backend.c                     |    2 +-
 src/collection/module-ews-backend.c                |    2 +
 src/configuration/e-ews-config-lookup.c            |    4 +-
 src/configuration/e-ews-config-utils.c             |    4 +-
 src/configuration/e-mail-config-ews-autodiscover.c |    4 +-
 src/configuration/e-mail-config-ews-backend.c      |  189 +++++++-
 src/configuration/module-ews-configuration.c       |    4 +
 src/server/CMakeLists.txt                          |    4 +
 src/server/camel-ews-settings.c                    |  278 ++++++++++-
 src/server/camel-ews-settings.h                    |   29 +-
 src/server/camel-sasl-xoauth2-office365.c          |   55 ++
 src/server/camel-sasl-xoauth2-office365.h          |   63 +++
 src/server/e-ews-connection-utils.c                |  354 +++++++++++++
 src/server/e-ews-connection-utils.h                |   19 +
 src/server/e-ews-connection.c                      |  546 ++++++++++----------
 src/server/e-ews-connection.h                      |   19 +-
 src/server/e-ews-notification.c                    |   86 +---
 src/server/e-oauth2-service-office365.c            |  381 ++++++++++++++
 src/server/e-oauth2-service-office365.h            |   64 +++
 tests/ews-test-common.c                            |    4 +-
 32 files changed, 1873 insertions(+), 364 deletions(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5b607d3..3efecee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -174,6 +174,28 @@ if(ENABLE_TESTS)
 endif(ENABLE_TESTS)
 
 # ******************************
+# Office365 OAuth2 support
+# ******************************
+
+add_printable_variable(WITH_OFFICE365_TENANT "Office365.com OAuth 2.0 tenant" "")
+
+if(WITH_OFFICE365_TENANT STREQUAL "")
+       set(WITH_OFFICE365_TENANT "")
+endif(WITH_OFFICE365_TENANT STREQUAL "")
+
+add_printable_variable(WITH_OFFICE365_CLIENT_ID "Office365.com OAuth 2.0 client ID" "")
+
+if(WITH_OFFICE365_CLIENT_ID STREQUAL "")
+       set(WITH_OFFICE365_CLIENT_ID "")
+endif(WITH_OFFICE365_CLIENT_ID STREQUAL "")
+
+add_printable_variable(WITH_OFFICE365_REDIRECT_URI "Office365.com OAuth 2.0 redirect URI" "")
+
+if(WITH_OFFICE365_REDIRECT_URI STREQUAL "")
+       set(WITH_OFFICE365_REDIRECT_URI "https://login.microsoftonline.com/common/oauth2/v2.0/nativeclient";)
+endif(WITH_OFFICE365_REDIRECT_URI STREQUAL "")
+
+# ******************************
 # Special directories
 # ******************************
 
diff --git a/config.h.in b/config.h.in
index 20f57d9..ad3f60c 100644
--- a/config.h.in
+++ b/config.h.in
@@ -20,3 +20,12 @@
 
 /* libmspack has OAB support */
 #cmakedefine WITH_MSPACK 1
+
+/* Define Office365 OAuth 2.0 default Tenant to use */
+#define OFFICE365_TENANT "@WITH_OFFICE365_TENANT@"
+
+/* Define Office365 OAuth 2.0 default Client ID to use */
+#define OFFICE365_CLIENT_ID "@WITH_OFFICE365_CLIENT_ID@"
+
+/* Define Office365 OAuth 2.0 default Redirect URI to use */
+#define OFFICE365_REDIRECT_URI "@WITH_OFFICE365_REDIRECT_URI@"
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a4271db..75b52d4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -24,6 +24,9 @@ src/configuration/e-mail-config-ews-folder-sizes-page.c
 src/configuration/e-mail-config-ews-gal.c
 src/configuration/e-mail-config-ews-ooo-page.c
 src/configuration/module-ews-configuration.error.xml
+src/server/camel-sasl-xoauth2-office365.c
 src/server/e-ews-camel-common.c
 src/server/e-ews-connection.c
+src/server/e-ews-connection-utils.c
 src/server/e-ews-folder.c
+src/server/e-oauth2-service-office365.c
diff --git a/src/addressbook/e-book-backend-ews-factory.c b/src/addressbook/e-book-backend-ews-factory.c
index d91a7da..1710085 100644
--- a/src/addressbook/e-book-backend-ews-factory.c
+++ b/src/addressbook/e-book-backend-ews-factory.c
@@ -27,6 +27,7 @@
 
 #include <libedata-book/libedata-book.h>
 
+#include "server/e-oauth2-service-office365.h"
 #include "server/e-source-ews-folder.h"
 
 #include "e-book-backend-ews.h"
@@ -80,6 +81,7 @@ e_module_load (GTypeModule *type_module)
        e_module = E_MODULE (type_module);
 
        e_source_ews_folder_type_register (type_module);
+       e_oauth2_service_office365_type_register (type_module);
 
        e_book_backend_ews_factory_register_type (type_module);
 }
diff --git a/src/addressbook/e-book-backend-ews.c b/src/addressbook/e-book-backend-ews.c
index 12fc11e..190b2b7 100644
--- a/src/addressbook/e-book-backend-ews.c
+++ b/src/addressbook/e-book-backend-ews.c
@@ -1887,7 +1887,7 @@ ebb_ews_download_gal_file (EBookBackendEws *bbews,
        cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews));
        download_path = g_build_filename (cache_dir, full->filename, NULL);
 
-       oab_cnc = e_ews_connection_new (full_url, ews_settings);
+       oab_cnc = e_ews_connection_new (e_backend_get_source (E_BACKEND (bbews)), full_url, ews_settings);
 
        e_binding_bind_property (
                bbews, "proxy-resolver",
@@ -2732,7 +2732,7 @@ ebb_ews_connect_sync (EBookMetaBackend *meta_backend,
        ews_settings = ebb_ews_get_collection_settings (bbews);
        hosturl = camel_ews_settings_dup_hosturl (ews_settings);
 
-       bbews->priv->cnc = e_ews_connection_new (hosturl, ews_settings);
+       bbews->priv->cnc = e_ews_connection_new (e_backend_get_source (E_BACKEND (bbews)), hosturl, 
ews_settings);
 
        e_binding_bind_property (
                bbews, "proxy-resolver",
@@ -2856,7 +2856,7 @@ ebb_ews_get_changes_sync (EBookMetaBackend *meta_backend,
                        if (sequence == -1)
                                sequence = 0;
 
-                       oab_cnc = e_ews_connection_new (oab_url, ews_settings);
+                       oab_cnc = e_ews_connection_new (e_backend_get_source (E_BACKEND (bbews)), oab_url, 
ews_settings);
 
                        e_binding_bind_property (
                                bbews, "proxy-resolver",
diff --git a/src/calendar/e-cal-backend-ews-factory.c b/src/calendar/e-cal-backend-ews-factory.c
index 2f4b979..7f176fd 100644
--- a/src/calendar/e-cal-backend-ews-factory.c
+++ b/src/calendar/e-cal-backend-ews-factory.c
@@ -12,6 +12,7 @@
 
 #include <libedata-cal/libedata-cal.h>
 
+#include "server/e-oauth2-service-office365.h"
 #include "server/e-source-ews-folder.h"
 
 #include "e-cal-backend-ews.h"
@@ -134,6 +135,7 @@ e_module_load (GTypeModule *type_module)
        e_module = E_MODULE (type_module);
 
        e_source_ews_folder_type_register (type_module);
+       e_oauth2_service_office365_type_register (type_module);
 
        e_cal_backend_ews_events_factory_register_type (type_module);
        e_cal_backend_ews_journal_factory_register_type (type_module);
diff --git a/src/calendar/e-cal-backend-ews.c b/src/calendar/e-cal-backend-ews.c
index d71cf97..b42f5fd 100644
--- a/src/calendar/e-cal-backend-ews.c
+++ b/src/calendar/e-cal-backend-ews.c
@@ -1365,7 +1365,7 @@ ecb_ews_connect_sync (ECalMetaBackend *meta_backend,
        ews_settings = ecb_ews_get_collection_settings (cbews);
        hosturl = camel_ews_settings_dup_hosturl (ews_settings);
 
-       cbews->priv->cnc = e_ews_connection_new (hosturl, ews_settings);
+       cbews->priv->cnc = e_ews_connection_new (e_backend_get_source (E_BACKEND (cbews)), hosturl, 
ews_settings);
 
        e_binding_bind_property (
                cbews, "proxy-resolver",
diff --git a/src/camel/camel-ews-provider.c b/src/camel/camel-ews-provider.c
index 3b47b89..56878dc 100644
--- a/src/camel/camel-ews-provider.c
+++ b/src/camel/camel-ews-provider.c
@@ -31,6 +31,8 @@
 #include <glib/gi18n-lib.h>
 #include <gmodule.h>
 
+#include "server/camel-sasl-xoauth2-office365.h"
+
 #include "camel-ews-store.h"
 #include "camel-ews-transport.h"
 
@@ -139,6 +141,8 @@ camel_provider_module_init (void)
        bindtextdomain (GETTEXT_PACKAGE, EXCHANGE_EWS_LOCALEDIR);
        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
+       g_type_ensure (CAMEL_TYPE_SASL_XOAUTH2_OFFICE365);
+
        camel_provider_register (&ews_provider);
 }
 
diff --git a/src/camel/camel-ews-store.c b/src/camel/camel-ews-store.c
index 66cd7cf..d309277 100644
--- a/src/camel/camel-ews-store.c
+++ b/src/camel/camel-ews-store.c
@@ -1819,6 +1819,7 @@ ews_authenticate_sync (CamelService *service,
        CamelSettings *settings;
        CamelEwsSettings *ews_settings;
        EEwsConnection *connection;
+       ESource *source;
        GSList *folders_created = NULL;
        GSList *folders_updated = NULL;
        GSList *folders_deleted = NULL;
@@ -1839,10 +1840,12 @@ ews_authenticate_sync (CamelService *service,
 
        ews_settings = CAMEL_EWS_SETTINGS (settings);
        hosturl = camel_ews_settings_dup_hosturl (ews_settings);
+       source = camel_ews_utils_ref_corresponding_source (service, cancellable);
 
-       connection = e_ews_connection_new (hosturl, ews_settings);
+       connection = e_ews_connection_new (source, hosturl, ews_settings);
        e_ews_connection_set_password (connection, password);
 
+       g_clear_object (&source);
        g_free (hosturl);
 
        g_object_unref (settings);
@@ -1976,6 +1979,7 @@ ews_store_query_auth_types_sync (CamelService *service,
                                  GError **error)
 {
        EEwsConnection *connection;
+       ESource *source;
        CamelSettings *settings;
        CamelEwsSettings *ews_settings;
        GList *auth_types = NULL;
@@ -1987,7 +1991,11 @@ ews_store_query_auth_types_sync (CamelService *service,
        settings = camel_service_ref_settings (service);
        ews_settings = CAMEL_EWS_SETTINGS (settings);
        hosturl = camel_ews_settings_dup_hosturl (ews_settings);
-       connection = e_ews_connection_new_full (hosturl, ews_settings, FALSE);
+       source = camel_ews_utils_ref_corresponding_source (service, cancellable);
+
+       connection = e_ews_connection_new_full (source, hosturl, ews_settings, FALSE);
+
+       g_clear_object (&source);
        g_free (hosturl);
        g_object_unref (settings);
 
@@ -2016,6 +2024,16 @@ ews_store_query_auth_types_sync (CamelService *service,
                                auth = "PLAIN";
                        else if (g_ascii_strcasecmp (auth, "Negotiate") == 0)
                                auth = "GSSAPI";
+                       else if (e_oauth2_services_is_supported () &&
+                                g_ascii_strcasecmp (auth, "Bearer") == 0) {
+                               /* Use Camel name for OAuth2. It's up to the caller to decide whether
+                                  it can be used or not. */
+                               authtype = camel_sasl_authtype ("XOAUTH2");
+                               if (authtype)
+                                       auth_types = g_list_prepend (auth_types, authtype);
+
+                               continue;
+                       }
 
                        for (siter = provider->authtypes; siter; siter = siter->next) {
                                authtype = siter->data;
diff --git a/src/camel/camel-ews-transport.c b/src/camel/camel-ews-transport.c
index 05f396e..af56912 100644
--- a/src/camel/camel-ews-transport.c
+++ b/src/camel/camel-ews-transport.c
@@ -221,6 +221,7 @@ ews_transport_authenticate_sync (CamelService *service,
        CamelSettings *settings;
        CamelEwsSettings *ews_settings;
        EEwsConnection *connection;
+       ESource *source;
        const gchar *password;
        gchar *hosturl, *new_sync_state = NULL;
        GSList *folders_created = NULL;
@@ -237,10 +238,12 @@ ews_transport_authenticate_sync (CamelService *service,
 
        ews_settings = CAMEL_EWS_SETTINGS (settings);
        hosturl = camel_ews_settings_dup_hosturl (ews_settings);
+       source = camel_ews_utils_ref_corresponding_source (service, cancellable);
 
-       connection = e_ews_connection_new (hosturl, ews_settings);
+       connection = e_ews_connection_new (source, hosturl, ews_settings);
        e_ews_connection_set_password (connection, password);
 
+       g_clear_object (&source);
        g_free (hosturl);
 
        g_object_unref (settings);
diff --git a/src/camel/camel-ews-utils.c b/src/camel/camel-ews-utils.c
index 617b49f..f0f7a3f 100644
--- a/src/camel/camel-ews-utils.c
+++ b/src/camel/camel-ews-utils.c
@@ -28,6 +28,8 @@
 #include <glib/gi18n-lib.h>
 #include <glib/gstdio.h>
 
+#include <libemail-engine/libemail-engine.h>
+
 #include "server/camel-ews-settings.h"
 #include "server/e-ews-camel-common.h"
 #include "server/e-ews-item-change.h"
@@ -1205,3 +1207,47 @@ camel_ews_utils_delete_folders_from_summary_recursive (CamelEwsStore *ews_store,
 
        return success;
 }
+
+/* Unref with g_object_unref() when done with it */
+ESource *
+camel_ews_utils_ref_corresponding_source (CamelService *service,
+                                         GCancellable *cancellable)
+{
+       ESourceRegistry *registry = NULL;
+       CamelSession *session;
+       ESource *source = NULL;
+
+       g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
+
+       session = camel_service_ref_session (service);
+       if (E_IS_MAIL_SESSION (session)) {
+               registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
+               if (registry)
+                       g_object_ref (registry);
+       }
+
+       g_clear_object (&session);
+
+       if (!registry)
+               registry = e_source_registry_new_sync (cancellable, NULL);
+
+       if (registry) {
+               source = e_source_registry_ref_source (registry, camel_service_get_uid (service));
+
+               while (source && e_source_get_parent (source) &&
+                      !e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
+                       ESource *parent;
+
+                       parent = e_source_registry_ref_source (registry, e_source_get_parent (source));
+                       if (!parent)
+                               break;
+
+                       g_clear_object (&source);
+                       source = parent;
+               }
+       }
+
+       g_clear_object (&registry);
+
+       return source;
+}
diff --git a/src/camel/camel-ews-utils.h b/src/camel/camel-ews-utils.h
index e6125c9..3a5a9c6 100644
--- a/src/camel/camel-ews-utils.h
+++ b/src/camel/camel-ews-utils.h
@@ -85,6 +85,9 @@ gboolean      camel_ews_utils_delete_folders_from_summary_recursive
                                                 CamelFolderInfo *folder_info,
                                                 gboolean send_signals,
                                                 GError **error);
+ESource *      camel_ews_utils_ref_corresponding_source
+                                               (CamelService *service,
+                                                GCancellable *cancellable);
 
 G_END_DECLS
 
diff --git a/src/collection/e-ews-backend.c b/src/collection/e-ews-backend.c
index a0d61ff..651694b 100644
--- a/src/collection/e-ews-backend.c
+++ b/src/collection/e-ews-backend.c
@@ -1262,7 +1262,7 @@ e_ews_backend_ref_connection_sync (EEwsBackend *backend,
 
        settings = ews_backend_get_settings (backend);
        hosturl = camel_ews_settings_dup_hosturl (settings);
-       connection = e_ews_connection_new_full (hosturl, settings, FALSE);
+       connection = e_ews_connection_new_full (e_backend_get_source (E_BACKEND (backend)), hosturl, 
settings, FALSE);
        g_free (hosturl);
 
        e_binding_bind_property (
diff --git a/src/collection/module-ews-backend.c b/src/collection/module-ews-backend.c
index 8bfac43..6f63634 100644
--- a/src/collection/module-ews-backend.c
+++ b/src/collection/module-ews-backend.c
@@ -20,6 +20,7 @@
 
 #include <glib/gi18n-lib.h>
 
+#include "server/e-oauth2-service-office365.h"
 #include "server/e-source-ews-folder.h"
 
 #include "e-ews-backend.h"
@@ -37,6 +38,7 @@ e_module_load (GTypeModule *type_module)
 
        e_ews_backend_type_register (type_module);
        e_ews_backend_factory_type_register (type_module);
+       e_oauth2_service_office365_type_register (type_module);
 
        e_source_ews_folder_type_register (type_module);
 }
diff --git a/src/configuration/e-ews-config-lookup.c b/src/configuration/e-ews-config-lookup.c
index 3baf3df..c18a7ac 100644
--- a/src/configuration/e-ews-config-lookup.c
+++ b/src/configuration/e-ews-config-lookup.c
@@ -345,7 +345,7 @@ ews_config_lookup_worker_run (EConfigLookupWorker *lookup_worker,
        if (password) {
                const gchar *servers;
 
-               if (e_ews_autodiscover_ws_url_sync (ews_settings, email_address, password, cancellable, 
NULL)) {
+               if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, 
cancellable, NULL)) {
                        ews_config_lookup_worker_result_from_settings (lookup_worker, config_lookup, 
email_address, ews_settings, params);
                }
 
@@ -368,7 +368,7 @@ ews_config_lookup_worker_run (EConfigLookupWorker *lookup_worker,
 
                                camel_ews_settings_set_hosturl (ews_settings, server);
 
-                               if (e_ews_autodiscover_ws_url_sync (ews_settings, email_address, password, 
cancellable, NULL)) {
+                               if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, 
password, cancellable, NULL)) {
                                        ews_config_lookup_worker_result_from_settings (lookup_worker, 
config_lookup, email_address, ews_settings, params);
                                }
 
diff --git a/src/configuration/e-ews-config-utils.c b/src/configuration/e-ews-config-utils.c
index 4d9bc9b..4a7fbea 100644
--- a/src/configuration/e-ews-config-utils.c
+++ b/src/configuration/e-ews-config-utils.c
@@ -309,7 +309,7 @@ ews_config_utils_try_credentials_sync (ECredentialsPrompter *prompter,
        gboolean res = TRUE;
 
        hosturl = camel_ews_settings_dup_hosturl (data->ews_settings);
-       data->conn = e_ews_connection_new (data->connect_url ? data->connect_url : hosturl, 
data->ews_settings);
+       data->conn = e_ews_connection_new (source, data->connect_url ? data->connect_url : hosturl, 
data->ews_settings);
        g_free (hosturl);
 
        e_ews_connection_update_credentials (data->conn, credentials);
@@ -369,7 +369,7 @@ e_ews_config_utils_open_connection_for (ESource *source,
                        gchar *hosturl;
 
                        hosturl = camel_ews_settings_dup_hosturl (ews_settings);
-                       conn = e_ews_connection_new (connect_url && *connect_url ? connect_url : hosturl, 
ews_settings);
+                       conn = e_ews_connection_new (source, connect_url && *connect_url ? connect_url : 
hosturl, ews_settings);
                        g_free (hosturl);
 
                        e_ews_connection_update_credentials (conn, NULL);
diff --git a/src/configuration/e-mail-config-ews-autodiscover.c 
b/src/configuration/e-mail-config-ews-autodiscover.c
index c3f9824..3471a76 100644
--- a/src/configuration/e-mail-config-ews-autodiscover.c
+++ b/src/configuration/e-mail-config-ews-autodiscover.c
@@ -137,7 +137,7 @@ mail_config_ews_autodiscover_sync (ECredentialsPrompter *prompter,
        GError *local_error = NULL;
        gboolean res = TRUE;
 
-       e_ews_autodiscover_ws_url_sync (
+       e_ews_autodiscover_ws_url_sync (source,
                async_context->ews_settings, async_context->email_address,
                credentials && e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) ?
                e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) : "",
@@ -171,7 +171,7 @@ mail_config_ews_autodiscover_run_thread (GTask *task,
 
                without_password = e_ews_connection_utils_get_without_password (async_context->ews_settings);
                if (without_password) {
-                       success = e_ews_autodiscover_ws_url_sync (
+                       success = e_ews_autodiscover_ws_url_sync (async_context->source,
                                async_context->ews_settings, async_context->email_address, "",
                                cancellable, &local_error);
                }
diff --git a/src/configuration/e-mail-config-ews-backend.c b/src/configuration/e-mail-config-ews-backend.c
index 4859508..1ec22a1 100644
--- a/src/configuration/e-mail-config-ews-backend.c
+++ b/src/configuration/e-mail-config-ews-backend.c
@@ -39,12 +39,17 @@
        ((obj), E_TYPE_MAIL_CONFIG_EWS_BACKEND, EMailConfigEwsBackendPrivate))
 
 struct _EMailConfigEwsBackendPrivate {
-       GtkWidget *user_entry;          /* not referenced */
-       GtkWidget *host_entry;          /* not referenced */
-       GtkWidget *url_button;          /* not referenced */
-       GtkWidget *oab_entry;           /* not referenced */
-       GtkWidget *auth_check;          /* not referenced */
-       GtkWidget *impersonate_user_entry; /* not referenced */
+       GtkWidget *user_entry;
+       GtkWidget *host_entry;
+       GtkWidget *url_button;
+       GtkWidget *oab_entry;
+       GtkWidget *auth_check;
+       GtkWidget *impersonate_user_entry;
+       GtkGrid *oauth2_settings_grid;
+       GtkWidget *oauth2_override_check;
+       GtkWidget *oauth2_tenant_entry;
+       GtkWidget *oauth2_client_id_entry;
+       GtkWidget *oauth2_redirect_uri_entry;
 };
 
 G_DEFINE_DYNAMIC_TYPE (
@@ -84,6 +89,7 @@ search_for_impersonate_user_clicked_cb (GtkButton *button,
                                        EMailConfigServiceBackend *backend)
 {
        EMailConfigEwsBackendPrivate *priv;
+       ESource *source;
        CamelSettings *settings;
        EEwsConnection *conn;
        GtkWindow *parent;
@@ -91,9 +97,13 @@ search_for_impersonate_user_clicked_cb (GtkButton *button,
 
        g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
 
+       source = e_mail_config_service_backend_get_collection (backend);
+       if (!source)
+               source = e_mail_config_service_backend_get_source (backend);
+
        priv = E_MAIL_CONFIG_EWS_BACKEND_GET_PRIVATE (backend);
        settings = e_mail_config_service_backend_get_settings (backend);
-       conn = e_ews_connection_new (gtk_entry_get_text (GTK_ENTRY (priv->host_entry)), CAMEL_EWS_SETTINGS 
(settings));
+       conn = e_ews_connection_new (source, gtk_entry_get_text (GTK_ENTRY (priv->host_entry)), 
CAMEL_EWS_SETTINGS (settings));
        parent = e_ews_config_utils_get_widget_toplevel_window (GTK_WIDGET (button));
 
        if (e_ews_search_user_modal (parent, conn, NULL, NULL, &email)) {
@@ -104,6 +114,36 @@ search_for_impersonate_user_clicked_cb (GtkButton *button,
        g_free (email);
 }
 
+static gboolean
+mail_config_ews_backend_auth_mech_is_oauth2 (GBinding *binding,
+                                            const GValue *from_value,
+                                            GValue *to_value,
+                                            gpointer user_data)
+{
+       gboolean is_office365;
+       const gchar *active_mechanism;
+
+       active_mechanism = g_value_get_string (from_value);
+       is_office365 = g_strcmp0 (active_mechanism, "Office365") == 0;
+
+       g_value_set_boolean (to_value, is_office365);
+
+       return TRUE;
+}
+
+static void
+mail_config_ews_backend_set_oauth2_tooltip (GtkWidget *widget,
+                                           const gchar *value,
+                                           const gchar *when_value_empty,
+                                           gchar *when_value_filled) /* takes ownership */
+{
+       g_return_if_fail (GTK_IS_WIDGET (widget));
+
+       gtk_widget_set_tooltip_text (widget, value && *value ? when_value_filled : when_value_empty);
+
+       g_free (when_value_filled);
+}
+
 static void
 mail_config_ews_backend_insert_widgets (EMailConfigServiceBackend *backend,
                                         GtkBox *parent)
@@ -261,6 +301,117 @@ mail_config_ews_backend_insert_widgets (EMailConfigServiceBackend *backend,
        priv->auth_check = widget;  /* do not reference */
        gtk_widget_show (widget);
 
+       widget = gtk_grid_new ();
+       gtk_widget_set_margin_left (widget, 12);
+       gtk_box_pack_start (GTK_BOX (parent), widget, FALSE, FALSE, 0);
+       priv->oauth2_settings_grid = GTK_GRID (widget);
+
+       gtk_grid_set_column_spacing (priv->oauth2_settings_grid, 4);
+       gtk_grid_set_row_spacing (priv->oauth2_settings_grid, 4);
+
+       container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+       gtk_grid_attach (priv->oauth2_settings_grid, container, 0, 0, 2, 1);
+
+       widget = gtk_check_button_new_with_mnemonic (_("_Override Office365 OAuth2 settings"));
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       priv->oauth2_override_check = widget;
+
+       markup = g_markup_printf_escaped ("(<a 
href=\"https://wiki.gnome.org/Apps/Evolution/EWS/OAuth2\";>%s</a>)", _("Help…"));
+       widget = gtk_label_new (markup);
+       gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
+       gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       g_free (markup);
+
+       widget = gtk_label_new_with_mnemonic (_("_Tenant:"));
+       gtk_widget_set_margin_left (widget, 12);
+       gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+       gtk_grid_attach (priv->oauth2_settings_grid, widget, 0, 1, 1, 1);
+       label = GTK_LABEL (widget);
+
+       e_binding_bind_property (
+               priv->oauth2_override_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       widget = gtk_entry_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_label_set_mnemonic_widget (label, widget);
+       gtk_grid_attach (priv->oauth2_settings_grid, widget, 1, 1, 1, 1);
+       priv->oauth2_tenant_entry = widget;
+
+       e_binding_bind_property (
+               priv->oauth2_override_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       mail_config_ews_backend_set_oauth2_tooltip (widget, OFFICE365_TENANT,
+               _("There is not set any default tenant"),
+               g_strdup_printf (_("Default tenant is “%s”"), OFFICE365_TENANT));
+
+       widget = gtk_label_new_with_mnemonic (_("Application I_D:"));
+       gtk_widget_set_margin_left (widget, 12);
+       gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+       gtk_grid_attach (priv->oauth2_settings_grid, widget, 0, 2, 1, 1);
+       label = GTK_LABEL (widget);
+
+       e_binding_bind_property (
+               priv->oauth2_override_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       widget = gtk_entry_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_label_set_mnemonic_widget (label, widget);
+       gtk_grid_attach (priv->oauth2_settings_grid, widget, 1, 2, 1, 1);
+       priv->oauth2_client_id_entry = widget;
+
+       e_binding_bind_property (
+               priv->oauth2_override_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       mail_config_ews_backend_set_oauth2_tooltip (widget, OFFICE365_CLIENT_ID,
+               _("There is not set any default application ID"),
+               g_strdup_printf (_("Default application ID is “%s”"), OFFICE365_CLIENT_ID));
+
+       widget = gtk_label_new_with_mnemonic (_("_Redirect URI:"));
+       gtk_widget_set_margin_left (widget, 12);
+       gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+       gtk_grid_attach (priv->oauth2_settings_grid, widget, 0, 3, 1, 1);
+       label = GTK_LABEL (widget);
+
+       e_binding_bind_property (
+               priv->oauth2_override_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       widget = gtk_entry_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_label_set_mnemonic_widget (label, widget);
+       gtk_grid_attach (priv->oauth2_settings_grid, widget, 1, 3, 1, 1);
+       priv->oauth2_redirect_uri_entry = widget;
+
+       e_binding_bind_property (
+               priv->oauth2_override_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       markup = g_strdup_printf (_("Default redirect URI is “%s”"), 
"https://login.microsoftonline.com/common/oauth2/v2.0/nativeclient";);
+       mail_config_ews_backend_set_oauth2_tooltip (widget, OFFICE365_REDIRECT_URI,
+               markup,
+               g_strdup_printf (_("Default redirect URI is “%s”"), OFFICE365_REDIRECT_URI));
+       g_free (markup);
+
+       gtk_widget_show_all (GTK_WIDGET (priv->oauth2_settings_grid));
+
+       e_binding_bind_property_full (
+               priv->auth_check, "active-mechanism",
+               priv->oauth2_settings_grid, "visible",
+               G_BINDING_SYNC_CREATE,
+               mail_config_ews_backend_auth_mech_is_oauth2,
+               NULL, NULL, NULL);
+
        e_binding_bind_object_text_property (
                settings, "user",
                priv->user_entry, "text",
@@ -287,6 +438,30 @@ mail_config_ews_backend_insert_widgets (EMailConfigServiceBackend *backend,
                priv->auth_check, "active-mechanism",
                G_BINDING_BIDIRECTIONAL);
 
+       e_binding_bind_property (
+               settings, "override-oauth2",
+               priv->oauth2_override_check, "active",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       e_binding_bind_object_text_property (
+               settings, "oauth2-tenant",
+               priv->oauth2_tenant_entry, "text",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       e_binding_bind_object_text_property (
+               settings, "oauth2-client-id",
+               priv->oauth2_client_id_entry, "text",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       e_binding_bind_object_text_property (
+               settings, "oauth2-redirect_uri",
+               priv->oauth2_redirect_uri_entry, "text",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
        extension_name = E_SOURCE_EXTENSION_COLLECTION;
        source = e_mail_config_service_backend_get_collection (backend);
        extension = e_source_get_extension (source, extension_name);
diff --git a/src/configuration/module-ews-configuration.c b/src/configuration/module-ews-configuration.c
index ca43f4d..818daf0 100644
--- a/src/configuration/module-ews-configuration.c
+++ b/src/configuration/module-ews-configuration.c
@@ -36,6 +36,8 @@
 #include "e-ews-photo-source.h"
 
 #include "e-ews-config-ui-extension.h"
+#include "server/camel-sasl-xoauth2-office365.h"
+#include "server/e-oauth2-service-office365.h"
 #include "server/e-source-ews-folder.h"
 
 /* Module Entry Points */
@@ -63,6 +65,8 @@ e_module_load (GTypeModule *type_module)
        e_ews_config_ui_extension_type_register (type_module);
        e_ews_ooo_notificator_type_register (type_module);
        e_ews_photo_source_type_register (type_module);
+       camel_sasl_xoauth2_office365_type_register (type_module);
+       e_oauth2_service_office365_type_register (type_module);
 
        e_source_ews_folder_type_register (type_module);
 }
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt
index 8ce6c4e..a6ed430 100644
--- a/src/server/CMakeLists.txt
+++ b/src/server/CMakeLists.txt
@@ -3,6 +3,8 @@ glib_mkenums(e-ews-enumtypes e-ews-enums.h E_EWS_ENUMTYPES_H)
 set(SOURCES
        camel-ews-settings.c
        camel-ews-settings.h
+       camel-sasl-xoauth2-office365.c
+       camel-sasl-xoauth2-office365.h
        e-ews-calendar-utils.c
        e-ews-calendar-utils.h
        e-ews-camel-common.c
@@ -26,6 +28,8 @@ set(SOURCES
        e-ews-oof-settings.c
        e-ews-query-to-restriction.c
        e-ews-query-to-restriction.h
+       e-oauth2-service-office365.c
+       e-oauth2-service-office365.h
        e-soup-auth-negotiate.c
        e-soup-auth-negotiate.h
        e-soap-message.c
diff --git a/src/server/camel-ews-settings.c b/src/server/camel-ews-settings.c
index 58bbc6b..5736f75 100644
--- a/src/server/camel-ews-settings.c
+++ b/src/server/camel-ews-settings.c
@@ -43,6 +43,10 @@ struct _CamelEwsSettingsPrivate {
        gchar *impersonate_user;
        gboolean override_user_agent;
        gchar *user_agent;
+       gboolean override_oauth2;
+       gchar *oauth2_tenant;
+       gchar *oauth2_client_id;
+       gchar *oauth2_redirect_uri;
 };
 
 enum {
@@ -66,7 +70,11 @@ enum {
        PROP_USE_IMPERSONATION,
        PROP_IMPERSONATE_USER,
        PROP_OVERRIDE_USER_AGENT,
-       PROP_USER_AGENT
+       PROP_USER_AGENT,
+       PROP_OVERRIDE_OAUTH2,
+       PROP_OAUTH2_TENANT,
+       PROP_OAUTH2_CLIENT_ID,
+       PROP_OAUTH2_REDIRECT_URI
 };
 
 G_DEFINE_TYPE_WITH_CODE (
@@ -231,6 +239,30 @@ ews_settings_set_property (GObject *object,
                                CAMEL_EWS_SETTINGS (object),
                                g_value_get_string (value));
                        return;
+
+               case PROP_OVERRIDE_OAUTH2:
+                       camel_ews_settings_set_override_oauth2 (
+                               CAMEL_EWS_SETTINGS (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_OAUTH2_TENANT:
+                       camel_ews_settings_set_oauth2_tenant (
+                               CAMEL_EWS_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_OAUTH2_CLIENT_ID:
+                       camel_ews_settings_set_oauth2_client_id (
+                               CAMEL_EWS_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_OAUTH2_REDIRECT_URI:
+                       camel_ews_settings_set_oauth2_redirect_uri (
+                               CAMEL_EWS_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -382,6 +414,34 @@ ews_settings_get_property (GObject *object,
                                camel_ews_settings_dup_user_agent (
                                CAMEL_EWS_SETTINGS (object)));
                        return;
+
+               case PROP_OVERRIDE_OAUTH2:
+                       g_value_set_boolean (
+                               value,
+                               camel_ews_settings_get_override_oauth2 (
+                               CAMEL_EWS_SETTINGS (object)));
+                       return;
+
+               case PROP_OAUTH2_TENANT:
+                       g_value_take_string (
+                               value,
+                               camel_ews_settings_dup_oauth2_tenant (
+                               CAMEL_EWS_SETTINGS (object)));
+                       return;
+
+               case PROP_OAUTH2_CLIENT_ID:
+                       g_value_take_string (
+                               value,
+                               camel_ews_settings_dup_oauth2_client_id (
+                               CAMEL_EWS_SETTINGS (object)));
+                       return;
+
+               case PROP_OAUTH2_REDIRECT_URI:
+                       g_value_take_string (
+                               value,
+                               camel_ews_settings_dup_oauth2_redirect_uri (
+                               CAMEL_EWS_SETTINGS (object)));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -403,6 +463,9 @@ ews_settings_finalize (GObject *object)
        g_free (priv->oal_selected);
        g_free (priv->impersonate_user);
        g_free (priv->user_agent);
+       g_free (priv->oauth2_tenant);
+       g_free (priv->oauth2_client_id);
+       g_free (priv->oauth2_redirect_uri);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (camel_ews_settings_parent_class)->finalize (object);
@@ -629,6 +692,54 @@ camel_ews_settings_class_init (CamelEwsSettingsClass *class)
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT |
                        G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_OVERRIDE_OAUTH2,
+               g_param_spec_boolean (
+                       "override-oauth2",
+                       "Override OAuth2",
+                       "Whether to override OAuth2 values",
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_OAUTH2_TENANT,
+               g_param_spec_string (
+                       "oauth2-tenant",
+                       "OAuth2 Tenant",
+                       "OAuth2 Tenant to use, only if override-oauth2 is TRUE, otherwise the compile-time 
value is used",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_OAUTH2_CLIENT_ID,
+               g_param_spec_string (
+                       "oauth2-client-id",
+                       "OAuth2 Client ID",
+                       "OAuth2 Client-ID to use, only if override-oauth2 is TRUE, otherwise the compile-time 
value is used",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_OAUTH2_REDIRECT_URI,
+               g_param_spec_string (
+                       "oauth2-redirect-uri",
+                       "OAuth2 Redirect URI",
+                       "OAuth2 Redirect URI to use, only if override-oauth2 is TRUE, otherwise the 
compile-time value is used",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -675,6 +786,8 @@ camel_ews_settings_get_auth_mechanism (CamelEwsSettings *settings)
                result = EWS_AUTH_TYPE_BASIC;
        else if (auth_mech && g_ascii_strcasecmp (auth_mech, "GSSAPI") == 0)
                result = EWS_AUTH_TYPE_GSSAPI;
+       else if (auth_mech && g_ascii_strcasecmp (auth_mech, "Office365") == 0)
+               result = EWS_AUTH_TYPE_OAUTH2;
        else
                result = EWS_AUTH_TYPE_NTLM;
 
@@ -1267,3 +1380,166 @@ camel_ews_settings_set_user_agent (CamelEwsSettings *settings,
 
        g_object_notify (G_OBJECT (settings), "user-agent");
 }
+
+gboolean
+camel_ews_settings_get_override_oauth2 (CamelEwsSettings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), FALSE);
+
+       return settings->priv->override_oauth2;
+}
+
+void
+camel_ews_settings_set_override_oauth2 (CamelEwsSettings *settings,
+                                       gboolean override_oauth2)
+{
+       g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings));
+
+       if ((settings->priv->override_oauth2 ? 1 : 0) == (override_oauth2 ? 1 : 0))
+               return;
+
+       settings->priv->override_oauth2 = override_oauth2;
+
+       g_object_notify (G_OBJECT (settings), "override-oauth2");
+}
+
+const gchar *
+camel_ews_settings_get_oauth2_tenant (CamelEwsSettings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL);
+
+       return settings->priv->oauth2_tenant;
+}
+
+gchar *
+camel_ews_settings_dup_oauth2_tenant (CamelEwsSettings *settings)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL);
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       protected = camel_ews_settings_get_oauth2_tenant (settings);
+       duplicate = g_strdup (protected);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       return duplicate;
+}
+
+void
+camel_ews_settings_set_oauth2_tenant (CamelEwsSettings *settings,
+                                     const gchar *tenant)
+{
+       g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings));
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       if (g_strcmp0 (settings->priv->oauth2_tenant, tenant) == 0) {
+               g_mutex_unlock (&settings->priv->property_lock);
+               return;
+       }
+
+       g_free (settings->priv->oauth2_tenant);
+       settings->priv->oauth2_tenant = e_util_strdup_strip (tenant);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       g_object_notify (G_OBJECT (settings), "oauth2-tenant");
+}
+
+const gchar *
+camel_ews_settings_get_oauth2_client_id (CamelEwsSettings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL);
+
+       return settings->priv->oauth2_client_id;
+}
+
+gchar *
+camel_ews_settings_dup_oauth2_client_id (CamelEwsSettings *settings)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL);
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       protected = camel_ews_settings_get_oauth2_client_id (settings);
+       duplicate = g_strdup (protected);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       return duplicate;
+}
+
+void
+camel_ews_settings_set_oauth2_client_id (CamelEwsSettings *settings,
+                                        const gchar *client_id)
+{
+       g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings));
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       if (g_strcmp0 (settings->priv->oauth2_client_id, client_id) == 0) {
+               g_mutex_unlock (&settings->priv->property_lock);
+               return;
+       }
+
+       g_free (settings->priv->oauth2_client_id);
+       settings->priv->oauth2_client_id = e_util_strdup_strip (client_id);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       g_object_notify (G_OBJECT (settings), "oauth2-client-id");
+}
+
+const gchar *
+camel_ews_settings_get_oauth2_redirect_uri (CamelEwsSettings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL);
+
+       return settings->priv->oauth2_redirect_uri;
+}
+
+gchar *
+camel_ews_settings_dup_oauth2_redirect_uri (CamelEwsSettings *settings)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL);
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       protected = camel_ews_settings_get_oauth2_redirect_uri (settings);
+       duplicate = g_strdup (protected);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       return duplicate;
+}
+
+void
+camel_ews_settings_set_oauth2_redirect_uri (CamelEwsSettings *settings,
+                                           const gchar *redirect_uri)
+{
+       g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings));
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       if (g_strcmp0 (settings->priv->oauth2_redirect_uri, redirect_uri) == 0) {
+               g_mutex_unlock (&settings->priv->property_lock);
+               return;
+       }
+
+       g_free (settings->priv->oauth2_redirect_uri);
+       settings->priv->oauth2_redirect_uri = e_util_strdup_strip (redirect_uri);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       g_object_notify (G_OBJECT (settings), "oauth2-redirect-uri");
+}
diff --git a/src/server/camel-ews-settings.h b/src/server/camel-ews-settings.h
index ee3829b..b489159 100644
--- a/src/server/camel-ews-settings.h
+++ b/src/server/camel-ews-settings.h
@@ -58,7 +58,8 @@ struct _CamelEwsSettingsClass {
 typedef enum {
        EWS_AUTH_TYPE_NTLM,
        EWS_AUTH_TYPE_BASIC,
-       EWS_AUTH_TYPE_GSSAPI
+       EWS_AUTH_TYPE_GSSAPI,
+       EWS_AUTH_TYPE_OAUTH2
 } EwsAuthType;
 
 
@@ -140,6 +141,32 @@ gchar *            camel_ews_settings_dup_user_agent
 void           camel_ews_settings_set_user_agent
                                                (CamelEwsSettings *settings,
                                                 const gchar *user_agent);
+gboolean       camel_ews_settings_get_override_oauth2
+                                               (CamelEwsSettings *settings);
+void           camel_ews_settings_set_override_oauth2
+                                               (CamelEwsSettings *settings,
+                                                gboolean override_oauth2);
+const gchar *  camel_ews_settings_get_oauth2_tenant
+                                               (CamelEwsSettings *settings);
+gchar *                camel_ews_settings_dup_oauth2_tenant
+                                               (CamelEwsSettings *settings);
+void           camel_ews_settings_set_oauth2_tenant
+                                               (CamelEwsSettings *settings,
+                                                const gchar *tenant);
+const gchar *  camel_ews_settings_get_oauth2_client_id
+                                               (CamelEwsSettings *settings);
+gchar *                camel_ews_settings_dup_oauth2_client_id
+                                               (CamelEwsSettings *settings);
+void           camel_ews_settings_set_oauth2_client_id
+                                               (CamelEwsSettings *settings,
+                                                const gchar *client_id);
+const gchar *  camel_ews_settings_get_oauth2_redirect_uri
+                                               (CamelEwsSettings *settings);
+gchar *                camel_ews_settings_dup_oauth2_redirect_uri
+                                               (CamelEwsSettings *settings);
+void           camel_ews_settings_set_oauth2_redirect_uri
+                                               (CamelEwsSettings *settings,
+                                                const gchar *redirect_uri);
 
 G_END_DECLS
 
diff --git a/src/server/camel-sasl-xoauth2-office365.c b/src/server/camel-sasl-xoauth2-office365.c
new file mode 100644
index 0000000..8df8ddc
--- /dev/null
+++ b/src/server/camel-sasl-xoauth2-office365.c
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "camel-sasl-xoauth2-office365.h"
+
+static CamelServiceAuthType sasl_xoauth2_office365_auth_type = {
+       N_("OAuth2 (Office365)"),
+       N_("This option will use an OAuth 2.0 "
+          "access token to connect to the Office365.com server"),
+       "Office365",
+       FALSE
+};
+
+G_DEFINE_DYNAMIC_TYPE (CamelSaslXOAuth2Office365, camel_sasl_xoauth2_office365, CAMEL_TYPE_SASL_XOAUTH2)
+
+static void
+camel_sasl_xoauth2_office365_class_init (CamelSaslXOAuth2Office365Class *klass)
+{
+       CamelSaslClass *sasl_class;
+
+       sasl_class = CAMEL_SASL_CLASS (klass);
+       sasl_class->auth_type = &sasl_xoauth2_office365_auth_type;
+}
+
+static void
+camel_sasl_xoauth2_office365_class_finalize (CamelSaslXOAuth2Office365Class *klass)
+{
+}
+
+static void
+camel_sasl_xoauth2_office365_init (CamelSaslXOAuth2Office365 *sasl)
+{
+}
+
+void
+camel_sasl_xoauth2_office365_type_register (GTypeModule *type_module)
+{
+       camel_sasl_xoauth2_office365_register_type (type_module);
+}
diff --git a/src/server/camel-sasl-xoauth2-office365.h b/src/server/camel-sasl-xoauth2-office365.h
new file mode 100644
index 0000000..8cb3135
--- /dev/null
+++ b/src/server/camel-sasl-xoauth2-office365.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CAMEL_SASL_XOAUTH2_OFFICE365_H
+#define CAMEL_SASL_XOAUTH2_OFFICE365_H
+
+#include <gmodule.h>
+#include <camel/camel.h>
+
+/* Standard GObject macros */
+#define CAMEL_TYPE_SASL_XOAUTH2_OFFICE365 \
+       (camel_sasl_xoauth2_office365_get_type ())
+#define CAMEL_SASL_XOAUTH2_OFFICE365(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), CAMEL_TYPE_SASL_XOAUTH2_OFFICE365, CamelSaslXOAuth2Office365))
+#define CAMEL_SASL_XOAUTH2_OFFICE365_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), CAMEL_TYPE_SASL_XOAUTH2_OFFICE365, CamelSaslXOAuth2Office365Class))
+#define CAMEL_IS_SASL_XOAUTH2_OFFICE365(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), CAMEL_TYPE_SASL_XOAUTH2_OFFICE365))
+#define CAMEL_IS_SASL_XOAUTH2_OFFICE365_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), CAMEL_TYPE_SASL_XOAUTH2_OFFICE365))
+#define CAMEL_SASL_XOAUTH2_OFFICE365_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), CAMEL_TYPE_SASL_XOAUTH2_OFFICE365, CamelSaslXOAuth2Office365Class))
+
+G_BEGIN_DECLS
+
+typedef struct _CamelSaslXOAuth2Office365 CamelSaslXOAuth2Office365;
+typedef struct _CamelSaslXOAuth2Office365Class CamelSaslXOAuth2Office365Class;
+typedef struct _CamelSaslXOAuth2Office365Private CamelSaslXOAuth2Office365Private;
+
+struct _CamelSaslXOAuth2Office365 {
+       CamelSaslXOAuth2 parent;
+       CamelSaslXOAuth2Office365Private *priv;
+};
+
+struct _CamelSaslXOAuth2Office365Class {
+       CamelSaslXOAuth2Class parent_class;
+};
+
+GType          camel_sasl_xoauth2_office365_get_type   (void) G_GNUC_CONST;
+
+void           camel_sasl_xoauth2_office365_type_register
+                                                       (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* CAMEL_SASL_XOAUTH2_OFFICE365_H */
diff --git a/src/server/e-ews-connection-utils.c b/src/server/e-ews-connection-utils.c
index ae6435a..822e53b 100644
--- a/src/server/e-ews-connection-utils.c
+++ b/src/server/e-ews-connection-utils.c
@@ -21,9 +21,11 @@
 #include <string.h>
 
 #include <glib.h>
+#include <glib/gi18n-lib.h>
 #include <glib/gstdio.h>
 
 #include "e-ews-connection-utils.h"
+#include "e-soup-auth-negotiate.h"
 #include "camel-ews-settings.h"
 
 static gpointer
@@ -166,6 +168,7 @@ e_ews_connection_utils_get_without_password (CamelEwsSettings *ews_settings)
 {
        switch (camel_ews_settings_get_auth_mechanism (ews_settings)) {
        case EWS_AUTH_TYPE_GSSAPI:
+       case EWS_AUTH_TYPE_OAUTH2:
                return TRUE;
 
        case EWS_AUTH_TYPE_NTLM:
@@ -181,3 +184,354 @@ e_ews_connection_utils_get_without_password (CamelEwsSettings *ews_settings)
 
        return FALSE;
 }
+
+void
+e_ews_connection_utils_expired_password_to_error (const gchar *service_url,
+                                                 GError **error)
+{
+       if (!error)
+               return;
+
+       if (service_url) {
+               g_set_error (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_PASSWORDEXPIRED,
+                       _("Password expired. Change password at “%s”."), service_url);
+       } else {
+               g_set_error_literal (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_PASSWORDEXPIRED,
+                       _("Password expired."));
+       }
+}
+
+gboolean
+e_ews_connection_utils_check_x_ms_credential_headers (SoupMessage *message,
+                                                     gint *out_expire_in_days,
+                                                     gboolean *out_expired,
+                                                     gchar **out_service_url)
+{
+       gboolean any_found = FALSE;
+       const gchar *header;
+
+       if (!message || !message->response_headers)
+               return FALSE;
+
+       header = soup_message_headers_get_list (message->response_headers, 
"X-MS-Credential-Service-CredExpired");
+       if (header && g_ascii_strcasecmp (header, "true") == 0) {
+               any_found = TRUE;
+
+               if (out_expired)
+                       *out_expired = TRUE;
+       }
+
+       header = soup_message_headers_get_list (message->response_headers, "X-MS-Credentials-Expire");
+       if (header) {
+               gint in_days;
+
+               in_days = g_ascii_strtoll (header, NULL, 10);
+               if (in_days <= 30 && in_days >= 0) {
+                       any_found = TRUE;
+
+                       if (out_expire_in_days)
+                               *out_expire_in_days = in_days;
+               }
+       }
+
+       if (any_found && out_service_url) {
+               header = soup_message_headers_get_list (message->response_headers, 
"X-MS-Credential-Service-Url");
+
+               *out_service_url = g_strdup (header);
+       }
+
+       return any_found;
+}
+
+void
+e_ews_connection_utils_prepare_auth_method (SoupSession *soup_session,
+                                           EwsAuthType auth_method)
+{
+       /* We used to disable Basic auth to avoid it getting in the way of
+        * our GSSAPI hacks. But leave it enabled in the case where NTLM is
+        * enabled, which is the default configuration. It's a useful fallback
+        * which people may be relying on. */
+       if (auth_method == EWS_AUTH_TYPE_GSSAPI) {
+               soup_session_add_feature_by_type (soup_session, E_SOUP_TYPE_AUTH_NEGOTIATE);
+               soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_AUTH_BASIC);
+       } else if (auth_method == EWS_AUTH_TYPE_OAUTH2) {
+               soup_session_add_feature_by_type (soup_session, E_TYPE_SOUP_AUTH_BEARER);
+               soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_AUTH_BASIC);
+       } else if (auth_method == EWS_AUTH_TYPE_NTLM) {
+               soup_session_add_feature_by_type (soup_session, SOUP_TYPE_AUTH_NTLM);
+       }
+}
+
+static void
+ews_connection_utils_ensure_bearer_auth_usage (SoupSession *session,
+                                              SoupMessage *message,
+                                              ESoupAuthBearer *bearer)
+{
+       SoupSessionFeature *feature;
+       SoupURI *soup_uri;
+
+       g_return_if_fail (SOUP_IS_SESSION (session));
+
+       /* Preload the SoupAuthManager with a valid "Bearer" token
+        * when using OAuth 2.0. This avoids an extra unauthorized
+        * HTTP round-trip, which apparently Google doesn't like. */
+
+       feature = soup_session_get_feature (SOUP_SESSION (session), SOUP_TYPE_AUTH_MANAGER);
+
+       if (!soup_session_feature_has_feature (feature, E_TYPE_SOUP_AUTH_BEARER)) {
+               /* Add the "Bearer" auth type to support OAuth 2.0. */
+               soup_session_feature_add_feature (feature, E_TYPE_SOUP_AUTH_BEARER);
+       }
+
+       soup_uri = message ? soup_message_get_uri (message) : NULL;
+       if (soup_uri && soup_uri->host && *soup_uri->host) {
+               soup_uri = soup_uri_copy_host (soup_uri);
+       } else {
+               soup_uri = NULL;
+       }
+
+       g_return_if_fail (soup_uri != NULL);
+
+       soup_auth_manager_use_auth (
+               SOUP_AUTH_MANAGER (feature),
+               soup_uri, SOUP_AUTH (bearer));
+
+       soup_uri_free (soup_uri);
+}
+
+static gboolean
+ews_connection_utils_setup_bearer_auth (EEwsConnection *cnc,
+                                       SoupMessage *message,
+                                       gboolean is_in_authenticate_handler,
+                                       ESoupAuthBearer *bearer,
+                                       GCancellable *cancellable,
+                                       GError **error)
+{
+       ESource *source;
+       gchar *access_token = NULL;
+       gint expires_in_seconds = -1;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (bearer), FALSE);
+
+       source = e_ews_connection_get_source (cnc);
+
+       success = e_source_get_oauth2_access_token_sync (source, cancellable,
+               &access_token, &expires_in_seconds, error);
+
+       if (success) {
+               e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds);
+
+               if (!is_in_authenticate_handler) {
+                       SoupSession *session;
+
+                       session = e_ews_connection_ref_soup_session (cnc);
+
+                       ews_connection_utils_ensure_bearer_auth_usage (session, message, bearer);
+
+                       g_clear_object (&session);
+               }
+       }
+
+       g_free (access_token);
+
+       return success;
+}
+
+static gboolean
+ews_connection_utils_maybe_prepare_bearer_auth (EEwsConnection *cnc,
+                                               SoupMessage *message,
+                                               GCancellable *cancellable)
+{
+       ESource *source;
+       ESoupAuthBearer *using_bearer_auth;
+       gchar *auth_method = NULL;
+       gboolean success;
+       GError *local_error = NULL;
+
+       g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE);
+
+       source = e_ews_connection_get_source (cnc);
+       if (!source)
+               return TRUE;
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+               ESourceAuthentication *extension;
+
+               extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+               auth_method = e_source_authentication_dup_method (extension);
+       } else {
+               return TRUE;
+       }
+
+       if (g_strcmp0 (auth_method, "OAuth2") != 0 && !e_oauth2_services_is_oauth2_alias_static 
(auth_method)) {
+               g_free (auth_method);
+               return TRUE;
+       }
+
+       g_free (auth_method);
+
+       using_bearer_auth = e_ews_connection_ref_bearer_auth (cnc);
+       if (using_bearer_auth) {
+               success = ews_connection_utils_setup_bearer_auth (cnc, message, FALSE, using_bearer_auth, 
cancellable, &local_error);
+               g_clear_object (&using_bearer_auth);
+       } else {
+               SoupAuth *soup_auth;
+               SoupURI *soup_uri;
+
+               soup_uri = message ? soup_message_get_uri (message) : NULL;
+               if (soup_uri && soup_uri->host && *soup_uri->host) {
+                       soup_uri = soup_uri_copy_host (soup_uri);
+               } else {
+                       soup_uri = NULL;
+               }
+
+               g_warn_if_fail (soup_uri != NULL);
+
+               if (!soup_uri) {
+                       soup_message_set_status_full (message, SOUP_STATUS_MALFORMED, "Cannot get host from 
message");
+                       return FALSE;
+               }
+
+               soup_auth = g_object_new (E_TYPE_SOUP_AUTH_BEARER, SOUP_AUTH_HOST, soup_uri->host, NULL);
+
+               success = ews_connection_utils_setup_bearer_auth (cnc, message, FALSE, E_SOUP_AUTH_BEARER 
(soup_auth), cancellable, &local_error);
+               if (success)
+                       e_ews_connection_set_bearer_auth (cnc, E_SOUP_AUTH_BEARER (soup_auth));
+
+               g_object_unref (soup_auth);
+               soup_uri_free (soup_uri);
+       }
+
+       if (!success) {
+               if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                       soup_message_set_status (message, SOUP_STATUS_CANCELLED);
+               else if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED) ||
+                        g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                       soup_message_set_status_full (message, SOUP_STATUS_UNAUTHORIZED, 
local_error->message);
+               else
+                       soup_message_set_status_full (message, SOUP_STATUS_MALFORMED, local_error ? 
local_error->message : _("Unknown error"));
+       }
+
+       g_clear_error (&local_error);
+
+       return success;
+}
+
+/* Callback implementation for SoupSession::authenticate */
+void
+e_ews_connection_utils_authenticate (EEwsConnection *cnc,
+                                    SoupSession *session,
+                                    SoupMessage *msg,
+                                    SoupAuth *auth,
+                                    gboolean retrying)
+{
+       CamelNetworkSettings *network_settings;
+       ESoupAuthBearer *using_bearer_auth;
+       gchar *user, *password, *service_url = NULL;
+       gboolean expired = FALSE;
+
+       g_return_if_fail (cnc != NULL);
+
+       using_bearer_auth = e_ews_connection_ref_bearer_auth (cnc);
+
+       if (E_IS_SOUP_AUTH_BEARER (auth)) {
+               g_object_ref (auth);
+               g_warn_if_fail ((gpointer) using_bearer_auth == (gpointer) auth);
+
+               g_clear_object (&using_bearer_auth);
+               using_bearer_auth = E_SOUP_AUTH_BEARER (auth);
+
+               e_ews_connection_set_bearer_auth (cnc, using_bearer_auth);
+       }
+
+       if (retrying)
+               e_ews_connection_set_password (cnc, NULL);
+
+       if (using_bearer_auth) {
+               GError *local_error = NULL;
+
+               ews_connection_utils_setup_bearer_auth (cnc, msg, TRUE, E_SOUP_AUTH_BEARER (auth), NULL, 
&local_error);
+
+               if (local_error)
+                       soup_message_set_status_full (msg, SOUP_STATUS_IO_ERROR, local_error->message);
+
+               g_object_unref (using_bearer_auth);
+               g_clear_error (&local_error);
+
+               return;
+       }
+
+       if (e_ews_connection_utils_check_x_ms_credential_headers (msg, NULL, &expired, &service_url) && 
expired) {
+               GError *local_error = NULL;
+
+               e_ews_connection_utils_expired_password_to_error (service_url, &local_error);
+
+               if (local_error)
+                       soup_message_set_status_full (msg, SOUP_STATUS_IO_ERROR, local_error->message);
+
+               g_clear_error (&local_error);
+               g_free (service_url);
+
+               return;
+       }
+
+       g_free (service_url);
+
+       network_settings = CAMEL_NETWORK_SETTINGS (e_ews_connection_ref_settings (cnc));
+       user = camel_network_settings_dup_user (network_settings);
+
+       password = e_ews_connection_dup_password (cnc);
+       if (password != NULL) {
+               soup_auth_authenticate (auth, user, password);
+       } else {
+               /* The NTLM implementation in libsoup doesn't cope very well
+                * with recovering from authentication failures (bug 703181).
+                * So cancel the message now while it's in-flight, and we'll
+                * get a shiny new connection for the next attempt. */
+               const gchar *scheme = soup_auth_get_scheme_name (auth);
+
+               if (!g_ascii_strcasecmp(scheme, "NTLM")) {
+                       soup_session_cancel_message (session, msg, SOUP_STATUS_UNAUTHORIZED);
+               }
+       }
+
+       g_clear_object (&network_settings);
+       g_free (password);
+       g_free (user);
+}
+
+/* Returns whether succeeded */
+gboolean
+e_ews_connection_utils_prepare_message (EEwsConnection *cnc,
+                                       SoupMessage *message,
+                                       GCancellable *cancellable)
+{
+       ESoupAuthBearer *using_bearer_auth;
+       GError *local_error = NULL;
+
+       if (!ews_connection_utils_maybe_prepare_bearer_auth (cnc, message, cancellable))
+               return FALSE;
+
+       using_bearer_auth = e_ews_connection_ref_bearer_auth (cnc);
+
+       if (using_bearer_auth &&
+           e_soup_auth_bearer_is_expired (using_bearer_auth) &&
+           !ews_connection_utils_setup_bearer_auth (cnc, message, FALSE, using_bearer_auth, cancellable, 
&local_error)) {
+               if (local_error) {
+                       soup_message_set_status_full (message, SOUP_STATUS_BAD_REQUEST, local_error->message);
+                       g_clear_error (&local_error);
+               } else {
+                       soup_message_set_status (message, SOUP_STATUS_BAD_REQUEST);
+               }
+
+               g_object_unref (using_bearer_auth);
+
+               return FALSE;
+       }
+
+       g_clear_object (&using_bearer_auth);
+
+       return TRUE;
+}
diff --git a/src/server/e-ews-connection-utils.h b/src/server/e-ews-connection-utils.h
index 71ad8d3..ffdfbf3 100644
--- a/src/server/e-ews-connection-utils.h
+++ b/src/server/e-ews-connection-utils.h
@@ -36,6 +36,25 @@ void         e_ews_connection_utils_force_off_ntlm_auth_check
                                                        (void);
 gboolean       e_ews_connection_utils_get_without_password
                                                        (CamelEwsSettings *ews_settings);
+void           e_ews_connection_utils_expired_password_to_error
+                                                       (const gchar *service_url,
+                                                        GError **error);
+gboolean       e_ews_connection_utils_check_x_ms_credential_headers
+                                                       (SoupMessage *message,
+                                                        gint *out_expire_in_days,
+                                                        gboolean *out_expired,
+                                                        gchar **out_service_url);
+void           e_ews_connection_utils_prepare_auth_method
+                                                       (SoupSession *soup_session,
+                                                        EwsAuthType auth_method);
+void           e_ews_connection_utils_authenticate     (EEwsConnection *cnc,
+                                                        SoupSession *session,
+                                                        SoupMessage *msg,
+                                                        SoupAuth *auth,
+                                                        gboolean retrying);
+gboolean       e_ews_connection_utils_prepare_message  (EEwsConnection *cnc,
+                                                        SoupMessage *message,
+                                                        GCancellable *cancellable);
 
 G_END_DECLS
 
diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c
index 8daf945..47f8ec4 100644
--- a/src/server/e-ews-connection.c
+++ b/src/server/e-ews-connection.c
@@ -44,7 +44,6 @@
 #include "e-ews-item-change.h"
 #include "e-ews-debug.h"
 #include "e-ews-notification.h"
-#include "e-soup-auth-negotiate.h"
 
 #define d(x) x
 
@@ -80,6 +79,8 @@ static void   ews_connection_authenticate     (SoupSession *sess,
 /* Connection APIS */
 
 struct _EEwsConnectionPrivate {
+       ESource *source;
+       ESoupAuthBearer *bearer_auth;
        SoupSession *soup_session;
        GThread *soup_thread;
        GMainLoop *soup_loop;
@@ -113,7 +114,8 @@ enum {
        PROP_0,
        PROP_PASSWORD,
        PROP_PROXY_RESOLVER,
-       PROP_SETTINGS
+       PROP_SETTINGS,
+       PROP_SOURCE
 };
 
 enum {
@@ -500,11 +502,27 @@ ews_connection_scheduled_cb (gpointer user_data)
 
        switch (sd->op) {
        case EWS_SCHEDULE_OP_QUEUE_MESSAGE:
-               e_ews_debug_dump_raw_soup_request (sd->message);
+               if (!e_ews_connection_utils_prepare_message (sd->cnc, sd->message, NULL)) {
+                       e_ews_debug_dump_raw_soup_request (sd->message);
 
-               soup_session_queue_message (
-                       sd->cnc->priv->soup_session, sd->message,
-                       sd->queue_callback, sd->queue_user_data);
+                       if (sd->queue_callback) {
+                               sd->queue_callback (sd->cnc->priv->soup_session, sd->message, 
sd->queue_user_data);
+                       } else {
+                               /* This should not happen */
+                               g_warn_if_reached ();
+
+                               soup_session_queue_message (
+                                       sd->cnc->priv->soup_session, sd->message,
+                                       sd->queue_callback, sd->queue_user_data);
+                               soup_session_cancel_message (sd->cnc->priv->soup_session, sd->message, 
sd->message->status_code);
+                       }
+               } else {
+                       e_ews_debug_dump_raw_soup_request (sd->message);
+
+                       soup_session_queue_message (
+                               sd->cnc->priv->soup_session, sd->message,
+                               sd->queue_callback, sd->queue_user_data);
+               }
                break;
        case EWS_SCHEDULE_OP_CANCEL:
                soup_session_cancel_message (sd->cnc->priv->soup_session, sd->message, SOUP_STATUS_CANCELLED);
@@ -644,10 +662,16 @@ ews_next_request (gpointer _cnc)
        if (cnc->priv->soup_session) {
                SoupMessage *msg = SOUP_MESSAGE (node->msg);
 
-               e_ews_debug_dump_raw_soup_request (msg);
+               if (!e_ews_connection_utils_prepare_message (cnc, msg, node->cancellable)) {
+                       e_ews_debug_dump_raw_soup_request (msg);
+                       QUEUE_UNLOCK (cnc);
 
-               soup_session_queue_message (cnc->priv->soup_session, msg, ews_response_cb, node);
-               QUEUE_UNLOCK (cnc);
+                       ews_response_cb (cnc->priv->soup_session, msg, node);
+               } else {
+                       e_ews_debug_dump_raw_soup_request (msg);
+                       soup_session_queue_message (cnc->priv->soup_session, msg, ews_response_cb, node);
+                       QUEUE_UNLOCK (cnc);
+               }
        } else {
                QUEUE_UNLOCK (cnc);
 
@@ -779,64 +803,6 @@ e_ews_connection_queue_request (EEwsConnection *cnc,
        ews_trigger_next_request (cnc);
 }
 
-static void
-ews_connection_expired_password_to_error (const gchar *service_url,
-                                         GError **error)
-{
-       if (!error)
-               return;
-
-       if (service_url) {
-               g_set_error (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_PASSWORDEXPIRED,
-                       _("Password expired. Change password at “%s”."), service_url);
-       } else {
-               g_set_error_literal (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_PASSWORDEXPIRED,
-                       _("Password expired."));
-       }
-}
-
-static gboolean
-ews_connection_check_x_ms_credential_headers (SoupMessage *message,
-                                             gint *out_expire_in_days,
-                                             gboolean *out_expired,
-                                             gchar **out_service_url)
-{
-       gboolean any_found = FALSE;
-       const gchar *header;
-
-       if (!message || !message->response_headers)
-               return FALSE;
-
-       header = soup_message_headers_get_list (message->response_headers, 
"X-MS-Credential-Service-CredExpired");
-       if (header && g_ascii_strcasecmp (header, "true") == 0) {
-               any_found = TRUE;
-
-               if (out_expired)
-                       *out_expired = TRUE;
-       }
-
-       header = soup_message_headers_get_list (message->response_headers, "X-MS-Credentials-Expire");
-       if (header) {
-               gint in_days;
-
-               in_days = g_ascii_strtoll (header, NULL, 10);
-               if (in_days <= 30 && in_days >= 0) {
-                       any_found = TRUE;
-
-                       if (out_expire_in_days)
-                               *out_expire_in_days = in_days;
-               }
-       }
-
-       if (any_found && out_service_url) {
-               header = soup_message_headers_get_list (message->response_headers, 
"X-MS-Credential-Service-Url");
-
-               *out_service_url = g_strdup (header);
-       }
-
-       return any_found;
-}
-
 static gboolean
 ews_connection_credentials_failed (EEwsConnection *connection,
                                   SoupMessage *message,
@@ -850,13 +816,13 @@ ews_connection_credentials_failed (EEwsConnection *connection,
        g_return_val_if_fail (SOUP_IS_MESSAGE (message), FALSE);
        g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), FALSE);
 
-       if (!ews_connection_check_x_ms_credential_headers (message, &expire_in_days, &expired, &service_url))
+       if (!e_ews_connection_utils_check_x_ms_credential_headers (message, &expire_in_days, &expired, 
&service_url))
                return FALSE;
 
        if (expired) {
                GError *error = NULL;
 
-               ews_connection_expired_password_to_error (service_url, &error);
+               e_ews_connection_utils_expired_password_to_error (service_url, &error);
                g_simple_async_result_take_error (simple, error);
        } else if (expire_in_days > 0) {
                g_signal_emit (connection, signals[PASSWORD_WILL_EXPIRE], 0, expire_in_days, service_url);
@@ -1694,6 +1660,69 @@ create_folder_response_cb (ESoapResponse *response,
        }
 }
 
+static gpointer
+e_ews_soup_thread (gpointer user_data)
+{
+       EEwsConnection *cnc = user_data;
+
+       g_main_context_push_thread_default (cnc->priv->soup_context);
+       g_main_loop_run (cnc->priv->soup_loop);
+       g_main_context_pop_thread_default (cnc->priv->soup_context);
+
+       /* abort any pending operations */
+       soup_session_abort (cnc->priv->soup_session);
+
+       g_object_unref (cnc->priv->soup_session);
+       cnc->priv->soup_session = NULL;
+
+       return NULL;
+}
+
+/*
+ * e_ews_debug_handler:
+ *
+ * GLib debug message handler, which is passed all messages from g_debug() calls,
+ * and decides whether to print them.
+ */
+static void
+e_ews_debug_handler (const gchar *log_domain,
+                    GLogLevelFlags log_level,
+                    const gchar *message,
+                    gpointer user_data)
+{
+       if (e_ews_debug_get_log_level () >= 3)
+               g_log_default_handler (log_domain, log_level, message, NULL);
+}
+
+/*
+ * e_ews_soup_log_print:
+ *
+ * Log printer for the libsoup logging functionality, which just marshal all soup log
+ * output to the standard GLib logging framework (and thus to debug_handler(), above).
+ */
+static void
+e_ews_soup_log_printer (SoupLogger *logger,
+                       SoupLoggerLogLevel level,
+                       char direction,
+                       const gchar *data,
+                       gpointer user_data)
+{
+       const gchar *filtered_data = NULL;
+
+       if (e_ews_debug_get_log_level () >= 3) {
+               if (direction == '>' && g_ascii_strncasecmp (data, "Host:", 5) == 0)
+                       filtered_data = "Host: <redacted>";
+               else if (direction == '>' && g_ascii_strncasecmp (data, "Authorization:", 14) == 0)
+                       filtered_data = "Authorization: <redacted>";
+               else if (direction == '<' && g_ascii_strncasecmp (data, "Set-Cookie:", 11) == 0)
+                       filtered_data = "Set-Cookie: <redacted>";
+               else
+                       filtered_data = data;
+       }
+
+       g_debug ("%c %s", direction, filtered_data ? filtered_data : data);
+}
+
 static void
 ews_connection_set_settings (EEwsConnection *connection,
                              CamelEwsSettings *settings)
@@ -1705,6 +1734,17 @@ ews_connection_set_settings (EEwsConnection *connection,
 }
 
 static void
+ews_connection_set_source (EEwsConnection *connection,
+                          ESource *source)
+{
+       if (source)
+               g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (connection->priv->source == NULL);
+
+       connection->priv->source = source ? g_object_ref (source) : NULL;
+}
+
+static void
 ews_connection_set_property (GObject *object,
                              guint property_id,
                              const GValue *value,
@@ -1728,6 +1768,12 @@ ews_connection_set_property (GObject *object,
                                E_EWS_CONNECTION (object),
                                g_value_get_object (value));
                        return;
+
+               case PROP_SOURCE:
+                       ews_connection_set_source (
+                               E_EWS_CONNECTION (object),
+                               g_value_get_object (value));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -1760,12 +1806,75 @@ ews_connection_get_property (GObject *object,
                                e_ews_connection_ref_settings (
                                E_EWS_CONNECTION (object)));
                        return;
+
+               case PROP_SOURCE:
+                       g_value_set_object (
+                               value,
+                               e_ews_connection_get_source (
+                               E_EWS_CONNECTION (object)));
+                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
 static void
+ews_connection_constructed (GObject *object)
+{
+       EEwsConnection *cnc = E_EWS_CONNECTION (object);
+       gint log_level;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_ews_connection_parent_class)->constructed (object);
+
+       cnc->priv->soup_thread = g_thread_new (NULL, e_ews_soup_thread, cnc);
+
+       cnc->priv->soup_session = soup_session_async_new_with_options (
+               SOUP_SESSION_ASYNC_CONTEXT, cnc->priv->soup_context,
+               NULL);
+
+       /* Do not use G_BINDING_SYNC_CREATE because the property_lock is
+        * not initialized and we don't have a GProxyResolver yet anyway. */
+       e_binding_bind_property (
+               cnc, "proxy-resolver",
+               cnc->priv->soup_session, "proxy-resolver",
+               G_BINDING_DEFAULT);
+
+       cnc->priv->version = E_EWS_EXCHANGE_UNKNOWN;
+
+       log_level = e_ews_debug_get_log_level ();
+
+       if (log_level >= 2) {
+               SoupLogger *logger;
+               logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+
+               if (log_level >= 3) {
+                       soup_logger_set_printer (logger, e_ews_soup_log_printer, NULL, NULL);
+                       g_log_set_handler (
+                               G_LOG_DOMAIN,
+                               G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
+                               G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO,
+                               e_ews_debug_handler, cnc);
+               }
+
+               soup_session_add_feature (
+                       cnc->priv->soup_session,
+                       SOUP_SESSION_FEATURE (logger));
+               g_object_unref (logger);
+       }
+
+       soup_session_add_feature_by_type (cnc->priv->soup_session,
+                                         SOUP_TYPE_COOKIE_JAR);
+
+       g_signal_connect (
+               cnc->priv->soup_session, "authenticate",
+               G_CALLBACK (ews_connection_authenticate), cnc);
+
+       e_ews_connection_utils_prepare_auth_method (cnc->priv->soup_session,
+               camel_ews_settings_get_auth_mechanism (cnc->priv->settings));
+}
+
+static void
 ews_connection_dispose (GObject *object)
 {
        EEwsConnectionPrivate *priv;
@@ -1802,11 +1911,8 @@ ews_connection_dispose (GObject *object)
        }
 
        g_clear_object (&priv->proxy_resolver);
-
-       if (priv->settings != NULL) {
-               g_object_unref (priv->settings);
-               priv->settings = NULL;
-       }
+       g_clear_object (&priv->source);
+       g_clear_object (&priv->settings);
 
        e_ews_connection_set_password (E_EWS_CONNECTION (object), NULL);
 
@@ -1841,6 +1947,8 @@ ews_connection_finalize (GObject *object)
        g_free (priv->hash_key);
        g_free (priv->impersonate_user);
 
+       g_clear_object (&priv->bearer_auth);
+
        g_mutex_clear (&priv->property_lock);
        g_rec_mutex_clear (&priv->queue_lock);
        g_mutex_clear (&priv->notification_lock);
@@ -1849,33 +1957,6 @@ ews_connection_finalize (GObject *object)
        G_OBJECT_CLASS (e_ews_connection_parent_class)->finalize (object);
 }
 
-static GObject *
-ews_connection_constructor (GType gtype, guint n_properties,
-                           GObjectConstructParam *properties)
-{
-       GObject *obj = G_OBJECT_CLASS (e_ews_connection_parent_class)->
-               constructor (gtype, n_properties, properties);
-       EEwsConnectionPrivate *priv = E_EWS_CONNECTION_GET_PRIVATE (obj);
-       EwsAuthType mech;
-
-       mech = camel_ews_settings_get_auth_mechanism (priv->settings);
-
-       /* We used to disable Basic auth to avoid it getting in the way of
-        * our GSSAPI hacks. But leave it enabled in the case where NTLM is
-        * enabled, which is the default configuration. It's a useful fallback
-        * which people may be relying on. */
-       if (mech == EWS_AUTH_TYPE_GSSAPI) {
-               soup_session_add_feature_by_type (priv->soup_session,
-                                                 E_SOUP_TYPE_AUTH_NEGOTIATE);
-               soup_session_remove_feature_by_type (priv->soup_session,
-                                                    SOUP_TYPE_AUTH_BASIC);
-       } else if (mech == EWS_AUTH_TYPE_NTLM)
-               soup_session_add_feature_by_type (priv->soup_session,
-                                                 SOUP_TYPE_AUTH_NTLM);
-
-       return obj;
-}
-
 static void
 e_ews_connection_class_init (EEwsConnectionClass *class)
 {
@@ -1884,9 +1965,9 @@ e_ews_connection_class_init (EEwsConnectionClass *class)
        g_type_class_add_private (class, sizeof (EEwsConnectionPrivate));
 
        object_class = G_OBJECT_CLASS (class);
-       object_class->constructor = ews_connection_constructor;
        object_class->set_property = ews_connection_set_property;
        object_class->get_property = ews_connection_get_property;
+       object_class->constructed = ews_connection_constructed;
        object_class->dispose = ews_connection_dispose;
        object_class->finalize = ews_connection_finalize;
 
@@ -1924,6 +2005,18 @@ e_ews_connection_class_init (EEwsConnectionClass *class)
                        G_PARAM_CONSTRUCT_ONLY |
                        G_PARAM_STATIC_STRINGS));
 
+       g_object_class_install_property (
+               object_class,
+               PROP_SOURCE,
+               g_param_spec_object (
+                       "source",
+                       "Source",
+                       "Corresponding ESource",
+                       E_TYPE_SOURCE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
        signals[SERVER_NOTIFICATION] = g_signal_new (
                "server-notification",
                G_OBJECT_CLASS_TYPE (object_class),
@@ -1950,118 +2043,14 @@ e_ews_connection_folders_list_free (gpointer data)
        g_slist_free_full ((GSList *) data, g_free);
 }
 
-static gpointer
-e_ews_soup_thread (gpointer user_data)
-{
-       EEwsConnection *cnc = user_data;
-
-       g_main_context_push_thread_default (cnc->priv->soup_context);
-       g_main_loop_run (cnc->priv->soup_loop);
-       g_main_context_pop_thread_default (cnc->priv->soup_context);
-
-       /* abort any pending operations */
-       soup_session_abort (cnc->priv->soup_session);
-
-       g_object_unref (cnc->priv->soup_session);
-       cnc->priv->soup_session = NULL;
-
-       return NULL;
-}
-
-/*
- * e_ews_debug_handler:
- *
- * GLib debug message handler, which is passed all messages from g_debug() calls,
- * and decides whether to print them.
- */
-static void
-e_ews_debug_handler (const gchar *log_domain,
-                    GLogLevelFlags log_level,
-                    const gchar *message,
-                    gpointer user_data)
-{
-       if (e_ews_debug_get_log_level () >= 3)
-               g_log_default_handler (log_domain, log_level, message, NULL);
-}
-
-/*
- * e_ews_soup_log_print:
- *
- * Log printer for the libsoup logging functionality, which just marshal all soup log
- * output to the standard GLib logging framework (and thus to debug_handler(), above).
- */
-static void
-e_ews_soup_log_printer (SoupLogger *logger,
-                       SoupLoggerLogLevel level,
-                       char direction,
-                       const gchar *data,
-                       gpointer user_data)
-{
-       const gchar *filtered_data = NULL;
-
-       if (e_ews_debug_get_log_level () >= 3) {
-               if (direction == '>' && g_ascii_strncasecmp (data, "Host:", 5) == 0)
-                       filtered_data = "Host: <redacted>";
-               else if (direction == '>' && g_ascii_strncasecmp (data, "Authorization:", 14) == 0)
-                       filtered_data = "Authorization: <redacted>";
-               else if (direction == '<' && g_ascii_strncasecmp (data, "Set-Cookie:", 11) == 0)
-                       filtered_data = "Set-Cookie: <redacted>";
-               else
-                       filtered_data = data;
-       }
-
-       g_debug ("%c %s", direction, filtered_data ? filtered_data : data);
-}
-
 static void
 e_ews_connection_init (EEwsConnection *cnc)
 {
-       gint log_level;
-
        cnc->priv = E_EWS_CONNECTION_GET_PRIVATE (cnc);
 
        cnc->priv->soup_context = g_main_context_new ();
        cnc->priv->soup_loop = g_main_loop_new (cnc->priv->soup_context, FALSE);
 
-       cnc->priv->soup_thread = g_thread_new (NULL, e_ews_soup_thread, cnc);
-
-       cnc->priv->soup_session = soup_session_async_new_with_options (
-               SOUP_SESSION_ASYNC_CONTEXT, cnc->priv->soup_context,
-               NULL);
-
-       /* Do not use G_BINDING_SYNC_CREATE because the property_lock is
-        * not initialized and we don't have a GProxyResolver yet anyway. */
-       e_binding_bind_property (
-               cnc, "proxy-resolver",
-               cnc->priv->soup_session, "proxy-resolver",
-               G_BINDING_DEFAULT);
-
-       cnc->priv->version = E_EWS_EXCHANGE_UNKNOWN;
-
-       log_level = e_ews_debug_get_log_level ();
-
-       if (log_level >= 2) {
-               SoupLogger *logger;
-               logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
-
-               if (log_level >= 3) {
-                       soup_logger_set_printer (logger, e_ews_soup_log_printer, NULL, NULL);
-                       g_log_set_handler (
-                               G_LOG_DOMAIN,
-                               G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
-                               G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO,
-                               e_ews_debug_handler, cnc);
-               }
-
-               soup_session_add_feature (
-                       cnc->priv->soup_session,
-                       SOUP_SESSION_FEATURE (logger));
-               g_object_unref (logger);
-       }
-
-       soup_session_add_feature_by_type (cnc->priv->soup_session,
-                                         SOUP_TYPE_COOKIE_JAR);
-
        cnc->priv->subscriptions = g_hash_table_new_full (
                        g_direct_hash, g_direct_equal,
                        NULL, e_ews_connection_folders_list_free);
@@ -2069,10 +2058,6 @@ e_ews_connection_init (EEwsConnection *cnc)
        g_mutex_init (&cnc->priv->property_lock);
        g_rec_mutex_init (&cnc->priv->queue_lock);
        g_mutex_init (&cnc->priv->notification_lock);
-
-       g_signal_connect (
-               cnc->priv->soup_session, "authenticate",
-               G_CALLBACK (ews_connection_authenticate), cnc);
 }
 
 static void
@@ -2083,53 +2068,10 @@ ews_connection_authenticate (SoupSession *sess,
                              gpointer data)
 {
        EEwsConnection *cnc = data;
-       CamelNetworkSettings *network_settings;
-       gchar *user, *password, *service_url = NULL;
-       gboolean expired = FALSE;
 
        g_return_if_fail (cnc != NULL);
 
-       if (retrying)
-               e_ews_connection_set_password (cnc, NULL);
-
-       if (ews_connection_check_x_ms_credential_headers (msg, NULL, &expired, &service_url) && expired) {
-               GError *error = NULL;
-
-               ews_connection_expired_password_to_error (service_url, &error);
-
-               if (error)
-                       soup_message_set_status_full (msg, SOUP_STATUS_IO_ERROR, error->message);
-
-               g_clear_error (&error);
-               g_free (service_url);
-
-               return;
-       }
-
-       g_free (service_url);
-
-       network_settings = CAMEL_NETWORK_SETTINGS (cnc->priv->settings);
-       user = camel_network_settings_dup_user (network_settings);
-
-       password = e_ews_connection_dup_password (cnc);
-       if (password != NULL) {
-               soup_auth_authenticate (auth, user, password);
-       } else {
-               /* The NTLM implementation in libsoup doesn't cope very well
-                * with recovering from authentication failures (bug 703181).
-                * So cancel the message now while it's in-flight, and we'll
-                * get a shiny new connection for the next attempt. */
-               const char *scheme = soup_auth_get_scheme_name (auth);
-
-               if (!g_ascii_strcasecmp(scheme, "NTLM")) {
-                       soup_session_cancel_message(cnc->priv->soup_session,
-                                                   msg,
-                                                   SOUP_STATUS_UNAUTHORIZED);
-               }
-       }
-
-       g_free (password);
-       g_free (user);
+       e_ews_connection_utils_authenticate (cnc, sess, msg, auth, retrying);
 }
 
 void
@@ -2419,6 +2361,7 @@ e_ews_connection_list_existing (void)
 
 /**
  * e_ews_connection_new_full
+ * @source: corresponding #ESource
  * @uri: Exchange server uri
  * @settings: a #CamelEwsSettings
  * @allow_connection_reuse: whether can return already created connection
@@ -2429,7 +2372,8 @@ e_ews_connection_list_existing (void)
  * Returns: EEwsConnection
  **/
 EEwsConnection *
-e_ews_connection_new_full (const gchar *uri,
+e_ews_connection_new_full (ESource *source,
+                          const gchar *uri,
                           CamelEwsSettings *settings,
                           gboolean allow_connection_reuse)
 {
@@ -2438,6 +2382,8 @@ e_ews_connection_new_full (const gchar *uri,
        gchar *hash_key;
        gchar *user;
 
+       if (source)
+               g_return_val_if_fail (E_IS_SOURCE (source), NULL);
        g_return_val_if_fail (uri != NULL, NULL);
        g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL);
 
@@ -2464,9 +2410,10 @@ e_ews_connection_new_full (const gchar *uri,
        }
 
        /* not found, so create a new connection */
-       cnc = g_object_new (
-               E_TYPE_EWS_CONNECTION,
-               "settings", settings, NULL);
+       cnc = g_object_new (E_TYPE_EWS_CONNECTION,
+               "settings", settings,
+               "source", source,
+               NULL);
 
        cnc->priv->uri = g_strdup (uri);
        cnc->priv->hash_key = hash_key;  /* takes ownership */
@@ -2505,10 +2452,11 @@ e_ews_connection_new_full (const gchar *uri,
 }
 
 EEwsConnection *
-e_ews_connection_new (const gchar *uri,
+e_ews_connection_new (ESource *source,
+                     const gchar *uri,
                      CamelEwsSettings *settings)
 {
-       return e_ews_connection_new_full (uri, settings, TRUE);
+       return e_ews_connection_new_full (source, uri, settings, TRUE);
 }
 
 void
@@ -2574,8 +2522,9 @@ e_ews_connection_try_credentials_sync (EEwsConnection *cnc,
                if (auth_failed) {
                        g_clear_error (&local_error);
 
-                       if (camel_ews_settings_get_auth_mechanism (cnc->priv->settings) != 
EWS_AUTH_TYPE_GSSAPI && (!credentials ||
-                           !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD))) {
+                       if (camel_ews_settings_get_auth_mechanism (cnc->priv->settings) != 
EWS_AUTH_TYPE_GSSAPI &&
+                           camel_ews_settings_get_auth_mechanism (cnc->priv->settings) != 
EWS_AUTH_TYPE_OAUTH2 &&
+                           (!credentials || !e_named_parameters_exists (credentials, 
E_SOURCE_CREDENTIAL_PASSWORD))) {
                                result = E_SOURCE_AUTHENTICATION_REQUIRED;
                        } else {
                                result = E_SOURCE_AUTHENTICATION_REJECTED;
@@ -2591,6 +2540,14 @@ e_ews_connection_try_credentials_sync (EEwsConnection *cnc,
        return result;
 }
 
+ESource *
+e_ews_connection_get_source (EEwsConnection *cnc)
+{
+       g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL);
+
+       return cnc->priv->source;
+}
+
 const gchar *
 e_ews_connection_get_uri (EEwsConnection *cnc)
 {
@@ -2599,6 +2556,43 @@ e_ews_connection_get_uri (EEwsConnection *cnc)
        return cnc->priv->uri;
 }
 
+ESoupAuthBearer *
+e_ews_connection_ref_bearer_auth (EEwsConnection *cnc)
+{
+       ESoupAuthBearer *bearer_auth;
+
+       g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL);
+
+       g_mutex_lock (&cnc->priv->property_lock);
+       bearer_auth = cnc->priv->bearer_auth;
+       if (bearer_auth)
+               g_object_ref (bearer_auth);
+       g_mutex_unlock (&cnc->priv->property_lock);
+
+       return bearer_auth;
+}
+
+void
+e_ews_connection_set_bearer_auth (EEwsConnection *cnc,
+                                 ESoupAuthBearer *bearer_auth)
+{
+       g_return_if_fail (E_IS_EWS_CONNECTION (cnc));
+       if (bearer_auth)
+               g_return_if_fail (E_IS_SOUP_AUTH_BEARER (bearer_auth));
+
+       g_mutex_lock (&cnc->priv->property_lock);
+
+       if (bearer_auth != cnc->priv->bearer_auth) {
+               g_clear_object (&cnc->priv->bearer_auth);
+               cnc->priv->bearer_auth = bearer_auth;
+
+               if (cnc->priv->bearer_auth)
+                       g_object_ref (cnc->priv->bearer_auth);
+       }
+
+       g_mutex_unlock (&cnc->priv->property_lock);
+}
+
 const gchar *
 e_ews_connection_get_password (EEwsConnection *cnc)
 {
@@ -2819,8 +2813,8 @@ autodiscover_response_cb (SoupSession *session,
                gboolean expired = FALSE;
                gchar *service_url = NULL;
 
-               if (ews_connection_check_x_ms_credential_headers (msg, NULL, &expired, &service_url) && 
expired) {
-                       ews_connection_expired_password_to_error (service_url, &error);
+               if (e_ews_connection_utils_check_x_ms_credential_headers (msg, NULL, &expired, &service_url) 
&& expired) {
+                       e_ews_connection_utils_expired_password_to_error (service_url, &error);
                } else {
                        g_set_error (
                                &error, SOUP_HTTP_ERROR, status,
@@ -3022,7 +3016,8 @@ e_ews_get_msg_for_url (CamelEwsSettings *settings,
 }
 
 gboolean
-e_ews_autodiscover_ws_url_sync (CamelEwsSettings *settings,
+e_ews_autodiscover_ws_url_sync (ESource *source,
+                               CamelEwsSettings *settings,
                                 const gchar *email_address,
                                 const gchar *password,
                                 GCancellable *cancellable,
@@ -3038,8 +3033,7 @@ e_ews_autodiscover_ws_url_sync (CamelEwsSettings *settings,
 
        closure = e_async_closure_new ();
 
-       e_ews_autodiscover_ws_url (
-               settings, email_address, password, cancellable,
+       e_ews_autodiscover_ws_url (source, settings, email_address, password, cancellable,
                e_async_closure_callback, closure);
 
        result = e_async_closure_wait (closure);
@@ -3052,7 +3046,8 @@ e_ews_autodiscover_ws_url_sync (CamelEwsSettings *settings,
 }
 
 void
-e_ews_autodiscover_ws_url (CamelEwsSettings *settings,
+e_ews_autodiscover_ws_url (ESource *source,
+                          CamelEwsSettings *settings,
                            const gchar *email_address,
                            const gchar *password,
                            GCancellable *cancellable,
@@ -3119,7 +3114,7 @@ e_ews_autodiscover_ws_url (CamelEwsSettings *settings,
        url3 = g_strdup_printf ("http%s://%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain);
        url4 = g_strdup_printf ("http%s://autodiscover.%s/autodiscover/autodiscover.xml", use_secure ? "s" : 
"", domain);
 
-       cnc = e_ews_connection_new (url3, settings);
+       cnc = e_ews_connection_new (source, url3, settings);
        e_ews_connection_set_password (cnc, password);
 
        /*
@@ -9582,6 +9577,7 @@ ews_connection_gather_auth_methods_cb (SoupMessage *message,
 {
        EwsAsyncData *async_data;
        const gchar *auths_lst;
+       gboolean has_bearer = FALSE;
        gchar **auths;
        gint ii;
 
@@ -9603,6 +9599,7 @@ ews_connection_gather_auth_methods_cb (SoupMessage *message,
                        if (space)
                                *space = '\0';
 
+                       has_bearer = has_bearer || g_ascii_strcasecmp (auth, "Bearer") == 0;
                        async_data->items = g_slist_prepend (async_data->items, auth);
                } else {
                        g_free (auth);
@@ -9611,6 +9608,17 @@ ews_connection_gather_auth_methods_cb (SoupMessage *message,
 
        g_strfreev (auths);
 
+       if (!has_bearer) {
+               /* Special-case Office365 OAuth2, because outlook.office365.com doesn't advertise Bearer */
+               SoupURI *suri;
+
+               suri = soup_message_get_uri (message);
+               if (suri && soup_uri_get_host (suri) &&
+                   g_ascii_strcasecmp (soup_uri_get_host (suri), "outlook.office365.com") == 0) {
+                       async_data->items = g_slist_prepend (async_data->items, g_strdup ("Bearer"));
+               }
+       }
+
        g_object_set_data (G_OBJECT (simple), EWS_OBJECT_KEY_AUTHS_GATHERED, GINT_TO_POINTER (1));
 
        soup_message_set_status_full (message, SOUP_STATUS_CANCELLED, "EWS auths gathered");
diff --git a/src/server/e-ews-connection.h b/src/server/e-ews-connection.h
index af74708..ab3e298 100644
--- a/src/server/e-ews-connection.h
+++ b/src/server/e-ews-connection.h
@@ -27,6 +27,8 @@
 #include <glib-object.h>
 #include <gio/gio.h>
 #include <libsoup/soup.h>
+#include <libedataserver/libedataserver.h>
+
 #include "e-soap-message.h"
 #include "ews-errors.h"
 #include "e-ews-folder.h"
@@ -404,9 +406,11 @@ void               ews_oal_free                    (EwsOAL *oal);
 void           ews_oal_details_free            (EwsOALDetails *details);
 
 GType          e_ews_connection_get_type       (void);
-EEwsConnection *e_ews_connection_new           (const gchar *uri,
+EEwsConnection *e_ews_connection_new           (ESource *source,
+                                                const gchar *uri,
                                                 CamelEwsSettings *settings);
-EEwsConnection *e_ews_connection_new_full      (const gchar *uri,
+EEwsConnection *e_ews_connection_new_full      (ESource *source,
+                                                const gchar *uri,
                                                 CamelEwsSettings *settings,
                                                 gboolean allow_connection_reuse);
 void           e_ews_connection_update_credentials
@@ -418,7 +422,12 @@ ESourceAuthenticationResult
                                                 const ENamedParameters *credentials,
                                                 GCancellable *cancellable,
                                                 GError **error);
+ESource *      e_ews_connection_get_source     (EEwsConnection *cnc);
 const gchar *  e_ews_connection_get_uri        (EEwsConnection *cnc);
+ESoupAuthBearer *
+               e_ews_connection_ref_bearer_auth(EEwsConnection *cnc);
+void           e_ews_connection_set_bearer_auth(EEwsConnection *cnc,
+                                                ESoupAuthBearer *bearer_auth);
 const gchar *  e_ews_connection_get_password   (EEwsConnection *cnc);
 gchar *                e_ews_connection_dup_password   (EEwsConnection *cnc);
 void           e_ews_connection_set_password   (EEwsConnection *cnc,
@@ -445,12 +454,14 @@ void              e_ews_connection_queue_request  (EEwsConnection *cnc,
                                                 GCancellable *cancellable,
                                                 GSimpleAsyncResult *simple);
 
-gboolean       e_ews_autodiscover_ws_url_sync  (CamelEwsSettings *settings,
+gboolean       e_ews_autodiscover_ws_url_sync  (ESource *source,
+                                                CamelEwsSettings *settings,
                                                 const gchar *email_address,
                                                 const gchar *password,
                                                 GCancellable *cancellable,
                                                 GError **error);
-void           e_ews_autodiscover_ws_url       (CamelEwsSettings *settings,
+void           e_ews_autodiscover_ws_url       (ESource *source,
+                                                CamelEwsSettings *settings,
                                                 const gchar *email_address,
                                                 const gchar *password,
                                                 GCancellable *cancellable,
diff --git a/src/server/e-ews-notification.c b/src/server/e-ews-notification.c
index da63d01..0dde2ba 100644
--- a/src/server/e-ews-notification.c
+++ b/src/server/e-ews-notification.c
@@ -69,43 +69,11 @@ ews_notification_authenticate (SoupSession *session,
                               gpointer data)
 {
        EEwsNotification *notification = data;
-       EEwsConnection *connection;
-       CamelNetworkSettings *network_settings;
-       gchar *user, *password;
 
        g_return_if_fail (notification != NULL);
        g_return_if_fail (notification->priv->connection != NULL);
 
-       connection = notification->priv->connection;
-
-       if (retrying)
-               e_ews_connection_set_password (connection, NULL);
-
-       network_settings = CAMEL_NETWORK_SETTINGS (e_ews_connection_ref_settings (connection));
-       user = camel_network_settings_dup_user (network_settings);
-
-       password = e_ews_connection_dup_password (connection);
-
-       if (password != NULL) {
-               soup_auth_authenticate (auth, user, password);
-       } else {
-               /* The NTLM implementation in libsoup doesn't cope very well
-                * with recovering from authentication failures (bug 703181).
-                * So cancel the message now while it's in-flight, and we'll
-                * get a shiny new connection for the next attempt. */
-               const char *scheme = soup_auth_get_scheme_name (auth);
-
-               if (!g_ascii_strcasecmp(scheme, "NTLM")) {
-                       soup_session_cancel_message(notification->priv->soup_session,
-                                                   message,
-                                                   SOUP_STATUS_UNAUTHORIZED);
-               }
-       }
-
-       g_free (password);
-       g_free (user);
-       g_object_unref (network_settings);
-
+       e_ews_connection_utils_authenticate (notification->priv->connection, session, message, auth, 
retrying);
 }
 
 EEwsNotification *
@@ -178,6 +146,24 @@ ews_notification_get_property (GObject *object,
 }
 
 static void
+ews_notification_constructed (GObject *object)
+{
+       EEwsNotification *notif;
+       CamelEwsSettings *ews_settings;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_ews_notification_parent_class)->constructed (object);
+
+       notif = E_EWS_NOTIFICATION (object);
+       ews_settings = e_ews_connection_ref_settings (notif->priv->connection);
+
+       e_ews_connection_utils_prepare_auth_method (notif->priv->soup_session,
+               camel_ews_settings_get_auth_mechanism (ews_settings));
+
+       g_object_unref (ews_settings);
+}
+
+static void
 ews_notification_dispose (GObject *object)
 {
        EEwsNotificationPrivate *priv;
@@ -209,38 +195,6 @@ ews_notification_dispose (GObject *object)
        G_OBJECT_CLASS (e_ews_notification_parent_class)->dispose (object);
 }
 
-static GObject *
-ews_notification_constructor (GType gtype, guint n_properties,
-                             GObjectConstructParam *properties)
-{
-       GObject *obj = G_OBJECT_CLASS (e_ews_notification_parent_class)->
-               constructor (gtype, n_properties, properties);
-       EEwsNotificationPrivate *priv;
-       CamelEwsSettings *ews_settings;
-       EwsAuthType mech;
-
-       priv = E_EWS_NOTIFICATION_GET_PRIVATE (obj);
-       ews_settings = e_ews_connection_ref_settings (priv->connection);
-       mech = camel_ews_settings_get_auth_mechanism (ews_settings);
-
-       g_object_unref (ews_settings);
-
-       /* We used to disable Basic auth to avoid it getting in the way of
-        * our GSSAPI hacks. But leave it enabled in the case where NTLM is
-        * enabled, which is the default configuration. It's a useful fallback
-        * which people may be relying on. */
-       if (mech == EWS_AUTH_TYPE_GSSAPI) {
-               soup_session_add_feature_by_type (priv->soup_session,
-                                                 E_SOUP_TYPE_AUTH_NEGOTIATE);
-               soup_session_remove_feature_by_type (priv->soup_session,
-                                                    SOUP_TYPE_AUTH_BASIC);
-       } else if (mech == EWS_AUTH_TYPE_NTLM)
-               soup_session_add_feature_by_type (priv->soup_session,
-                                                 SOUP_TYPE_AUTH_NTLM);
-
-       return obj;
-}
-
 static void
 e_ews_notification_class_init (EEwsNotificationClass *class)
 {
@@ -249,9 +203,9 @@ e_ews_notification_class_init (EEwsNotificationClass *class)
        g_type_class_add_private (class, sizeof (EEwsNotificationPrivate));
 
        object_class = G_OBJECT_CLASS (class);
-       object_class->constructor = ews_notification_constructor;
        object_class->set_property = ews_notification_set_property;
        object_class->get_property = ews_notification_get_property;
+       object_class->constructed = ews_notification_constructed;
        object_class->dispose = ews_notification_dispose;
 
        g_object_class_install_property (
diff --git a/src/server/e-oauth2-service-office365.c b/src/server/e-oauth2-service-office365.c
new file mode 100644
index 0000000..e629845
--- /dev/null
+++ b/src/server/e-oauth2-service-office365.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+#include <libedataserver/libedataserver.h>
+
+#include "server/camel-ews-settings.h"
+
+#include "e-oauth2-service-office365.h"
+
+/* https://portal.azure.com/
+   https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-developers-guide
+   https://tsmatz.wordpress.com/2016/10/07/application-permission-with-v2-endpoint-and-microsoft-graph/
+*/
+
+#define OFFICE365_SCOPE "offline_access " \
+       "https://outlook.office.com/Mail.ReadWrite " \
+       "https://outlook.office.com/Mail.Send " \
+       "https://outlook.office.com/ReadWrite " \
+       "https://outlook.office.com/ReadWrite " \
+       "https://outlook.office.com/Tasks.ReadWrite";
+
+       /*"https://outlook.office.com/Mail.ReadWrite.Shared " \
+       "https://outlook.office.com/Mail.Send.Shared " \
+       "https://outlook.office.com/Calendars.ReadWrite.Shared " \
+       "https://outlook.office.com/Contacts.ReadWrite.Shared " \
+       "https://outlook.office.com/Tasks.ReadWrite.Shared"*/
+
+struct _EOAuth2ServiceOffice365Private
+{
+       GMutex string_cache_lock;
+       GHashTable *string_cache;
+};
+
+/* Forward Declarations */
+static void e_oauth2_service_office365_oauth2_service_init (EOAuth2ServiceInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (EOAuth2ServiceOffice365, e_oauth2_service_office365, 
E_TYPE_OAUTH2_SERVICE_BASE, 0,
+       G_IMPLEMENT_INTERFACE_DYNAMIC (E_TYPE_OAUTH2_SERVICE, e_oauth2_service_office365_oauth2_service_init))
+
+static const gchar *
+eos_office365_cache_string (EOAuth2ServiceOffice365 *oauth2_office365,
+                           gchar *str) /* takes ownership of the 'str' */
+{
+       const gchar *cached_str;
+
+       g_return_val_if_fail (E_IS_OAUTH2_SERVICE_OFFICE365 (oauth2_office365), NULL);
+
+       if (!str)
+               return NULL;
+
+       if (!*str)
+               return "";
+
+       g_mutex_lock (&oauth2_office365->priv->string_cache_lock);
+
+       cached_str = g_hash_table_lookup (oauth2_office365->priv->string_cache, str);
+       if (cached_str) {
+               g_free (str);
+       } else {
+               g_hash_table_insert (oauth2_office365->priv->string_cache, str, str);
+               cached_str = str;
+       }
+
+       g_mutex_unlock (&oauth2_office365->priv->string_cache_lock);
+
+       return cached_str;
+}
+
+static CamelEwsSettings *
+eos_office365_get_camel_settings (ESource *source)
+{
+       ESourceCamel *extension;
+
+       if (!source)
+               return NULL;
+
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+       extension = e_source_get_extension (source, e_source_camel_get_extension_name ("ews"));
+
+       return CAMEL_EWS_SETTINGS (e_source_camel_get_settings (extension));
+}
+
+static gboolean
+eos_office365_guess_can_process (EOAuth2Service *service,
+                                const gchar *protocol,
+                                const gchar *hostname)
+{
+       return e_oauth2_services_is_supported () && g_getenv ("EWS_WITH_OAUTH2") &&
+               protocol && g_ascii_strcasecmp (protocol, "ews") == 0 &&
+               hostname && e_util_utf8_strstrcase (hostname, "outlook.office365.com");
+}
+
+static const gchar *
+eos_office365_get_name (EOAuth2Service *service)
+{
+       return "Office365";
+}
+
+static const gchar *
+eos_office365_get_display_name (EOAuth2Service *service)
+{
+       /* Translators: This is a user-visible string, display name of an OAuth2 service. */
+       return C_("OAuth2Service", "Office365");
+}
+
+static const gchar *
+eos_office365_get_client_id (EOAuth2Service *service,
+                            ESource *source)
+{
+       EOAuth2ServiceOffice365 *oauth2_office365 = E_OAUTH2_SERVICE_OFFICE365 (service);
+       CamelEwsSettings *ews_settings;
+
+       ews_settings = eos_office365_get_camel_settings (source);
+       if (ews_settings && camel_ews_settings_get_override_oauth2 (ews_settings)) {
+               gchar *client_id = camel_ews_settings_dup_oauth2_client_id (ews_settings);
+
+               if (client_id && !*client_id) {
+                       g_free (client_id);
+                       client_id = NULL;
+               }
+
+               if (client_id)
+                       return eos_office365_cache_string (oauth2_office365, client_id);
+       }
+
+       return OFFICE365_CLIENT_ID;
+}
+
+static const gchar *
+eos_office365_get_client_secret (EOAuth2Service *service,
+                                ESource *source)
+{
+       return NULL;
+}
+
+static const gchar *
+eos_office365_get_authentication_uri (EOAuth2Service *service,
+                                     ESource *source)
+{
+       EOAuth2ServiceOffice365 *oauth2_office365 = E_OAUTH2_SERVICE_OFFICE365 (service);
+       CamelEwsSettings *ews_settings;
+
+       ews_settings = eos_office365_get_camel_settings (source);
+       if (ews_settings && camel_ews_settings_get_override_oauth2 (ews_settings)) {
+               gchar *tenant;
+               const gchar *res;
+
+               tenant = camel_ews_settings_dup_oauth2_tenant (ews_settings);
+               if (tenant && !*tenant) {
+                       g_free (tenant);
+                       tenant = NULL;
+               }
+
+               res = eos_office365_cache_string (oauth2_office365,
+                       g_strdup_printf ("https://login.microsoftonline.com/%s/oauth2/v2.0/authorize";,
+                               tenant ? tenant : OFFICE365_TENANT));
+
+               g_free (tenant);
+
+               return res;
+       }
+
+       return "https://login.microsoftonline.com/"; OFFICE365_TENANT "/oauth2/v2.0/authorize";
+}
+
+static const gchar *
+eos_office365_get_refresh_uri (EOAuth2Service *service,
+                              ESource *source)
+{
+       EOAuth2ServiceOffice365 *oauth2_office365 = E_OAUTH2_SERVICE_OFFICE365 (service);
+       CamelEwsSettings *ews_settings;
+
+       ews_settings = eos_office365_get_camel_settings (source);
+       if (ews_settings && camel_ews_settings_get_override_oauth2 (ews_settings)) {
+               gchar *tenant;
+               const gchar *res;
+
+               tenant = camel_ews_settings_dup_oauth2_tenant (ews_settings);
+               if (tenant && !*tenant) {
+                       g_free (tenant);
+                       tenant = NULL;
+               }
+
+               res = eos_office365_cache_string (oauth2_office365,
+                       g_strdup_printf ("https://login.microsoftonline.com/%s/oauth2/v2.0/token";,
+                               tenant ? tenant : OFFICE365_TENANT));
+
+               g_free (tenant);
+
+               return res;
+       }
+
+       return "https://login.microsoftonline.com/"; OFFICE365_TENANT "/oauth2/v2.0/token";
+}
+
+static const gchar *
+eos_office365_get_redirect_uri (EOAuth2Service *service,
+                               ESource *source)
+{
+       EOAuth2ServiceOffice365 *oauth2_office365 = E_OAUTH2_SERVICE_OFFICE365 (service);
+       CamelEwsSettings *ews_settings;
+       const gchar *res;
+
+       ews_settings = eos_office365_get_camel_settings (source);
+       if (ews_settings && camel_ews_settings_get_override_oauth2 (ews_settings)) {
+               gchar *redirect_uri;
+
+               redirect_uri = camel_ews_settings_dup_oauth2_redirect_uri (ews_settings);
+
+               if (redirect_uri && !*redirect_uri) {
+                       g_free (redirect_uri);
+                       redirect_uri = NULL;
+               }
+
+               if (redirect_uri)
+                       return eos_office365_cache_string (oauth2_office365, redirect_uri);
+       }
+
+       res = OFFICE365_REDIRECT_URI;
+       if (res && *res)
+               return res;
+
+       return "https://login.microsoftonline.com/common/oauth2/v2.0/nativeclient";;
+}
+
+static void
+eos_office365_prepare_authentication_uri_query (EOAuth2Service *service,
+                                               ESource *source,
+                                               GHashTable *uri_query)
+{
+       g_return_if_fail (uri_query != NULL);
+
+       e_oauth2_service_util_set_to_form (uri_query, "response_mode", "query");
+       e_oauth2_service_util_set_to_form (uri_query, "scope", OFFICE365_SCOPE);
+}
+
+static gboolean
+eos_office365_extract_authorization_code (EOAuth2Service *service,
+                                         ESource *source,
+                                         const gchar *page_title,
+                                         const gchar *page_uri,
+                                         const gchar *page_content,
+                                         gchar **out_authorization_code)
+{
+       SoupURI *suri;
+       gboolean known = FALSE;
+
+       g_return_val_if_fail (out_authorization_code != NULL, FALSE);
+
+       *out_authorization_code = NULL;
+
+       if (!page_uri || !*page_uri)
+               return FALSE;
+
+       suri = soup_uri_new (page_uri);
+       if (!suri)
+               return FALSE;
+
+       if (suri->query) {
+               GHashTable *uri_query = soup_form_decode (suri->query);
+
+               if (uri_query) {
+                       const gchar *code;
+
+                       code = g_hash_table_lookup (uri_query, "code");
+
+                       if (code && *code) {
+                               *out_authorization_code = g_strdup (code);
+                               known = TRUE;
+                       } else if (g_hash_table_lookup (uri_query, "error")) {
+                               known = TRUE;
+                               if (g_strcmp0 (g_hash_table_lookup (uri_query, "error"), "access_denied") != 
0) {
+                                       const gchar *description;
+
+                                       description = g_hash_table_lookup (uri_query, "error_description");
+                                       if (description) {
+                                               g_warning ("%s: error:%s description:%s", G_STRFUNC,
+                                                       (const gchar *) g_hash_table_lookup (uri_query, 
"error"),
+                                                       description);
+                                       }
+                               }
+                       }
+
+                       g_hash_table_unref (uri_query);
+               }
+       }
+
+       soup_uri_free (suri);
+
+       return known;
+}
+
+static void
+eos_office365_prepare_refresh_token_form (EOAuth2Service *service,
+                                         ESource *source,
+                                         const gchar *refresh_token,
+                                         GHashTable *form)
+{
+       g_return_if_fail (form != NULL);
+
+       e_oauth2_service_util_set_to_form (form, "scope", OFFICE365_SCOPE);
+       e_oauth2_service_util_set_to_form (form, "redirect_uri", e_oauth2_service_get_redirect_uri (service, 
source));
+}
+
+static void
+eos_office365_finalize (GObject *object)
+{
+       EOAuth2ServiceOffice365 *oauth2_office365 = E_OAUTH2_SERVICE_OFFICE365 (object);
+
+       g_mutex_lock (&oauth2_office365->priv->string_cache_lock);
+       g_hash_table_destroy (oauth2_office365->priv->string_cache);
+       g_mutex_unlock (&oauth2_office365->priv->string_cache_lock);
+       g_mutex_clear (&oauth2_office365->priv->string_cache_lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_oauth2_service_office365_parent_class)->finalize (object);
+}
+
+static void
+e_oauth2_service_office365_oauth2_service_init (EOAuth2ServiceInterface *iface)
+{
+       iface->guess_can_process = eos_office365_guess_can_process;
+       iface->get_name = eos_office365_get_name;
+       iface->get_display_name = eos_office365_get_display_name;
+       iface->get_client_id = eos_office365_get_client_id;
+       iface->get_client_secret = eos_office365_get_client_secret;
+       iface->get_authentication_uri = eos_office365_get_authentication_uri;
+       iface->get_refresh_uri = eos_office365_get_refresh_uri;
+       iface->get_redirect_uri = eos_office365_get_redirect_uri;
+       iface->prepare_authentication_uri_query = eos_office365_prepare_authentication_uri_query;
+       iface->extract_authorization_code = eos_office365_extract_authorization_code;
+       iface->prepare_refresh_token_form = eos_office365_prepare_refresh_token_form;
+}
+
+static void
+e_oauth2_service_office365_class_init (EOAuth2ServiceOffice365Class *klass)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (EOAuth2ServiceOffice365Private));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->finalize = eos_office365_finalize;
+}
+
+static void
+e_oauth2_service_office365_class_finalize (EOAuth2ServiceOffice365Class *klass)
+{
+}
+
+static void
+e_oauth2_service_office365_init (EOAuth2ServiceOffice365 *oauth2_office365)
+{
+       oauth2_office365->priv = G_TYPE_INSTANCE_GET_PRIVATE (oauth2_office365, 
E_TYPE_OAUTH2_SERVICE_OFFICE365, EOAuth2ServiceOffice365Private);
+
+       g_mutex_init (&oauth2_office365->priv->string_cache_lock);
+       oauth2_office365->priv->string_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+}
+
+void
+e_oauth2_service_office365_type_register (GTypeModule *type_module)
+{
+       e_oauth2_service_office365_register_type (type_module);
+}
diff --git a/src/server/e-oauth2-service-office365.h b/src/server/e-oauth2-service-office365.h
new file mode 100644
index 0000000..427bfd1
--- /dev/null
+++ b/src/server/e-oauth2-service-office365.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_OAUTH2_SERVICE_OFFICE365_H
+#define E_OAUTH2_SERVICE_OFFICE365_H
+
+#include <gmodule.h>
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_OAUTH2_SERVICE_OFFICE365 \
+       (e_oauth2_service_office365_get_type ())
+#define E_OAUTH2_SERVICE_OFFICE365(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_OAUTH2_SERVICE_OFFICE365, EOAuth2ServiceOffice365))
+#define E_OAUTH2_SERVICE_OFFICE365_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_OAUTH2_SERVICE_OFFICE365, EOAuth2ServiceOffice365Class))
+#define E_IS_OAUTH2_SERVICE_OFFICE365(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_OAUTH2_SERVICE_OFFICE365))
+#define E_IS_OAUTH2_SERVICE_OFFICE365_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_OAUTH2_SERVICE_OFFICE365))
+#define E_OAUTH2_SERVICE_OFFICE365_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_OAUTH2_SERVICE_OFFICE365, EOAuth2ServiceOffice365Class))
+
+G_BEGIN_DECLS
+
+typedef struct _EOAuth2ServiceOffice365 EOAuth2ServiceOffice365;
+typedef struct _EOAuth2ServiceOffice365Class EOAuth2ServiceOffice365Class;
+typedef struct _EOAuth2ServiceOffice365Private EOAuth2ServiceOffice365Private;
+
+struct _EOAuth2ServiceOffice365 {
+       EOAuth2ServiceBase parent;
+       EOAuth2ServiceOffice365Private *priv;
+};
+
+struct _EOAuth2ServiceOffice365Class {
+       EOAuth2ServiceBaseClass parent_class;
+};
+
+GType          e_oauth2_service_office365_get_type     (void) G_GNUC_CONST;
+
+void           e_oauth2_service_office365_type_register
+                                                       (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_OAUTH2_SERVICE_OFFICE365_H */
diff --git a/tests/ews-test-common.c b/tests/ews-test-common.c
index 448f142..21ef472 100644
--- a/tests/ews-test-common.c
+++ b/tests/ews-test-common.c
@@ -245,7 +245,7 @@ ews_test_init (gint argc,
                                NULL);
 
                        etd->version = g_list_nth_data (versions, i);
-                       etd->connection = e_ews_connection_new (g_list_nth_data (server_uris, i), settings);
+                       etd->connection = e_ews_connection_new (NULL, g_list_nth_data (server_uris, i), 
settings);
                        e_ews_connection_set_password (etd->connection, g_list_nth_data (passwords, i));
                        e_ews_connection_set_server_version_from_string (etd->connection, etd->version);
 
@@ -321,7 +321,7 @@ ews_test_set_https_port (UhmServer *server,
                "user", "foo",
                NULL);
 
-       etd->connection = e_ews_connection_new (uri, ews_settings);
+       etd->connection = e_ews_connection_new (NULL, uri, ews_settings);
        e_ews_connection_set_password (etd->connection, "bar");
        e_ews_connection_set_server_version_from_string (etd->connection, etd->version);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]