[evolution-ews/wip/mcrha/office365] Detect and configure .source-s for user address books



commit 6ecf61652946ab4c978b95fd67028b07fdf1ef3d
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jul 8 19:53:00 2020 +0200

    Detect and configure .source-s for user address books

 src/Office365/camel/camel-o365-store.c        |  28 ++-
 src/Office365/common/e-o365-connection.c      |  75 +++++-
 src/Office365/common/e-o365-connection.h      |  11 +-
 src/Office365/common/e-o365-json-utils.c      |  16 +-
 src/Office365/common/e-o365-json-utils.h      |   8 +-
 src/Office365/common/e-source-o365-folder.c   |  96 +++----
 src/Office365/common/e-source-o365-folder.h   |  12 +-
 src/Office365/registry/CMakeLists.txt         |   2 +
 src/Office365/registry/e-o365-backend.c       | 345 ++++++++++++++++++++++----
 src/Office365/registry/e-source-o365-deltas.c | 166 +++++++++++++
 src/Office365/registry/e-source-o365-deltas.h |  72 ++++++
 src/Office365/registry/module-o365-backend.c  |   2 +
 12 files changed, 686 insertions(+), 147 deletions(-)
---
diff --git a/src/Office365/camel/camel-o365-store.c b/src/Office365/camel/camel-o365-store.c
index 101d74b9..a5c4af67 100644
--- a/src/Office365/camel/camel-o365-store.c
+++ b/src/Office365/camel/camel-o365-store.c
@@ -742,14 +742,14 @@ o365_store_create_folder_sync (CamelStore *store,
        flags = e_o365_mail_folder_get_child_folder_count (mail_folder) ? CAMEL_STORE_INFO_FOLDER_CHILDREN : 
CAMEL_STORE_INFO_FOLDER_NOCHILDREN;
 
        camel_o365_store_summary_set_folder (o365_store->priv->summary, TRUE,
-               e_o365_mail_folder_get_id (mail_folder),
-               e_o365_mail_folder_get_parent_folder_id (mail_folder),
-               e_o365_mail_folder_get_display_name (mail_folder),
+               e_o365_folder_get_id (mail_folder),
+               e_o365_folder_get_parent_folder_id (mail_folder),
+               e_o365_folder_get_display_name (mail_folder),
                e_o365_mail_folder_get_total_item_count (mail_folder),
                e_o365_mail_folder_get_unread_item_count (mail_folder),
                flags, E_O365_FOLDER_KIND_MAIL, FALSE, FALSE);
 
-       fi = camel_o365_store_summary_build_folder_info_for_id (o365_store->priv->summary, 
e_o365_mail_folder_get_id (mail_folder));
+       fi = camel_o365_store_summary_build_folder_info_for_id (o365_store->priv->summary, 
e_o365_folder_get_id (mail_folder));
 
        camel_store_folder_created (store, fi);
        camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (o365_store), fi);
@@ -801,7 +801,7 @@ o365_store_move_mail_folder (CamelO365Store *o365_store,
 
                fi = camel_o365_store_summary_build_folder_info_for_id (o365_store->priv->summary, folder_id);
 
-               camel_o365_store_summary_set_folder_parent_id (o365_store->priv->summary, folder_id, 
e_o365_mail_folder_get_parent_folder_id (moved_mail_folder));
+               camel_o365_store_summary_set_folder_parent_id (o365_store->priv->summary, folder_id, 
e_o365_folder_get_parent_folder_id (moved_mail_folder));
                camel_o365_store_summary_rebuild_hashes (o365_store->priv->summary);
 
                camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (o365_store), fi);
@@ -1057,7 +1057,7 @@ o365_store_rename_folder_sync (CamelStore *store,
 
                if (mail_folder) {
                        camel_o365_store_summary_set_folder_display_name (o365_store->priv->summary, 
folder_id,
-                               e_o365_mail_folder_get_display_name (mail_folder), TRUE);
+                               e_o365_folder_get_display_name (mail_folder), TRUE);
 
                        json_object_unref (mail_folder);
                }
