[evolution-ews/wip/mcrha/office365] Add mostly blank stubs into src/Office365/



commit 5265a45581ca24bd76a3327d63d9e95297239023
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jun 3 15:52:19 2020 +0200

    Add mostly blank stubs into src/Office365/

 CMakeLists.txt                                     |   2 +-
 src/CMakeLists.txt                                 |   1 +
 src/Office365/CMakeLists.txt                       |  64 ++
 src/Office365/addressbook/CMakeLists.txt           |  65 ++
 .../addressbook/e-book-backend-o365-factory.c      |  81 ++
 src/Office365/addressbook/e-book-backend-o365.c    | 482 ++++++++++++
 src/Office365/addressbook/e-book-backend-o365.h    |  43 ++
 src/Office365/calendar/CMakeLists.txt              |  61 ++
 .../calendar/e-cal-backend-o365-factory.c          | 157 ++++
 src/Office365/calendar/e-cal-backend-o365.c        | 461 ++++++++++++
 src/Office365/calendar/e-cal-backend-o365.h        |  48 ++
 src/Office365/camel/CMakeLists.txt                 |  65 ++
 src/Office365/camel/camel-o365-provider.c          | 128 ++++
 src/Office365/camel/camel-o365-store.c             | 383 ++++++++++
 src/Office365/camel/camel-o365-store.h             |  67 ++
 src/Office365/camel/camel-o365-transport.c         | 189 +++++
 src/Office365/camel/camel-o365-transport.h         |  61 ++
 src/Office365/camel/libcameloffice365.urls         |   1 +
 src/Office365/common/CMakeLists.txt                |  53 ++
 src/Office365/common/camel-o365-settings.c         | 826 +++++++++++++++++++++
 src/Office365/common/camel-o365-settings.h         | 125 ++++
 src/Office365/common/e-source-o365-folder.c        | 242 ++++++
 src/Office365/common/e-source-o365-folder.h        |  76 ++
 src/Office365/evolution/CMakeLists.txt             |  39 +
 src/Office365/evolution/e-book-config-o365.c       |  77 ++
 src/Office365/evolution/e-book-config-o365.h       |  62 ++
 src/Office365/evolution/e-cal-config-o365.c        |  92 +++
 src/Office365/evolution/e-cal-config-o365.h        |  62 ++
 .../evolution/e-mail-config-o365-backend.c         | 461 ++++++++++++
 .../evolution/e-mail-config-o365-backend.h         |  64 ++
 .../evolution/module-o365-configuration.c          |  48 ++
 src/Office365/registry/CMakeLists.txt              |  22 +
 src/Office365/registry/e-o365-backend-factory.c    |  89 +++
 src/Office365/registry/e-o365-backend-factory.h    |  63 ++
 src/Office365/registry/e-o365-backend.c            | 533 +++++++++++++
 src/Office365/registry/e-o365-backend.h            |  62 ++
 src/Office365/registry/module-o365-backend.c       |  45 ++
 37 files changed, 5399 insertions(+), 1 deletion(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 27179a6b..05c6f4ea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -188,7 +188,7 @@ 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 "")
+       set(WITH_OFFICE365_CLIENT_ID "20460e5d-ce91-49af-a3a5-70b6be7486d1")
 endif(WITH_OFFICE365_CLIENT_ID STREQUAL "")
 
 add_printable_variable(WITH_OFFICE365_REDIRECT_URI "Office365.com OAuth 2.0 redirect URI" "")
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5b514ee3..40579bf1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -62,3 +62,4 @@ add_subdirectory(camel)
 add_subdirectory(collection)
 add_subdirectory(configuration)
 add_subdirectory(server)