@@ -1126,7 +1126,7 @@ typedef struct _FoldersDeltaData {
 static gboolean
 camel_o365_got_folders_delta_cb (EO365Connection *cnc,
                                 const GSList *results, /* JsonObject * - the returned objects from the 
server */
-                                gpointer user_data, /* expects GSList **, aka pointer to a GSList *, where 
it copies the 'results' */
+                                gpointer user_data,
                                 GCancellable *cancellable,
                                 GError **error)
 {
@@ -1139,7 +1139,7 @@ camel_o365_got_folders_delta_cb (EO365Connection *cnc,
 
        for (link = (GSList *) results; link; link = g_slist_next (link)) {
                JsonObject *object = link->data;
-               const gchar *id = e_o365_mail_folder_get_id (object);
+               const gchar *id = e_o365_folder_get_id (object);
 
                if (e_o365_delta_is_removed_object (object)) {
                        CamelFolderInfo *info;
@@ -1162,8 +1162,8 @@ camel_o365_got_folders_delta_cb (EO365Connection *cnc,
                        flags |= GPOINTER_TO_UINT (g_hash_table_lookup 
(fdd->o365_store->priv->default_folders, id));
 
                        camel_o365_store_summary_set_folder (fdd->o365_store->priv->summary, FALSE, id,
-                               e_o365_mail_folder_get_parent_folder_id (object),
-                               e_o365_mail_folder_get_display_name (object),
+                               e_o365_folder_get_parent_folder_id (object),
+                               e_o365_folder_get_display_name (object),
                                e_o365_mail_folder_get_total_item_count (object),
                                e_o365_mail_folder_get_unread_item_count (object),
                                flags, E_O365_FOLDER_KIND_MAIL, FALSE, FALSE);
@@ -1266,16 +1266,18 @@ o365_store_get_folder_info_sync (CamelStore *store,
                                fdd.renamed_data = NULL;
                                fdd.removed_fis = NULL;
 
-                               success = e_o365_connection_get_mail_folders_delta_sync (cnc, NULL, NULL, 
old_delta_link, 0,
+                               success = e_o365_connection_get_folders_delta_sync (cnc, NULL, 
E_O365_FOLDER_KIND_MAIL, NULL, old_delta_link, 0,
                                        camel_o365_got_folders_delta_cb, &fdd, &new_delta_link, cancellable, 
&local_error);
 
-                               if (old_delta_link && *old_delta_link && g_error_matches (local_error, 
SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+                               if (old_delta_link && *old_delta_link && (
+                                   g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) 
||
+                                   g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_BAD_REQUEST))) 
{
                                        g_clear_pointer (&old_delta_link, g_free);
                                        g_clear_error (&local_error);
 
                                        o365_store_forget_all_folders (o365_store);
 
-                                       success = e_o365_connection_get_mail_folders_delta_sync (cnc, NULL, 
NULL, NULL, 0,
+                                       success = e_o365_connection_get_folders_delta_sync (cnc, NULL, 
E_O365_FOLDER_KIND_MAIL, NULL, NULL, 0,
                                                camel_o365_got_folders_delta_cb, &fdd, &new_delta_link, 
cancellable, error);
                                }
 
diff --git a/src/Office365/common/e-o365-connection.c b/src/Office365/common/e-o365-connection.c
index 4412ed11..ef5be174 100644
--- a/src/Office365/common/e-o365-connection.c
+++ b/src/Office365/common/e-o365-connection.c
@@ -2230,16 +2230,17 @@ e_o365_connection_list_mail_folders_sync (EO365Connection *cnc,
 }
 
 gboolean
-e_o365_connection_get_mail_folders_delta_sync (EO365Connection *cnc,
-                                              const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                              const gchar *select, /* properties to select, nullable */
-                                              const gchar *delta_link, /* previous delta link */
-                                              guint max_page_size, /* 0 for default by the server */
-                                              EO365ConnectionJsonFunc func, /* function to call with each 
result set */
-                                              gpointer func_user_data, /* user data passed into the 'func' */
-                                              gchar **out_delta_link,
-                                              GCancellable *cancellable,
-                                              GError **error)
+e_o365_connection_get_folders_delta_sync (EO365Connection *cnc,
+                                         const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                         EO365FolderKind kind,
+                                         const gchar *select, /* properties to select, nullable */
+                                         const gchar *delta_link, /* previous delta link */
+                                         guint max_page_size, /* 0 for default by the server */
+                                         EO365ConnectionJsonFunc func, /* function to call with each result 
set */
+                                         gpointer func_user_data, /* user data passed into the 'func' */
+                                         gchar **out_delta_link,
+                                         GCancellable *cancellable,
+                                         GError **error)
 {
        EO365ResponseData rd;
        SoupMessage *message = NULL;
@@ -2253,10 +2254,25 @@ e_o365_connection_get_mail_folders_delta_sync (EO365Connection *cnc,
                message = o365_connection_new_soup_message (SOUP_METHOD_GET, delta_link, CSM_DEFAULT, NULL);
 
        if (!message) {
+               const gchar *kind_str = NULL;
                gchar *uri;
 
+               switch (kind) {
+               case E_O365_FOLDER_KIND_CONTACTS:
+                       kind_str = "contactFolders";
+                       break;
+               case E_O365_FOLDER_KIND_MAIL:
+                       kind_str = "mailFolders";
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       break;
+               }
+
+               g_return_val_if_fail (kind_str != NULL, FALSE);
+
                uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
-                       "mailFolders",
+                       kind_str,
                        NULL,
                        "delta",
                        "$select", select,
@@ -3140,3 +3156,40 @@ e_o365_connection_send_mail_sync (EO365Connection *cnc,
 
        return success;
 }
+
+gboolean
+e_o365_connection_get_contacts_folder_sync (EO365Connection *cnc,
+                                           const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                           EO365Folder **out_folder,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       SoupMessage *message;
+       gchar *uri;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (out_folder != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               "contactFolders",
+               "contacts",
+               NULL,
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       success = o365_connection_send_request_sync (cnc, message, e_o365_read_json_object_response_cb, NULL, 
out_folder, cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
diff --git a/src/Office365/common/e-o365-connection.h b/src/Office365/common/e-o365-connection.h
index 5174641f..a19fbb20 100644
--- a/src/Office365/common/e-o365-connection.h
+++ b/src/Office365/common/e-o365-connection.h
@@ -53,7 +53,7 @@
 
 G_BEGIN_DECLS
 
-typedef enum {
+typedef enum _EO365ApiVersion {
        E_O365_API_V1_0,
        E_O365_API_BETA
 } EO365ApiVersion;
@@ -173,9 +173,10 @@ gboolean   e_o365_connection_list_mail_folders_sync
                                                 GSList **out_folders, /* EO365MailFolder * - the returned 
mailFolder objects */
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_o365_connection_get_mail_folders_delta_sync
+gboolean       e_o365_connection_get_folders_delta_sync
                                                (EO365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                EO365FolderKind kind,
                                                 const gchar *select, /* properties to select, nullable */
                                                 const gchar *delta_link, /* previous delta link */
                                                 guint max_page_size, /* 0 for default by the server */
@@ -293,6 +294,12 @@ gboolean   e_o365_connection_send_mail_sync
                                                 JsonBuilder *request, /* filled sendMail object */
                                                 GCancellable *cancellable,
                                                 GError **error);
+gboolean       e_o365_connection_get_contacts_folder_sync
+                                               (EO365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                EO365Folder **out_folder,
+                                                GCancellable *cancellable,
+                                                GError **error);
 
 G_END_DECLS
 
diff --git a/src/Office365/common/e-o365-json-utils.c b/src/Office365/common/e-o365-json-utils.c
index d803a26b..a3cdb79f 100644
--- a/src/Office365/common/e-o365-json-utils.c
+++ b/src/Office365/common/e-o365-json-utils.c
@@ -363,24 +363,26 @@ e_o365_category_get_color (EO365Category *category)
        return NULL;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0 */
+/* https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0
+   https://docs.microsoft.com/en-us/graph/api/resources/contactfolder?view=graph-rest-1.0
+ */
 
 const gchar *
-e_o365_mail_folder_get_display_name (EO365MailFolder *folder)
+e_o365_folder_get_id (EO365Folder *folder)
 {
-       return e_o365_json_get_string_member (folder, "displayName", NULL);
+       return e_o365_json_get_string_member (folder, "id", NULL);
 }
 
 const gchar *
-e_o365_mail_folder_get_id (EO365MailFolder *folder)
+e_o365_folder_get_parent_folder_id (EO365Folder *folder)
 {
-       return e_o365_json_get_string_member (folder, "id", NULL);
+       return e_o365_json_get_string_member (folder, "parentFolderId", NULL);
 }
 
 const gchar *
-e_o365_mail_folder_get_parent_folder_id (EO365MailFolder *folder)
+e_o365_folder_get_display_name (EO365Folder *folder)
 {
-       return e_o365_json_get_string_member (folder, "parentFolderId", NULL);
+       return e_o365_json_get_string_member (folder, "displayName", NULL);
 }
 
 gint32
diff --git a/src/Office365/common/e-o365-json-utils.h b/src/Office365/common/e-o365-json-utils.h
index be33cf8b..0f766d6f 100644
--- a/src/Office365/common/e-o365-json-utils.h
+++ b/src/Office365/common/e-o365-json-utils.h
@@ -65,6 +65,7 @@ typedef enum _EO365ItemBodyContentTypeType {
 #define EO365Attachment                        JsonObject
 #define EO365Category                  JsonObject
 #define EO365DateTimeWithZone          JsonObject
+#define EO365Folder                    JsonObject
 #define EO365FollowupFlag              JsonObject
 #define EO365InternetMessageHeader     JsonObject
 #define EO365ItemBody                  JsonObject
@@ -124,9 +125,10 @@ const gchar *      e_o365_category_get_display_name        (EO365Category *category);
 const gchar *  e_o365_category_get_id                  (EO365Category *category);
 const gchar *  e_o365_category_get_color               (EO365Category *category);
 
-const gchar *  e_o365_mail_folder_get_display_name     (EO365MailFolder *folder);
-const gchar *  e_o365_mail_folder_get_id               (EO365MailFolder *folder);
-const gchar *  e_o365_mail_folder_get_parent_folder_id (EO365MailFolder *folder);
+const gchar *  e_o365_folder_get_id                    (EO365Folder *folder);
+const gchar *  e_o365_folder_get_parent_folder_id      (EO365Folder *folder);
+const gchar *  e_o365_folder_get_display_name          (EO365Folder *folder);
+
 gint32         e_o365_mail_folder_get_child_folder_count
                                                        (EO365MailFolder *folder);
 gint32         e_o365_mail_folder_get_total_item_count (EO365MailFolder *folder);
diff --git a/src/Office365/common/e-source-o365-folder.c b/src/Office365/common/e-source-o365-folder.c
index 2d1b7a70..8d7ca61e 100644
--- a/src/Office365/common/e-source-o365-folder.c
+++ b/src/Office365/common/e-source-o365-folder.c
@@ -20,14 +20,14 @@
 #include "e-source-o365-folder.h"
 
 struct _ESourceO365FolderPrivate {
-       gchar *change_key;
        gchar *id;
+       gboolean is_default;
 };
 
 enum {
        PROP_0,
-       PROP_CHANGE_KEY,
-       PROP_ID
+       PROP_ID,
+       PROP_IS_DEFAULT
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (ESourceO365Folder, e_source_o365_folder, E_TYPE_SOURCE_EXTENSION)
@@ -39,10 +39,10 @@ source_o365_folder_set_property (GObject *object,
                                 GParamSpec *pspec)
 {
        switch (property_id) {
-               case PROP_CHANGE_KEY:
-                       e_source_o365_folder_set_change_key (
+               case PROP_IS_DEFAULT:
+                       e_source_o365_folder_set_is_default (
                                E_SOURCE_O365_FOLDER (object),
-                               g_value_get_string (value));
+                               g_value_get_boolean (value));
                        return;
 
                case PROP_ID:
@@ -62,10 +62,10 @@ source_o365_folder_get_property (GObject *object,
                                 GParamSpec *pspec)
 {
        switch (property_id) {
-               case PROP_CHANGE_KEY:
-                       g_value_take_string (
+               case PROP_IS_DEFAULT:
+                       g_value_set_boolean (
                                value,
-                               e_source_o365_folder_dup_change_key (
+                               e_source_o365_folder_get_is_default (
                                E_SOURCE_O365_FOLDER (object)));
                        return;
 
@@ -85,7 +85,6 @@ 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. */
@@ -108,11 +107,11 @@ e_source_o365_folder_class_init (ESourceO365FolderClass *class)
 
        g_object_class_install_property (
                object_class,
-               PROP_CHANGE_KEY,
+               PROP_ID,
                g_param_spec_string (
-                       "change-key",
-                       "Change Key",
-                       "Essentially an entity tag, used when submitting changes",
+                       "id",
+                       "ID",
+                       "The server-assigned folder ID",
                        NULL,
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT |
@@ -121,12 +120,12 @@ e_source_o365_folder_class_init (ESourceO365FolderClass *class)
 
        g_object_class_install_property (
                object_class,
-               PROP_ID,
-               g_param_spec_string (
-                       "id",
-                       "ID",
-                       "The server-assigned folder ID",
-                       NULL,
+               PROP_IS_DEFAULT,
+               g_param_spec_boolean (
+                       "is-default",
+                       "Is Default",
+                       "Whether it's user's default folder (like 'contacts', which are not part of the 
contactFolders)",
+                       FALSE,
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT |
                        G_PARAM_STATIC_STRINGS |
@@ -148,15 +147,15 @@ e_source_o365_folder_type_register (GTypeModule *type_module)
 }
 
 const gchar *
-e_source_o365_folder_get_change_key (ESourceO365Folder *extension)
+e_source_o365_folder_get_id (ESourceO365Folder *extension)
 {
        g_return_val_if_fail (E_IS_SOURCE_O365_FOLDER (extension), NULL);
 
-       return extension->priv->change_key;
+       return extension->priv->id;
 }
 
 gchar *
-e_source_o365_folder_dup_change_key (ESourceO365Folder *extension)
+e_source_o365_folder_dup_id (ESourceO365Folder *extension)
 {
        const gchar *protected;
        gchar *duplicate;
@@ -165,7 +164,7 @@ e_source_o365_folder_dup_change_key (ESourceO365Folder *extension)
 
        e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
 
-       protected = e_source_o365_folder_get_change_key (extension);
+       protected = e_source_o365_folder_get_id (extension);
        duplicate = g_strdup (protected);
 
        e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
@@ -174,69 +173,50 @@ e_source_o365_folder_dup_change_key (ESourceO365Folder *extension)
 }
 
 void
-e_source_o365_folder_set_change_key (ESourceO365Folder *extension,
-                                    const gchar *change_key)
+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->change_key, change_key) == 0) {
+       if (g_strcmp0 (extension->priv->id, id) == 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);
+       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), "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;
+       g_object_notify (G_OBJECT (extension), "id");
 }
 
-gchar *
-e_source_o365_folder_dup_id (ESourceO365Folder *extension)
+gboolean
+e_source_o365_folder_get_is_default (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));
+       g_return_val_if_fail (E_IS_SOURCE_O365_FOLDER (extension), FALSE);
 
-       protected = e_source_o365_folder_get_id (extension);
-       duplicate = g_strdup (protected);
-
-       e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
-
-       return duplicate;
+       return extension->priv->is_default;
 }
 
 void
-e_source_o365_folder_set_id (ESourceO365Folder *extension,
-                            const gchar *id)
+e_source_o365_folder_set_is_default (ESourceO365Folder *extension,
+                                    gboolean value)
 {
        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) {
+       if ((extension->priv->is_default ? 1 : 0) == (value ? 1 : 0)) {
                e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
                return;
        }
 
-       g_free (extension->priv->id);
-       extension->priv->id = g_strdup (id);
+       extension->priv->is_default = value;
 
        e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
 
-       g_object_notify (G_OBJECT (extension), "id");
+       g_object_notify (G_OBJECT (extension), "is-default");
 }
diff --git a/src/Office365/common/e-source-o365-folder.h b/src/Office365/common/e-source-o365-folder.h
index f124eca1..4b1ddc63 100644
--- a/src/Office365/common/e-source-o365-folder.h
+++ b/src/Office365/common/e-source-o365-folder.h
@@ -59,17 +59,15 @@ struct _ESourceO365FolderClass {
 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);
+gboolean       e_source_o365_folder_get_is_default
+                                               (ESourceO365Folder *extension);
+void           e_source_o365_folder_set_is_default
+                                               (ESourceO365Folder *extension,
+                                                gboolean value);
 
 G_END_DECLS
 
diff --git a/src/Office365/registry/CMakeLists.txt b/src/Office365/registry/CMakeLists.txt
index dd8b7be3..dcd7e56c 100644
--- a/src/Office365/registry/CMakeLists.txt
+++ b/src/Office365/registry/CMakeLists.txt
@@ -5,6 +5,8 @@ set(sources
        e-o365-backend.h
        e-o365-backend-factory.c
        e-o365-backend-factory.h
+       e-source-o365-deltas.c
+       e-source-o365-deltas.h
 )
 set(extra_defines)
 set(extra_cflags)
diff --git a/src/Office365/registry/e-o365-backend.c b/src/Office365/registry/e-o365-backend.c
index 1ced46dc..d1b1c3c7 100644
--- a/src/Office365/registry/e-o365-backend.c
+++ b/src/Office365/registry/e-o365-backend.c
@@ -23,11 +23,18 @@
 #include "common/e-source-o365-folder.h"
 #include "common/camel-o365-settings.h"
 
+#include "e-source-o365-deltas.h"
+
 #include "e-o365-backend.h"
 
+#define LOCK(_backend) g_mutex_lock (&_backend->priv->property_lock)
+#define UNLOCK(_backend) g_mutex_unlock (&_backend->priv->property_lock)
+
 struct _EO365BackendPrivate {
        GMutex property_lock;
 
+       GHashTable *folder_sources; /* gchar *folder_id ~> ESource * */
+
        gboolean need_update_folders;
 
        gulong source_changed_id;
@@ -76,6 +83,269 @@ o365_backend_populate (ECollectionBackend *backend)
                e_backend_schedule_authenticate (E_BACKEND (backend), NULL);
 }
 
+static void
+o365_backend_update_resource (EO365Backend *o365_backend,
+                             const gchar *extension_name,
+                             const gchar *id,
+                             const gchar *display_name,
+                             gboolean is_default,
+                             const gchar *calendar_color)
+{
+       ESource *source;
+       gboolean is_new;
+
+       LOCK (o365_backend);
+       source = g_hash_table_lookup (o365_backend->priv->folder_sources, id);
+       if (source)
+               g_object_ref (source);
+       UNLOCK (o365_backend);
+
+       is_new = !source;
+
+       if (is_new)
+               source = e_collection_backend_new_child (E_COLLECTION_BACKEND (o365_backend), id);
+
+       if (source) {
+               e_source_set_display_name (source, display_name);
+
+               if (calendar_color && g_ascii_strcasecmp (calendar_color, "auto") != 0 && (
+                   g_strcmp0 (extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0 ||
+                   g_strcmp0 (extension_name, E_SOURCE_EXTENSION_TASK_LIST) == 0 ||
+                   g_strcmp0 (extension_name, E_SOURCE_EXTENSION_MEMO_LIST) == 0)) {
+                       ESourceSelectable *selectable;
+
+                       selectable = e_source_get_extension (source, extension_name);
+                       e_source_selectable_set_color (selectable, calendar_color);
+               }
+
+               if (is_new) {
+                       ESourceRegistryServer *server;
+                       gpointer extension;
+
+                       extension = e_source_get_extension (source, extension_name);
+                       e_source_backend_set_backend_name (E_SOURCE_BACKEND (extension), "office365");
+
+                       /* Do not notify with too old reminders */
+                       if (g_strcmp0 (extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0 ||
+                           g_strcmp0 (extension_name, E_SOURCE_EXTENSION_TASK_LIST) == 0) {
+                               ESourceAlarms *alarms;
+                               gchar *today;
+                               GTimeVal today_tv;
+                               GDate dt;
+
+                               g_date_clear (&dt, 1);
+                               g_get_current_time (&today_tv);
+                               g_date_set_time_val (&dt, &today_tv);
+
+                               /* midnight UTC */
+                               today = g_strdup_printf ("%04d-%02d-%02dT00:00:00Z", g_date_get_year (&dt), 
g_date_get_month (&dt), g_date_get_day (&dt));
+
+                               alarms = e_source_get_extension (source, E_SOURCE_EXTENSION_ALARMS);
+                               e_source_alarms_set_last_notified (alarms, today);
+
+                               g_free (today);
+                       }
+
+                       extension = e_source_get_extension (source, E_SOURCE_EXTENSION_O365_FOLDER);
+                       e_source_o365_folder_set_id (extension, id);
+                       e_source_o365_folder_set_is_default (extension, is_default);
+
+                       server = e_collection_backend_ref_server (E_COLLECTION_BACKEND (o365_backend));
+
+                       e_source_registry_server_add_source (server, source);
+
+                       g_clear_object (&server);
+               }
+       }
+
+       g_clear_object (&source);
+}
+
+static void
+o365_backend_remove_resource (EO365Backend *o365_backend,
+                             const gchar *extension_name,
+                             const gchar *id) /* NULL to remove the "is-default" resource for the 
extension_name */
+{
+       ESource *existing_source;
+
+       LOCK (o365_backend);
+
+       if (id) {
+               existing_source = g_hash_table_lookup (o365_backend->priv->folder_sources, id);
+       } else {
+               GHashTableIter iter;
+               gpointer value;
+
+               g_hash_table_iter_init (&iter, o365_backend->priv->folder_sources);
+               while (g_hash_table_iter_next (&iter, NULL, &value)) {
+                       ESource *source = value;
+
+                       if (value && e_source_has_extension (source, extension_name) &&
+                           e_source_o365_folder_get_is_default (e_source_get_extension (source, 
E_SOURCE_EXTENSION_O365_FOLDER))) {
+                               existing_source = source;
+                               break;
+                       }
+               }
+       }
+
+       if (existing_source)
+               g_object_ref (existing_source);
+
+       UNLOCK (o365_backend);
+
+       if (existing_source)
+               e_source_remove_sync (existing_source, NULL, NULL);
+
+       g_clear_object (&existing_source);
+}
+
+static void
+o365_backend_forget_folders (EO365Backend *o365_backend,
+                            const gchar *extension_name)
+{
+       GHashTableIter iter;
+       GSList *ids = NULL, *link;
+       gpointer value;
+
+       LOCK (o365_backend);
+
+       g_hash_table_iter_init (&iter, o365_backend->priv->folder_sources);
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               ESource *source = value;
+
+               if (source && e_source_has_extension (source, extension_name))
+                       ids = g_slist_prepend (ids, e_source_o365_folder_dup_id (e_source_get_extension 
(source, E_SOURCE_EXTENSION_O365_FOLDER)));
+       }
+
+       UNLOCK (o365_backend);
+
+       for (link = ids; link; link = g_slist_next (link)) {
+               const gchar *id = link->data;
+
+               if (id)
+                       o365_backend_remove_resource (o365_backend, extension_name, id);
+       }
+
+       g_slist_free_full (ids, g_free);
+}
+
+static gboolean
+o365_backend_got_contact_folders_delta_cb (EO365Connection *cnc,
+                                          const GSList *results, /* JsonObject * - the returned objects from 
the server */
+                                          gpointer user_data,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+       EO365Backend *o365_backend = user_data;
+       GSList *link;
+
+       g_return_val_if_fail (E_IS_O365_BACKEND (o365_backend), FALSE);
+
+       for (link = (GSList *) results; link; link = g_slist_next (link)) {
+               JsonObject *object = link->data;
+               const gchar *id = e_o365_folder_get_id (object);
+
+               if (!id)
+                       continue;
+
+               if (e_o365_delta_is_removed_object (object)) {
+                       o365_backend_remove_resource (o365_backend, E_SOURCE_EXTENSION_ADDRESS_BOOK, id);
+               } else {
+                       o365_backend_update_resource (o365_backend, E_SOURCE_EXTENSION_ADDRESS_BOOK,
+                             id, e_o365_folder_get_display_name (object),
+                             FALSE, NULL);
+               }
+       }
+
+       return TRUE;
+}
+
+static void
+o365_backend_sync_folders_thread (GTask *task,
+                                 gpointer source_object,
+                                 gpointer task_data,
+                                 GCancellable *cancellable)
+{
+       EO365Backend *o365_backend = source_object;
+       EO365Connection *cnc = task_data;
+       ESourceO365Deltas *o365_deltas;
+       EO365Folder *user_contacts = NULL;
+       gchar *old_delta_link, *new_delta_link;
+       gboolean success;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_O365_BACKEND (o365_backend));
+       g_return_if_fail (E_IS_O365_CONNECTION (cnc));
+
+       o365_deltas = e_source_get_extension (e_backend_get_source (E_BACKEND (o365_backend)), 
E_SOURCE_EXTENSION_O365_DELTAS);
+
+       if (e_o365_connection_get_contacts_folder_sync (cnc, NULL, &user_contacts, cancellable, &error)) {
+               const gchar *id, *display_name;
+
+               id = e_o365_folder_get_id (user_contacts);
+               display_name = e_o365_folder_get_display_name (user_contacts);
+
+               g_warn_if_fail (id != NULL);
+               g_warn_if_fail (display_name != NULL);
+
+               o365_backend_update_resource (o365_backend,
+                       E_SOURCE_EXTENSION_ADDRESS_BOOK,
+                       id, display_name, TRUE, NULL);
+
+               json_object_unref (user_contacts);
+       } else if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND) ||
+                  g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+               o365_backend_remove_resource (o365_backend, E_SOURCE_EXTENSION_ADDRESS_BOOK, NULL);
+       }
+
+       g_clear_error (&error);
+
+       new_delta_link = NULL;
+       old_delta_link = e_source_o365_deltas_dup_contacts_link (o365_deltas);
+
+       success = e_o365_connection_get_folders_delta_sync (cnc, NULL, E_O365_FOLDER_KIND_CONTACTS, NULL, 
old_delta_link, 0,
+               o365_backend_got_contact_folders_delta_cb, o365_backend, &new_delta_link, cancellable, 
&error);
+
+       if (old_delta_link && *old_delta_link && (
+           g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
+           g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_BAD_REQUEST))) {
+               g_clear_pointer (&old_delta_link, g_free);
+               g_clear_error (&error);
+
+               o365_backend_forget_folders (o365_backend, E_SOURCE_EXTENSION_ADDRESS_BOOK);
+
+               success = e_o365_connection_get_folders_delta_sync (cnc, NULL, E_O365_FOLDER_KIND_CONTACTS, 
NULL, NULL, 0,
+                       o365_backend_got_contact_folders_delta_cb, o365_backend, &new_delta_link, 
cancellable, &error);
+       }
+
+       if (success)
+               e_source_o365_deltas_set_contacts_link (o365_deltas, new_delta_link);
+
+       g_clear_pointer (&old_delta_link, g_free);
+       g_clear_pointer (&new_delta_link, g_free);
+       g_clear_error (&error);
+}
+
+static void
+o365_backend_sync_folders (EO365Backend *o365_backend,
+                          EO365Connection *cnc,
+                          GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+       GTask *task;
+
+       o365_backend->priv->need_update_folders = FALSE;
+
+       task = g_task_new (o365_backend, cancellable, callback, user_data);
+
+       g_task_set_check_cancellable (task, TRUE);
+       g_task_set_task_data (task, g_object_ref (cnc), g_object_unref);
+       g_task_run_in_thread (task, o365_backend_sync_folders_thread);
+
+       g_object_unref (task);
+}
+
 static gchar *
 o365_backend_dup_resource_id (ECollectionBackend *backend,
                              ESource *child_source)
@@ -93,35 +363,19 @@ 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)) {
+       if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION) && (
+           e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) ||
+           e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_IDENTITY) ||
+           e_source_has_extension (child_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT))) {
                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);
+               collection_extension = e_source_get_extension (collection_source, 
E_SOURCE_EXTENSION_COLLECTION);
+               auth_child_extension = e_source_get_extension (child_source, 
E_SOURCE_EXTENSION_AUTHENTICATION);
 
                e_binding_bind_property (
                        collection_extension, "identity",
@@ -130,22 +384,22 @@ o365_backend_child_added (ECollectionBackend *backend,
        }
 
        /* 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)) {
+       if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_O365_FOLDER)) {
                ESourceO365Folder *extension;
                gchar *folder_id;
 
-               extension = e_source_get_extension (
-                       child_source, extension_name);
+               extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_O365_FOLDER);
                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);
+
+               if (folder_id) {
+                       EO365Backend *o365_backend = E_O365_BACKEND (backend);
+
+                       LOCK (o365_backend);
+                       g_hash_table_insert (o365_backend->priv->folder_sources, folder_id, g_object_ref 
(child_source));
+                       UNLOCK (o365_backend);
                }
        }
-#endif
+
        /* Chain up to parent's method. */
        E_COLLECTION_BACKEND_CLASS (e_o365_backend_parent_class)->child_added (backend, child_source);
 }
@@ -154,23 +408,22 @@ 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)) {
+       if (e_source_has_extension (child_source, E_SOURCE_EXTENSION_O365_FOLDER)) {
                ESourceO365Folder *extension;
                const gchar *folder_id;
 
-               extension = e_source_get_extension (
-                       child_source, extension_name);
+               extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_O365_FOLDER);
                folder_id = e_source_o365_folder_get_id (extension);
-               if (folder_id != NULL)
-                       o365_backend_folders_remove (
-                               E_O365_BACKEND (backend), folder_id);
+
+               if (folder_id) {
+                       EO365Backend *o365_backend = E_O365_BACKEND (backend);
+
+                       LOCK (o365_backend);
+                       g_hash_table_remove (o365_backend->priv->folder_sources, folder_id);
+                       UNLOCK (o365_backend);
+               }
        }
-#endif
 
        /* Chain up to parent's method. */
        E_COLLECTION_BACKEND_CLASS (e_o365_backend_parent_class)->child_removed (backend, child_source);
@@ -379,7 +632,6 @@ o365_backend_authenticate_sync (EBackend *backend,
                                GError **error)
 {
        CamelO365Settings *o365_settings;
-       /*EO365Backend *o365_backend;*/
        EO365Connection *cnc;
        ESourceAuthenticationResult result;
 
@@ -394,8 +646,7 @@ o365_backend_authenticate_sync (EBackend *backend,
 
        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); */
+               o365_backend_sync_folders (E_O365_BACKEND (backend), cnc, NULL, NULL, NULL);
        } else if (result == E_SOURCE_AUTHENTICATION_REJECTED &&
                   !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD)) {
                result = E_SOURCE_AUTHENTICATION_REQUIRED;
@@ -453,6 +704,7 @@ o365_backend_finalize (GObject *object)
 {
        EO365Backend *o365_backend = E_O365_BACKEND (object);
 
+       g_hash_table_destroy (o365_backend->priv->folder_sources);
        g_mutex_clear (&o365_backend->priv->property_lock);
 
        /* Chain up to parent's method. */
@@ -496,6 +748,7 @@ static void
 e_o365_backend_init (EO365Backend *backend)
 {
        backend->priv = e_o365_backend_get_instance_private (backend);
+       backend->priv->folder_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
g_object_unref);
 
        g_mutex_init (&backend->priv->property_lock);
 }
diff --git a/src/Office365/registry/e-source-o365-deltas.c b/src/Office365/registry/e-source-o365-deltas.c
new file mode 100644
index 00000000..94b3cffe
--- /dev/null
+++ b/src/Office365/registry/e-source-o365-deltas.c
@@ -0,0 +1,166 @@
+/* -*- 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-deltas.h"
+
+struct _ESourceO365DeltasPrivate {
+       gchar *contacts_link;
+};
+
+enum {
+       PROP_0,
+       PROP_CONTACTS_LINK
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ESourceO365Deltas, e_source_o365_deltas, E_TYPE_SOURCE_EXTENSION)
+
+static void
+source_o365_deltas_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CONTACTS_LINK:
+                       e_source_o365_deltas_set_contacts_link (
+                               E_SOURCE_O365_DELTAS (object),
+                               g_value_get_string (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_o365_deltas_get_property (GObject *object,
+                                guint property_id,
+                                GValue *value,
+                                GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CONTACTS_LINK:
+                       g_value_take_string (
+                               value,
+                               e_source_o365_deltas_dup_contacts_link (
+                               E_SOURCE_O365_DELTAS (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_o365_deltas_finalize (GObject *object)
+{
+       ESourceO365Deltas *o365_deltas = E_SOURCE_O365_DELTAS (object);
+
+       g_free (o365_deltas->priv->contacts_link);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_source_o365_deltas_parent_class)->finalize (object);
+}
+
+static void
+e_source_o365_deltas_class_init (ESourceO365DeltasClass *class)
+{
+       GObjectClass *object_class;
+       ESourceExtensionClass *extension_class;
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = source_o365_deltas_set_property;
+       object_class->get_property = source_o365_deltas_get_property;
+       object_class->finalize = source_o365_deltas_finalize;
+
+       extension_class = E_SOURCE_EXTENSION_CLASS (class);
+       extension_class->name = E_SOURCE_EXTENSION_O365_DELTAS;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CONTACTS_LINK,
+               g_param_spec_string (
+                       "contacts-link",
+                       "Contacts Link",
+                       "The delta link for contacts",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS |
+                       E_SOURCE_PARAM_SETTING));
+}
+
+static void
+e_source_o365_deltas_init (ESourceO365Deltas *extension)
+{
+       extension->priv = e_source_o365_deltas_get_instance_private (extension);
+}
+
+void
+e_source_o365_deltas_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_DELTAS);
+}
+
+const gchar *
+e_source_o365_deltas_get_contacts_link (ESourceO365Deltas *extension)
+{
+       g_return_val_if_fail (E_IS_SOURCE_O365_DELTAS (extension), NULL);
+
+       return extension->priv->contacts_link;
+}
+
+gchar *
+e_source_o365_deltas_dup_contacts_link (ESourceO365Deltas *extension)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (E_IS_SOURCE_O365_DELTAS (extension), NULL);
+
+       e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
+
+       protected = e_source_o365_deltas_get_contacts_link (extension);
+       duplicate = g_strdup (protected);
+
+       e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+
+       return duplicate;
+}
+
+void
+e_source_o365_deltas_set_contacts_link (ESourceO365Deltas *extension,
+                                       const gchar *delta_link)
+{
+       g_return_if_fail (E_IS_SOURCE_O365_DELTAS (extension));
+
+       e_source_extension_property_lock (E_SOURCE_EXTENSION (extension));
+
+       if (g_strcmp0 (extension->priv->contacts_link, delta_link) == 0) {
+               e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+               return;
+       }
+
+       g_free (extension->priv->contacts_link);
+       extension->priv->contacts_link = g_strdup (delta_link);
+
+       e_source_extension_property_unlock (E_SOURCE_EXTENSION (extension));
+
+       g_object_notify (G_OBJECT (extension), "contacts-link");
+}
diff --git a/src/Office365/registry/e-source-o365-deltas.h b/src/Office365/registry/e-source-o365-deltas.h
new file mode 100644
index 00000000..dac6e770
--- /dev/null
+++ b/src/Office365/registry/e-source-o365-deltas.h
@@ -0,0 +1,72 @@
+/* -*- 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_DELTAS_H
+#define E_SOURCE_O365_DELTAS_H
+
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_O365_DELTAS \
+       (e_source_o365_deltas_get_type ())
+#define E_SOURCE_O365_DELTAS(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SOURCE_O365_DELTAS, ESourceO365Deltas))
+#define E_SOURCE_O365_DELTAS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SOURCE_O365_DELTAS, ESourceO365DeltasClass))
+#define E_IS_SOURCE_O365_DELTAS(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SOURCE_O365_DELTAS))
+#define E_IS_SOURCE_O365_DELTAS_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SOURCE_O365_DELTAS))
+#define E_SOURCE_O365_DELTAS_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SOURCE_O365_DELTAS, ESourceO365DeltasClass))
+
+#define E_SOURCE_EXTENSION_O365_DELTAS "Office365 Deltas"
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceO365Deltas ESourceO365Deltas;
+typedef struct _ESourceO365DeltasClass ESourceO365DeltasClass;
+typedef struct _ESourceO365DeltasPrivate ESourceO365DeltasPrivate;
+
+struct _ESourceO365Deltas {
+       ESourceExtension parent;
+       ESourceO365DeltasPrivate *priv;
+};
+
+struct _ESourceO365DeltasClass {
+       ESourceExtensionClass parent_class;
+};
+
+GType          e_source_o365_deltas_get_type   (void) G_GNUC_CONST;
+void           e_source_o365_deltas_type_register
+                                               (GTypeModule *type_module);
+const gchar *  e_source_o365_deltas_get_contacts_link
+                                               (ESourceO365Deltas *extension);
+gchar *                e_source_o365_deltas_dup_contacts_link
+                                               (ESourceO365Deltas *extension);
+void           e_source_o365_deltas_set_contacts_link
+                                               (ESourceO365Deltas *extension,
+                                                const gchar *delta_link);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_O365_DELTAS_H */
diff --git a/src/Office365/registry/module-o365-backend.c b/src/Office365/registry/module-o365-backend.c
index 7129b501..1997ee6f 100644
--- a/src/Office365/registry/module-o365-backend.c
+++ b/src/Office365/registry/module-o365-backend.c
@@ -24,6 +24,7 @@
 
 #include "e-o365-backend.h"
 #include "e-o365-backend-factory.h"
+#include "e-source-o365-deltas.h"
 
 /* Module Entry Points */
 void e_module_load (GTypeModule *type_module);
@@ -38,6 +39,7 @@ e_module_load (GTypeModule *type_module)
        e_oauth2_service_office365_type_register (type_module);
        e_source_o365_folder_type_register (type_module);
 
+       e_source_o365_deltas_type_register (type_module);
        e_o365_backend_type_register (type_module);
        e_o365_backend_factory_type_register (type_module);
 }



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