+add_subdirectory(Office365)
diff --git a/src/Office365/CMakeLists.txt b/src/Office365/CMakeLists.txt
new file mode 100644
index 00000000..76469509
--- /dev/null
+++ b/src/Office365/CMakeLists.txt
@@ -0,0 +1,64 @@
+macro(add_simple_module_o365 _name _sourcesvar _depsvar _defsvar _cflagsvar _incdirsvar _ldflagsvar 
_destination)
+       set(DEPENDENCIES
+               evolution-office365
+       )
+
+       set(SOURCES
+               ${${_sourcesvar}}
+       )
+
+       add_library(${_name} MODULE
+               ${SOURCES}
+       )
+
+       set_target_properties(${_name} PROPERTIES
+               PREFIX ""
+       )
+
+       add_dependencies(${_name}
+               ${DEPENDENCIES}
+               ${${_depsvar}}
+       )
+
+       target_compile_definitions(${_name} PRIVATE
+               -DG_LOG_DOMAIN=\"${_name}\"
+               -DO365_LOCALEDIR=\"${LOCALE_INSTALL_DIR}\"
+               ${${_defsvar}}
+       )
+
+       target_compile_options(${_name} PUBLIC
+               ${LIBEBACKEND_CFLAGS}
+               ${LIBEDATASERVER_CFLAGS}
+               ${SOUP_CFLAGS}
+               ${${_cflagsvar}}
+       )
+
+       target_include_directories(${_name} PUBLIC
+               ${CMAKE_BINARY_DIR}
+               ${CMAKE_SOURCE_DIR}
+               ${LIBEBACKEND_INCLUDE_DIRS}
+               ${LIBEDATASERVER_INCLUDE_DIRS}
+               ${SOUP_INCLUDE_DIRS}
+               ${${_incdirsvar}}
+       )
+
+       target_link_libraries(${_name}
+               ${DEPENDENCIES}
+               ${${_depsvar}}
+               ${LIBEBACKEND_LDFLAGS}
+               ${LIBEDATASERVER_LDFLAGS}
+               ${SOUP_LDFLAGS}
+               ${${_ldflagsvar}}
+       )
+
+       install(TARGETS ${_name}
+               DESTINATION ${_destination}
+       )
+endmacro(add_simple_module_o365)
+
+add_subdirectory(addressbook)
+add_subdirectory(calendar)
+add_subdirectory(camel)
+add_subdirectory(common)
+add_subdirectory(evolution)
+add_subdirectory(registry)
diff --git a/src/Office365/addressbook/CMakeLists.txt b/src/Office365/addressbook/CMakeLists.txt
new file mode 100644
index 00000000..8886cebd
--- /dev/null
+++ b/src/Office365/addressbook/CMakeLists.txt
@@ -0,0 +1,65 @@
+set(DEPENDENCIES
+       evolution-office365
+)
+
+set(SOURCES
+       e-book-backend-o365.c
+       e-book-backend-o365.h
+       e-book-backend-o365-factory.c
+)
+
+add_library(ebookbackendoffice365 MODULE
+       ${SOURCES}
+)
+
+add_dependencies(ebookbackendoffice365
+       ${DEPENDENCIES}
+)
+
+target_compile_definitions(ebookbackendoffice365 PRIVATE
+       -DG_LOG_DOMAIN=\"ebookbackendoffice365\"
+       -DBACKENDDIR=\"${ebook_backenddir}\"
+       -DO365_LOCALEDIR=\"${LOCALE_INSTALL_DIR}\"
+)
+
+target_compile_options(ebookbackendoffice365 PUBLIC
+       ${LIBEBACKEND_CFLAGS}
+       ${LIBEBOOK_CFLAGS}
+       ${LIBEDATABOOK_CFLAGS}
+       ${LIBEDATASERVER_CFLAGS}
+       ${LIBICAL_GLIB_CFLAGS}
+       ${MSPACK_CFLAGS}
+       ${SOUP_CFLAGS}
+)
+
+target_include_directories(ebookbackendoffice365 PUBLIC
+       ${CMAKE_BINARY_DIR}
+       ${CMAKE_SOURCE_DIR}
+       ${CMAKE_BINARY_DIR}/src/Office365
+       ${CMAKE_SOURCE_DIR}/src/Office365
+       ${CMAKE_CURRENT_BINARY_DIR}
+       ${CMAKE_CURRENT_SOURCE_DIR}
+       ${LIBEBACKEND_INCLUDE_DIRS}
+       ${LIBEBOOK_INCLUDE_DIRS}
+       ${LIBEDATABOOK_INCLUDE_DIRS}
+       ${LIBEDATASERVER_INCLUDE_DIRS}
+       ${LIBICAL_GLIB_INCLUDE_DIRS}
+       ${MSPACK_INCLUDE_DIRS}
+       ${SOUP_INCLUDE_DIRS}
+)
+
+target_link_libraries(ebookbackendoffice365
+       ${DEPENDENCIES}
+       ${LIBEBACKEND_LDFLAGS}
+       ${LIBEBOOK_LDFLAGS}
+       ${LIBEDATABOOK_LDFLAGS}
+       ${LIBEDATASERVER_LDFLAGS}
+       ${LIBICAL_GLIB_LDFLAGS}
+       ${MATH_LDFLAGS}
+       ${MSPACK_LDFLAGS}
+       ${SOUP_LDFLAGS}
+)
+
+install(TARGETS ebookbackendoffice365
+       DESTINATION ${ebook_backenddir}
+)
diff --git a/src/Office365/addressbook/e-book-backend-o365-factory.c 
b/src/Office365/addressbook/e-book-backend-o365-factory.c
new file mode 100644
index 00000000..767351ba
--- /dev/null
+++ b/src/Office365/addressbook/e-book-backend-o365-factory.c
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <libedata-book/libedata-book.h>
+
+#include "common/e-source-o365-folder.h"
+
+#include "e-book-backend-o365.h"
+
+typedef EBookBackendFactory EBookBackendO365Factory;
+typedef EBookBackendFactoryClass EBookBackendO365FactoryClass;
+
+static EModule *e_module;
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_book_backend_o365_factory_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (EBookBackendO365Factory, e_book_backend_o365_factory, E_TYPE_BOOK_BACKEND_FACTORY)
+
+static void
+e_book_backend_o365_factory_class_init (EBookBackendFactoryClass *class)
+{
+       EBackendFactoryClass *backend_factory_class;
+
+       backend_factory_class = E_BACKEND_FACTORY_CLASS (class);
+       backend_factory_class->e_module = e_module;
+       backend_factory_class->share_subprocess = TRUE;
+
+       class->factory_name = "office365";
+       class->backend_type = E_TYPE_BOOK_BACKEND_O365;
+}
+
+static void
+e_book_backend_o365_factory_class_finalize (EBookBackendFactoryClass *class)
+{
+}
+
+static void
+e_book_backend_o365_factory_init (EBookBackendFactory *factory)
+{
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+       bindtextdomain (GETTEXT_PACKAGE, O365_LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+       e_module = E_MODULE (type_module);
+
+       e_source_o365_folder_type_register (type_module);
+       e_book_backend_o365_factory_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+       e_module = NULL;
+}
diff --git a/src/Office365/addressbook/e-book-backend-o365.c b/src/Office365/addressbook/e-book-backend-o365.c
new file mode 100644
index 00000000..d60003ba
--- /dev/null
+++ b/src/Office365/addressbook/e-book-backend-o365.c
@@ -0,0 +1,482 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#define LIBICAL_GLIB_UNSTABLE_API
+#include <libical-glib/libical-glib.h>
+#undef LIBICAL_GLIB_UNSTABLE_API
+
+#include <libedata-book/libedata-book.h>
+
+#include "common/camel-o365-settings.h"
+
+#include "e-book-backend-o365.h"
+
+#ifdef G_OS_WIN32
+#ifdef gmtime_r
+#undef gmtime_r
+#endif
+
+/* The gmtime() in Microsoft's C library is MT-safe */
+#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
+#endif
+
+#define d(x)
+
+#define EC_ERROR_EX(_code,_msg) e_client_error_create (_code, _msg)
+#define EBC_ERROR_EX(_code,_msg) e_book_client_error_create (_code, _msg)
+
+#define EBB_O365_DATA_VERSION 1
+#define EBB_O365_DATA_VERSION_KEY "o365-data-version"
+
+struct _EBookBackendO365Private {
+       GRecMutex property_lock;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (EBookBackendO365, e_book_backend_o365, E_TYPE_BOOK_META_BACKEND)
+
+static void
+ebb_o365_convert_error_to_client_error (GError **perror)
+{
+       GError *error = NULL;
+
+       if (!perror || !*perror || (*perror)->domain == E_CLIENT_ERROR || (*perror)->domain == 
E_BOOK_CLIENT_ERROR)
+               return;
+
+       /*if ((*perror)->domain == EWS_CONNECTION_ERROR) {
+               switch ((*perror)->code) {
+               case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED:
+                       error = EC_ERROR_EX (E_CLIENT_ERROR_AUTHENTICATION_FAILED, (*perror)->message);
+                       break;
+               case EWS_CONNECTION_ERROR_FOLDERNOTFOUND:
+               case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND:
+               case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND:
+               case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND:
+                       error = EBC_ERROR_EX (E_BOOK_CLIENT_ERROR_NO_SUCH_BOOK, (*perror)->message);
+                       break;
+               case EWS_CONNECTION_ERROR_EVENTNOTFOUND:
+               case EWS_CONNECTION_ERROR_ITEMNOTFOUND:
+                       error = EBC_ERROR_EX (E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND, (*perror)->message);
+                       break;
+               case EWS_CONNECTION_ERROR_UNAVAILABLE:
+                       g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, 
(*perror)->message);
+                       break;
+               }
+
+               if (!error)
+                       error = EC_ERROR_EX (E_CLIENT_ERROR_OTHER_ERROR, (*perror)->message);
+       }*/
+
+       if (error) {
+               g_error_free (*perror);
+               *perror = error;
+       }
+}
+
+static void
+ebb_o365_maybe_disconnect_sync (EBookBackendO365 *bbo365,
+                               GError **in_perror,
+                               GCancellable *cancellable)
+{
+       g_return_if_fail (E_IS_BOOK_BACKEND_O365 (bbo365));
+
+       if (in_perror && g_error_matches (*in_perror, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_FAILED)) {
+               e_book_meta_backend_disconnect_sync (E_BOOK_META_BACKEND (bbo365), cancellable, NULL);
+               e_backend_schedule_credentials_required (E_BACKEND (bbo365), 
E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC);
+       }
+}
+
+static void
+ebb_o365_unset_connection (EBookBackendO365 *bbo365,
+                          gboolean is_disconnect)
+{
+       g_return_if_fail (E_IS_BOOK_BACKEND_O365 (bbo365));
+
+       g_rec_mutex_lock (&bbo365->priv->property_lock);
+
+       /*if (bbo365->priv->cnc) {
+               if (is_disconnect)
+                       e_o365_connection_set_disconnected_flag (bbo365->priv->cnc, TRUE);
+       }
+
+       g_clear_object (&bbo365->priv->cnc);*/
+
+       g_rec_mutex_unlock (&bbo365->priv->property_lock);
+}
+
+static gboolean
+ebb_o365_connect_sync (EBookMetaBackend *meta_backend,
+                      const ENamedParameters *credentials,
+                      ESourceAuthenticationResult *out_auth_result,
+                      gchar **out_certificate_pem,
+                      GTlsCertificateFlags *out_certificate_errors,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+       EBookBackendO365 *bbo365;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
+       g_return_val_if_fail (out_auth_result != NULL, FALSE);
+
+       bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
+
+       g_rec_mutex_lock (&bbo365->priv->property_lock);
+
+       /*if (bbo365->priv->cnc)*/ {
+               g_rec_mutex_unlock (&bbo365->priv->property_lock);
+
+               *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+
+               return TRUE;
+       }
+
+       g_rec_mutex_unlock (&bbo365->priv->property_lock);
+
+       return success;
+}
+
+static gboolean
+ebb_o365_disconnect_sync (EBookMetaBackend *meta_backend,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
+
+       ebb_o365_unset_connection (E_BOOK_BACKEND_O365 (meta_backend), TRUE);
+
+       return TRUE;
+}
+
+static gboolean
+ebb_o365_get_changes_sync (EBookMetaBackend *meta_backend,
+                          const gchar *last_sync_tag,
+                          gboolean is_repeat,
+                          gchar **out_new_sync_tag,
+                          gboolean *out_repeat,
+                          GSList **out_created_objects,
+                          GSList **out_modified_objects,
+                          GSList **out_removed_objects,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       EBookBackendO365 *bbo365;
+       EBookCache *book_cache;
+       gboolean success = TRUE;
+       /*GError *local_error = NULL;*/
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
+       g_return_val_if_fail (out_new_sync_tag != NULL, FALSE);
+       g_return_val_if_fail (out_repeat != NULL, FALSE);
+       g_return_val_if_fail (out_created_objects != NULL, FALSE);
+       g_return_val_if_fail (out_modified_objects != NULL, FALSE);
+       g_return_val_if_fail (out_removed_objects != NULL, FALSE);
+
+       *out_created_objects = NULL;
+       *out_modified_objects = NULL;
+       *out_removed_objects = NULL;
+
+       bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
+
+       book_cache = e_book_meta_backend_ref_cache (meta_backend);
+       g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE);
+
+       g_rec_mutex_lock (&bbo365->priv->property_lock);
+
+       g_rec_mutex_unlock (&bbo365->priv->property_lock);
+
+       ebb_o365_convert_error_to_client_error (error);
+       ebb_o365_maybe_disconnect_sync (bbo365, error, cancellable);
+
+       g_clear_object (&book_cache);
+
+       return success;
+}
+
+static gboolean
+ebb_o365_load_contact_sync (EBookMetaBackend *meta_backend,
+                           const gchar *uid,
+                           const gchar *extra,
+                           EContact **out_contact,
+                           gchar **out_extra,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+       EBookBackendO365 *bbo365;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (out_contact, FALSE);
+
+       bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
+
+       g_rec_mutex_lock (&bbo365->priv->property_lock);
+
+       g_rec_mutex_unlock (&bbo365->priv->property_lock);
+
+       ebb_o365_convert_error_to_client_error (error);
+       ebb_o365_maybe_disconnect_sync (bbo365, error, cancellable);
+
+       return success;
+}
+
+static gboolean
+ebb_o365_save_contact_sync (EBookMetaBackend *meta_backend,
+                           gboolean overwrite_existing,
+                           EConflictResolution conflict_resolution,
+                           /* const */ EContact *contact,
+                           const gchar *extra,
+                           guint32 opflags,
+                           gchar **out_new_uid,
+                           gchar **out_new_extra,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+       EBookBackendO365 *bbo365;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
+       g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
+       g_return_val_if_fail (out_new_uid != NULL, FALSE);
+       g_return_val_if_fail (out_new_extra != NULL, FALSE);
+
+       bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
+
+       g_rec_mutex_lock (&bbo365->priv->property_lock);
+
+       g_rec_mutex_unlock (&bbo365->priv->property_lock);
+
+       ebb_o365_convert_error_to_client_error (error);
+       ebb_o365_maybe_disconnect_sync (bbo365, error, cancellable);
+
+       return success;
+}
+
+static gboolean
+ebb_o365_remove_contact_sync (EBookMetaBackend *meta_backend,
+                             EConflictResolution conflict_resolution,
+                             const gchar *uid,
+                             const gchar *extra,
+                             const gchar *object,
+                             guint32 opflags,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+       EBookBackendO365 *bbo365;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
+
+       bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
+
+       g_rec_mutex_lock (&bbo365->priv->property_lock);
+
+       g_rec_mutex_unlock (&bbo365->priv->property_lock);
+
+       ebb_o365_convert_error_to_client_error (error);
+       ebb_o365_maybe_disconnect_sync (bbo365, error, cancellable);
+
+       return success;
+}
+
+static gboolean
+ebb_o365_search_sync (EBookMetaBackend *meta_backend,
+                     const gchar *expr,
+                     gboolean meta_contact,
+                     GSList **out_contacts,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
+
+       /* Ignore errors, just try its best */
+       /*ebb_o365_update_cache_for_expression (E_BOOK_BACKEND_O365 (meta_backend), expr, cancellable, 
NULL);*/
+
+       /* Chain up to parent's method */
+       if (!E_BOOK_META_BACKEND_CLASS (e_book_backend_o365_parent_class)->search_sync (meta_backend, expr, 
meta_contact,
+               out_contacts, cancellable, error))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean
+ebb_o365_search_uids_sync (EBookMetaBackend *meta_backend,
+                          const gchar *expr,
+                          GSList **out_uids,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
+
+       /* Ignore errors, just try its best */
+       /*ebb_o365_update_cache_for_expression (E_BOOK_BACKEND_O365 (meta_backend), expr, cancellable, 
NULL);*/
+
+       /* Chain up to parent's method */
+       return E_BOOK_META_BACKEND_CLASS (e_book_backend_o365_parent_class)->search_uids_sync (meta_backend, 
expr,
+               out_uids, cancellable, error);
+}
+
+static gchar *
+ebb_o365_get_backend_property (EBookBackend *book_backend,
+                              const gchar *prop_name)
+{
+       g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (book_backend), NULL);
+       g_return_val_if_fail (prop_name != NULL, NULL);
+
+       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+               return g_strjoin (",",
+                       "net",
+                       "contact-lists",
+                       "do-initial-query",
+                       e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (book_backend)),
+                       NULL);
+       } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
+               return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
+       } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
+               GString *buffer;
+               gchar *fields;
+               /*gint ii;*/
+
+               buffer = g_string_sized_new (1024);
+
+               /*for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
+                       if (mappings[ii].element_type != ELEMENT_TYPE_SIMPLE)
+                               continue;
+
+                       if (buffer->len > 0)
+                               g_string_append_c (buffer, ',');
+                       g_string_append (buffer, e_contact_field_name (mappings[ii].field_id));
+               }
+
+               for (ii = 0; ii < G_N_ELEMENTS (phone_field_map); ii++) {
+                       if (buffer->len > 0)
+                               g_string_append_c (buffer, ',');
+                       g_string_append (buffer, e_contact_field_name (phone_field_map[ii].field));
+               }*/
+
+               fields = g_strjoin (
+                       ",",
+                       buffer->str,
+                       e_contact_field_name (E_CONTACT_FULL_NAME),
+                       e_contact_field_name (E_CONTACT_NICKNAME),
+                       e_contact_field_name (E_CONTACT_FAMILY_NAME),
+                       e_contact_field_name (E_CONTACT_EMAIL_1),
+                       e_contact_field_name (E_CONTACT_EMAIL_2),
+                       e_contact_field_name (E_CONTACT_EMAIL_3),
+                       e_contact_field_name (E_CONTACT_ADDRESS_WORK),
+                       e_contact_field_name (E_CONTACT_ADDRESS_HOME),
+                       e_contact_field_name (E_CONTACT_ADDRESS_OTHER),
+                       e_contact_field_name (E_CONTACT_ANNIVERSARY),
+                       e_contact_field_name (E_CONTACT_BIRTH_DATE),
+                       e_contact_field_name (E_CONTACT_NOTE),
+                       e_contact_field_name (E_CONTACT_PHOTO),
+                       NULL);
+
+               g_string_free (buffer, TRUE);
+
+               return fields;
+       }
+
+       /* Chain up to parent's method. */
+       return E_BOOK_BACKEND_CLASS (e_book_backend_o365_parent_class)->impl_get_backend_property 
(book_backend, prop_name);
+}
+
+static gboolean
+ebb_o365_get_destination_address (EBackend *backend,
+                                 gchar **host,
+                                 guint16 *port)
+{
+       g_return_val_if_fail (port != NULL, FALSE);
+       g_return_val_if_fail (host != NULL, FALSE);
+
+       /* Sanity checking */
+       if (!e_book_backend_get_registry (E_BOOK_BACKEND (backend)) ||
+           !e_backend_get_source (backend))
+               return FALSE;
+
+       *host = g_strdup ("graph.microsoft.com");
+       *port = 443;
+
+       return TRUE;
+}
+
+static void
+e_book_backend_o365_dispose (GObject *object)
+{
+       EBookBackendO365 *bbo365 = E_BOOK_BACKEND_O365 (object);
+
+       ebb_o365_unset_connection (bbo365, FALSE);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_book_backend_o365_parent_class)->dispose (object);
+}
+
+static void
+e_book_backend_o365_finalize (GObject *object)
+{
+       EBookBackendO365 *bbo365 = E_BOOK_BACKEND_O365 (object);
+
+       g_rec_mutex_clear (&bbo365->priv->property_lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_book_backend_o365_parent_class)->finalize (object);
+}
+
+static void
+e_book_backend_o365_init (EBookBackendO365 *bbo365)
+{
+       bbo365->priv = e_book_backend_o365_get_instance_private (bbo365);
+
+       g_rec_mutex_init (&bbo365->priv->property_lock);
+}
+
+static void
+e_book_backend_o365_class_init (EBookBackendO365Class *klass)
+{
+       GObjectClass *object_class;
+       EBackendClass *backend_class;
+       EBookBackendClass *book_backend_class;
+       EBookMetaBackendClass *book_meta_backend_class;
+
+       book_meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass);
+       book_meta_backend_class->backend_module_filename = "libebookbackendoffice365.so";
+       book_meta_backend_class->backend_factory_type_name = "EBookBackendO365Factory";
+       book_meta_backend_class->connect_sync = ebb_o365_connect_sync;
+       book_meta_backend_class->disconnect_sync = ebb_o365_disconnect_sync;
+       book_meta_backend_class->get_changes_sync = ebb_o365_get_changes_sync;
+       book_meta_backend_class->load_contact_sync = ebb_o365_load_contact_sync;
+       book_meta_backend_class->save_contact_sync = ebb_o365_save_contact_sync;
+       book_meta_backend_class->remove_contact_sync = ebb_o365_remove_contact_sync;
+       book_meta_backend_class->search_sync = ebb_o365_search_sync;
+       book_meta_backend_class->search_uids_sync = ebb_o365_search_uids_sync;
+
+       book_backend_class = E_BOOK_BACKEND_CLASS (klass);
+       book_backend_class->impl_get_backend_property = ebb_o365_get_backend_property;
+
+       backend_class = E_BACKEND_CLASS (klass);
+       backend_class->get_destination_address = ebb_o365_get_destination_address;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->dispose = e_book_backend_o365_dispose;
+       object_class->finalize = e_book_backend_o365_finalize;
+}
diff --git a/src/Office365/addressbook/e-book-backend-o365.h b/src/Office365/addressbook/e-book-backend-o365.h
new file mode 100644
index 00000000..b91b36a6
--- /dev/null
+++ b/src/Office365/addressbook/e-book-backend-o365.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_BOOK_BACKEND_O365_H
+#define E_BOOK_BACKEND_O365_H
+
+#include <libedata-book/libedata-book.h>
+
+#define E_TYPE_BOOK_BACKEND_O365         (e_book_backend_o365_get_type ())
+#define E_BOOK_BACKEND_O365(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_BOOK_BACKEND_O365, 
EBookBackendO365))
+#define E_BOOK_BACKEND_O365_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), E_TYPE_BOOK_BACKEND_O365, 
EBookBackendO365Class))
+#define E_IS_BOOK_BACKEND_O365(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_BOOK_BACKEND_O365))
+#define E_IS_BOOK_BACKEND_O365_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_BOOK_BACKEND_O365))
+#define E_BOOK_BACKEND_O365_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_BOOK_BACKEND_O365, 
EBookBackenO365Class))
+
+typedef struct _EBookBackendO365Private EBookBackendO365Private;
+
+typedef struct {
+       EBookMetaBackend parent_object;
+       EBookBackendO365Private *priv;
+} EBookBackendO365;
+
+typedef struct {
+       EBookMetaBackendClass parent_class;
+} EBookBackendO365Class;
+
+GType       e_book_backend_o365_get_type (void);
+
+#endif /* E_BOOK_BACKEND_O365_H */
diff --git a/src/Office365/calendar/CMakeLists.txt b/src/Office365/calendar/CMakeLists.txt
new file mode 100644
index 00000000..5a5400ff
--- /dev/null
+++ b/src/Office365/calendar/CMakeLists.txt
@@ -0,0 +1,61 @@
+set(DEPENDENCIES
+       evolution-office365
+)
+
+set(SOURCES
+       e-cal-backend-o365.c
+       e-cal-backend-o365.h
+       e-cal-backend-o365-factory.c
+)
+
+add_library(ecalbackendoffice365 MODULE
+       ${SOURCES}
+)
+
+add_dependencies(ecalbackendoffice365
+       ${DEPENDENCIES}
+)
+
+target_compile_definitions(ecalbackendoffice365 PRIVATE
+       -DG_LOG_DOMAIN=\"ecalbackendoffice365\"
+       -DO365_DATADIR=\"${ewsdatadir}\"
+       -DO365_LOCALEDIR=\"${LOCALE_INSTALL_DIR}\"
+       -DO365_SRCDIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"
+)
+
+target_compile_options(ecalbackendoffice365 PUBLIC
+       ${CAMEL_CFLAGS}
+       ${EVOLUTION_CALENDAR_CFLAGS}
+       ${LIBEBACKEND_CFLAGS}
+       ${LIBECAL_CFLAGS}
+       ${LIBEDATACAL_CFLAGS}
+       ${SOUP_CFLAGS}
+)
+
+target_include_directories(ecalbackendoffice365 PUBLIC
+       ${CMAKE_BINARY_DIR}
+       ${CMAKE_SOURCE_DIR}
+       ${CMAKE_BINARY_DIR}/src/Office365
+       ${CMAKE_SOURCE_DIR}/src/Office365
+       ${CMAKE_CURRENT_BINARY_DIR}
+       ${CAMEL_INCLUDE_DIRS}
+       ${EVOLUTION_CALENDAR_INCLUDE_DIRS}
+       ${LIBEBACKEND_INCLUDE_DIRS}
+       ${LIBECAL_INCLUDE_DIRS}
+       ${LIBEDATACAL_INCLUDE_DIRS}
+       ${SOUP_INCLUDE_DIRS}
+)
+
+target_link_libraries(ecalbackendoffice365
+       ${DEPENDENCIES}
+       ${CAMEL_LDFLAGS}
+       ${EVOLUTION_CALENDAR_LDFLAGS}
+       ${LIBEBACKEND_LDFLAGS}
+       ${LIBECAL_LDFLAGS}
+       ${LIBEDATACAL_LDFLAGS}
+       ${SOUP_LDFLAGS}
+)
+
+install(TARGETS ecalbackendoffice365
+       DESTINATION ${ecal_backenddir}
+)
diff --git a/src/Office365/calendar/e-cal-backend-o365-factory.c 
b/src/Office365/calendar/e-cal-backend-o365-factory.c
new file mode 100644
index 00000000..83710795
--- /dev/null
+++ b/src/Office365/calendar/e-cal-backend-o365-factory.c
@@ -0,0 +1,157 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedata-cal/libedata-cal.h>
+
+#include "common/e-source-o365-folder.h"
+
+#include "e-cal-backend-o365.h"
+
+#define FACTORY_NAME "office365"
+
+typedef ECalBackendFactory ECalBackendO365EventsFactory;
+typedef ECalBackendFactoryClass ECalBackendO365EventsFactoryClass;
+
+typedef ECalBackendFactory ECalBackendO365JournalFactory;
+typedef ECalBackendFactoryClass ECalBackendO365JournalFactoryClass;
+
+typedef ECalBackendFactory ECalBackendO365TodosFactory;
+typedef ECalBackendFactoryClass ECalBackendO365TodosFactoryClass;
+
+static EModule *e_module;
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_cal_backend_o365_events_factory_get_type (void);
+GType e_cal_backend_o365_journal_factory_get_type (void);
+GType e_cal_backend_o365_todos_factory_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (
+       ECalBackendO365EventsFactory,
+       e_cal_backend_o365_events_factory,
+       E_TYPE_CAL_BACKEND_FACTORY)
+
+G_DEFINE_DYNAMIC_TYPE (
+       ECalBackendO365JournalFactory,
+       e_cal_backend_o365_journal_factory,
+       E_TYPE_CAL_BACKEND_FACTORY)
+
+G_DEFINE_DYNAMIC_TYPE (
+       ECalBackendO365TodosFactory,
+       e_cal_backend_o365_todos_factory,
+       E_TYPE_CAL_BACKEND_FACTORY)
+
+static void
+e_cal_backend_o365_events_factory_class_init (ECalBackendFactoryClass *class)
+{
+       EBackendFactoryClass *backend_factory_class;
+
+       backend_factory_class = E_BACKEND_FACTORY_CLASS (class);
+       backend_factory_class->e_module = e_module;
+       backend_factory_class->share_subprocess = TRUE;
+
+       class->factory_name = FACTORY_NAME;
+       class->component_kind = I_CAL_VEVENT_COMPONENT;
+       class->backend_type = E_TYPE_CAL_BACKEND_O365;
+}
+
+static void
+e_cal_backend_o365_events_factory_class_finalize (ECalBackendFactoryClass *class)
+{
+}
+
+static void
+e_cal_backend_o365_events_factory_init (ECalBackendFactory *factory)
+{
+}
+
+static void
+e_cal_backend_o365_journal_factory_class_init (ECalBackendFactoryClass *class)
+{
+       EBackendFactoryClass *backend_factory_class;
+
+       backend_factory_class = E_BACKEND_FACTORY_CLASS (class);
+       backend_factory_class->e_module = e_module;
+       backend_factory_class->share_subprocess = TRUE;
+
+       class->factory_name = FACTORY_NAME;
+       class->component_kind = I_CAL_VJOURNAL_COMPONENT;
+       class->backend_type = E_TYPE_CAL_BACKEND_O365;
+}
+
+static void
+e_cal_backend_o365_journal_factory_class_finalize (ECalBackendFactoryClass *class)
+{
+}
+
+static void
+e_cal_backend_o365_journal_factory_init (ECalBackendFactory *factory)
+{
+}
+
+static void
+e_cal_backend_o365_todos_factory_class_init (ECalBackendFactoryClass *class)
+{
+       EBackendFactoryClass *backend_factory_class;
+
+       backend_factory_class = E_BACKEND_FACTORY_CLASS (class);
+       backend_factory_class->e_module = e_module;
+       backend_factory_class->share_subprocess = TRUE;
+
+       class->factory_name = FACTORY_NAME;
+       class->component_kind = I_CAL_VTODO_COMPONENT;
+       class->backend_type = E_TYPE_CAL_BACKEND_O365;
+}
+
+static void
+e_cal_backend_o365_todos_factory_class_finalize (ECalBackendFactoryClass *class)
+{
+}
+
+static void
+e_cal_backend_o365_todos_factory_init (ECalBackendFactory *factory)
+{
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+       bindtextdomain (GETTEXT_PACKAGE, O365_LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+       e_module = E_MODULE (type_module);
+
+       e_source_o365_folder_type_register (type_module);
+
+       e_cal_backend_o365_events_factory_register_type (type_module);
+       e_cal_backend_o365_journal_factory_register_type (type_module);
+       e_cal_backend_o365_todos_factory_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+       e_module = NULL;
+}
diff --git a/src/Office365/calendar/e-cal-backend-o365.c b/src/Office365/calendar/e-cal-backend-o365.c
new file mode 100644
index 00000000..f130ac37
--- /dev/null
+++ b/src/Office365/calendar/e-cal-backend-o365.c
@@ -0,0 +1,461 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedata-cal/libedata-cal.h>
+#include <libecal/libecal.h>
+
+#include "common/camel-o365-settings.h"
+#include "common/e-source-o365-folder.h"
+
+#include "e-cal-backend-o365.h"
+
+#ifdef G_OS_WIN32
+#ifdef gmtime_r
+#undef gmtime_r
+#endif
+
+/* The gmtime() in Microsoft's C library is MT-safe */
+#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
+#endif
+
+#define EC_ERROR(_code) e_client_error_create (_code, NULL)
+#define EC_ERROR_EX(_code, _msg) e_client_error_create (_code, _msg)
+#define ECC_ERROR(_code) e_cal_client_error_create (_code, NULL)
+#define ECC_ERROR_EX(_code, _msg) e_cal_client_error_create (_code, _msg)
+
+/* Private part of the CalBackendO365 structure */
+struct _ECalBackendO365Private {
+       GRecMutex property_lock;
+
+       gchar *attachments_dir;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ECalBackendO365, e_cal_backend_o365, E_TYPE_CAL_META_BACKEND)
+
+static void
+ecb_o365_convert_error_to_edc_error (GError **perror)
+{
+       GError *error = NULL;
+
+       if (!perror || !*perror ||
+           (*perror)->domain == E_CLIENT_ERROR ||
+           (*perror)->domain == E_CAL_CLIENT_ERROR)
+               return;
+
+       /*if ((*perror)->domain == O365_CONNECTION_ERROR) {
+               switch ((*perror)->code) {
+               case O365_CONNECTION_ERROR_AUTHENTICATION_FAILED:
+                       error = EC_ERROR_EX (E_CLIENT_ERROR_AUTHENTICATION_FAILED, (*perror)->message);
+                       break;
+               case O365_CONNECTION_ERROR_FOLDERNOTFOUND:
+               case O365_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND:
+               case O365_CONNECTION_ERROR_PARENTFOLDERNOTFOUND:
+               case O365_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND:
+                       error = ECC_ERROR_EX (E_CAL_CLIENT_ERROR_NO_SUCH_CALENDAR, (*perror)->message);
+                       break;
+               case O365_CONNECTION_ERROR_EVENTNOTFOUND:
+               case O365_CONNECTION_ERROR_ITEMNOTFOUND:
+                       error = ECC_ERROR_EX (E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND, (*perror)->message);
+                       break;
+               case O365_CONNECTION_ERROR_UNAVAILABLE:
+                       g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, 
(*perror)->message);
+                       break;
+               }
+
+               if (!error)
+                       error = EC_ERROR_EX (E_CLIENT_ERROR_OTHER_ERROR, (*perror)->message);
+       }*/
+
+       if (error) {
+               g_error_free (*perror);
+               *perror = error;
+       }
+}
+
+static void
+ecb_o365_maybe_disconnect_sync (ECalBackendO365 *cbo365,
+                               GError **in_perror,
+                               GCancellable *cancellable)
+{
+       g_return_if_fail (E_IS_CAL_BACKEND_O365 (cbo365));
+
+       if (in_perror && g_error_matches (*in_perror, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_FAILED)) {
+               e_cal_meta_backend_disconnect_sync (E_CAL_META_BACKEND (cbo365), cancellable, NULL);
+               e_backend_schedule_credentials_required (E_BACKEND (cbo365), 
E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC);
+       }
+}
+
+static void
+ecb_o365_unset_connection (ECalBackendO365 *cbo365,
+                          gboolean is_disconnect)
+{
+       g_return_if_fail (E_IS_CAL_BACKEND_O365 (cbo365));
+
+       g_rec_mutex_lock (&cbo365->priv->property_lock);
+
+       /*if (cbo365->priv->cnc) {
+               if (is_disconnect)
+                       e_o365_connection_set_disconnected_flag (cbo365->priv->cnc, TRUE);
+       }
+
+       g_clear_object (&cbo365->priv->cnc);*/
+
+       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+}
+
+static gboolean
+ecb_o365_connect_sync (ECalMetaBackend *meta_backend,
+                      const ENamedParameters *credentials,
+                      ESourceAuthenticationResult *out_auth_result,
+                      gchar **out_certificate_pem,
+                      GTlsCertificateFlags *out_certificate_errors,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+       ECalBackendO365 *cbo365;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (meta_backend), FALSE);
+       g_return_val_if_fail (out_auth_result != NULL, FALSE);
+
+       cbo365 = E_CAL_BACKEND_O365 (meta_backend);
+
+       g_rec_mutex_lock (&cbo365->priv->property_lock);
+
+       /*if (cbo365->priv->cnc)*/ {
+               g_rec_mutex_unlock (&cbo365->priv->property_lock);
+
+               *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+
+               return TRUE;
+       }
+
+       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+
+       return success;
+}
+
+static gboolean
+ecb_o365_disconnect_sync (ECalMetaBackend *meta_backend,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (meta_backend), FALSE);
+
+       ecb_o365_unset_connection (E_CAL_BACKEND_O365 (meta_backend), TRUE);
+
+       return TRUE;
+}
+
+static gboolean
+ecb_o365_get_changes_sync (ECalMetaBackend *meta_backend,
+                          const gchar *last_sync_tag,
+                          gboolean is_repeat,
+                          gchar **out_new_sync_tag,
+                          gboolean *out_repeat,
+                          GSList **out_created_objects,
+                          GSList **out_modified_objects,
+                          GSList **out_removed_objects,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       ECalBackendO365 *cbo365;
+       ECalCache *cal_cache;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (meta_backend), FALSE);
+       g_return_val_if_fail (out_new_sync_tag != NULL, FALSE);
+       g_return_val_if_fail (out_repeat != NULL, FALSE);
+       g_return_val_if_fail (out_created_objects != NULL, FALSE);
+       g_return_val_if_fail (out_modified_objects != NULL, FALSE);
+       g_return_val_if_fail (out_removed_objects != NULL, FALSE);
+
+       *out_created_objects = NULL;
+       *out_modified_objects = NULL;
+       *out_removed_objects = NULL;
+
+       cbo365 = E_CAL_BACKEND_O365 (meta_backend);
+
+       cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+       g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
+
+       g_rec_mutex_lock (&cbo365->priv->property_lock);
+
+       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+
+       ecb_o365_convert_error_to_edc_error (error);
+       ecb_o365_maybe_disconnect_sync (cbo365, error, cancellable);
+
+       return success;
+}
+
+static gboolean
+ecb_o365_load_component_sync (ECalMetaBackend *meta_backend,
+                             const gchar *uid,
+                             const gchar *extra,
+                             ICalComponent **out_component,
+                             gchar **out_extra,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+       ECalBackendO365 *cbo365;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (meta_backend), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (out_component != NULL, FALSE);
+       g_return_val_if_fail (out_extra != NULL, FALSE);
+
+       cbo365 = E_CAL_BACKEND_O365 (meta_backend);
+
+       g_rec_mutex_lock (&cbo365->priv->property_lock);
+
+       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+
+       ecb_o365_convert_error_to_edc_error (error);
+       ecb_o365_maybe_disconnect_sync (cbo365, error, cancellable);
+
+       return success;
+}
+
+static gboolean
+ecb_o365_save_component_sync (ECalMetaBackend *meta_backend,
+                             gboolean overwrite_existing,
+                             EConflictResolution conflict_resolution,
+                             const GSList *instances,
+                             const gchar *extra,
+                             guint32 opflags,
+                             gchar **out_new_uid,
+                             gchar **out_new_extra,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+       ECalBackendO365 *cbo365;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (meta_backend), FALSE);
+
+       cbo365 = E_CAL_BACKEND_O365 (meta_backend);
+
+       g_rec_mutex_lock (&cbo365->priv->property_lock);
+
+       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+
+       ecb_o365_convert_error_to_edc_error (error);
+       ecb_o365_maybe_disconnect_sync (cbo365, error, cancellable);
+
+       return success;
+}
+
+static gboolean
+ecb_o365_remove_component_sync (ECalMetaBackend *meta_backend,
+                               EConflictResolution conflict_resolution,
+                               const gchar *uid,
+                               const gchar *extra,
+                               const gchar *object,
+                               guint32 opflags,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       ECalBackendO365 *cbo365;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (meta_backend), FALSE);
+       g_return_val_if_fail (object != NULL, FALSE);
+
+       cbo365 = E_CAL_BACKEND_O365 (meta_backend);
+
+       g_rec_mutex_lock (&cbo365->priv->property_lock);
+
+       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+
+       ecb_o365_convert_error_to_edc_error (error);
+       ecb_o365_maybe_disconnect_sync (cbo365, error, cancellable);
+
+       return success;
+}
+
+static gchar *
+ecb_o365_get_backend_property (ECalBackend *cal_backend,
+                             const gchar *prop_name)
+{
+       ECalBackendO365 *cbo365;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (cal_backend), NULL);
+       g_return_val_if_fail (prop_name != NULL, NULL);
+
+       cbo365 = E_CAL_BACKEND_O365 (cal_backend);
+
+       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+               return g_strjoin (
+                       ",",
+                       E_CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS,
+                       E_CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS,
+                       E_CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS,
+                       E_CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY,
+                       E_CAL_STATIC_CAPABILITY_REMOVE_ALARMS,
+                       E_CAL_STATIC_CAPABILITY_NO_THISANDPRIOR,
+                       E_CAL_STATIC_CAPABILITY_NO_THISANDFUTURE,
+                       E_CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK,
+                       E_CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT,
+                       E_CAL_STATIC_CAPABILITY_SAVE_SCHEDULES,
+                       E_CAL_STATIC_CAPABILITY_NO_ALARM_AFTER_START,
+                       E_CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE,
+                       E_CAL_STATIC_CAPABILITY_ALL_DAY_EVENT_AS_TIME,
+                       E_CAL_STATIC_CAPABILITY_TASK_DATE_ONLY,
+                       E_CAL_STATIC_CAPABILITY_TASK_NO_ALARM,
+                       E_CAL_STATIC_CAPABILITY_TASK_CAN_RECUR,
+                       E_CAL_STATIC_CAPABILITY_TASK_HANDLE_RECUR,
+                       e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (cbo365)),
+                       NULL);
+       } else if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) {
+               /* return email address of the person who opened the calendar */
+               CamelO365Settings *o365_settings;
+
+               o365_settings = camel_o365_settings_get_from_backend (E_BACKEND (cal_backend), 
e_cal_backend_get_registry (cal_backend));
+
+               return camel_o365_settings_dup_email (o365_settings);
+       } else if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
+               /* Office 365 does not support email based alarms */
+               return NULL;
+       }
+
+       /* Chain up to parent's method. */
+       return E_CAL_BACKEND_CLASS (e_cal_backend_o365_parent_class)->impl_get_backend_property (cal_backend, 
prop_name);
+}
+
+static gboolean
+ecb_o365_get_destination_address (EBackend *backend,
+                                 gchar **host,
+                                 guint16 *port)
+{
+       g_return_val_if_fail (port != NULL, FALSE);
+       g_return_val_if_fail (host != NULL, FALSE);
+
+       /* Sanity checking */
+       if (!e_cal_backend_get_registry (E_CAL_BACKEND (backend)) ||
+           !e_backend_get_source (backend))
+               return FALSE;
+
+       *host = g_strdup ("graph.microsoft.com");
+       *port = 443;
+
+       return TRUE;
+}
+
+static gchar *
+ecb_o365_dup_component_revision (ECalCache *cal_cache,
+                                ICalComponent *icomp,
+                                gpointer user_data)
+{
+       g_return_val_if_fail (icomp != NULL, NULL);
+
+       return e_cal_util_component_dup_x_property (icomp, "X-EVOLUTION-CHANGEKEY");
+}
+
+static void
+ecb_o365_constructed (GObject *object)
+{
+       ECalBackendO365 *cbo365 = E_CAL_BACKEND_O365 (object);
+       ECalCache *cal_cache;
+       gchar *cache_dirname;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_cal_backend_o365_parent_class)->constructed (object);
+
+       /* Reset the connectable, it steals data from Authentication extension,
+          where is written incorrect address */
+       e_backend_set_connectable (E_BACKEND (object), NULL);
+
+       cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbo365));
+       g_return_if_fail (cal_cache != NULL);
+
+       cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (cal_cache)));
+       g_signal_connect (cal_cache, "dup-component-revision", G_CALLBACK (ecb_o365_dup_component_revision), 
NULL);
+
+       g_clear_object (&cal_cache);
+
+       cbo365->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL);
+       g_mkdir_with_parents (cbo365->priv->attachments_dir, 0777);
+
+       g_free (cache_dirname);
+}
+
+static void
+ecb_o365_dispose (GObject *object)
+{
+       ECalBackendO365 *cbo365 = E_CAL_BACKEND_O365 (object);
+
+       ecb_o365_unset_connection (cbo365, FALSE);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_cal_backend_o365_parent_class)->dispose (object);
+}
+
+static void
+ecb_o365_finalize (GObject *object)
+{
+       ECalBackendO365 *cbo365 = E_CAL_BACKEND_O365 (object);
+
+       g_free (cbo365->priv->attachments_dir);
+
+       g_rec_mutex_clear (&cbo365->priv->property_lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_cal_backend_o365_parent_class)->finalize (object);
+}
+
+static void
+e_cal_backend_o365_init (ECalBackendO365 *cbo365)
+{
+       cbo365->priv = e_cal_backend_o365_get_instance_private (cbo365);
+
+       g_rec_mutex_init (&cbo365->priv->property_lock);
+}
+
+static void
+e_cal_backend_o365_class_init (ECalBackendO365Class *klass)
+{
+       GObjectClass *object_class;
+       EBackendClass *backend_class;
+       ECalBackendClass *cal_backend_class;
+       ECalMetaBackendClass *cal_meta_backend_class;
+
+       cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass);
+       cal_meta_backend_class->connect_sync = ecb_o365_connect_sync;
+       cal_meta_backend_class->disconnect_sync = ecb_o365_disconnect_sync;
+       cal_meta_backend_class->get_changes_sync = ecb_o365_get_changes_sync;
+       cal_meta_backend_class->load_component_sync = ecb_o365_load_component_sync;
+       cal_meta_backend_class->save_component_sync = ecb_o365_save_component_sync;
+       cal_meta_backend_class->remove_component_sync = ecb_o365_remove_component_sync;
+
+       cal_backend_class = E_CAL_BACKEND_CLASS (klass);
+       cal_backend_class->impl_get_backend_property = ecb_o365_get_backend_property;
+
+       backend_class = E_BACKEND_CLASS (klass);
+       backend_class->get_destination_address = ecb_o365_get_destination_address;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = ecb_o365_constructed;
+       object_class->dispose = ecb_o365_dispose;
+       object_class->finalize = ecb_o365_finalize;
+}
diff --git a/src/Office365/calendar/e-cal-backend-o365.h b/src/Office365/calendar/e-cal-backend-o365.h
new file mode 100644
index 00000000..1ffeb8c9
--- /dev/null
+++ b/src/Office365/calendar/e-cal-backend-o365.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_CAL_BACKEND_O365_H
+#define E_CAL_BACKEND_O365_H
+
+#include <libedata-cal/libedata-cal.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_CAL_BACKEND_O365            (e_cal_backend_o365_get_type ())
+#define E_CAL_BACKEND_O365(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CAL_BACKEND_O365,      
  ECalBackendO365))
+#define E_CAL_BACKEND_O365_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CAL_BACKEND_O365, 
ECalBackendO365Class))
+#define E_IS_CAL_BACKEND_O365(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CAL_BACKEND_O365))
+#define E_IS_CAL_BACKEND_O365_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CAL_BACKEND_O365))
+
+typedef struct _ECalBackendO365        ECalBackendO365;
+typedef struct _ECalBackendO365Class   ECalBackendO365Class;
+typedef struct _ECalBackendO365Private ECalBackendO365Private;
+
+struct _ECalBackendO365 {
+       ECalMetaBackend parent_object;
+       ECalBackendO365Private *priv;
+};
+
+struct _ECalBackendO365Class {
+       ECalMetaBackendClass parent_class;
+};
+
+GType   e_cal_backend_o365_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_CAL_BACKEND_O365_H */
diff --git a/src/Office365/camel/CMakeLists.txt b/src/Office365/camel/CMakeLists.txt
new file mode 100644
index 00000000..56e506b9
--- /dev/null
+++ b/src/Office365/camel/CMakeLists.txt
@@ -0,0 +1,65 @@
+install(FILES libcameloffice365.urls
+       DESTINATION ${camel_providerdir}
+)
+
+set(DEPENDENCIES
+       evolution-office365
+)
+
+set(SOURCES
+       camel-o365-provider.c
+       camel-o365-store.c
+       camel-o365-store.h
+       camel-o365-transport.c
+       camel-o365-transport.h
+)
+
+add_library(cameloffice365 MODULE
+       ${SOURCES}
+)
+
+add_dependencies(cameloffice365
+       ${DEPENDENCIES}
+)
+
+target_compile_definitions(cameloffice365 PRIVATE
+       -DG_LOG_DOMAIN=\"camel-office365-provider\"
+       -DO365_LOCALEDIR=\"${LOCALE_INSTALL_DIR}\"
+)
+
+target_compile_options(cameloffice365 PUBLIC
+       ${CAMEL_CFLAGS}
+       ${EVOLUTION_SHELL_CFLAGS}
+       ${EVOLUTION_MAIL_CFLAGS}
+       ${LIBEDATASERVER_CFLAGS}
+       ${LIBECAL_CFLAGS}
+       ${SOUP_CFLAGS}
+)
+
+target_include_directories(cameloffice365 PUBLIC
+       ${CMAKE_BINARY_DIR}
+       ${CMAKE_SOURCE_DIR}
+       ${CMAKE_BINARY_DIR}/src/Office365
+       ${CMAKE_SOURCE_DIR}/src/Office365
+       ${CMAKE_CURRENT_BINARY_DIR}
+       ${CAMEL_INCLUDE_DIRS}
+       ${EVOLUTION_SHELL_INCLUDE_DIRS}
+       ${EVOLUTION_MAIL_INCLUDE_DIRS}
+       ${LIBEDATASERVER_INCLUDE_DIRS}
+       ${LIBECAL_INCLUDE_DIRS}
+       ${SOUP_INCLUDE_DIRS}
+)
+
+target_link_libraries(cameloffice365
+       ${DEPENDENCIES}
+       ${CAMEL_LDFLAGS}
+       ${EVOLUTION_SHELL_LDFLAGS}
+       ${EVOLUTION_MAIL_LDFLAGS}
+       ${LIBEDATASERVER_LDFLAGS}
+       ${LIBECAL_LDFLAGS}
+       ${SOUP_LDFLAGS}
+)
+
+install(TARGETS cameloffice365
+       DESTINATION ${camel_providerdir}
+)
diff --git a/src/Office365/camel/camel-o365-provider.c b/src/Office365/camel/camel-o365-provider.c
new file mode 100644
index 00000000..d57614af
--- /dev/null
+++ b/src/Office365/camel/camel-o365-provider.c
@@ -0,0 +1,128 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+
+#include "camel-o365-store.h"
+#include "camel-o365-transport.h"
+
+static void add_hash (guint *hash, gchar *s);
+static guint o365_url_hash (gconstpointer key);
+static gint o365_url_equal (gconstpointer a, gconstpointer b);
+
+static CamelProviderConfEntry o365_conf_entries[] = {
+       { CAMEL_PROVIDER_CONF_SECTION_START, "mailcheck", NULL,
+         N_("Checking for new mail") },
+       { CAMEL_PROVIDER_CONF_CHECKBOX, "check-all", NULL,
+         N_("C_heck for new messages in all folders"), "1" },
+       { CAMEL_PROVIDER_CONF_SECTION_END },
+
+       { CAMEL_PROVIDER_CONF_SECTION_START, "general", NULL, N_("Options") },
+       { CAMEL_PROVIDER_CONF_CHECKBOX, "filter-inbox", NULL,
+         N_("_Apply filters to new messages in Inbox on this server"), "0" },
+       { CAMEL_PROVIDER_CONF_CHECKBOX, "filter-junk", NULL,
+         N_("Check new messages for _Junk contents"), "0" },
+       { CAMEL_PROVIDER_CONF_CHECKBOX, "filter-junk-inbox", "filter-junk",
+         N_("Only check for Junk messages in the IN_BOX folder"), "0" },
+       { CAMEL_PROVIDER_CONF_CHECKBOX, "stay-synchronized", NULL,
+         N_("Synchroni_ze remote mail locally in all folders"), "0" },
+       { CAMEL_PROVIDER_CONF_PLACEHOLDER, "o365-limit-by-age-placeholder", NULL },
+       { CAMEL_PROVIDER_CONF_SECTION_END },
+
+       { CAMEL_PROVIDER_CONF_SECTION_START, "connection", NULL, N_("Connection") },
+       { CAMEL_PROVIDER_CONF_CHECKSPIN, "timeout", NULL,
+         /* Translators: '%s' is preplaced with a widget, where "
+          * user can select how long the timeout should be. */
+         N_("Connection _timeout (in seconds) %s"), "0:1:0:32768" },
+       { CAMEL_PROVIDER_CONF_CHECKSPIN, "concurrent-connections", NULL,
+         N_("Numbe_r of concurrent connections to use"), "y:1:1:7" },
+       { CAMEL_PROVIDER_CONF_SECTION_END },
+
+       { CAMEL_PROVIDER_CONF_END }
+};
+
+static CamelProvider o365_provider = {
+       "office365",
+       N_("Office 365"),
+
+       N_("For accessing Microsoft Office 365 server"),
+
+       "mail",
+
+       CAMEL_PROVIDER_IS_REMOTE | CAMEL_PROVIDER_IS_SOURCE |
+       CAMEL_PROVIDER_IS_STORAGE | CAMEL_PROVIDER_IS_EXTERNAL,
+
+       CAMEL_URL_ALLOW_USER | CAMEL_URL_ALLOW_AUTH | CAMEL_URL_HIDDEN_HOST,
+
+       o365_conf_entries,
+
+       /* ... */
+};
+
+void
+camel_provider_module_init (void)
+{
+       bindtextdomain (GETTEXT_PACKAGE, O365_LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+       o365_provider.url_hash = o365_url_hash;
+       o365_provider.url_equal = o365_url_equal;
+       o365_provider.authtypes = NULL;
+       o365_provider.translation_domain = GETTEXT_PACKAGE;
+       o365_provider.object_types[CAMEL_PROVIDER_STORE] =  CAMEL_TYPE_O365_STORE;
+       o365_provider.object_types[CAMEL_PROVIDER_TRANSPORT] = CAMEL_TYPE_O365_TRANSPORT;
+
+       camel_provider_register (&o365_provider);
+}
+
+static void
+add_hash (guint *hash,
+          gchar *s)
+{
+       if (s)
+               *hash ^= g_str_hash(s);
+}
+
+static guint
+o365_url_hash (gconstpointer key)
+{
+       const CamelURL *u = (CamelURL *) key;
+       guint hash = 0;
+
+       add_hash (&hash, u->user);
+       add_hash (&hash, u->host);
+       hash ^= u->port;
+
+       return hash;
+}
+
+static gint
+o365_url_equal (gconstpointer a,
+               gconstpointer b)
+{
+       const CamelURL *u1 = a, *u2 = b;
+
+       return ((g_strcmp0 (u1->protocol, u2->protocol) == 0)
+               && (g_strcmp0 (u1->user, u2->user) == 0)
+               && (g_strcmp0 (u1->host, u2->host) == 0)
+               && (u1->port == u2->port));
+}
diff --git a/src/Office365/camel/camel-o365-store.c b/src/Office365/camel/camel-o365-store.c
new file mode 100644
index 00000000..7efb4f1d
--- /dev/null
+++ b/src/Office365/camel/camel-o365-store.c
@@ -0,0 +1,383 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+
+#include "common/camel-o365-settings.h"
+
+#include "camel-o365-store.h"
+
+struct _CamelO365StorePrivate {
+       GRecMutex property_lock;
+       gchar *storage_path;
+};
+
+static void camel_o365_store_initable_init (GInitableIface *iface);
+static void camel_o365_subscribable_init (CamelSubscribableInterface *iface);
+static GInitableIface *parent_initable_interface;
+
+enum {
+       PROP_0,
+       PROP_CONNECTABLE,
+       PROP_HOST_REACHABLE
+};
+
+G_DEFINE_TYPE_WITH_CODE (CamelO365Store, camel_o365_store, CAMEL_TYPE_OFFLINE_STORE,
+       G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, camel_o365_store_initable_init)
+       G_IMPLEMENT_INTERFACE (CAMEL_TYPE_NETWORK_SERVICE, NULL)
+       G_IMPLEMENT_INTERFACE (CAMEL_TYPE_SUBSCRIBABLE, camel_o365_subscribable_init)
+       G_ADD_PRIVATE (CamelO365Store))
+
+static void
+o365_store_set_property (GObject *object,
+                        guint property_id,
+                        const GValue *value,
+                        GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CONNECTABLE:
+                       camel_network_service_set_connectable (
+                               CAMEL_NETWORK_SERVICE (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+o365_store_get_property (GObject *object,
+                        guint property_id,
+                        GValue *value,
+                        GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CONNECTABLE:
+                       g_value_take_object (
+                               value,
+                               camel_network_service_ref_connectable (
+                                       CAMEL_NETWORK_SERVICE (object)));
+                       return;
+               case PROP_HOST_REACHABLE:
+                       g_value_set_boolean (
+                               value,
+                               camel_network_service_get_host_reachable (
+                                       CAMEL_NETWORK_SERVICE (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static gboolean
+o365_store_construct (CamelService *service,
+                     CamelSession *session,
+                     CamelProvider *provider,
+                     GError **error)
+{
+       CamelO365Store *o365_store;
+       gchar /**summary_file,*/ *session_storage_path;
+       guint32 store_flags;
+
+       o365_store = (CamelO365Store *) service;
+
+       store_flags = camel_store_get_flags (CAMEL_STORE (o365_store));
+
+       /* Disable virtual trash and junk folders. Office365 has real
+        * folders for that */
+       store_flags &= ~(CAMEL_STORE_VTRASH | CAMEL_STORE_VJUNK);
+       store_flags |= CAMEL_STORE_REAL_JUNK_FOLDER;
+       camel_store_set_flags (CAMEL_STORE (o365_store), store_flags);
+
+       /*storage path*/
+       session_storage_path = g_strdup (camel_service_get_user_cache_dir (service));
+       if (!session_storage_path) {
+               g_set_error (
+                       error, CAMEL_STORE_ERROR,
+                       CAMEL_STORE_ERROR_INVALID,
+                       _("Session has no storage path"));
+               return FALSE;
+       }
+
+       o365_store->priv->storage_path = session_storage_path;
+
+       g_mkdir_with_parents (o365_store->priv->storage_path, 0700);
+       /*summary_file = g_build_filename (o365_store->storage_path, "folder-tree", NULL);
+       o365_store->summary = camel_o365_store_summary_new (summary_file);
+       camel_o365_store_summary_load (o365_store->summary, NULL);
+
+       g_free (summary_file);*/
+
+       return TRUE;
+}
+
+static gboolean
+o365_store_initable_init (GInitable *initable,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       CamelService *service;
+       CamelSession *session;
+       CamelStore *store;
+       gboolean ret;
+
+       store = CAMEL_STORE (initable);
+       service = CAMEL_SERVICE (initable);
+
+       camel_store_set_flags (store, camel_store_get_flags (store) |
+               CAMEL_STORE_USE_CACHE_DIR |
+               CAMEL_STORE_SUPPORTS_INITIAL_SETUP |
+               CAMEL_STORE_CAN_DELETE_FOLDERS_AT_ONCE);
+
+       /* Chain up to parent interface's method. */
+       if (!parent_initable_interface->init (initable, cancellable, error))
+               return FALSE;
+
+       session = camel_service_ref_session (service);
+
+       ret = o365_store_construct (service, session, NULL, error);
+
+       g_object_unref (session);
+
+       return ret;
+}
+
+static GList *
+o365_store_query_auth_types_sync (CamelService *service,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_STORE (service), NULL);
+
+       return NULL;
+}
+
+static gchar *
+o365_store_get_name (CamelService *service,
+                    gboolean brief)
+{
+       gchar *name;
+
+       if (brief)
+               name = g_strdup (_("Office365 server"));
+       else
+               name = g_strdup (_("Mail receive via Microsoft Office365"));
+
+       return name;
+}
+
+static gboolean
+o365_store_connect_sync (CamelService *service,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       /*CamelO365Store *o365_store;*/
+       gboolean success = FALSE;
+
+       /* Chain up to parent's method. */
+       if (!CAMEL_SERVICE_CLASS (camel_o365_store_parent_class)->connect_sync (service, cancellable, error))
+               return FALSE;
+
+       if (camel_service_get_connection_status (service) == CAMEL_SERVICE_DISCONNECTED)
+               return FALSE;
+
+       /*o365_store = CAMEL_O365_STORE (service);
+
+       g_signal_connect_swapped (
+               o365_settings,
+               "notify::listen-notifications",
+               G_CALLBACK (camel_o365_store_listen_notifications_cb),
+               o365_store);
+
+       g_signal_connect_swapped (
+               o365_settings,
+               "notify::check-all",
+               G_CALLBACK (camel_o365_store_check_all_cb),
+               o365_store);*/
+
+       return success;
+}
+
+static gboolean
+o365_store_disconnect_sync (CamelService *service,
+                           gboolean clean,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+       /*CamelO365Store *o365_store = CAMEL_O365_STORE (service);
+
+       g_mutex_lock (&o365_store->priv->connection_lock);
+       o365_store_unset_connection_locked (o365_store, TRUE);
+       g_mutex_unlock (&o365_store->priv->connection_lock);*/
+
+       return CAMEL_SERVICE_CLASS (camel_o365_store_parent_class)->disconnect_sync (service, clean, 
cancellable, error);
+}
+
+static CamelAuthenticationResult
+o365_store_authenticate_sync (CamelService *service,
+                             const gchar *mechanism,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+       CamelAuthenticationResult result;
+       /*CamelO365Store *o365_store;
+
+       o365_store = CAMEL_O365_STORE (service);*/
+
+       result = CAMEL_AUTHENTICATION_ERROR;
+
+       return result;
+}
+
+gboolean
+camel_o365_store_connected (CamelO365Store *o365_store,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+       if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (o365_store))) {
+               g_set_error (
+                       error, CAMEL_SERVICE_ERROR,
+                       CAMEL_SERVICE_ERROR_UNAVAILABLE,
+                       _("You must be working online to complete this operation"));
+               return FALSE;
+       }
+
+       if (!camel_service_connect_sync ((CamelService *) o365_store, cancellable, error))
+               return FALSE;
+
+       return TRUE;
+}
+
+void
+camel_o365_store_maybe_disconnect (CamelO365Store *store,
+                                  const GError *error)
+{
+       CamelService *service;
+
+       g_return_if_fail (store != NULL);
+
+       if (!error)
+               return;
+
+       service = CAMEL_SERVICE (store);
+
+       if (camel_service_get_connection_status (service) != CAMEL_SERVICE_CONNECTED)
+               return;
+
+#if 0
+       if (g_error_matches (error, O365_CONNECTION_ERROR, O365_CONNECTION_ERROR_NORESPONSE) ||
+           g_error_matches (error, O365_CONNECTION_ERROR, O365_CONNECTION_ERROR_AUTHENTICATION_FAILED))
+               camel_service_disconnect_sync (service, FALSE, NULL, NULL);
+#endif
+}
+
+static void
+o365_store_dispose (GObject *object)
+{
+       /*CamelO365Store *o365_store = CAMEL_O365_STORE (object);*/
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (camel_o365_store_parent_class)->dispose (object);
+}
+
+static void
+o365_store_finalize (GObject *object)
+{
+       CamelO365Store *o365_store;
+
+       o365_store = CAMEL_O365_STORE (object);
+
+       g_rec_mutex_clear (&o365_store->priv->property_lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (camel_o365_store_parent_class)->finalize (object);
+}
+
+static void
+camel_o365_store_class_init (CamelO365StoreClass *class)
+{
+       GObjectClass *object_class;
+       CamelServiceClass *service_class;
+       /*CamelStoreClass *store_class;*/
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = o365_store_set_property;
+       object_class->get_property = o365_store_get_property;
+       object_class->dispose = o365_store_dispose;
+       object_class->finalize = o365_store_finalize;
+
+       /* Inherited from CamelNetworkService */
+       g_object_class_override_property (
+               object_class,
+               PROP_CONNECTABLE,
+               "connectable");
+
+       /* Inherited from CamelNetworkService */
+       g_object_class_override_property (
+               object_class,
+               PROP_HOST_REACHABLE,
+               "host-reachable");
+
+       service_class = CAMEL_SERVICE_CLASS (class);
+       service_class->settings_type = CAMEL_TYPE_O365_SETTINGS;
+       service_class->query_auth_types_sync = o365_store_query_auth_types_sync;
+       service_class->get_name = o365_store_get_name;
+       service_class->connect_sync = o365_store_connect_sync;
+       service_class->disconnect_sync = o365_store_disconnect_sync;
+       service_class->authenticate_sync = o365_store_authenticate_sync;
+#if 0
+       store_class = CAMEL_STORE_CLASS (class);
+       store_class->get_folder_sync = o365_get_folder_sync;
+       store_class->create_folder_sync = o365_create_folder_sync;
+       store_class->delete_folder_sync = o365_delete_folder_sync;
+       store_class->rename_folder_sync = o365_rename_folder_sync;
+       store_class->get_folder_info_sync = o365_get_folder_info_sync;
+       store_class->initial_setup_sync = o365_initial_setup_sync;
+       store_class->get_trash_folder_sync = o365_get_trash_folder_sync;
+       store_class->get_junk_folder_sync = o365_get_junk_folder_sync;
+       store_class->can_refresh_folder = o365_can_refresh_folder;
+#endif
+}
+
+static void
+camel_o365_store_initable_init (GInitableIface *iface)
+{
+       parent_initable_interface = g_type_interface_peek_parent (iface);
+
+       iface->init = o365_store_initable_init;
+}
+
+static void
+camel_o365_subscribable_init (CamelSubscribableInterface *iface)
+{
+#if 0
+       iface->folder_is_subscribed = o365_store_folder_is_subscribed;
+       iface->subscribe_folder_sync = o365_store_subscribe_folder_sync;
+       iface->unsubscribe_folder_sync = o365_store_unsubscribe_folder_sync;
+#endif
+}
+
+static void
+camel_o365_store_init (CamelO365Store *o365_store)
+{
+       o365_store->priv = camel_o365_store_get_instance_private (o365_store);
+
+       g_rec_mutex_init (&o365_store->priv->property_lock);
+}
diff --git a/src/Office365/camel/camel-o365-store.h b/src/Office365/camel/camel-o365-store.h
new file mode 100644
index 00000000..1567d012
--- /dev/null
+++ b/src/Office365/camel/camel-o365-store.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CAMEL_O365_STORE_H
+#define CAMEL_O365_STORE_H
+
+#include <camel/camel.h>
+
+/* Standard GObject macros */
+#define CAMEL_TYPE_O365_STORE \
+       (camel_o365_store_get_type ())
+#define CAMEL_O365_STORE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), CAMEL_TYPE_O365_STORE, CamelO365Store))
+#define CAMEL_O365_STORE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), CAMEL_TYPE_O365_STORE, CamelO365StoreClass))
+#define CAMEL_IS_O365_STORE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), CAMEL_TYPE_O365_STORE))
+#define CAMEL_IS_O365_STORE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), CAMEL_TYPE_O365_STORE))
+#define CAMEL_O365_STORE_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), CAMEL_TYPE_O365_STORE, CamelO365StoreClass))
+
+G_BEGIN_DECLS
+
+typedef struct _CamelO365Store CamelO365Store;
+typedef struct _CamelO365StoreClass CamelO365StoreClass;
+typedef struct _CamelO365StorePrivate CamelO365StorePrivate;
+
+struct _CamelO365Store {
+       CamelOfflineStore parent;
+       CamelO365StorePrivate *priv;
+};
+
+struct _CamelO365StoreClass {
+       CamelOfflineStoreClass parent_class;
+};
+
+GType          camel_o365_store_get_type       (void);
+gboolean       camel_o365_store_connected      (CamelO365Store *store,
+                                                GCancellable *cancellable,
+                                                GError **error);
+void           camel_o365_store_maybe_disconnect
+                                               (CamelO365Store *store,
+                                                const GError *error);
+
+G_END_DECLS
+
+#endif /* CAMEL_O365_STORE_H */
diff --git a/src/Office365/camel/camel-o365-transport.c b/src/Office365/camel/camel-o365-transport.c
new file mode 100644
index 00000000..27bbfe91
--- /dev/null
+++ b/src/Office365/camel/camel-o365-transport.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "common/camel-o365-settings.h"
+
+#include "camel-o365-transport.h"
+
+struct _CamelO365TransportPrivate
+{
+       GMutex property_lock;
+       /*EO365Connection *connection;*/
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (CamelO365Transport, camel_o365_transport, CAMEL_TYPE_TRANSPORT)
+
+/*static EO365Connection *
+o365_transport_ref_connection (CamelO365Transport *o365_transport)
+{
+       EO365Connection *connection = NULL;
+
+       g_return_val_if_fail (CAMEL_IS_O365_TRANSPORT (o365_transport), NULL);
+
+       g_mutex_lock (&o365_transport->priv->property_lock);
+
+       if (o365_transport->priv->connection)
+               connection = g_object_ref (o365_transport->priv->connection);
+
+       g_mutex_unlock (&o365_transport->priv->property_lock);
+
+       return connection;
+}*/
+
+static gboolean
+o365_transport_connect_sync (CamelService *service,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       CamelSession *session;
+       gboolean success;
+
+       /* Chain up to parent's method. */
+       if (!CAMEL_SERVICE_CLASS (camel_o365_transport_parent_class)->connect_sync (service, cancellable, 
error))
+               return FALSE;
+
+       if (camel_service_get_connection_status (service) == CAMEL_SERVICE_DISCONNECTED)
+               return FALSE;
+
+       /*connection = o365_transport_ref_connection (CAMEL_O365_TRANSPORT (service));
+       if (connection) {
+               g_object_unref (connection);
+               return TRUE;
+       }*/
+
+       session = camel_service_ref_session (service);
+
+       success = camel_session_authenticate_sync (session, service, "Office365", cancellable, error);
+
+       g_object_unref (session);
+
+       return success;
+}
+
+static gboolean
+o365_transport_disconnect_sync (CamelService *service,
+                               gboolean clean,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       CamelO365Transport *o365_transport = CAMEL_O365_TRANSPORT (service);
+
+       g_mutex_lock (&o365_transport->priv->property_lock);
+       /*g_clear_object (&o365_transport->priv->connection);*/
+       g_mutex_unlock (&o365_transport->priv->property_lock);
+
+       return CAMEL_SERVICE_CLASS (camel_o365_transport_parent_class)->disconnect_sync (service, clean, 
cancellable, error);
+}
+
+static CamelAuthenticationResult
+o365_transport_authenticate_sync (CamelService *service,
+                                 const gchar *mechanism,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       CamelAuthenticationResult result;
+
+       result = CAMEL_AUTHENTICATION_ERROR;
+
+       return result;
+}
+
+static gchar *
+o365_transport_get_name (CamelService *service,
+                        gboolean brief)
+{
+       gchar *name;
+
+       if (brief)
+               name = g_strdup (_("Office365 server"));
+       else
+               name = g_strdup (_("Mail delivery via Microsoft Office365"));
+
+       return name;
+}
+
+static gboolean
+o365_send_to_sync (CamelTransport *transport,
+                  CamelMimeMessage *message,
+                  CamelAddress *from,
+                  CamelAddress *recipients,
+                  gboolean *out_sent_message_saved,
+                  GCancellable *cancellable,
+                  GError **error)
+{
+       return FALSE;
+}
+
+static void
+o365_transport_dispose (GObject *object)
+{
+       CamelO365Transport *o365_transport = CAMEL_O365_TRANSPORT (object);
+
+       g_mutex_lock (&o365_transport->priv->property_lock);
+       /*g_clear_object (&o365_transport->priv->connection);*/
+       g_mutex_unlock (&o365_transport->priv->property_lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (camel_o365_transport_parent_class)->dispose (object);
+}
+
+static void
+o365_transport_finalize (GObject *object)
+{
+       CamelO365Transport *o365_transport = CAMEL_O365_TRANSPORT (object);
+
+       g_mutex_clear (&o365_transport->priv->property_lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (camel_o365_transport_parent_class)->finalize (object);
+}
+
+static void
+camel_o365_transport_class_init (CamelO365TransportClass *class)
+{
+       GObjectClass *object_class;
+       CamelServiceClass *service_class;
+       CamelTransportClass *transport_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = o365_transport_dispose;
+       object_class->finalize = o365_transport_finalize;
+
+       service_class = CAMEL_SERVICE_CLASS (class);
+       service_class->settings_type = CAMEL_TYPE_O365_SETTINGS;
+       service_class->get_name = o365_transport_get_name;
+       service_class->connect_sync = o365_transport_connect_sync;
+       service_class->disconnect_sync = o365_transport_disconnect_sync;
+       service_class->authenticate_sync = o365_transport_authenticate_sync;
+
+       transport_class = CAMEL_TRANSPORT_CLASS (class);
+       transport_class->send_to_sync = o365_send_to_sync;
+}
+
+static void
+camel_o365_transport_init (CamelO365Transport *o365_transport)
+{
+       o365_transport->priv = camel_o365_transport_get_instance_private (o365_transport);
+
+       g_mutex_init (&o365_transport->priv->property_lock);
+}
diff --git a/src/Office365/camel/camel-o365-transport.h b/src/Office365/camel/camel-o365-transport.h
new file mode 100644
index 00000000..ed679f49
--- /dev/null
+++ b/src/Office365/camel/camel-o365-transport.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CAMEL_O365_TRANSPORT_H
+#define CAMEL_O365_TRANSPORT_H
+
+#include <camel/camel.h>
+
+/* Standard GObject macros */
+#define CAMEL_TYPE_O365_TRANSPORT \
+       (camel_o365_transport_get_type ())
+#define CAMEL_O365_TRANSPORT(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), CAMEL_TYPE_O365_TRANSPORT, CamelO365Transport))
+#define CAMEL_O365_TRANSPORT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), CAMEL_TYPE_O365_TRANSPORT, CamelO365TransportClass))
+#define CAMEL_IS_O365_TRANSPORT(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), CAMEL_TYPE_O365_TRANSPORT))
+#define CAMEL_IS_O365_TRANSPORT_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), CAMEL_TYPE_O365_TRANSPORT))
+#define CAMEL_O365_TRANSPORT_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), CAMEL_TYPE_O365_TRANSPORT, CamelO365TransportClass))
+
+G_BEGIN_DECLS
+
+typedef struct _CamelO365Transport CamelO365Transport;
+typedef struct _CamelO365TransportClass CamelO365TransportClass;
+typedef struct _CamelO365TransportPrivate CamelO365TransportPrivate;
+
+struct _CamelO365Transport {
+       CamelTransport parent;
+       CamelO365TransportPrivate *priv;
+};
+
+struct _CamelO365TransportClass {
+       CamelTransportClass parent_class;
+};
+
+GType camel_o365_transport_get_type (void);
+
+G_END_DECLS
+
+#endif /* CAMEL_O365_TRANSPORT_H */
diff --git a/src/Office365/camel/libcameloffice365.urls b/src/Office365/camel/libcameloffice365.urls
new file mode 100644
index 00000000..ea5d8c19
--- /dev/null
+++ b/src/Office365/camel/libcameloffice365.urls
@@ -0,0 +1 @@
+office365
diff --git a/src/Office365/common/CMakeLists.txt b/src/Office365/common/CMakeLists.txt
new file mode 100644
index 00000000..ff271000
--- /dev/null
+++ b/src/Office365/common/CMakeLists.txt
@@ -0,0 +1,53 @@
+set(SOURCES
+       camel-o365-settings.c
+       camel-o365-settings.h
+       e-source-o365-folder.c
+       e-source-o365-folder.h
+)
+
+add_library(evolution-office365 SHARED
+       ${SOURCES}
+)
+
+target_compile_definitions(evolution-office365 PRIVATE
+       -DG_LOG_DOMAIN=\"evolution-office365\"
+)
+
+target_compile_options(evolution-office365 PUBLIC
+       ${CAMEL_CFLAGS}
+       ${EVOLUTION_CALENDAR_CFLAGS}
+       ${LIBEBACKEND_CFLAGS}
+       ${LIBECAL_CFLAGS}
+       ${LIBEDATACAL_CFLAGS}
+       ${LIBEDATASERVER_CFLAGS}
+       ${SOUP_CFLAGS}
+)
+
+target_include_directories(evolution-office365 PUBLIC
+       ${CMAKE_BINARY_DIR}
+       ${CMAKE_SOURCE_DIR}
+       ${CMAKE_BINARY_DIR}/src/Office365
+       ${CMAKE_SOURCE_DIR}/src/Office365
+       ${CMAKE_CURRENT_BINARY_DIR}
+       ${CAMEL_INCLUDE_DIRS}
+       ${EVOLUTION_CALENDAR_INCLUDE_DIRS}
+       ${LIBEBACKEND_INCLUDE_DIRS}
+       ${LIBECAL_INCLUDE_DIRS}
+       ${LIBEDATACAL_INCLUDE_DIRS}
+       ${LIBEDATASERVER_INCLUDE_DIRS}
+       ${SOUP_INCLUDE_DIRS}
+)
+
+target_link_libraries(evolution-office365
+       ${CAMEL_LDFLAGS}
+       ${EVOLUTION_CALENDAR_LDFLAGS}
+       ${LIBEBACKEND_LDFLAGS}
+       ${LIBECAL_LDFLAGS}
+       ${LIBEDATACAL_LDFLAGS}
+       ${LIBEDATASERVER_LDFLAGS}
+       ${SOUP_LDFLAGS}
+)
+
+install(TARGETS evolution-office365
+       DESTINATION ${privsolibdir}
+)
diff --git a/src/Office365/common/camel-o365-settings.c b/src/Office365/common/camel-o365-settings.c
new file mode 100644
index 00000000..c103962f
--- /dev/null
+++ b/src/Office365/common/camel-o365-settings.c
@@ -0,0 +1,826 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
+
+#include "camel-o365-settings.h"
+
+struct _CamelO365SettingsPrivate {
+       GMutex property_lock;
+       gboolean check_all;
+       gboolean filter_junk;
+       gboolean filter_junk_inbox;
+       gboolean override_oauth2;
+       guint timeout;
+       guint concurrent_connections;
+       gchar *email;
+       gchar *oauth2_tenant;
+       gchar *oauth2_client_id;
+       gchar *oauth2_redirect_uri;
+};
+
+enum {
+       PROP_0,
+       PROP_AUTH_MECHANISM,
+       PROP_CHECK_ALL,
+       PROP_EMAIL,
+       PROP_FILTER_JUNK,
+       PROP_FILTER_JUNK_INBOX,
+       PROP_HOST,
+       PROP_PORT,
+       PROP_SECURITY_METHOD,
+       PROP_TIMEOUT,
+       PROP_USER,
+       PROP_OVERRIDE_OAUTH2,
+       PROP_OAUTH2_TENANT,
+       PROP_OAUTH2_CLIENT_ID,
+       PROP_OAUTH2_REDIRECT_URI,
+       PROP_CONCURRENT_CONNECTIONS
+};
+
+G_DEFINE_TYPE_WITH_CODE (CamelO365Settings, camel_o365_settings, CAMEL_TYPE_OFFLINE_SETTINGS,
+       G_IMPLEMENT_INTERFACE (CAMEL_TYPE_NETWORK_SETTINGS, NULL)
+       G_ADD_PRIVATE (CamelO365Settings))
+
+static void
+o365_settings_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_AUTH_MECHANISM:
+                       camel_network_settings_set_auth_mechanism (
+                               CAMEL_NETWORK_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_CHECK_ALL:
+                       camel_o365_settings_set_check_all (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_EMAIL:
+                       camel_o365_settings_set_email (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_FILTER_JUNK:
+                       camel_o365_settings_set_filter_junk (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_FILTER_JUNK_INBOX:
+                       camel_o365_settings_set_filter_junk_inbox (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_HOST:
+                       camel_network_settings_set_host (
+                               CAMEL_NETWORK_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_PORT:
+                       camel_network_settings_set_port (
+                               CAMEL_NETWORK_SETTINGS (object),
+                               g_value_get_uint (value));
+                       return;
+
+               case PROP_SECURITY_METHOD:
+                       camel_network_settings_set_security_method (
+                               CAMEL_NETWORK_SETTINGS (object),
+                               g_value_get_enum (value));
+                       return;
+
+               case PROP_TIMEOUT:
+                       camel_o365_settings_set_timeout (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_uint (value));
+                       return;
+
+               case PROP_USER:
+                       camel_network_settings_set_user (
+                               CAMEL_NETWORK_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_OVERRIDE_OAUTH2:
+                       camel_o365_settings_set_override_oauth2 (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_OAUTH2_TENANT:
+                       camel_o365_settings_set_oauth2_tenant (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_OAUTH2_CLIENT_ID:
+                       camel_o365_settings_set_oauth2_client_id (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_OAUTH2_REDIRECT_URI:
+                       camel_o365_settings_set_oauth2_redirect_uri (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_CONCURRENT_CONNECTIONS:
+                       camel_o365_settings_set_concurrent_connections (
+                               CAMEL_O365_SETTINGS (object),
+                               g_value_get_uint (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+o365_settings_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_AUTH_MECHANISM:
+                       g_value_take_string (
+                               value,
+                               camel_network_settings_dup_auth_mechanism (
+                               CAMEL_NETWORK_SETTINGS (object)));
+                       return;
+
+               case PROP_CHECK_ALL:
+                       g_value_set_boolean (
+                               value,
+                               camel_o365_settings_get_check_all (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_EMAIL:
+                       g_value_take_string (
+                               value,
+                               camel_o365_settings_dup_email (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_FILTER_JUNK:
+                       g_value_set_boolean (
+                               value,
+                               camel_o365_settings_get_filter_junk (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_FILTER_JUNK_INBOX:
+                       g_value_set_boolean (
+                               value,
+                               camel_o365_settings_get_filter_junk_inbox (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_HOST:
+                       g_value_take_string (
+                               value,
+                               camel_network_settings_dup_host (
+                               CAMEL_NETWORK_SETTINGS (object)));
+                       return;
+
+               case PROP_PORT:
+                       g_value_set_uint (
+                               value,
+                               camel_network_settings_get_port (
+                               CAMEL_NETWORK_SETTINGS (object)));
+                       return;
+
+               case PROP_SECURITY_METHOD:
+                       g_value_set_enum (
+                               value,
+                               camel_network_settings_get_security_method (
+                               CAMEL_NETWORK_SETTINGS (object)));
+                       return;
+
+               case PROP_TIMEOUT:
+                       g_value_set_uint (
+                               value,
+                               camel_o365_settings_get_timeout (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_USER:
+                       g_value_take_string (
+                               value,
+                               camel_network_settings_dup_user (
+                               CAMEL_NETWORK_SETTINGS (object)));
+                       return;
+
+               case PROP_OVERRIDE_OAUTH2:
+                       g_value_set_boolean (
+                               value,
+                               camel_o365_settings_get_override_oauth2 (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_OAUTH2_TENANT:
+                       g_value_take_string (
+                               value,
+                               camel_o365_settings_dup_oauth2_tenant (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_OAUTH2_CLIENT_ID:
+                       g_value_take_string (
+                               value,
+                               camel_o365_settings_dup_oauth2_client_id (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_OAUTH2_REDIRECT_URI:
+                       g_value_take_string (
+                               value,
+                               camel_o365_settings_dup_oauth2_redirect_uri (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+
+               case PROP_CONCURRENT_CONNECTIONS:
+                       g_value_set_uint (
+                               value,
+                               camel_o365_settings_get_concurrent_connections (
+                               CAMEL_O365_SETTINGS (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+o365_settings_finalize (GObject *object)
+{
+       CamelO365Settings *o365_settings = CAMEL_O365_SETTINGS (object);
+
+       g_mutex_clear (&o365_settings->priv->property_lock);
+
+       g_free (o365_settings->priv->email);
+       g_free (o365_settings->priv->oauth2_tenant);
+       g_free (o365_settings->priv->oauth2_client_id);
+       g_free (o365_settings->priv->oauth2_redirect_uri);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (camel_o365_settings_parent_class)->finalize (object);
+}
+
+static void
+camel_o365_settings_class_init (CamelO365SettingsClass *class)
+{
+       GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = o365_settings_set_property;
+       object_class->get_property = o365_settings_get_property;
+       object_class->finalize = o365_settings_finalize;
+
+       /* Inherited from CamelNetworkSettings. */
+       g_object_class_override_property (
+               object_class,
+               PROP_AUTH_MECHANISM,
+               "auth-mechanism");
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CHECK_ALL,
+               g_param_spec_boolean (
+                       "check-all",
+                       "Check All",
+                       "Check all folders for new messages",
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_EMAIL,
+               g_param_spec_string (
+                       "email",
+                       "Email",
+                       "Email",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_FILTER_JUNK,
+               g_param_spec_boolean (
+                       "filter-junk",
+                       "Filter Junk",
+                       "Whether to filter junk from all folders",
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_FILTER_JUNK_INBOX,
+               g_param_spec_boolean (
+                       "filter-junk-inbox",
+                       "Filter Junk Inbox",
+                       "Whether to filter junk from Inbox only",
+                       FALSE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       /* Inherited from CamelNetworkSettings. */
+       g_object_class_override_property (
+               object_class,
+               PROP_HOST,
+               "host");
+
+       /* Inherited from CamelNetworkSettings. */
+       g_object_class_override_property (
+               object_class,
+               PROP_PORT,
+               "port");
+
+       g_object_class_install_property (
+               object_class,
+               PROP_TIMEOUT,
+               g_param_spec_uint (
+                       "timeout",
+                       "timeout",
+                       "Connection timeout in seconds",
+                       0, G_MAXUINT, 120,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       /* Inherited from CamelNetworkSettings. */
+       g_object_class_override_property (
+               object_class,
+               PROP_SECURITY_METHOD,
+               "security-method");
+
+       /* Inherited from CamelNetworkSettings. */
+       g_object_class_override_property (
+               object_class,
+               PROP_USER,
+               "user");
+
+       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));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CONCURRENT_CONNECTIONS,
+               g_param_spec_uint (
+                       "concurrent-connections",
+                       "Concurrent Connections",
+                       "Number of concurrent connections to use",
+                       MIN_CONCURRENT_CONNECTIONS,
+                       MAX_CONCURRENT_CONNECTIONS,
+                       1,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_EXPLICIT_NOTIFY |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+camel_o365_settings_init (CamelO365Settings *settings)
+{
+       settings->priv = camel_o365_settings_get_instance_private (settings);
+
+       g_mutex_init (&settings->priv->property_lock);
+}
+
+/* transfer none. Checks the settings from the ESource extension related to backend. */
+CamelO365Settings *
+camel_o365_settings_get_from_backend (struct _EBackend *backend,
+                                     struct _ESourceRegistry *registry)
+{
+       ESource *source;
+       ESource *collection;
+       ESourceCamel *extension;
+       CamelSettings *settings;
+       const gchar *extension_name;
+
+       g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
+       if (registry)
+               g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+       source = e_backend_get_source (backend);
+       extension_name = e_source_camel_get_extension_name ("office365");
+       e_source_camel_generate_subtype ("office365", CAMEL_TYPE_O365_SETTINGS);
+
+       if (registry) {
+               /* It's either in the 'source' or in the collection parent. */
+               collection = e_source_registry_find_extension (registry, source, extension_name);
+
+               g_return_val_if_fail (collection != NULL, NULL);
+       } else {
+               collection = g_object_ref (source);
+       }
+
+       extension = e_source_get_extension (collection, extension_name);
+       settings = e_source_camel_get_settings (extension);
+
+       g_object_unref (collection);
+
+       return CAMEL_O365_SETTINGS (settings);
+}
+
+gboolean
+camel_o365_settings_get_check_all (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), FALSE);
+
+       return settings->priv->check_all;
+}
+
+void
+camel_o365_settings_set_check_all (CamelO365Settings *settings,
+                                  gboolean check_all)
+{
+       g_return_if_fail (CAMEL_IS_O365_SETTINGS (settings));
+
+       if ((settings->priv->check_all ? 1 : 0) == (check_all ? 1 : 0))
+               return;
+
+       settings->priv->check_all = check_all;
+
+       g_object_notify (G_OBJECT (settings), "check-all");
+}
+
+const gchar *
+camel_o365_settings_get_email (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), NULL);
+
+       return settings->priv->email;
+}
+
+gchar *
+camel_o365_settings_dup_email (CamelO365Settings *settings)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), NULL);
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       protected = camel_o365_settings_get_email (settings);
+       duplicate = g_strdup (protected);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       return duplicate;
+}
+
+void
+camel_o365_settings_set_email (CamelO365Settings *settings,
+                              const gchar *email)
+{
+       g_return_if_fail (CAMEL_IS_O365_SETTINGS (settings));
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       if (g_strcmp0 (settings->priv->email, email) == 0) {
+               g_mutex_unlock (&settings->priv->property_lock);
+               return;
+       }
+
+       g_free (settings->priv->email);
+       settings->priv->email = e_util_strdup_strip (email);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       g_object_notify (G_OBJECT (settings), "email");
+}
+
+gboolean
+camel_o365_settings_get_filter_junk (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), FALSE);
+
+       return settings->priv->filter_junk;
+}
+
+void
+camel_o365_settings_set_filter_junk (CamelO365Settings *settings,
+                                    gboolean filter_junk)
+{
+       g_return_if_fail (CAMEL_IS_O365_SETTINGS (settings));
+
+       if ((settings->priv->filter_junk ? 1 : 0) == (filter_junk ? 1 : 0))
+               return;
+
+       settings->priv->filter_junk = filter_junk;
+
+       g_object_notify (G_OBJECT (settings), "filter-junk");
+}
+
+gboolean
+camel_o365_settings_get_filter_junk_inbox (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), FALSE);
+
+       return settings->priv->filter_junk_inbox;
+}
+
+void
+camel_o365_settings_set_filter_junk_inbox (CamelO365Settings *settings,
+                                          gboolean filter_junk_inbox)
+{
+       g_return_if_fail (CAMEL_IS_O365_SETTINGS (settings));
+
+       if ((settings->priv->filter_junk_inbox ? 1 : 0) == (filter_junk_inbox ? 1 : 0))
+               return;
+
+       settings->priv->filter_junk_inbox = filter_junk_inbox;
+
+       g_object_notify (G_OBJECT (settings), "filter-junk-inbox");
+}
+
+guint
+camel_o365_settings_get_timeout (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), 0);
+
+       return settings->priv->timeout;
+}
+
+void
+camel_o365_settings_set_timeout (CamelO365Settings *settings,
+                                guint timeout)
+{
+       g_return_if_fail (CAMEL_IS_O365_SETTINGS (settings));
+
+       if (settings->priv->timeout == timeout)
+               return;
+
+       settings->priv->timeout = timeout;
+
+       g_object_notify (G_OBJECT (settings), "timeout");
+}
+
+gboolean
+camel_o365_settings_get_override_oauth2 (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), FALSE);
+
+       return settings->priv->override_oauth2;
+}
+
+void
+camel_o365_settings_set_override_oauth2 (CamelO365Settings *settings,
+                                        gboolean override_oauth2)
+{
+       g_return_if_fail (CAMEL_IS_O365_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_o365_settings_get_oauth2_tenant (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), NULL);
+
+       return settings->priv->oauth2_tenant;
+}
+
+gchar *
+camel_o365_settings_dup_oauth2_tenant (CamelO365Settings *settings)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), NULL);
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       protected = camel_o365_settings_get_oauth2_tenant (settings);
+       duplicate = g_strdup (protected);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       return duplicate;
+}
+
+void
+camel_o365_settings_set_oauth2_tenant (CamelO365Settings *settings,
+                                      const gchar *tenant)
+{
+       g_return_if_fail (CAMEL_IS_O365_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_o365_settings_get_oauth2_client_id (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), NULL);
+
+       return settings->priv->oauth2_client_id;
+}
+
+gchar *
+camel_o365_settings_dup_oauth2_client_id (CamelO365Settings *settings)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), NULL);
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       protected = camel_o365_settings_get_oauth2_client_id (settings);
+       duplicate = g_strdup (protected);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       return duplicate;
+}
+
+void
+camel_o365_settings_set_oauth2_client_id (CamelO365Settings *settings,
+                                         const gchar *client_id)
+{
+       g_return_if_fail (CAMEL_IS_O365_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_o365_settings_get_oauth2_redirect_uri (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), NULL);
+
+       return settings->priv->oauth2_redirect_uri;
+}
+
+gchar *
+camel_o365_settings_dup_oauth2_redirect_uri (CamelO365Settings *settings)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), NULL);
+
+       g_mutex_lock (&settings->priv->property_lock);
+
+       protected = camel_o365_settings_get_oauth2_redirect_uri (settings);
+       duplicate = g_strdup (protected);
+
+       g_mutex_unlock (&settings->priv->property_lock);
+
+       return duplicate;
+}
+
+void
+camel_o365_settings_set_oauth2_redirect_uri (CamelO365Settings *settings,
+                                            const gchar *redirect_uri)
+{
+       g_return_if_fail (CAMEL_IS_O365_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");
+}
+
+guint
+camel_o365_settings_get_concurrent_connections (CamelO365Settings *settings)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_SETTINGS (settings), 1);
+
+       return settings->priv->concurrent_connections;
+}
+
+void
+camel_o365_settings_set_concurrent_connections (CamelO365Settings *settings,
+                                               guint concurrent_connections)
+{
+       g_return_if_fail (CAMEL_IS_O365_SETTINGS (settings));
+
+       concurrent_connections = CLAMP (
+               concurrent_connections,
+               MIN_CONCURRENT_CONNECTIONS,
+               MAX_CONCURRENT_CONNECTIONS);
+
+       if (settings->priv->concurrent_connections == concurrent_connections)
+               return;
+
+       settings->priv->concurrent_connections = concurrent_connections;
+
+       g_object_notify (G_OBJECT (settings), "concurrent-connections");
+}
diff --git a/src/Office365/common/camel-o365-settings.h b/src/Office365/common/camel-o365-settings.h
new file mode 100644
index 00000000..3f05b178
--- /dev/null
+++ b/src/Office365/common/camel-o365-settings.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CAMEL_O365_SETTINGS_H
+#define CAMEL_O365_SETTINGS_H
+
+#include <camel/camel.h>
+
+/* Standard GObject macros */
+#define CAMEL_TYPE_O365_SETTINGS \
+       (camel_o365_settings_get_type ())
+#define CAMEL_O365_SETTINGS(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), CAMEL_TYPE_O365_SETTINGS, CamelO365Settings))
+#define CAMEL_O365_SETTINGS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), CAMEL_TYPE_O365_SETTINGS, CamelO365SettingsClass))
+#define CAMEL_IS_O365_SETTINGS(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), CAMEL_TYPE_O365_SETTINGS))
+#define CAMEL_IS_O365_SETTINGS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), CAMEL_TYPE_O365_SETTINGS))
+#define CAMEL_O365_SETTINGS_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), CAMEL_TYPE_O365_SETTINGS))
+
+#define MIN_CONCURRENT_CONNECTIONS 1
+#define MAX_CONCURRENT_CONNECTIONS 7
+
+G_BEGIN_DECLS
+
+/* Forward declaration, to not include libedataserver.h/libebackend.h here */
+struct _EBackend;
+struct _ESourceRegistry;
+
+typedef struct _CamelO365Settings CamelO365Settings;
+typedef struct _CamelO365SettingsClass CamelO365SettingsClass;
+typedef struct _CamelO365SettingsPrivate CamelO365SettingsPrivate;
+
+struct _CamelO365Settings {
+       CamelOfflineSettings parent;
+       CamelO365SettingsPrivate *priv;
+};
+
+struct _CamelO365SettingsClass {
+       CamelOfflineSettingsClass parent_class;
+};
+
+GType          camel_o365_settings_get_type    (void) G_GNUC_CONST;
+CamelO365Settings *
+               camel_o365_settings_get_from_backend
+                                               (struct _EBackend *backend,
+                                                struct _ESourceRegistry *registry);
+gboolean       camel_o365_settings_get_check_all
+                                               (CamelO365Settings *settings);
+void           camel_o365_settings_set_check_all
+                                               (CamelO365Settings *settings,
+                                                gboolean check_all);
+const gchar *  camel_o365_settings_get_email   (CamelO365Settings *settings);
+gchar *                camel_o365_settings_dup_email   (CamelO365Settings *settings);
+void           camel_o365_settings_set_email   (CamelO365Settings *settings,
+                                                const gchar *email);
+gboolean       camel_o365_settings_get_filter_junk
+                                               (CamelO365Settings *settings);
+void           camel_o365_settings_set_filter_junk
+                                               (CamelO365Settings *settings,
+                                                gboolean filter_junk);
+gboolean       camel_o365_settings_get_filter_junk_inbox
+                                               (CamelO365Settings *settings);
+void           camel_o365_settings_set_filter_junk_inbox
+                                               (CamelO365Settings *settings,
+                                                gboolean filter_junk_inbox);
+guint          camel_o365_settings_get_timeout (CamelO365Settings *settings);
+void           camel_o365_settings_set_timeout (CamelO365Settings *settings,
+                                                guint timeout);
+gboolean       camel_o365_settings_get_override_oauth2
+                                               (CamelO365Settings *settings);
+void           camel_o365_settings_set_override_oauth2
+                                               (CamelO365Settings *settings,
+                                                gboolean override_oauth2);
+const gchar *  camel_o365_settings_get_oauth2_tenant
+                                               (CamelO365Settings *settings);
+gchar *                camel_o365_settings_dup_oauth2_tenant
+                                               (CamelO365Settings *settings);
+void           camel_o365_settings_set_oauth2_tenant
+                                               (CamelO365Settings *settings,
+                                                const gchar *tenant);
+const gchar *  camel_o365_settings_get_oauth2_client_id
+                                               (CamelO365Settings *settings);
+gchar *                camel_o365_settings_dup_oauth2_client_id
+                                               (CamelO365Settings *settings);
+void           camel_o365_settings_set_oauth2_client_id
+                                               (CamelO365Settings *settings,
+                                                const gchar *client_id);
+const gchar *  camel_o365_settings_get_oauth2_redirect_uri
+                                               (CamelO365Settings *settings);
+gchar *                camel_o365_settings_dup_oauth2_redirect_uri
+                                               (CamelO365Settings *settings);
+void           camel_o365_settings_set_oauth2_redirect_uri
+                                               (CamelO365Settings *settings,
+                                                const gchar *redirect_uri);
+guint          camel_o365_settings_get_concurrent_connections
+                                               (CamelO365Settings *settings);
+void           camel_o365_settings_set_concurrent_connections
+                                               (CamelO365Settings *settings,
+                                                guint concurrent_connections);
+
+G_END_DECLS
+
+#endif /* CAMEL_O365_SETTINGS_H */
diff --git a/src/Office365/common/e-source-o365-folder.c b/src/Office365/common/e-source-o365-folder.c
new file mode 100644
index 00000000..2d1b7a70
--- /dev/null
+++ b/src/Office365/common/e-source-o365-folder.c
@@ -0,0 +1,242 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include "e-source-o365-folder.h"
+
+struct _ESourceO365FolderPrivate {
+       gchar *change_key;
+       gchar *id;
+};
+
+enum {
+       PROP_0,
+       PROP_CHANGE_KEY,
+       PROP_ID
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ESourceO365Folder, e_source_o365_folder, E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_o365_folder_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CHANGE_KEY:
+                       e_source_o365_folder_set_change_key (
+                               E_SOURCE_O365_FOLDER (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_ID:
+                       e_source_o365_folder_set_id (
+                               E_SOURCE_O365_FOLDER (object),
+                               g_value_get_string (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_o365_folder_get_property (GObject *object,
+                                guint property_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CHANGE_KEY:
+                       g_value_take_string (
+                               value,
+                               e_source_o365_folder_dup_change_key (
+                               E_SOURCE_O365_FOLDER (object)));
+                       return;
+
+               case PROP_ID:
+                       g_value_take_string (
+                               value,
+                               e_source_o365_folder_dup_id (
+                               E_SOURCE_O365_FOLDER (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_o365_folder_finalize (GObject *object)
+{
+       ESourceO365Folder *o365_folder = E_SOURCE_O365_FOLDER (object);
+
+       g_free (o365_folder->priv->change_key);
+       g_free (o365_folder->priv->id);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_source_o365_folder_parent_class)->finalize (object);
+}
+
+static void
+e_source_o365_folder_class_init (ESourceO365FolderClass *class)
+{
+       GObjectClass *object_class;
+       ESourceExtensionClass *extension_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = source_o365_folder_set_property;
+       object_class->get_property = source_o365_folder_get_property;
+       object_class->finalize = source_o365_folder_finalize;
+
+       extension_class = E_SOURCE_EXTENSION_CLASS (class);
+       extension_class->name = E_SOURCE_EXTENSION_O365_FOLDER;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CHANGE_KEY,
+               g_param_spec_string (
+                       "change-key",
+                       "Change Key",
+                       "Essentially an entity tag, used when submitting changes",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS |
+                       E_SOURCE_PARAM_SETTING));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_ID,
+               g_param_spec_string (
+                       "id",
+                       "ID",
+                       "The server-assigned folder ID",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS |
+                       E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_o365_folder_init (ESourceO365Folder *extension)
+{
+       extension->priv = e_source_o365_folder_get_instance_private (extension);
+}
+
+void
+e_source_o365_folder_type_register (GTypeModule *type_module)
+{
+       /* We need to ensure this is registered, because it's looked up
+        * by name in e_source_get_extension(). */
+       g_type_ensure (E_TYPE_SOURCE_O365_FOLDER);
+}
+
+const gchar *
+e_source_o365_folder_get_change_key (ESourceO365Folder *extension)
+{
+       g_return_val_if_fail (E_IS_SOURCE_O365_FOLDER (extension), NULL);
+
+       return extension->priv->change_key;
+}
+
+gchar *
+e_source_o365_folder_dup_change_key (ESourceO365Folder *extension)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (E_IS_SOURCE_O365_FOLDER (extension), NULL);
+
+       e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
+
+       protected = e_source_o365_folder_get_change_key (extension);
+       duplicate = g_strdup (protected);
+
+       e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+
+       return duplicate;
+}
+
+void
+e_source_o365_folder_set_change_key (ESourceO365Folder *extension,
+                                    const gchar *change_key)
+{
+       g_return_if_fail (E_IS_SOURCE_O365_FOLDER (extension));
+
+       e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
+
+       if (g_strcmp0 (extension->priv->change_key, change_key) == 0) {
+               e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+               return;
+       }
+
+       g_free (extension->priv->change_key);
+       extension->priv->change_key = g_strdup (change_key);
+
+       e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+
+       g_object_notify (G_OBJECT (extension), "change-key");
+}
+
+const gchar *
+e_source_o365_folder_get_id (ESourceO365Folder *extension)
+{
+       g_return_val_if_fail (E_IS_SOURCE_O365_FOLDER (extension), NULL);
+
+       return extension->priv->id;
+}
+
+gchar *
+e_source_o365_folder_dup_id (ESourceO365Folder *extension)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (E_IS_SOURCE_O365_FOLDER (extension), NULL);
+
+       e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
+
+       protected = e_source_o365_folder_get_id (extension);
+       duplicate = g_strdup (protected);
+
+       e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+
+       return duplicate;
+}
+
+void
+e_source_o365_folder_set_id (ESourceO365Folder *extension,
+                            const gchar *id)
+{
+       g_return_if_fail (E_IS_SOURCE_O365_FOLDER (extension));
+
+       e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
+
+       if (g_strcmp0 (extension->priv->id, id) == 0) {
+               e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+               return;
+       }
+
+       g_free (extension->priv->id);
+       extension->priv->id = g_strdup (id);
+
+       e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+
+       g_object_notify (G_OBJECT (extension), "id");
+}
diff --git a/src/Office365/common/e-source-o365-folder.h b/src/Office365/common/e-source-o365-folder.h
new file mode 100644
index 00000000..775dfbf0
--- /dev/null
+++ b/src/Office365/common/e-source-o365-folder.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_SOURCE_O365_FOLDER_H
+#define E_SOURCE_O365_FOLDER_H
+
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_O365_FOLDER \
+       (e_source_o365_folder_get_type ())
+#define E_SOURCE_O365_FOLDER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SOURCE_O365_FOLDER, ESourceO365Folder))
+#define E_SOURCE_O365_FOLDER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SOURCE_O365_FOLDER, ESourceO365FolderClass))
+#define E_IS_SOURCE_O365_FOLDER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SOURCE_O365_FOLDER))
+#define E_IS_SOURCE_O365_FOLDER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SOURCE_O365_FOLDER))
+#define E_SOURCE_O365_FOLDER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SOURCE_O365_FOLDER, ESourceO365FolderClass))
+
+#define E_SOURCE_EXTENSION_O365_FOLDER "Exchange Web Services Folder"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceO365Folder ESourceO365Folder;
+typedef struct _ESourceO365FolderClass ESourceO365FolderClass;
+typedef struct _ESourceO365FolderPrivate ESourceO365FolderPrivate;
+
+struct _ESourceO365Folder {
+       ESourceExtension parent;
+       ESourceO365FolderPrivate *priv;
+};
+
+struct _ESourceO365FolderClass {
+       ESourceExtensionClass parent_class;
+};
+
+GType          e_source_o365_folder_get_type   (void) G_GNUC_CONST;
+void           e_source_o365_folder_type_register
+                                               (GTypeModule *type_module);
+const gchar *  e_source_o365_folder_get_change_key
+                                               (ESourceO365Folder *extension);
+gchar *                e_source_o365_folder_dup_change_key
+                                               (ESourceO365Folder *extension);
+void           e_source_o365_folder_set_change_key
+                                               (ESourceO365Folder *extension,
+                                                const gchar *change_key);
+const gchar *  e_source_o365_folder_get_id     (ESourceO365Folder *extension);
+gchar *                e_source_o365_folder_dup_id     (ESourceO365Folder *extension);
+void           e_source_o365_folder_set_id     (ESourceO365Folder *extension,
+                                                const gchar *id);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_O365_FOLDER_H */
diff --git a/src/Office365/evolution/CMakeLists.txt b/src/Office365/evolution/CMakeLists.txt
new file mode 100644
index 00000000..eb4db02d
--- /dev/null
+++ b/src/Office365/evolution/CMakeLists.txt
@@ -0,0 +1,39 @@
+set(sources
+       e-cal-config-o365.c
+       e-cal-config-o365.h
+       e-book-config-o365.c
+       e-book-config-o365.h
+       e-mail-config-o365-backend.c
+       e-mail-config-o365-backend.h
+       module-o365-configuration.c
+)
+set(extra_deps)
+set(extra_defines)
+set(extra_cflags
+       ${EVOLUTION_MAIL_CFLAGS}
+       ${EVOLUTION_SHELL_CFLAGS}
+       ${LIBECAL_CFLAGS}
+       ${LIBEBOOK_CFLAGS}
+)
+set(extra_incdirs
+       ${EVOLUTION_MAIL_INCLUDE_DIRS}
+       ${EVOLUTION_SHELL_INCLUDE_DIRS}
+       ${LIBECAL_INCLUDE_DIRS}
+       ${LIBEBOOK_INCLUDE_DIRS}
+)
+set(extra_ldflags
+       ${EVOLUTION_MAIL_LDFLAGS}
+       ${EVOLUTION_SHELL_LDFLAGS}
+       ${LIBECAL_LDFLAGS}
+       ${LIBEBOOK_LDFLAGS}
+)
+
+add_simple_module_o365(module-office365-configuration
+       sources
+       extra_deps
+       extra_defines
+       extra_cflags
+       extra_incdirs
+       extra_ldflags
+       "${evo_moduledir}"
+)
diff --git a/src/Office365/evolution/e-book-config-o365.c b/src/Office365/evolution/e-book-config-o365.c
new file mode 100644
index 00000000..b769d29d
--- /dev/null
+++ b/src/Office365/evolution/e-book-config-o365.c
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "common/camel-o365-settings.h"
+#include "common/e-source-o365-folder.h"
+
+#include "e-book-config-o365.h"
+
+G_DEFINE_DYNAMIC_TYPE (EBookConfigO365, e_book_config_o365, E_TYPE_SOURCE_CONFIG_BACKEND)
+
+static gboolean
+book_config_o365_allow_creation (ESourceConfigBackend *backend)
+{
+       return TRUE;
+}
+
+static void
+book_config_o365_insert_widgets (ESourceConfigBackend *backend,
+                                ESource *scratch_source)
+{
+       if (!scratch_source)
+               return;
+
+       e_source_config_add_refresh_interval (e_source_config_backend_get_config (backend), scratch_source);
+}
+
+static void
+e_book_config_o365_class_init (EBookConfigO365Class *class)
+{
+       EExtensionClass *extension_class;
+       ESourceConfigBackendClass *backend_class;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_BOOK_SOURCE_CONFIG;
+
+       backend_class = E_SOURCE_CONFIG_BACKEND_CLASS (class);
+       backend_class->backend_name = "office365";
+       backend_class->allow_creation = book_config_o365_allow_creation;
+       backend_class->insert_widgets = book_config_o365_insert_widgets;
+}
+
+static void
+e_book_config_o365_class_finalize (EBookConfigO365Class *class)
+{
+}
+
+static void
+e_book_config_o365_init (EBookConfigO365 *backend)
+{
+}
+
+void
+e_book_config_o365_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_book_config_o365_register_type (type_module);
+}
diff --git a/src/Office365/evolution/e-book-config-o365.h b/src/Office365/evolution/e-book-config-o365.h
new file mode 100644
index 00000000..5a9eb765
--- /dev/null
+++ b/src/Office365/evolution/e-book-config-o365.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_BOOK_CONFIG_O365_H
+#define E_BOOK_CONFIG_O365_H
+
+#include <e-util/e-util.h>
+
+/* Standard GObject macros */
+#define E_TYPE_BOOK_CONFIG_O365 \
+       (e_book_config_o365_get_type ())
+#define E_BOOK_CONFIG_O365(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_BOOK_CONFIG_O365, EBookConfigO365))
+#define E_BOOK_CONFIG_O365_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_BOOK_CONFIG_O365, EBookConfigO365Class))
+#define E_IS_BOOK_CONFIG_O365(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_BOOK_CONFIG_O365))
+#define E_IS_BOOK_CONFIG_O365_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_BOOK_CONFIG_O365))
+#define E_BOOK_CONFIG_O365_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_BOOK_CONFIG_O365, EBookConfigO365Class))
+
+G_BEGIN_DECLS
+
+typedef struct _EBookConfigO365 EBookConfigO365;
+typedef struct _EBookConfigO365Class EBookConfigO365Class;
+typedef struct _EBookConfigO365Private EBookConfigO365Private;
+
+struct _EBookConfigO365 {
+       ESourceConfigBackend parent;
+       EBookConfigO365Private *priv;
+};
+
+struct _EBookConfigO365Class {
+       ESourceConfigBackendClass parent_class;
+};
+
+GType          e_book_config_o365_get_type     (void) G_GNUC_CONST;
+void           e_book_config_o365_type_register(GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_BOOK_CONFIG_O365_H */
diff --git a/src/Office365/evolution/e-cal-config-o365.c b/src/Office365/evolution/e-cal-config-o365.c
new file mode 100644
index 00000000..663da38e
--- /dev/null
+++ b/src/Office365/evolution/e-cal-config-o365.c
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include "e-cal-config-o365.h"
+
+G_DEFINE_DYNAMIC_TYPE (ECalConfigO365, e_cal_config_o365, E_TYPE_SOURCE_CONFIG_BACKEND)
+
+static gboolean
+cal_config_o365_allow_creation (ESourceConfigBackend *backend)
+{
+       ESourceConfig *config;
+       ECalSourceConfig *cal_config;
+       ECalClientSourceType source_type;
+       gboolean allow_creation = FALSE;
+
+       config = e_source_config_backend_get_config (backend);
+
+       cal_config = E_CAL_SOURCE_CONFIG (config);
+       source_type = e_cal_source_config_get_source_type (cal_config);
+
+       switch (source_type) {
+               case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
+               case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
+                       allow_creation = TRUE;
+                       break;
+
+               default:
+                       break;
+       }
+
+       return allow_creation;
+}
+
+static void
+cal_config_o365_insert_widgets (ESourceConfigBackend *backend,
+                               ESource *scratch_source)
+{
+       if (!scratch_source)
+               return;
+
+       e_source_config_add_refresh_interval (e_source_config_backend_get_config (backend), scratch_source);
+}
+
+static void
+e_cal_config_o365_class_init (ECalConfigO365Class *class)
+{
+       EExtensionClass *extension_class;
+       ESourceConfigBackendClass *backend_class;
+
+       extension_class = E_EXTENSION_CLASS (class);
+       extension_class->extensible_type = E_TYPE_CAL_SOURCE_CONFIG;
+
+       backend_class = E_SOURCE_CONFIG_BACKEND_CLASS (class);
+       backend_class->backend_name = "office365";
+       backend_class->allow_creation = cal_config_o365_allow_creation;
+       backend_class->insert_widgets = cal_config_o365_insert_widgets;
+}
+
+static void
+e_cal_config_o365_class_finalize (ECalConfigO365Class *class)
+{
+}
+
+static void
+e_cal_config_o365_init (ECalConfigO365 *backend)
+{
+}
+
+void
+e_cal_config_o365_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_cal_config_o365_register_type (type_module);
+}
diff --git a/src/Office365/evolution/e-cal-config-o365.h b/src/Office365/evolution/e-cal-config-o365.h
new file mode 100644
index 00000000..a0295c8c
--- /dev/null
+++ b/src/Office365/evolution/e-cal-config-o365.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_CAL_CONFIG_O365_H
+#define E_CAL_CONFIG_O365_H
+
+#include <e-util/e-util.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CAL_CONFIG_O365 \
+       (e_cal_config_o365_get_type ())
+#define E_CAL_CONFIG_O365(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_CAL_CONFIG_O365, ECalConfigO365))
+#define E_CAL_CONFIG_O365_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_CAL_CONFIG_O365, ECalConfigO365Class))
+#define E_IS_CAL_CONFIG_O365(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_CAL_CONFIG_O365))
+#define E_IS_CAL_CONFIG_O365_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_CAL_CONFIG_O365))
+#define E_CAL_CONFIG_O365_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_CAL_CONFIG_O365, ECalConfigO365Class))
+
+G_BEGIN_DECLS
+
+typedef struct _ECalConfigO365 ECalConfigO365;
+typedef struct _ECalConfigO365Class ECalConfigO365Class;
+typedef struct _ECalConfigO365Private ECalConfigO365Private;
+
+struct _ECalConfigO365 {
+       ESourceConfigBackend parent;
+       ECalConfigO365Private *priv;
+};
+
+struct _ECalConfigO365Class {
+       ESourceConfigBackendClass parent_class;
+};
+
+GType          e_cal_config_o365_get_type      (void) G_GNUC_CONST;
+void           e_cal_config_o365_type_register (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_CAL_CONFIG_O365_H */
diff --git a/src/Office365/evolution/e-mail-config-o365-backend.c 
b/src/Office365/evolution/e-mail-config-o365-backend.c
new file mode 100644
index 00000000..d7bcc1a6
--- /dev/null
+++ b/src/Office365/evolution/e-mail-config-o365-backend.c
@@ -0,0 +1,461 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <camel/camel.h>
+#include <libebackend/libebackend.h>
+
+#include <mail/e-mail-config-auth-check.h>
+#include <mail/e-mail-config-receiving-page.h>
+
+#include "common/camel-o365-settings.h"
+
+#include "e-mail-config-o365-backend.h"
+
+struct _EMailConfigO365BackendPrivate {
+       GtkWidget *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_EXTENDED (EMailConfigO365Backend, e_mail_config_o365_backend, 
E_TYPE_MAIL_CONFIG_SERVICE_BACKEND, 0,
+       G_ADD_PRIVATE_DYNAMIC (EMailConfigO365Backend))
+
+static ESource *
+mail_config_o365_backend_new_collection (EMailConfigServiceBackend *backend)
+{
+       EMailConfigServiceBackendClass *class;
+       ESourceBackend *extension;
+       ESource *source;
+       const gchar *extension_name;
+
+       /* This backend serves double duty.  One instance holds the
+        * mail account source, another holds the mail transport source.
+        * We can differentiate by examining the EMailConfigServicePage
+        * the backend is associated with.  We return a new collection
+        * for both the Receiving Page and Sending Page.  Although the
+        * Sending Page instance ultimately gets discarded, it's still
+        * needed to avoid creating an [Office365 Backend] extension
+        * in the mail transport source. */
+
+       class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
+
+       source = e_source_new (NULL, NULL, NULL);
+       extension_name = E_SOURCE_EXTENSION_COLLECTION;
+       extension = e_source_get_extension (source, extension_name);
+       e_source_backend_set_backend_name (extension, class->backend_name);
+
+       return source;
+}
+
+static void
+mail_config_o365_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_o365_backend_insert_widgets (EMailConfigServiceBackend *backend,
+                                        GtkBox *parent)
+{
+       EMailConfigO365Backend *o365_backend;
+       EMailConfigServicePage *page;
+       ESource *source;
+       ESourceExtension *extension;
+       CamelSettings *settings;
+       GtkLabel *label;
+       GtkWidget *widget;
+       GtkWidget *container;
+       const gchar *extension_name;
+       const gchar *text;
+       gchar *markup;
+
+       o365_backend = E_MAIL_CONFIG_O365_BACKEND (backend);
+       page = e_mail_config_service_backend_get_page (backend);
+
+       /* This backend serves double duty.  One instance holds the
+        * mail account source, another holds the mail transport source.
+        * We can differentiate by examining the EMailConfigServicePage
+        * the backend is associated with.  This method only applies to
+        * the Receiving Page. */
+       if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
+               return;
+
+       /* This needs to come _after_ the page type check so we don't
+        * introduce a backend extension in the mail transport source. */
+       settings = e_mail_config_service_backend_get_settings (backend);
+
+       text = _("Configuration");
+       markup = g_markup_printf_escaped ("<b>%s</b>", text);
+       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 (parent), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+       g_free (markup);
+
+       widget = gtk_grid_new ();
+       gtk_widget_set_margin_left (widget, 12);
+       gtk_grid_set_row_spacing (GTK_GRID (widget), 6);
+       gtk_grid_set_column_spacing (GTK_GRID (widget), 6);
+       gtk_box_pack_start (GTK_BOX (parent), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       container = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("User_name:"));
+       gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
+       gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1);
+       gtk_widget_show (widget);
+
+       label = GTK_LABEL (widget);
+
+       widget = gtk_entry_new ();
+       gtk_widget_set_hexpand (widget, TRUE);
+       gtk_label_set_mnemonic_widget (label, widget);
+       gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 2, 1);
+       o365_backend->priv->user_entry = widget;  /* do not reference */
+       gtk_widget_show (widget);
+
+       text = _("Authentication");
+       markup = g_markup_printf_escaped ("<b>%s</b>", text);
+       widget = gtk_label_new (markup);
+       gtk_widget_set_margin_top (widget, 6);
+       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 (parent), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+       g_free (markup);
+
+       widget = gtk_grid_new ();
+       gtk_widget_set_margin_left (widget, 12);
+       gtk_box_pack_start (GTK_BOX (parent), widget, FALSE, FALSE, 0);
+       o365_backend->priv->oauth2_settings_grid = GTK_GRID (widget);
+
+       gtk_grid_set_column_spacing (o365_backend->priv->oauth2_settings_grid, 4);
+       gtk_grid_set_row_spacing (o365_backend->priv->oauth2_settings_grid, 4);
+
+       container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+       gtk_grid_attach (o365_backend->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);
+       o365_backend->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);
+
+       /* Translators: 'Tenant' here means a term used by Microsoft to identify a company or organization in 
an Office 365 world.
+          You probably do not want to translate it. More for example here: 
https://powerbi.microsoft.com/en-us/blog/what-is-a-tenant/ */
+       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 (o365_backend->priv->oauth2_settings_grid, widget, 0, 1, 1, 1);
+       label = GTK_LABEL (widget);
+
+       e_binding_bind_property (
+               o365_backend->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 (o365_backend->priv->oauth2_settings_grid, widget, 1, 1, 1, 1);
+       o365_backend->priv->oauth2_tenant_entry = widget;
+
+       e_binding_bind_property (
+               o365_backend->priv->oauth2_override_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       mail_config_o365_backend_set_oauth2_tooltip (widget, OFFICE365_TENANT,
+               /* Translators: 'Tenant' here means a term used by Microsoft to identify a company or 
organization in an Office 365 world. Same for 'common', it's a default URL path.
+                  You probably do not want to translate it. More for example here: 
https://powerbi.microsoft.com/en-us/blog/what-is-a-tenant/ */
+               _("Default tenant is “common“"),
+               /* Translators: 'Tenant' here means a term used by Microsoft to identify a company or 
organization in an Office 365 world.
+                  You probably do not want to translate it. More for example here: 
https://powerbi.microsoft.com/en-us/blog/what-is-a-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 (o365_backend->priv->oauth2_settings_grid, widget, 0, 2, 1, 1);
+       label = GTK_LABEL (widget);
+
+       e_binding_bind_property (
+               o365_backend->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 (o365_backend->priv->oauth2_settings_grid, widget, 1, 2, 1, 1);
+       o365_backend->priv->oauth2_client_id_entry = widget;
+
+       e_binding_bind_property (
+               o365_backend->priv->oauth2_override_check, "active",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       mail_config_o365_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 (o365_backend->priv->oauth2_settings_grid, widget, 0, 3, 1, 1);
+       label = GTK_LABEL (widget);
+
+       e_binding_bind_property (
+               o365_backend->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 (o365_backend->priv->oauth2_settings_grid, widget, 1, 3, 1, 1);
+       o365_backend->priv->oauth2_redirect_uri_entry = widget;
+
+       e_binding_bind_property (
+               o365_backend->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/nativeclient";);
+       mail_config_o365_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 (o365_backend->priv->oauth2_settings_grid));
+
+       e_binding_bind_object_text_property (
+               settings, "user",
+               o365_backend->priv->user_entry, "text",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       e_binding_bind_property (
+               settings, "override-oauth2",
+               o365_backend->priv->oauth2_override_check, "active",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       e_binding_bind_object_text_property (
+               settings, "oauth2-tenant",
+               o365_backend->priv->oauth2_tenant_entry, "text",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       e_binding_bind_object_text_property (
+               settings, "oauth2-client-id",
+               o365_backend->priv->oauth2_client_id_entry, "text",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+
+       e_binding_bind_object_text_property (
+               settings, "oauth2-redirect_uri",
+               o365_backend->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);
+
+       /* The collection identity is the user name. */
+       e_binding_bind_property (
+               settings, "user",
+               extension, "identity",
+               G_BINDING_BIDIRECTIONAL |
+               G_BINDING_SYNC_CREATE);
+}
+
+static void
+mail_config_o365_backend_setup_defaults (EMailConfigServiceBackend *backend)
+{
+       CamelSettings *settings;
+       EMailConfigServicePage *page;
+       const gchar *email_address;
+
+       page = e_mail_config_service_backend_get_page (backend);
+
+       /* This backend serves double duty.  One instance holds the
+        * mail account source, another holds the mail transport source.
+        * We can differentiate by examining the EMailConfigServicePage
+        * the backend is associated with.  This method only applies to
+        * the Receiving Page. */
+       if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
+               return;
+
+       /* This needs to come _after_ the page type check so we don't
+        * introduce a backend extension in the mail transport source. */
+       email_address = e_mail_config_service_page_get_email_address (page);
+       settings = e_mail_config_service_backend_get_settings (backend);
+
+       camel_o365_settings_set_email (CAMEL_O365_SETTINGS (settings), email_address);
+       camel_network_settings_set_user (CAMEL_NETWORK_SETTINGS (settings), email_address);
+}
+
+static gboolean
+mail_config_o365_backend_auto_configure (EMailConfigServiceBackend *backend,
+                                        EConfigLookup *config_lookup,
+                                        gint *out_priority,
+                                        gboolean *out_is_complete)
+{
+       return e_mail_config_service_backend_auto_configure_for_kind (backend, config_lookup,
+               E_CONFIG_LOOKUP_RESULT_COLLECTION, NULL,
+               e_mail_config_service_backend_get_collection (backend),
+               out_priority, out_is_complete);
+}
+
+static gboolean
+mail_config_o365_backend_check_complete (EMailConfigServiceBackend *backend)
+{
+       EMailConfigServicePage *page;
+       EMailConfigO365Backend *o365_backend;
+       CamelSettings *settings;
+       CamelNetworkSettings *network_settings;
+       const gchar *user;
+       gboolean correct, complete = TRUE;
+
+       o365_backend = E_MAIL_CONFIG_O365_BACKEND (backend);
+       page = e_mail_config_service_backend_get_page (backend);
+
+       /* This backend serves double duty.  One instance holds the
+        * mail account source, another holds the mail transport source.
+        * We can differentiate by examining the EMailConfigServicePage
+        * the backend is associated with.  This method only applies to
+        * the Receiving Page. */
+       if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
+               return TRUE;
+
+       /* This needs to come _after_ the page type check so we don't
+        * introduce a backend extension in the mail transport source. */
+       settings = e_mail_config_service_backend_get_settings (backend);
+
+       network_settings = CAMEL_NETWORK_SETTINGS (settings);
+       user = camel_network_settings_get_user (network_settings);
+
+       correct = user != NULL && *user != '\0';
+       complete = complete && correct;
+
+       e_util_set_entry_issue_hint (o365_backend->priv->user_entry, correct ? NULL : _("User name cannot be 
empty"));
+
+       if (correct) {
+               CamelO365Settings *o365_settings = CAMEL_O365_SETTINGS (settings);
+               const gchar *client_id;
+
+               if (camel_o365_settings_get_override_oauth2 (o365_settings)) {
+                       client_id = camel_o365_settings_get_oauth2_client_id (o365_settings);
+               } else {
+                       client_id = OFFICE365_CLIENT_ID;
+               }
+
+               correct = client_id && *client_id;
+               complete = complete && correct;
+
+               e_util_set_entry_issue_hint (o365_backend->priv->oauth2_client_id_entry, correct ? NULL : 
_("Application ID cannot be empty"));
+       }
+
+       return complete;
+}
+
+static void
+mail_config_o365_backend_commit_changes (EMailConfigServiceBackend *backend)
+{
+       CamelSettings *settings;
+       CamelO365Settings *o365_settings;
+       EMailConfigServicePage *page;
+       const gchar *email_address;
+
+       page = e_mail_config_service_backend_get_page (backend);
+
+       /* This backend serves double duty.  One instance holds the
+        * mail account source, another holds the mail transport source.
+        * We can differentiate by examining the EMailConfigServicePage
+        * the backend is associated with.  This method only applies to
+        * the Receiving Page. */
+       if (!E_IS_MAIL_CONFIG_RECEIVING_PAGE (page))
+               return;
+
+       /* This needs to come _after_ the page type check so we don't
+        * introduce a backend extension in the mail transport source. */
+       settings = e_mail_config_service_backend_get_settings (backend);
+
+       email_address = e_mail_config_service_page_get_email_address (page);
+       if (email_address != NULL) {
+               o365_settings = CAMEL_O365_SETTINGS (settings);
+               camel_o365_settings_set_email (o365_settings, email_address);
+       }
+}
+
+static void
+e_mail_config_o365_backend_class_init (EMailConfigO365BackendClass *class)
+{
+       EMailConfigServiceBackendClass *backend_class;
+
+       backend_class = E_MAIL_CONFIG_SERVICE_BACKEND_CLASS (class);
+       backend_class->backend_name = "office365";
+       backend_class->new_collection = mail_config_o365_backend_new_collection;
+       backend_class->insert_widgets = mail_config_o365_backend_insert_widgets;
+       backend_class->setup_defaults = mail_config_o365_backend_setup_defaults;
+       backend_class->auto_configure = mail_config_o365_backend_auto_configure;
+       backend_class->check_complete = mail_config_o365_backend_check_complete;
+       backend_class->commit_changes = mail_config_o365_backend_commit_changes;
+}
+
+static void
+e_mail_config_o365_backend_class_finalize (EMailConfigO365BackendClass *class)
+{
+}
+
+static void
+e_mail_config_o365_backend_init (EMailConfigO365Backend *backend)
+{
+       backend->priv = e_mail_config_o365_backend_get_instance_private (backend);
+}
+
+void
+e_mail_config_o365_backend_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_mail_config_o365_backend_register_type (type_module);
+}
diff --git a/src/Office365/evolution/e-mail-config-o365-backend.h 
b/src/Office365/evolution/e-mail-config-o365-backend.h
new file mode 100644
index 00000000..52b4d1f4
--- /dev/null
+++ b/src/Office365/evolution/e-mail-config-o365-backend.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_MAIL_CONFIG_O365_BACKEND_H
+#define E_MAIL_CONFIG_O365_BACKEND_H
+
+#include <mail/e-mail-config-service-backend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MAIL_CONFIG_O365_BACKEND \
+       (e_mail_config_o365_backend_get_type ())
+#define E_MAIL_CONFIG_O365_BACKEND(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_MAIL_CONFIG_O365_BACKEND, EMailConfigO365Backend))
+#define E_MAIL_CONFIG_O365_BACKEND_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_MAIL_CONFIG_O365_BACKEND, EMailConfigO365BackendClass))
+#define E_IS_MAIL_CONFIG_O365_BACKEND(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_MAIL_CONFIG_O365_BACKEND))
+#define E_IS_MAIL_CONFIG_O365_BACKEND_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_MAIL_CONFIG_O365_BACKEND))
+#define E_MAIL_CONFIG_O365_BACKEND_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_MAIL_CONFIG_O365_BACKEND, EMailConfigO365BackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMailConfigO365Backend EMailConfigO365Backend;
+typedef struct _EMailConfigO365BackendClass EMailConfigO365BackendClass;
+typedef struct _EMailConfigO365BackendPrivate EMailConfigO365BackendPrivate;
+
+struct _EMailConfigO365Backend {
+       EMailConfigServiceBackend parent;
+       EMailConfigO365BackendPrivate *priv;
+};
+
+struct _EMailConfigO365BackendClass {
+       EMailConfigServiceBackendClass parent_class;
+};
+
+GType          e_mail_config_o365_backend_get_type
+                                               (void) G_GNUC_CONST;
+void           e_mail_config_o365_backend_type_register
+                                               (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_MAIL_CONFIG_O365_BACKEND_H */
diff --git a/src/Office365/evolution/module-o365-configuration.c 
b/src/Office365/evolution/module-o365-configuration.c
new file mode 100644
index 00000000..37dff72c
--- /dev/null
+++ b/src/Office365/evolution/module-o365-configuration.c
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-cal-config-o365.h"
+#include "e-book-config-o365.h"
+#include "e-mail-config-o365-backend.h"
+
+#include "common/e-source-o365-folder.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+       bindtextdomain (GETTEXT_PACKAGE, O365_LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+       e_source_o365_folder_type_register (type_module);
+
+       e_cal_config_o365_type_register (type_module);
+       e_book_config_o365_type_register (type_module);
+       e_mail_config_o365_backend_type_register (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/src/Office365/registry/CMakeLists.txt b/src/Office365/registry/CMakeLists.txt
new file mode 100644
index 00000000..dd8b7be3
--- /dev/null
+++ b/src/Office365/registry/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(extra_deps)
+set(sources
+       module-o365-backend.c
+       e-o365-backend.c
+       e-o365-backend.h
+       e-o365-backend-factory.c
+       e-o365-backend-factory.h
+)
+set(extra_defines)
+set(extra_cflags)
+set(extra_incdirs)
+set(extra_ldflags)
+
+add_simple_module_o365(module-office365-backend
+       sources
+       extra_deps
+       extra_defines
+       extra_cflags
+       extra_incdirs
+       extra_ldflags
+       "${eds_moduledir}"
+)
diff --git a/src/Office365/registry/e-o365-backend-factory.c b/src/Office365/registry/e-o365-backend-factory.c
new file mode 100644
index 00000000..a50d9f61
--- /dev/null
+++ b/src/Office365/registry/e-o365-backend-factory.c
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include "e-o365-backend.h"
+
+#include "e-o365-backend-factory.h"
+
+G_DEFINE_DYNAMIC_TYPE (EO365BackendFactory, e_o365_backend_factory, E_TYPE_COLLECTION_BACKEND_FACTORY)
+
+static void
+o365_backend_prepare_mail_account_source (ESource *source)
+{
+       ESourceBackend *extension;
+       const gchar *extension_name;
+
+       extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+       extension = e_source_get_extension (source, extension_name);
+       e_source_backend_set_backend_name (extension, "office365");
+}
+
+static void
+o365_backend_prepare_mail_transport_source (ESource *source)
+{
+       ESourceBackend *extension;
+       const gchar *extension_name;
+
+       extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
+       extension = e_source_get_extension (source, extension_name);
+       e_source_backend_set_backend_name (extension, "office365");
+}
+
+static void
+o365_backend_factory_prepare_mail (ECollectionBackendFactory *factory,
+                                  ESource *mail_account_source,
+                                  ESource *mail_identity_source,
+                                  ESource *mail_transport_source)
+{
+       /* Chain up to parent's method. */
+       E_COLLECTION_BACKEND_FACTORY_CLASS (e_o365_backend_factory_parent_class)->prepare_mail (factory, 
mail_account_source, mail_identity_source, mail_transport_source);
+
+       o365_backend_prepare_mail_account_source (mail_account_source);
+       o365_backend_prepare_mail_transport_source (mail_transport_source);
+}
+
+static void
+e_o365_backend_factory_class_init (EO365BackendFactoryClass *class)
+{
+       ECollectionBackendFactoryClass *factory_class;
+
+       factory_class = E_COLLECTION_BACKEND_FACTORY_CLASS (class);
+       factory_class->factory_name = "office365";
+       factory_class->backend_type = E_TYPE_O365_BACKEND;
+       factory_class->prepare_mail = o365_backend_factory_prepare_mail;
+}
+
+static void
+e_o365_backend_factory_class_finalize (EO365BackendFactoryClass *class)
+{
+}
+
+static void
+e_o365_backend_factory_init (EO365BackendFactory *factory)
+{
+}
+
+void
+e_o365_backend_factory_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_o365_backend_factory_register_type (type_module);
+}
diff --git a/src/Office365/registry/e-o365-backend-factory.h b/src/Office365/registry/e-o365-backend-factory.h
new file mode 100644
index 00000000..85080d32
--- /dev/null
+++ b/src/Office365/registry/e-o365-backend-factory.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_O365_BACKEND_FACTORY_H
+#define E_O365_BACKEND_FACTORY_H
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_O365_BACKEND_FACTORY \
+       (e_o365_backend_factory_get_type ())
+#define E_O365_BACKEND_FACTORY(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_O365_BACKEND_FACTORY, EO365BackendFactory))
+#define E_O365_BACKEND_FACTORY_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_O365_BACKEND_FACTORY, EO365BackendFactoryClass))
+#define E_IS_O365_BACKEND_FACTORY(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_O365_BACKEND_FACTORY))
+#define E_IS_O365_BACKEND_FACTORY_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_O365_BACKEND_FACTORY))
+#define E_O365_BACKEND_FACTORY_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_O365_BACKEND_FACTORY, EO365BackendFactoryClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EO365BackendFactory EO365BackendFactory;
+typedef struct _EO365BackendFactoryClass EO365BackendFactoryClass;
+typedef struct _EO365BackendFactoryPrivate EO365BackendFactoryPrivate;
+
+struct _EO365BackendFactory {
+       ECollectionBackendFactory parent;
+       EO365BackendFactoryPrivate *priv;
+};
+
+struct _EO365BackendFactoryClass {
+       ECollectionBackendFactoryClass parent_class;
+};
+
+GType          e_o365_backend_factory_get_type (void) G_GNUC_CONST;
+void           e_o365_backend_factory_type_register
+                                               (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_O365_BACKEND_FACTORY_H */
diff --git a/src/Office365/registry/e-o365-backend.c b/src/Office365/registry/e-o365-backend.c
new file mode 100644
index 00000000..08fc6af8
--- /dev/null
+++ b/src/Office365/registry/e-o365-backend.c
@@ -0,0 +1,533 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "common/e-source-o365-folder.h"
+#include "common/camel-o365-settings.h"
+
+#include "e-o365-backend.h"
+
+struct _EO365BackendPrivate {
+       GMutex property_lock;
+
+       gboolean need_update_folders;
+
+       gulong source_changed_id;
+};
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (EO365Backend, e_o365_backend, E_TYPE_COLLECTION_BACKEND, 0,
+       G_ADD_PRIVATE_DYNAMIC (EO365Backend))
+
+static void o365_backend_populate (ECollectionBackend *backend);
+
+#if 0
+static void
+o365_backend_source_changed_cb (ESource *source,
+                               EO365Backend *backend)
+{
+       if (!e_source_get_enabled (source)) {
+               backend->priv->need_update_folders = TRUE;
+               return;
+       }
+
+       if (!backend->priv->need_update_folders)
+               return;
+
+       o365_backend_populate (E_COLLECTION_BACKEND (backend));
+}
+#endif
+
+static void
+o365_backend_populate (ECollectionBackend *backend)
+{
+#if 0
+       ESource *source;
+       EO365Backend *o365_backend = E_O365_BACKEND (backend);
+
+       source = e_backend_get_source (E_BACKEND (backend));
+
+       o365_backend->priv->need_update_folders = TRUE;
+
+       if (!o365_backend->priv->source_changed_id) {
+               o365_backend->priv->source_changed_id = g_signal_connect (source, "changed",
+                       G_CALLBACK (o365_backend_source_changed_cb), o365_backend);
+       }
+
+       /* do not do anything, if account is disabled */
+       if (!e_source_get_enabled (source))
+               return;
+
+       o365_backend_add_gal_source (o365_backend);
+       o365_backend_claim_old_resources (backend);
+
+       if (e_backend_get_online (E_BACKEND (backend))) {
+               CamelO365Settings *o365_settings;
+
+               o365_settings = camel_o365_settings_get_from_backend (o365_backend, NULL);
+
+               if (e_o365_connection_utils_get_without_password (o365_settings)) {
+                       e_backend_schedule_authenticate (E_BACKEND (backend), NULL);
+               } else {
+                       e_backend_credentials_required_sync (E_BACKEND (backend),
+                               E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
+                               NULL, NULL);
+               }
+       }
+#endif
+}
+
+static gchar *
+o365_backend_dup_resource_id (ECollectionBackend *backend,
+                             ESource *child_source)
+{
+       ESourceO365Folder *extension;
+       const gchar *extension_name;
+
+       extension_name = E_SOURCE_EXTENSION_O365_FOLDER;
+       extension = e_source_get_extension (child_source, extension_name);
+
+       return e_source_o365_folder_dup_id (extension);
+}
+
+static void
+o365_backend_child_added (ECollectionBackend *backend,
+                         ESource *child_source)
+{
+#if 0
+       ESource *collection_source;
+       const gchar *extension_name;
+       gboolean is_mail = FALSE;
+
+       collection_source = e_backend_get_source (E_BACKEND (backend));
+
+       extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+       is_mail |= e_source_has_extension (child_source, extension_name);
+
+       extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
+       is_mail |= e_source_has_extension (child_source, extension_name);
+
+       extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
+       is_mail |= e_source_has_extension (child_source, extension_name);
+
+       /* Synchronize mail-related user with the collection identity. */
+       extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+       if (is_mail && e_source_has_extension (child_source, extension_name)) {
+               ESourceAuthentication *auth_child_extension;
+               ESourceCollection *collection_extension;
+
+               extension_name = E_SOURCE_EXTENSION_COLLECTION;
+               collection_extension = e_source_get_extension (
+                       collection_source, extension_name);
+
+               extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+               auth_child_extension = e_source_get_extension (
+                       child_source, extension_name);
+
+               e_binding_bind_property (
+                       collection_extension, "identity",
+                       auth_child_extension, "user",
+                       G_BINDING_SYNC_CREATE);
+       }
+
+       /* We track O365 folders in a hash table by folder ID. */
+       extension_name = E_SOURCE_EXTENSION_O365_FOLDER;
+       if (e_source_has_extension (child_source, extension_name)) {
+               ESourceO365Folder *extension;
+               gchar *folder_id;
+
+               extension = e_source_get_extension (
+                       child_source, extension_name);
+               folder_id = e_source_o365_folder_dup_id (extension);
+               if (folder_id != NULL) {
+                       o365_backend_folders_insert (
+                               E_O365_BACKEND (backend),
+                               folder_id, child_source);
+                       g_free (folder_id);
+               }
+       }
+#endif
+       /* Chain up to parent's method. */
+       E_COLLECTION_BACKEND_CLASS (e_o365_backend_parent_class)->child_added (backend, child_source);
+}
+
+static void
+o365_backend_child_removed (ECollectionBackend *backend,
+                           ESource *child_source)
+{
+#if 0
+       const gchar *extension_name;
+
+       /* We track O365 folders in a hash table by folder ID. */
+       extension_name = E_SOURCE_EXTENSION_O365_FOLDER;
+       if (e_source_has_extension (child_source, extension_name)) {
+               ESourceO365Folder *extension;
+               const gchar *folder_id;
+
+               extension = e_source_get_extension (
+                       child_source, extension_name);
+               folder_id = e_source_o365_folder_get_id (extension);
+               if (folder_id != NULL)
+                       o365_backend_folders_remove (
+                               E_O365_BACKEND (backend), folder_id);
+       }
+#endif
+
+       /* Chain up to parent's method. */
+       E_COLLECTION_BACKEND_CLASS (e_o365_backend_parent_class)->child_removed (backend, child_source);
+}
+
+static gboolean
+o365_backend_create_resource_sync (ECollectionBackend *backend,
+                                  ESource *source,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+#if 0
+       EO365Connection *connection = NULL;
+       O365FolderId *out_folder_id = NULL;
+       EO365FolderType folder_type = E_O365_FOLDER_TYPE_UNKNOWN;
+       const gchar *extension_name;
+       const gchar *parent_folder_id = NULL;
+       gchar *folder_name;
+       gboolean success = FALSE;
+
+       extension_name = E_SOURCE_EXTENSION_O365_FOLDER;
+       if (e_source_has_extension (source, extension_name)) {
+               ESourceO365Folder *extension;
+
+               /* foreign and public folders are just added */
+               extension = e_source_get_extension (source, extension_name);
+               if (e_source_o365_folder_get_foreign (extension) ||
+                   e_source_o365_folder_get_public (extension))
+                       success = TRUE;
+       }
+
+       if (!success) {
+               connection = e_o365_backend_ref_connection_sync (E_O365_BACKEND (backend), NULL, NULL, NULL, 
cancellable, error);
+               if (connection == NULL)
+                       return FALSE;
+
+               extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
+               if (e_source_has_extension (source, extension_name)) {
+                       folder_type = E_O365_FOLDER_TYPE_CONTACTS;
+                       parent_folder_id = "contacts";
+               }
+
+               extension_name = E_SOURCE_EXTENSION_CALENDAR;
+               if (e_source_has_extension (source, extension_name)) {
+                       folder_type = E_O365_FOLDER_TYPE_CALENDAR;
+                       parent_folder_id = "calendar";
+               }
+
+               extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+               if (e_source_has_extension (source, extension_name)) {
+                       folder_type = E_O365_FOLDER_TYPE_TASKS;
+                       parent_folder_id = "tasks";
+               }
+
+               /* FIXME No support for memo lists. */
+
+               if (parent_folder_id == NULL) {
+                       g_set_error (
+                               error, G_IO_ERROR,
+                               G_IO_ERROR_INVALID_ARGUMENT,
+                               _("Could not determine a suitable folder "
+                               "class for a new folder named “%s”"),
+                               e_source_get_display_name (source));
+                       goto exit;
+               }
+
+               folder_name = e_source_dup_display_name (source);
+
+               success = e_o365_connection_create_folder_sync (
+                       connection, O365_PRIORITY_MEDIUM,
+                       parent_folder_id, TRUE,
+                       folder_name, folder_type,
+                       &out_folder_id, cancellable, error);
+
+               g_free (folder_name);
+
+               /* Sanity check */
+               g_warn_if_fail (
+                       (success && out_folder_id != NULL) ||
+                       (!success && out_folder_id == NULL));
+
+               if (out_folder_id != NULL) {
+                       ESourceO365Folder *extension;
+                       const gchar *extension_name;
+
+                       extension_name = E_SOURCE_EXTENSION_O365_FOLDER;
+                       extension = e_source_get_extension (source, extension_name);
+                       e_source_o365_folder_set_id (
+                               extension, out_folder_id->id);
+                       e_source_o365_folder_set_change_key (
+                               extension, out_folder_id->change_key);
+
+                       e_o365_folder_id_free (out_folder_id);
+               }
+       }
+
+       if (success) {
+               ESourceRegistryServer *server;
+               ESource *parent_source;
+               const gchar *cache_dir;
+               const gchar *parent_uid;
+
+               /* Configure the source as a collection member. */
+               parent_source = e_backend_get_source (E_BACKEND (backend));
+               parent_uid = e_source_get_uid (parent_source);
+               e_source_set_parent (source, parent_uid);
+
+               /* Changes should be written back to the cache directory. */
+               cache_dir = e_collection_backend_get_cache_dir (backend);
+               e_server_side_source_set_write_directory (
+                       E_SERVER_SIDE_SOURCE (source), cache_dir);
+
+               /* Set permissions for clients. */
+               e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
+               e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE);
+
+               server = e_collection_backend_ref_server (backend);
+               e_source_registry_server_add_source (server, source);
+               g_object_unref (server);
+       }
+
+ exit:
+       if (connection)
+               g_object_unref (connection);
+
+       return success;
+#endif
+       return FALSE;
+}
+
+static gboolean
+o365_backend_delete_resource_sync (ECollectionBackend *backend,
+                                  ESource *source,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+#if 0
+       EO365Connection *connection;
+       ESourceO365Folder *extension;
+       const gchar *extension_name;
+       gboolean success = FALSE;
+
+       connection = e_o365_backend_ref_connection_sync (E_O365_BACKEND (backend), NULL, NULL, NULL, 
cancellable, error);
+       if (connection == NULL)
+               return FALSE;
+
+       extension_name = E_SOURCE_EXTENSION_O365_FOLDER;
+       if (!e_source_has_extension (source, extension_name)) {
+               g_set_error (
+                       error, G_IO_ERROR,
+                       G_IO_ERROR_INVALID_ARGUMENT,
+                       _("Data source “%s” does not represent an Office365 folder"),
+                       e_source_get_display_name (source));
+               goto exit;
+       }
+       extension = e_source_get_extension (source, extension_name);
+
+       if (e_source_o365_folder_get_foreign (extension) ||
+           e_source_o365_folder_get_public (extension)) {
+               /* do not delete foreign or public folders,
+                * just remove them from local store */
+               success = TRUE;
+       } else {
+               gchar *folder_id;
+
+               folder_id = e_source_o365_folder_dup_id (extension);
+
+               success = e_o365_connection_delete_folder_sync (
+                       connection, O365_PRIORITY_MEDIUM, folder_id,
+                       FALSE, "HardDelete", cancellable, error);
+
+               g_free (folder_id);
+       }
+
+       if (success)
+               success = e_source_remove_sync (source, cancellable, error);
+
+ exit:
+       g_object_unref (connection);
+
+       return success;
+#endif
+       return FALSE;
+}
+
+static gboolean
+o365_backend_get_destination_address (EBackend *backend,
+                                     gchar **host,
+                                     guint16 *port)
+{
+       g_return_val_if_fail (port != NULL, FALSE);
+       g_return_val_if_fail (host != NULL, FALSE);
+
+       *host = g_strdup ("graph.microsoft.com");
+       *port = 443;
+
+       return TRUE;
+}
+
+static ESourceAuthenticationResult
+o365_backend_authenticate_sync (EBackend *backend,
+                               const ENamedParameters *credentials,
+                               gchar **out_certificate_pem,
+                               GTlsCertificateFlags *out_certificate_errors,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       /*EO365Backend *o365_backend;
+       EO365Connection *connection;*/
+       CamelO365Settings *o365_settings;
+       ESourceAuthenticationResult result = E_SOURCE_AUTHENTICATION_ERROR;
+
+       g_return_val_if_fail (E_IS_O365_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR);
+
+       o365_settings = camel_o365_settings_get_from_backend (backend, NULL);
+       g_return_val_if_fail (o365_settings != NULL, E_SOURCE_AUTHENTICATION_ERROR);
+
+       /*o365_backend = E_O365_BACKEND (backend);
+       g_mutex_lock (&o365_backend->priv->connection_lock);
+       g_clear_object (&o365_backend->priv->connection);
+       e_named_parameters_free (o365_backend->priv->credentials);
+       o365_backend->priv->credentials = e_named_parameters_new_clone (credentials);
+       g_mutex_unlock (&o365_backend->priv->connection_lock);
+
+       connection = e_o365_backend_ref_connection_sync (o365_backend, &result, out_certificate_pem, 
out_certificate_errors, cancellable, error);
+       g_clear_object (&connection);
+
+       if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
+               e_collection_backend_authenticate_children (E_COLLECTION_BACKEND (backend), credentials);
+
+               e_o365_backend_sync_folders (o365_backend, NULL, o365_backend_folders_synced_cb, NULL);
+       } else if (e_o365_connection_utils_get_without_password (o365_settings) &&
+                  result == E_SOURCE_AUTHENTICATION_REJECTED &&
+                  !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD)) {
+               e_o365_connection_utils_force_off_ntlm_auth_check ();
+               result = E_SOURCE_AUTHENTICATION_REQUIRED;
+       }*/
+
+       return result;
+}
+
+static void
+o365_backend_constructed (GObject *object)
+{
+       EBackend *backend;
+       ESource *source;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_o365_backend_parent_class)->constructed (object);
+
+       backend = E_BACKEND (object);
+       source = e_backend_get_source (backend);
+
+       e_server_side_source_set_remote_creatable (E_SERVER_SIDE_SOURCE (source), TRUE);
+
+       /* Reset the connectable, it steals data from Authentication extension,
+          where is written incorrect address */
+       e_backend_set_connectable (backend, NULL);
+
+       if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
+               ESourceCollection *collection_extension;
+
+               collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
+               e_source_collection_set_allow_sources_rename (collection_extension, TRUE);
+       }
+}
+
+static void
+o365_backend_dispose (GObject *object)
+{
+       EO365Backend *o365_backend = E_O365_BACKEND (object);
+       ESource *source;
+
+       source = e_backend_get_source (E_BACKEND (object));
+       if (source && o365_backend->priv->source_changed_id) {
+               g_signal_handler_disconnect (source, o365_backend->priv->source_changed_id);
+               o365_backend->priv->source_changed_id = 0;
+       }
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_o365_backend_parent_class)->dispose (object);
+}
+
+static void
+o365_backend_finalize (GObject *object)
+{
+       EO365Backend *o365_backend = E_O365_BACKEND (object);
+
+       g_mutex_clear (&o365_backend->priv->property_lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_o365_backend_parent_class)->finalize (object);
+}
+
+static void
+e_o365_backend_class_init (EO365BackendClass *class)
+{
+       GObjectClass *object_class;
+       EBackendClass *backend_class;
+       ECollectionBackendClass *collection_backend_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = o365_backend_dispose;
+       object_class->finalize = o365_backend_finalize;
+       object_class->constructed = o365_backend_constructed;
+
+       collection_backend_class = E_COLLECTION_BACKEND_CLASS (class);
+       collection_backend_class->populate = o365_backend_populate;
+       collection_backend_class->dup_resource_id = o365_backend_dup_resource_id;
+       collection_backend_class->child_added = o365_backend_child_added;
+       collection_backend_class->child_removed = o365_backend_child_removed;
+       collection_backend_class->create_resource_sync = o365_backend_create_resource_sync;
+       collection_backend_class->delete_resource_sync = o365_backend_delete_resource_sync;
+
+       backend_class = E_BACKEND_CLASS (class);
+       backend_class->get_destination_address = o365_backend_get_destination_address;
+       backend_class->authenticate_sync = o365_backend_authenticate_sync;
+
+       /* This generates an ESourceCamel subtype for CamelO365Settings. */
+       e_source_camel_generate_subtype ("office365", CAMEL_TYPE_O365_SETTINGS);
+}
+
+static void
+e_o365_backend_class_finalize (EO365BackendClass *class)
+{
+}
+
+static void
+e_o365_backend_init (EO365Backend *backend)
+{
+       backend->priv = e_o365_backend_get_instance_private (backend);
+
+       g_mutex_init (&backend->priv->property_lock);
+}
+
+void
+e_o365_backend_type_register (GTypeModule *type_module)
+{
+       /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+        *     function, so we have to wrap it with a public function in
+        *     order to register types from a separate compilation unit. */
+       e_o365_backend_register_type (type_module);
+}
diff --git a/src/Office365/registry/e-o365-backend.h b/src/Office365/registry/e-o365-backend.h
new file mode 100644
index 00000000..dba02943
--- /dev/null
+++ b/src/Office365/registry/e-o365-backend.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_O365_BACKEND_H
+#define E_O365_BACKEND_H
+
+#include <libebackend/libebackend.h>
+
+/* Standard GObject macros */
+#define E_TYPE_O365_BACKEND \
+       (e_o365_backend_get_type ())
+#define E_O365_BACKEND(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_O365_BACKEND, EO365Backend))
+#define E_O365_BACKEND_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_O365_BACKEND, EO365BackendClass))
+#define E_IS_O365_BACKEND(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_O365_BACKEND))
+#define E_IS_O365_BACKEND_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_O365_BACKEND))
+#define E_O365_BACKEND_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_O365_BACKEND, EO365BackendClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EO365Backend EO365Backend;
+typedef struct _EO365BackendClass EO365BackendClass;
+typedef struct _EO365BackendPrivate EO365BackendPrivate;
+
+struct _EO365Backend {
+       ECollectionBackend parent;
+       EO365BackendPrivate *priv;
+};
+
+struct _EO365BackendClass {
+       ECollectionBackendClass parent_class;
+};
+
+GType          e_o365_backend_get_type         (void) G_GNUC_CONST;
+void           e_o365_backend_type_register    (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_O365_BACKEND_H */
diff --git a/src/Office365/registry/module-o365-backend.c b/src/Office365/registry/module-o365-backend.c
new file mode 100644
index 00000000..5f79c6c6
--- /dev/null
+++ b/src/Office365/registry/module-o365-backend.c
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "common/e-source-o365-folder.h"
+
+#include "e-o365-backend.h"
+#include "e-o365-backend-factory.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+       bindtextdomain (GETTEXT_PACKAGE, O365_LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+       e_o365_backend_type_register (type_module);
+       e_o365_backend_factory_type_register (type_module);
+       e_source_o365_folder_type_register (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}


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