[evolution-ews] Microsoft365: Split iCalendar component manipulation code into separate file



commit b9e6394348d0772899d987ed7bf90e2db7bf3b46
Author: Milan Crha <mcrha redhat com>
Date:   Thu Jul 14 11:07:49 2022 +0200

    Microsoft365: Split iCalendar component manipulation code into separate file
    
    This way it can be shared in the EWS part.

 po/POTFILES.in                                     |    1 +
 src/Microsoft365/calendar/CMakeLists.txt           |    2 +
 .../calendar/e-cal-backend-m365-utils.c            | 2811 ++++++++++++++++++++
 .../calendar/e-cal-backend-m365-utils.h            |   53 +
 src/Microsoft365/calendar/e-cal-backend-m365.c     | 2614 +-----------------
 src/Microsoft365/common/e-m365-connection.c        |   49 +-
 6 files changed, 2929 insertions(+), 2601 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1c543ee9..e879e5b5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -38,6 +38,7 @@ src/EWS/registry/e-ews-backend.c
 src/EWS/registry/module-ews-backend.c
 src/Microsoft365/addressbook/e-book-backend-m365.c
 src/Microsoft365/calendar/e-cal-backend-m365.c
+src/Microsoft365/calendar/e-cal-backend-m365-utils.c
 src/Microsoft365/camel/camel-m365-folder.c
 src/Microsoft365/camel/camel-m365-provider.c
 src/Microsoft365/camel/camel-m365-store.c
diff --git a/src/Microsoft365/calendar/CMakeLists.txt b/src/Microsoft365/calendar/CMakeLists.txt
index 56f36cee..676e2d7f 100644
--- a/src/Microsoft365/calendar/CMakeLists.txt
+++ b/src/Microsoft365/calendar/CMakeLists.txt
@@ -6,6 +6,8 @@ set(SOURCES
        e-cal-backend-m365.c
        e-cal-backend-m365.h
        e-cal-backend-m365-factory.c
+       e-cal-backend-m365-utils.c
+       e-cal-backend-m365-utils.h
 )
 
 add_library(ecalbackendmicrosoft365 MODULE
diff --git a/src/Microsoft365/calendar/e-cal-backend-m365-utils.c 
b/src/Microsoft365/calendar/e-cal-backend-m365-utils.c
new file mode 100644
index 00000000..7dacc34d
--- /dev/null
+++ b/src/Microsoft365/calendar/e-cal-backend-m365-utils.c
@@ -0,0 +1,2811 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2020 Red Hat (www.redhat.com)
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "evolution-ews-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <libecal/libecal.h>
+
+#include "common/e-m365-connection.h"
+#include "common/e-m365-tz-utils.h"
+#include "e-cal-backend-m365-utils.h"
+
+#define EC_ERROR_EX(_code, _msg) e_client_error_create (_code, _msg)
+#define ECC_ERROR_EX(_code, _msg) e_cal_client_error_create (_code, _msg)
+
+static void
+ecb_m365_get_uid (EM365Connection *cnc,
+                 const gchar *group_id,
+                 const gchar *folder_id,
+                 const gchar *attachments_dir,
+                 ETimezoneCache *timezone_cache,
+                 JsonObject *m365_object,
+                 ICalComponent *inout_comp,
+                 ICalPropertyKind prop_kind)
+{
+       const gchar *id;
+
+       switch (i_cal_component_isa (inout_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               id = e_m365_event_get_id (m365_object);
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               id = e_m365_task_get_id (m365_object);
+               break;
+       default:
+               g_warn_if_reached ();
+               return;
+       }
+
+       i_cal_component_set_uid (inout_comp, id);
+}
+
+static void
+ecb_m365_get_date_time (EM365Connection *cnc,
+                       const gchar *group_id,
+                       const gchar *folder_id,
+                       const gchar *attachments_dir,
+                       ETimezoneCache *timezone_cache,
+                       JsonObject *m365_object,
+                       ICalComponent *inout_comp,
+                       ICalPropertyKind prop_kind)
+{
+       time_t tt = (time_t) 0;
+
+       if (prop_kind == I_CAL_CREATED_PROPERTY) {
+               switch (i_cal_component_isa (inout_comp)) {
+               case I_CAL_VEVENT_COMPONENT:
+                       tt = e_m365_event_get_created_date_time (m365_object);
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       tt = e_m365_task_get_created_date_time (m365_object);
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return;
+               }
+       } else if (prop_kind == I_CAL_LASTMODIFIED_PROPERTY) {
+               switch (i_cal_component_isa (inout_comp)) {
+               case I_CAL_VEVENT_COMPONENT:
+                       tt = e_m365_event_get_last_modified_date_time (m365_object);
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       tt = e_m365_task_get_last_modified_date_time (m365_object);
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return;
+               }
+       } else {
+               g_warn_if_reached ();
+       }
+
+       if (tt > (time_t) 0) {
+               ICalProperty *prop;
+               ICalTime *itt;
+
+               itt = i_cal_time_new_from_timet_with_zone (tt, FALSE, i_cal_timezone_get_utc_timezone ());
+
+               if (prop_kind == I_CAL_CREATED_PROPERTY)
+                       prop = i_cal_property_new_created (itt);
+               else /* I_CAL_LASTMODIFIED_PROPERTY */
+                       prop = i_cal_property_new_lastmodified (itt);
+
+               i_cal_component_take_property (inout_comp, prop);
+
+               g_clear_object (&itt);
+       }
+}
+
+static void
+ecb_m365_get_date_time_zone (EM365Connection *cnc,
+                            const gchar *group_id,
+                            const gchar *folder_id,
+                            const gchar *attachments_dir,
+                            ETimezoneCache *timezone_cache,
+                            JsonObject *m365_object,
+                            ICalComponent *inout_comp,
+                            ICalPropertyKind prop_kind)
+{
+       EM365DateTimeWithZone *value;
+       ICalTimezone *tz;
+       ICalTime *itt;
+       time_t tt;
+       const gchar *tzid, *zone;
+       gboolean is_date;
+
+       if (prop_kind == I_CAL_DTSTART_PROPERTY) {
+               switch (i_cal_component_isa (inout_comp)) {
+               case I_CAL_VEVENT_COMPONENT:
+                       value = e_m365_event_get_start (m365_object);
+                       tzid = e_m365_event_get_original_start_timezone (m365_object);
+                       is_date = e_m365_event_get_is_all_day (m365_object);
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       value = e_m365_task_get_start_date_time (m365_object);
+                       tzid = "UTC";
+                       is_date = TRUE;
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return;
+               }
+       } else if (prop_kind == I_CAL_DTEND_PROPERTY) {
+               value = e_m365_event_get_end (m365_object);
+               tzid = e_m365_event_get_original_end_timezone (m365_object);
+               is_date = e_m365_event_get_is_all_day (m365_object);
+       } else if (prop_kind == I_CAL_COMPLETED_PROPERTY) {
+               value = e_m365_task_get_completed_date_time (m365_object);
+               tzid = "UTC";
+               is_date = TRUE;
+       } else if (prop_kind == I_CAL_DUE_PROPERTY) {
+               value = e_m365_task_get_due_date_time (m365_object);
+               tzid = "UTC";
+               is_date = TRUE;
+       } else {
+               g_warn_if_reached ();
+               return;
+       }
+
+       if (!value)
+               return;
+
+       tt = e_m365_date_time_get_date_time (value);
+       zone = e_m365_date_time_get_time_zone (value);
+
+       if (zone && *zone)
+               zone = e_m365_tz_utils_get_ical_equivalent (zone);
+
+       tz = zone && *zone ? e_timezone_cache_get_timezone (timezone_cache, zone) : NULL;
+
+       if (!tz)
+               tz = i_cal_timezone_get_utc_timezone ();
+
+       itt = i_cal_time_new_from_timet_with_zone (tt, is_date, tz);
+
+       tzid = e_m365_tz_utils_get_ical_equivalent (tzid);
+
+       if (!tzid)
+               tzid = "UTC";
+
+       tz = e_timezone_cache_get_timezone (timezone_cache, tzid);
+
+       if (tz && !is_date)
+               i_cal_time_convert_to_zone_inplace (itt, tz);
+
+       if (prop_kind == I_CAL_DTSTART_PROPERTY)
+               i_cal_component_set_dtstart (inout_comp, itt);
+       else if (prop_kind == I_CAL_DTEND_PROPERTY)
+               i_cal_component_set_dtend (inout_comp, itt);
+       else if (prop_kind == I_CAL_COMPLETED_PROPERTY)
+               i_cal_component_take_property (inout_comp, i_cal_property_new_completed (itt));
+       else /* if (prop_kind == I_CAL_DUE_PROPERTY) */
+               i_cal_component_set_due (inout_comp, itt);
+
+       g_clear_object (&itt);
+}
+
+static void
+ecb_m365_add_date_time_zone (EM365Connection *cnc,
+                            const gchar *group_id,
+                            const gchar *folder_id,
+                            ETimezoneCache *timezone_cache,
+                            ICalComponent *new_comp,
+                            ICalComponent *old_comp,
+                            ICalPropertyKind prop_kind,
+                            JsonBuilder *builder)
+{
+       ICalProperty *new_prop;
+       ICalParameter *new_param;
+       ICalTime *old_value, *new_value;
+       const gchar *new_tzid = NULL;
+       void (* add_func) (JsonBuilder *builder, time_t date_time, const gchar *zone) = NULL;
+       gboolean same = FALSE;
+
+       if (prop_kind == I_CAL_DTSTART_PROPERTY) {
+               new_value = i_cal_component_get_dtstart (new_comp);
+               old_value = old_comp ? i_cal_component_get_dtstart (old_comp) : NULL;
+
+               switch (i_cal_component_isa (new_comp)) {
+               case I_CAL_VEVENT_COMPONENT:
+                       add_func = e_m365_event_add_start;
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       add_func = e_m365_task_add_start_date_time;
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return;
+               }
+       } else if (prop_kind == I_CAL_DTEND_PROPERTY) {
+               new_value = i_cal_component_get_dtend (new_comp);
+               old_value = old_comp ? i_cal_component_get_dtend (old_comp) : NULL;
+               add_func = e_m365_event_add_end;
+       } else if (prop_kind == I_CAL_COMPLETED_PROPERTY) {
+               ICalProperty *new_prop, *old_prop;
+
+               new_prop = i_cal_component_get_first_property (new_comp, prop_kind);
+               old_prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
+               new_value = new_prop ? i_cal_property_get_completed (new_prop) : NULL;
+               old_value = old_prop ? i_cal_property_get_completed (old_prop) : NULL;
+               add_func = e_m365_task_add_completed_date_time;
+
+               g_clear_object (&new_prop);
+               g_clear_object (&old_prop);
+       } else if (prop_kind == I_CAL_DUE_PROPERTY) {
+               new_value = i_cal_component_get_due (new_comp);
+               old_value = old_comp ? i_cal_component_get_due (old_comp) : NULL;
+               add_func = e_m365_task_add_due_date_time;
+       } else {
+               g_warn_if_reached ();
+               return;
+       }
+
+       if (!new_value && !old_value)
+               return;
+
+       new_prop = i_cal_component_get_first_property (new_comp, prop_kind);
+       new_param = new_prop ? i_cal_property_get_first_parameter (new_prop, I_CAL_TZID_PARAMETER) : NULL;
+
+       if (new_param)
+               new_tzid = i_cal_parameter_get_tzid (new_param);
+
+       if (new_value && old_value) {
+               same = i_cal_time_compare (new_value, old_value) == 0;
+
+               if (same) {
+                       ICalProperty *old_prop;
+                       ICalParameter *old_param;
+                       const gchar *old_tzid;
+
+                       old_prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
+                       old_param = old_prop ? i_cal_property_get_first_parameter (old_prop, 
I_CAL_TZID_PARAMETER) : NULL;
+                       old_tzid = old_param ? i_cal_parameter_get_tzid (old_param) : NULL;
+
+                       same = g_strcmp0 (old_tzid, new_tzid) == 0;
+
+                       g_clear_object (&old_param);
+                       g_clear_object (&old_prop);
+               }
+       }
+
+       if (!same) {
+               ICalTimezone *izone = NULL;
+               const gchar *wzone = NULL;
+               time_t tt;
+
+               if (new_tzid) {
+                       izone = e_timezone_cache_get_timezone (timezone_cache, new_tzid);
+
+                       if (izone)
+                               wzone = e_m365_tz_utils_get_msdn_equivalent (i_cal_timezone_get_location 
(izone));
+               }
+
+               tt = i_cal_time_as_timet_with_zone (new_value, wzone ? NULL : izone);
+
+               add_func (builder, tt, wzone);
+       }
+
+       g_clear_object (&new_prop);
+       g_clear_object (&new_param);
+       g_clear_object (&new_value);
+       g_clear_object (&old_value);
+}
+
+static void
+ecb_m365_get_categories (EM365Connection *cnc,
+                        const gchar *group_id,
+                        const gchar *folder_id,
+                        const gchar *attachments_dir,
+                        ETimezoneCache *timezone_cache,
+                        JsonObject *m365_object,
+                        ICalComponent *inout_comp,
+                        ICalPropertyKind prop_kind)
+{
+       JsonArray *categories;
+
+       switch (i_cal_component_isa (inout_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               categories = e_m365_event_get_categories (m365_object);
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               categories = e_m365_task_get_categories (m365_object);
+               break;
+       default:
+               g_warn_if_reached ();
+               return;
+       }
+
+       if (categories) {
+               GString *categories_str = NULL;
+               guint ii, len;
+
+               len = json_array_get_length (categories);
+
+               for (ii = 0; ii < len; ii++) {
+                       const gchar *category;
+
+                       category = json_array_get_string_element (categories, ii);
+
+                       if (category && *category) {
+                               gchar *ical_str = i_cal_value_encode_ical_string (category);
+
+                               if (ical_str && *ical_str) {
+                                       if (!categories_str) {
+                                               categories_str = g_string_new (ical_str);
+                                       } else {
+                                               g_string_append_c (categories_str, ',');
+                                               g_string_append (categories_str, ical_str);
+                                       }
+                               }
+
+                               g_free (ical_str);
+                       }
+               }
+
+               if (categories_str) {
+                       i_cal_component_take_property (inout_comp, i_cal_property_new_categories 
(categories_str->str));
+
+                       g_string_free (categories_str, TRUE);
+               }
+       }
+}
+
+static void
+ecb_m365_extract_categories (ICalComponent *comp,
+                            GHashTable **out_hash, /* gchar * ~> NULL */
+                            GSList **out_slist) /* gchar * */
+{
+       ICalProperty *prop;
+
+       if (!comp)
+               return;
+
+       for (prop = i_cal_component_get_first_property (comp, I_CAL_CATEGORIES_PROPERTY);
+            prop;
+            g_object_unref (prop), prop = i_cal_component_get_next_property (comp, 
I_CAL_CATEGORIES_PROPERTY)) {
+               const gchar *categories;
+
+               categories = i_cal_property_get_categories (prop);
+
+               if (categories && *categories) {
+                       if (out_hash && !*out_hash)
+                               *out_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+                       if (strchr (categories, ',')) {
+                               gchar **strv;
+                               guint ii;
+
+                               strv = g_strsplit (categories, ",", -1);
+
+                               for (ii = 0; strv[ii]; ii++) {
+                                       gchar *category = g_strchomp (strv[ii]);
+
+                                       if (*category) {
+                                               if (out_hash) {
+                                                       g_hash_table_insert (*out_hash, category, NULL);
+                                               } else if (out_slist) {
+                                                       *out_slist = g_slist_prepend (*out_slist, category);
+                                               } else {
+                                                       g_warn_if_reached ();
+                                                       g_free (category);
+                                               }
+                                       } else {
+                                               g_free (category);
+                                       }
+                               }
+
+                               g_free (strv);
+                       } else if (out_hash) {
+                               g_hash_table_insert (*out_hash, g_strchomp (g_strdup (categories)), NULL);
+                       } else if (out_slist) {
+                               *out_slist = g_slist_prepend (*out_slist, g_strchomp (g_strdup (categories)));
+                       } else {
+                               g_warn_if_reached ();
+                       }
+               }
+       }
+
+       g_clear_object (&prop);
+
+       if (out_slist && *out_slist)
+               *out_slist = g_slist_reverse (*out_slist);
+}
+
+static void
+ecb_m365_add_categories (EM365Connection *cnc,
+                        const gchar *group_id,
+                        const gchar *folder_id,
+                        ETimezoneCache *timezone_cache,
+                        ICalComponent *new_comp,
+                        ICalComponent *old_comp,
+                        ICalPropertyKind prop_kind,
+                        JsonBuilder *builder)
+{
+       GHashTable *old_value = NULL;
+       GSList *new_value = NULL;
+       void (* begin_categories_func) (JsonBuilder *builder);
+       void (* end_categories_func) (JsonBuilder *builder);
+       void (* add_category_func) (JsonBuilder *builder, const gchar *category);
+
+       switch (i_cal_component_isa (new_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               begin_categories_func = e_m365_event_begin_categories;
+               end_categories_func = e_m365_event_end_categories;
+               add_category_func = e_m365_event_add_category;
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               begin_categories_func = e_m365_task_begin_categories;
+               end_categories_func = e_m365_task_end_categories;
+               add_category_func = e_m365_task_add_category;
+               break;
+       default:
+               g_warn_if_reached ();
+               return;
+       }
+
+       ecb_m365_extract_categories (new_comp, NULL, &new_value);
+       ecb_m365_extract_categories (old_comp, &old_value, NULL);
+
+       if (!new_value && !old_value)
+               return;
+
+       if (new_value) {
+               GSList *link;
+               gboolean same = FALSE;
+
+               if (old_value && g_hash_table_size (old_value) == g_slist_length (new_value)) {
+                       same = TRUE;
+
+                       for (link = new_value; link && same; link = g_slist_next (link)) {
+                               const gchar *category = link->data;
+
+                               same = g_hash_table_contains (old_value, category);
+                       }
+               }
+
+               if (!same) {
+                       begin_categories_func (builder);
+
+                       for (link = new_value; link; link = g_slist_next (link)) {
+                               const gchar *category = link->data;
+
+                               add_category_func (builder, category);
+                       }
+
+                       end_categories_func (builder);
+               }
+       } else {
+               begin_categories_func (builder);
+               end_categories_func (builder);
+       }
+
+       if (new_value)
+               g_slist_free_full (new_value, g_free);
+       if (old_value)
+               g_hash_table_destroy (old_value);
+}
+
+static void
+ecb_m365_get_subject (EM365Connection *cnc,
+                     const gchar *group_id,
+                     const gchar *folder_id,
+                     const gchar *attachments_dir,
+                     ETimezoneCache *timezone_cache,
+                     EM365Event *m365_object,
+                     ICalComponent *inout_comp,
+                     ICalPropertyKind prop_kind)
+{
+       const gchar *subject;
+
+       switch (i_cal_component_isa (inout_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               subject = e_m365_event_get_subject (m365_object);
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               subject = e_m365_task_get_subject (m365_object);
+               break;
+       default:
+               g_warn_if_reached ();
+               return;
+       }
+
+       if (subject)
+               i_cal_component_set_summary (inout_comp, subject);
+}
+
+static void
+ecb_m365_add_subject (EM365Connection *cnc,
+                     const gchar *group_id,
+                     const gchar *folder_id,
+                     ETimezoneCache *timezone_cache,
+                     ICalComponent *new_comp,
+                     ICalComponent *old_comp,
+                     ICalPropertyKind prop_kind,
+                     JsonBuilder *builder)
+{
+       const gchar *new_value, *old_value;
+
+       new_value = i_cal_component_get_summary (new_comp);
+       old_value = old_comp ? i_cal_component_get_summary (old_comp) : NULL;
+
+       if (g_strcmp0 (new_value, old_value) != 0) {
+               switch (i_cal_component_isa (new_comp)) {
+               case I_CAL_VEVENT_COMPONENT:
+                       e_m365_event_add_subject (builder, new_value ? new_value : "");
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       e_m365_task_add_subject (builder, new_value ? new_value : "");
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return;
+               }
+       }
+}
+
+static void
+ecb_m365_get_body (EM365Connection *cnc,
+                  const gchar *group_id,
+                  const gchar *folder_id,
+                  const gchar *attachments_dir,
+                  ETimezoneCache *timezone_cache,
+                  JsonObject *m365_object,
+                  ICalComponent *inout_comp,
+                  ICalPropertyKind prop_kind)
+{
+       EM365ItemBody *value;
+       const gchar *content;
+
+       switch (i_cal_component_isa (inout_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               value = e_m365_event_get_body (m365_object);
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               value = e_m365_task_get_body (m365_object);
+               break;
+       default:
+               g_warn_if_reached ();
+               return;
+       }
+
+       content = value ? e_m365_item_body_get_content (value) : NULL;
+
+       if (content && *content && strcmp (content, "\r\n") != 0)
+               i_cal_component_set_description (inout_comp, content);
+}
+
+static void
+ecb_m365_add_body (EM365Connection *cnc,
+                  const gchar *group_id,
+                  const gchar *folder_id,
+                  ETimezoneCache *timezone_cache,
+                  ICalComponent *new_comp,
+                  ICalComponent *old_comp,
+                  ICalPropertyKind prop_kind,
+                  JsonBuilder *builder)
+{
+       const gchar *new_value, *old_value;
+
+       new_value = i_cal_component_get_description (new_comp);
+       old_value = old_comp ? i_cal_component_get_description (old_comp) : NULL;
+
+       if (g_strcmp0 (new_value, old_value) != 0) {
+               switch (i_cal_component_isa (new_comp)) {
+               case I_CAL_VEVENT_COMPONENT:
+                       e_m365_event_add_body (builder, E_M365_ITEM_BODY_CONTENT_TYPE_TEXT, new_value);
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       e_m365_task_add_body (builder, E_M365_ITEM_BODY_CONTENT_TYPE_TEXT, new_value);
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return;
+               }
+       }
+}
+
+static void
+ecb_m365_get_sensitivity (EM365Connection *cnc,
+                         const gchar *group_id,
+                         const gchar *folder_id,
+                         const gchar *attachments_dir,
+                         ETimezoneCache *timezone_cache,
+                         JsonObject *m365_object,
+                         ICalComponent *inout_comp,
+                         ICalPropertyKind prop_kind)
+{
+       EM365SensitivityType value;
+       ICalProperty_Class cls = I_CAL_CLASS_NONE;
+
+       switch (i_cal_component_isa (inout_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               value = e_m365_event_get_sensitivity (m365_object);
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               value = e_m365_task_get_sensitivity (m365_object);
+               break;
+       default:
+               g_warn_if_reached ();
+               return;
+       }
+
+       if (value == E_M365_SENSITIVITY_NORMAL)
+               cls = I_CAL_CLASS_PUBLIC;
+       else if (value == E_M365_SENSITIVITY_PERSONAL || value == E_M365_SENSITIVITY_PRIVATE)
+               cls = I_CAL_CLASS_PRIVATE;
+       else if (value == E_M365_SENSITIVITY_CONFIDENTIAL)
+               cls = I_CAL_CLASS_CONFIDENTIAL;
+
+       if (cls != I_CAL_CLASS_NONE)
+               i_cal_component_take_property (inout_comp, i_cal_property_new_class (cls));
+}
+
+static void
+ecb_m365_add_sensitivity (EM365Connection *cnc,
+                         const gchar *group_id,
+                         const gchar *folder_id,
+                         ETimezoneCache *timezone_cache,
+                         ICalComponent *new_comp,
+                         ICalComponent *old_comp,
+                         ICalPropertyKind prop_kind,
+                         JsonBuilder *builder)
+{
+       ICalProperty_Class new_value = I_CAL_CLASS_NONE, old_value = I_CAL_CLASS_NONE;
+       ICalProperty *prop;
+
+       prop = i_cal_component_get_first_property (new_comp, prop_kind);
+
+       if (prop) {
+               new_value = i_cal_property_get_class (prop);
+               g_clear_object (&prop);
+       }
+
+       prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
+
+       if (prop) {
+               old_value = i_cal_property_get_class (prop);
+               g_clear_object (&prop);
+       }
+
+       if (new_value != old_value) {
+               EM365SensitivityType value = E_M365_SENSITIVITY_NOT_SET;
+
+               if (new_value == I_CAL_CLASS_PUBLIC)
+                       value = E_M365_SENSITIVITY_NORMAL;
+               else if (new_value == I_CAL_CLASS_PRIVATE)
+                       value = E_M365_SENSITIVITY_PRIVATE;
+               else if (new_value == I_CAL_CLASS_CONFIDENTIAL)
+                       value = E_M365_SENSITIVITY_CONFIDENTIAL;
+
+               switch (i_cal_component_isa (new_comp)) {
+               case I_CAL_VEVENT_COMPONENT:
+                       e_m365_event_add_sensitivity (builder, value);
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       e_m365_task_add_sensitivity (builder, value);
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       return;
+               }
+       }
+}
+
+static void
+ecb_m365_get_show_as (EM365Connection *cnc,
+                     const gchar *group_id,
+                     const gchar *folder_id,
+                     const gchar *attachments_dir,
+                     ETimezoneCache *timezone_cache,
+                     EM365Event *m365_event,
+                     ICalComponent *inout_comp,
+                     ICalPropertyKind prop_kind)
+{
+       EM365FreeBusyStatusType value;
+       ICalPropertyTransp transp = I_CAL_TRANSP_NONE;
+
+       value = e_m365_event_get_show_as (m365_event);
+
+       if (value == E_M365_FREE_BUSY_STATUS_FREE)
+               transp = I_CAL_TRANSP_TRANSPARENT;
+       else if (value == E_M365_FREE_BUSY_STATUS_BUSY)
+               transp = I_CAL_TRANSP_OPAQUE;
+
+       if (transp != I_CAL_TRANSP_NONE)
+               i_cal_component_take_property (inout_comp, i_cal_property_new_transp (transp));
+}
+
+static void
+ecb_m365_add_show_as (EM365Connection *cnc,
+                     const gchar *group_id,
+                     const gchar *folder_id,
+                     ETimezoneCache *timezone_cache,
+                     ICalComponent *new_comp,
+                     ICalComponent *old_comp,
+                     ICalPropertyKind prop_kind,
+                     JsonBuilder *builder)
+{
+       ICalPropertyTransp new_value = I_CAL_TRANSP_NONE, old_value = I_CAL_TRANSP_NONE;
+       ICalProperty *prop;
+
+       prop = i_cal_component_get_first_property (new_comp, prop_kind);
+
+       if (prop) {
+               new_value = i_cal_property_get_transp (prop);
+               g_clear_object (&prop);
+       }
+
+       prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
+
+       if (prop) {
+               old_value = i_cal_property_get_transp (prop);
+               g_clear_object (&prop);
+       }
+
+       if (new_value != old_value) {
+               EM365FreeBusyStatusType value = E_M365_FREE_BUSY_STATUS_NOT_SET;
+
+               if (new_value == I_CAL_TRANSP_TRANSPARENT)
+                       value = E_M365_FREE_BUSY_STATUS_FREE;
+               else if (new_value == I_CAL_TRANSP_OPAQUE)
+                       value = E_M365_FREE_BUSY_STATUS_BUSY;
+
+               e_m365_event_add_show_as (builder, value);
+       }
+}
+
+static void
+ecb_m365_get_location (EM365Connection *cnc,
+                      const gchar *group_id,
+                      const gchar *folder_id,
+                      const gchar *attachments_dir,
+                      ETimezoneCache *timezone_cache,
+                      EM365Event *m365_event,
+                      ICalComponent *inout_comp,
+                      ICalPropertyKind prop_kind)
+{
+       EM365Location *value;
+       const gchar *tmp;
+
+       value = e_m365_event_get_location (m365_event);
+
+       if (!value)
+               return;
+
+       tmp = e_m365_location_get_display_name (value);
+
+       if (tmp && *tmp)
+               i_cal_component_set_location (inout_comp, tmp);
+}
+
+static void
+ecb_m365_add_location (EM365Connection *cnc,
+                      const gchar *group_id,
+                      const gchar *folder_id,
+                      ETimezoneCache *timezone_cache,
+                      ICalComponent *new_comp,
+                      ICalComponent *old_comp,
+                      ICalPropertyKind prop_kind,
+                      JsonBuilder *builder)
+{
+       const gchar *new_value, *old_value;
+
+       new_value = i_cal_component_get_location (new_comp);
+       old_value = old_comp ? i_cal_component_get_location (old_comp) : NULL;
+
+       if (g_strcmp0 (new_value, old_value) != 0) {
+               if (new_value && *new_value) {
+                       e_m365_event_begin_location (builder);
+                       e_m365_location_add_display_name (builder, new_value);
+                       e_m365_event_end_location (builder);
+               } else {
+                       e_m365_event_add_null_location (builder);
+               }
+       }
+}
+
+static void
+ecb_m365_get_organizer (EM365Connection *cnc,
+                       const gchar *group_id,
+                       const gchar *folder_id,
+                       const gchar *attachments_dir,
+                       ETimezoneCache *timezone_cache,
+                       EM365Event *m365_event,
+                       ICalComponent *inout_comp,
+                       ICalPropertyKind prop_kind)
+{
+       EM365Recipient *value;
+       JsonArray *attendees;
+       const gchar *name;
+       const gchar *address;
+
+       value = e_m365_event_get_organizer (m365_event);
+
+       if (!value)
+               return;
+
+       /* Include the organizer only if there is at least one attendee */
+       attendees = e_m365_event_get_attendees (m365_event);
+
+       if (!attendees || !json_array_get_length (attendees))
+               return;
+
+       name = e_m365_recipient_get_name (value);
+       address = e_m365_recipient_get_address (value);
+
+       if (address && *address) {
+               ECalComponentOrganizer *organizer;
+               gchar *mailto_addr;
+
+               mailto_addr = g_strconcat ("mailto:";, address, NULL);
+               organizer = e_cal_component_organizer_new ();
+               e_cal_component_organizer_set_value (organizer, mailto_addr);
+               g_free (mailto_addr);
+
+               if (name && *name)
+                       e_cal_component_organizer_set_cn (organizer, name);
+
+               i_cal_component_take_property (inout_comp, e_cal_component_organizer_get_as_property 
(organizer));
+               e_cal_component_organizer_free (organizer);
+       }
+}
+
+static const gchar *
+ecb_m365_strip_mailto (const gchar *value)
+{
+       if (value && g_ascii_strncasecmp (value, "mailto:";, 7) == 0)
+               return value + 7;
+
+       return value;
+}
+
+static void
+ecb_m365_add_organizer (EM365Connection *cnc,
+                       const gchar *group_id,
+                       const gchar *folder_id,
+                       ETimezoneCache *timezone_cache,
+                       ICalComponent *new_comp,
+                       ICalComponent *old_comp,
+                       ICalPropertyKind prop_kind,
+                       JsonBuilder *builder)
+{
+       ECalComponentOrganizer *new_value = NULL, *old_value = NULL;
+       ICalProperty *prop;
+
+       prop = i_cal_component_get_first_property (new_comp, prop_kind);
+
+       if (prop) {
+               new_value = e_cal_component_organizer_new_from_property (prop);
+               g_clear_object (&prop);
+       }
+
+       prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
+
+       if (prop) {
+               old_value = e_cal_component_organizer_new_from_property (prop);
+               g_clear_object (&prop);
+       }
+
+       if (new_value != old_value && (
+           g_strcmp0 (new_value ? e_cal_component_organizer_get_cn (new_value) : NULL,
+                      old_value ? e_cal_component_organizer_get_cn (old_value) : NULL) != 0 ||
+           g_strcmp0 (new_value ? ecb_m365_strip_mailto (e_cal_component_organizer_get_value (new_value)) : 
NULL,
+                      old_value ? ecb_m365_strip_mailto (e_cal_component_organizer_get_value (old_value)) : 
NULL) != 0)) {
+               if (new_value) {
+                       e_m365_event_add_organizer (builder,
+                                                   e_cal_component_organizer_get_cn (new_value),
+                                                   ecb_m365_strip_mailto 
(e_cal_component_organizer_get_value (new_value)));
+               } else {
+                       e_m365_event_add_null_organizer (builder);
+               }
+       }
+
+       e_cal_component_organizer_free (new_value);
+       e_cal_component_organizer_free (old_value);
+}
+
+static void
+ecb_m365_get_attendees (EM365Connection *cnc,
+                       const gchar *group_id,
+                       const gchar *folder_id,
+                       const gchar *attachments_dir,
+                       ETimezoneCache *timezone_cache,
+                       EM365Event *m365_event,
+                       ICalComponent *inout_comp,
+                       ICalPropertyKind prop_kind)
+{
+       JsonArray *array;
+       guint ii, sz;
+
+       array = e_m365_event_get_attendees (m365_event);
+
+       if (!array)
+               return;
+
+       sz = json_array_get_length (array);
+
+       for (ii = 0; ii < sz; ii++) {
+               EM365Attendee *m365_attendee;
+               EM365ResponseStatus *m365_status;
+               EM365AttendeeType m365_att_type;
+               EM365EmailAddress *m365_address;
+               ECalComponentAttendee *e_attendee;
+               ICalParameterRole role = I_CAL_ROLE_NONE;
+               gchar *mailto_addr;
+
+               m365_attendee = json_array_get_object_element (array, ii);
+
+               if (!m365_attendee)
+                       continue;
+
+               m365_address = e_m365_attendee_get_email_address (m365_attendee);
+
+               if (!m365_address || !e_m365_email_address_get_address (m365_address))
+                       continue;
+
+               e_attendee = e_cal_component_attendee_new ();
+
+               mailto_addr = g_strconcat ("mailto:";, e_m365_email_address_get_address (m365_address), NULL);
+               e_cal_component_attendee_set_value (e_attendee, mailto_addr);
+               g_free (mailto_addr);
+
+               if (e_m365_email_address_get_name (m365_address))
+                       e_cal_component_attendee_set_cn (e_attendee, e_m365_email_address_get_name 
(m365_address));
+
+               m365_status = e_m365_attendee_get_status (m365_attendee);
+
+               if (m365_status) {
+                       EM365ResponseType m365_response;
+                       ICalParameterPartstat partstat = I_CAL_PARTSTAT_NONE;
+
+                       m365_response = e_m365_response_status_get_response (m365_status);
+
+                       if (m365_response == E_M365_RESPONSE_TENTATIVELY_ACCEPTED)
+                               partstat = I_CAL_PARTSTAT_TENTATIVE;
+                       else if (m365_response == E_M365_RESPONSE_ACCEPTED)
+                               partstat = I_CAL_PARTSTAT_ACCEPTED;
+                       else if (m365_response == E_M365_RESPONSE_DECLINED)
+                               partstat = I_CAL_PARTSTAT_DECLINED;
+                       else if (m365_response == E_M365_RESPONSE_NOT_RESPONDED)
+                               partstat = I_CAL_PARTSTAT_NEEDSACTION;
+
+                       if (partstat != I_CAL_PARTSTAT_NONE) {
+                               time_t tt;
+
+                               e_cal_component_attendee_set_partstat (e_attendee, partstat);
+
+                               tt = e_m365_response_status_get_time (m365_status);
+
+                               if (tt > (time_t) 0) {
+                                       ECalComponentParameterBag *params;
+                                       ICalParameter *param;
+                                       gchar *tmp;
+
+                                       tmp = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) tt);
+                                       params = e_cal_component_attendee_get_parameter_bag (e_attendee);
+
+                                       param = i_cal_parameter_new_x (tmp);
+                                       i_cal_parameter_set_xname (param, "X-M365-STATUS-TIME");
+
+                                       e_cal_component_parameter_bag_take (params, param);
+
+                                       g_free (tmp);
+                               }
+                       }
+               }
+
+               m365_att_type = e_m365_attendee_get_type (m365_attendee);
+
+               if (m365_att_type == E_M365_ATTENDEE_REQUIRED) {
+                       role = I_CAL_ROLE_REQPARTICIPANT;
+                       e_cal_component_attendee_set_cutype (e_attendee, I_CAL_CUTYPE_INDIVIDUAL);
+               } else if (m365_att_type == E_M365_ATTENDEE_OPTIONAL) {
+                       role = I_CAL_ROLE_OPTPARTICIPANT;
+                       e_cal_component_attendee_set_cutype (e_attendee, I_CAL_CUTYPE_INDIVIDUAL);
+               } else if (m365_att_type == E_M365_ATTENDEE_RESOURCE) {
+                       e_cal_component_attendee_set_cutype (e_attendee, I_CAL_CUTYPE_RESOURCE);
+               }
+
+               if (role != I_CAL_ROLE_NONE)
+                       e_cal_component_attendee_set_role (e_attendee, role);
+
+               i_cal_component_take_property (inout_comp, e_cal_component_attendee_get_as_property 
(e_attendee));
+
+               e_cal_component_attendee_free (e_attendee);
+       }
+}
+
+static void
+ecb_m365_extract_attendees (ICalComponent *comp,
+                           GHashTable **out_hash, /* const gchar *ECalComponentAttendee::value ~> 
ECalComponentAttendee * */
+                           GSList **out_slist) /* ECalComponentAttendee * */
+{
+       ICalProperty *prop;
+
+       if (!comp)
+               return;
+
+       for (prop = i_cal_component_get_first_property (comp, I_CAL_ATTENDEE_PROPERTY);
+            prop;
+            g_object_unref (prop), prop = i_cal_component_get_next_property (comp, I_CAL_ATTENDEE_PROPERTY)) 
{
+               ECalComponentAttendee *attendee;
+
+               attendee = e_cal_component_attendee_new_from_property (prop);
+
+               if (attendee && e_cal_component_attendee_get_value (attendee)) {
+                       if (out_hash) {
+                               if (!*out_hash)
+                                       *out_hash = g_hash_table_new_full (camel_strcase_hash, 
camel_strcase_equal, NULL, e_cal_component_attendee_free);
+
+                               g_hash_table_insert (*out_hash, (gpointer) e_cal_component_attendee_get_value 
(attendee), attendee);
+                       } else if (out_slist) {
+                               *out_slist = g_slist_prepend (*out_slist, attendee);
+                       } else {
+                               g_warn_if_reached ();
+                               e_cal_component_attendee_free (attendee);
+                       }
+               } else {
+                       e_cal_component_attendee_free (attendee);
+               }
+       }
+
+       g_clear_object (&prop);
+
+       if (out_slist && *out_slist)
+               *out_slist = g_slist_reverse (*out_slist);
+}
+
+static void
+ecb_m365_add_attendees (EM365Connection *cnc,
+                       const gchar *group_id,
+                       const gchar *folder_id,
+                       ETimezoneCache *timezone_cache,
+                       ICalComponent *new_comp,
+                       ICalComponent *old_comp,
+                       ICalPropertyKind prop_kind,
+                       JsonBuilder *builder)
+{
+       GHashTable *old_value = NULL;
+       GSList *new_value = NULL;
+
+       ecb_m365_extract_attendees (new_comp, NULL, &new_value);
+       ecb_m365_extract_attendees (old_comp, &old_value, NULL);
+
+       if (!new_value && !old_value)
+               return;
+
+       if (new_value) {
+               GSList *link;
+               gboolean same = FALSE;
+
+               if (old_value && g_hash_table_size (old_value) == g_slist_length (new_value)) {
+                       same = TRUE;
+
+                       for (link = new_value; link && same; link = g_slist_next (link)) {
+                               ECalComponentAttendee *new_att = link->data, *old_att;
+
+                               old_att = g_hash_table_lookup (old_value, e_cal_component_attendee_get_value 
(new_att));
+
+                               same = old_att && e_cal_component_attendee_get_value (old_att) && 
e_cal_component_attendee_get_value (new_att) &&
+                                       g_ascii_strcasecmp (e_cal_component_attendee_get_value (old_att), 
e_cal_component_attendee_get_value (new_att)) == 0 &&
+                                       g_strcmp0 (e_cal_component_attendee_get_cn (old_att), 
e_cal_component_attendee_get_cn (new_att)) == 0 &&
+                                       e_cal_component_attendee_get_partstat (old_att) == 
e_cal_component_attendee_get_partstat (new_att) &&
+                                       e_cal_component_attendee_get_cutype (old_att) == 
e_cal_component_attendee_get_cutype (new_att) &&
+                                       e_cal_component_attendee_get_role (old_att) == 
e_cal_component_attendee_get_role (new_att);
+                       }
+               }
+
+               if (!same) {
+                       e_m365_event_begin_attendees (builder);
+
+                       for (link = new_value; link; link = g_slist_next (link)) {
+                               ECalComponentAttendee *attendee = link->data;
+                               EM365AttendeeType att_type;
+                               EM365ResponseType response = E_M365_RESPONSE_NONE;
+                               time_t response_time = (time_t) 0;
+                               ICalParameterPartstat partstat;
+                               const gchar *address;
+
+                               address = ecb_m365_strip_mailto (e_cal_component_attendee_get_value 
(attendee));
+
+                               if (e_cal_component_attendee_get_cutype (attendee) == I_CAL_CUTYPE_RESOURCE)
+                                       att_type = E_M365_ATTENDEE_RESOURCE;
+                               else if (e_cal_component_attendee_get_role (attendee) == 
I_CAL_ROLE_REQPARTICIPANT ||
+                                        e_cal_component_attendee_get_role (attendee) == I_CAL_ROLE_CHAIR)
+                                       att_type = E_M365_ATTENDEE_REQUIRED;
+                               else if (e_cal_component_attendee_get_role (attendee) == 
I_CAL_ROLE_OPTPARTICIPANT)
+                                       att_type = E_M365_ATTENDEE_OPTIONAL;
+                               else /* Fallback */
+                                       att_type = E_M365_ATTENDEE_REQUIRED;
+
+                               partstat = e_cal_component_attendee_get_partstat (attendee);
+
+                               if (partstat == I_CAL_PARTSTAT_TENTATIVE)
+                                       response = E_M365_RESPONSE_TENTATIVELY_ACCEPTED;
+                               else if (partstat == I_CAL_PARTSTAT_ACCEPTED)
+                                       response = E_M365_RESPONSE_ACCEPTED;
+                               else if (partstat == I_CAL_PARTSTAT_DECLINED)
+                                       response = E_M365_RESPONSE_DECLINED;
+                               else if (partstat == I_CAL_PARTSTAT_NEEDSACTION)
+                                       response = E_M365_RESPONSE_NOT_RESPONDED;
+
+                               if (response != E_M365_RESPONSE_NONE) {
+                                       ECalComponentParameterBag *params;
+                                       guint ii, sz;
+
+                                       params = e_cal_component_attendee_get_parameter_bag (attendee);
+                                       sz = e_cal_component_parameter_bag_get_count (params);
+
+                                       for (ii = 0; ii < sz; ii++) {
+                                               ICalParameter *param;
+
+                                               param = e_cal_component_parameter_bag_get (params, ii);
+
+                                               if (param && i_cal_parameter_isa (param) == I_CAL_X_PARAMETER 
&&
+                                                   i_cal_parameter_get_xname (param) &&
+                                                   g_ascii_strcasecmp (i_cal_parameter_get_xname (param), 
"X-M365-STATUS-TIME") == 0) {
+                                                       const gchar *xvalue;
+
+                                                       xvalue = i_cal_parameter_get_xvalue (param);
+
+                                                       if (xvalue && *xvalue) {
+                                                               gint64 value;
+
+                                                               value = g_ascii_strtoll (xvalue, NULL, 10);
+
+                                                               if (value)
+                                                                       response_time = (time_t) value;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               e_m365_event_add_attendee (builder, att_type, response, response_time, 
e_cal_component_attendee_get_cn (attendee), address);
+                       }
+
+                       e_m365_event_end_attendees (builder);
+               }
+       } else {
+               e_m365_event_add_null_attendees (builder);
+       }
+
+       if (new_value)
+               g_slist_free_full (new_value, e_cal_component_attendee_free);
+       if (old_value)
+               g_hash_table_destroy (old_value);
+}
+
+static void
+ecb_m365_get_importance (EM365Connection *cnc,
+                        const gchar *group_id,
+                        const gchar *folder_id,
+                        const gchar *attachments_dir,
+                        ETimezoneCache *timezone_cache,
+                        EM365Event *m365_event,
+                        ICalComponent *inout_comp,
+                        ICalPropertyKind prop_kind)
+{
+       EM365ImportanceType value;
+       ICalProperty *prop = NULL;
+
+       value = e_m365_event_get_importance (m365_event);
+
+       if (value == E_M365_IMPORTANCE_LOW)
+               prop = i_cal_property_new_priority (9);
+       else if (value == E_M365_IMPORTANCE_NORMAL)
+               prop = i_cal_property_new_priority (5);
+       else if (value == E_M365_IMPORTANCE_HIGH)
+               prop = i_cal_property_new_priority (1);
+
+       if (prop)
+               i_cal_component_take_property (inout_comp, prop);
+}
+
+static void
+ecb_m365_add_importance (EM365Connection *cnc,
+                        const gchar *group_id,
+                        const gchar *folder_id,
+                        ETimezoneCache *timezone_cache,
+                        ICalComponent *new_comp,
+                        ICalComponent *old_comp,
+                        ICalPropertyKind prop_kind,
+                        JsonBuilder *builder)
+{
+       gint old_value = -1, new_value = -1;
+       ICalProperty *prop;
+
+       prop = i_cal_component_get_first_property (new_comp, prop_kind);
+
+       if (prop) {
+               new_value = i_cal_property_get_priority (prop);
+               g_clear_object (&prop);
+       }
+
+       prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
+
+       if (prop) {
+               old_value = i_cal_property_get_priority (prop);
+               g_clear_object (&prop);
+       }
+
+       if (new_value != old_value) {
+               EM365ImportanceType value = E_M365_IMPORTANCE_NOT_SET;
+
+               if (new_value >= 1 && new_value <= 4) {
+                       value = E_M365_IMPORTANCE_HIGH;
+               } else if (new_value == 5) {
+                       value = E_M365_IMPORTANCE_NORMAL;
+               } else if (new_value >= 6 && new_value <= 9) {
+                       value = E_M365_IMPORTANCE_LOW;
+               }
+
+               e_m365_event_add_importance (builder, value);
+       }
+}
+
+static void
+ecb_m365_get_event_status (EM365Connection *cnc,
+                          const gchar *group_id,
+                          const gchar *folder_id,
+                          const gchar *attachments_dir,
+                          ETimezoneCache *timezone_cache,
+                          EM365Event *m365_event,
+                          ICalComponent *inout_comp,
+                          ICalPropertyKind prop_kind)
+{
+       ICalPropertyStatus status = I_CAL_STATUS_NONE;
+
+       if (e_m365_event_get_is_cancelled (m365_event)) {
+               status = I_CAL_STATUS_CANCELLED;
+       } else {
+               EM365ResponseStatus *response_status;
+
+               response_status = e_m365_event_get_response_status (m365_event);
+
+               if (response_status) {
+                       EM365ResponseType response;
+
+                       response = e_m365_response_status_get_response (response_status);
+
+                       if (response == E_M365_RESPONSE_TENTATIVELY_ACCEPTED)
+                               status = I_CAL_STATUS_TENTATIVE;
+                       else if (response == E_M365_RESPONSE_ACCEPTED)
+                               status = I_CAL_STATUS_CONFIRMED;
+                       else if (response == E_M365_RESPONSE_DECLINED)
+                               status = I_CAL_STATUS_CANCELLED;
+                       else if (response == E_M365_RESPONSE_NOT_RESPONDED)
+                               status = I_CAL_STATUS_NEEDSACTION;
+               }
+       }
+
+       if (status != I_CAL_STATUS_NONE)
+               i_cal_component_take_property (inout_comp, i_cal_property_new_status (status));
+}
+
+static ICalRecurrenceWeekday
+ecb_m365_day_of_week_to_ical (EM365DayOfWeekType dow)
+{
+       switch (dow) {
+       case E_M365_DAY_OF_WEEK_SUNDAY:
+               return I_CAL_SUNDAY_WEEKDAY;
+       case E_M365_DAY_OF_WEEK_MONDAY:
+               return I_CAL_MONDAY_WEEKDAY;
+       case E_M365_DAY_OF_WEEK_TUESDAY:
+               return I_CAL_TUESDAY_WEEKDAY;
+       case E_M365_DAY_OF_WEEK_WEDNESDAY:
+               return I_CAL_WEDNESDAY_WEEKDAY;
+       case E_M365_DAY_OF_WEEK_THURSDAY:
+               return I_CAL_THURSDAY_WEEKDAY;
+       case E_M365_DAY_OF_WEEK_FRIDAY:
+               return I_CAL_FRIDAY_WEEKDAY;
+       case E_M365_DAY_OF_WEEK_SATURDAY:
+               return I_CAL_SATURDAY_WEEKDAY;
+       default:
+               break;
+       }
+
+       return I_CAL_NO_WEEKDAY;
+}
+
+static EM365DayOfWeekType
+ecb_m365_day_of_week_from_ical (ICalRecurrenceWeekday dow)
+{
+       switch (dow) {
+       case I_CAL_SUNDAY_WEEKDAY:
+               return E_M365_DAY_OF_WEEK_SUNDAY;
+               break;
+       case I_CAL_MONDAY_WEEKDAY:
+               return E_M365_DAY_OF_WEEK_MONDAY;
+               break;
+       case I_CAL_TUESDAY_WEEKDAY:
+               return E_M365_DAY_OF_WEEK_TUESDAY;
+               break;
+       case I_CAL_WEDNESDAY_WEEKDAY:
+               return E_M365_DAY_OF_WEEK_WEDNESDAY;
+               break;
+       case I_CAL_THURSDAY_WEEKDAY:
+               return E_M365_DAY_OF_WEEK_THURSDAY;
+               break;
+       case I_CAL_FRIDAY_WEEKDAY:
+               return E_M365_DAY_OF_WEEK_FRIDAY;
+               break;
+       case I_CAL_SATURDAY_WEEKDAY:
+               return E_M365_DAY_OF_WEEK_SATURDAY;
+               break;
+       default:
+               break;
+       }
+
+       return E_M365_DAY_OF_WEEK_UNKNOWN;
+}
+
+static void
+ecb_m365_set_index_to_ical (ICalRecurrence *recr,
+                           EM365WeekIndexType index)
+{
+       gint by_pos = -2;
+
+       switch (index) {
+       case E_M365_WEEK_INDEX_FIRST:
+               by_pos = 1;
+               break;
+       case E_M365_WEEK_INDEX_SECOND:
+               by_pos = 2;
+               break;
+       case E_M365_WEEK_INDEX_THIRD:
+               by_pos = 3;
+               break;
+       case E_M365_WEEK_INDEX_FOURTH:
+               by_pos = 4;
+               break;
+       case E_M365_WEEK_INDEX_LAST:
+               by_pos = -1;
+               break;
+       default:
+               break;
+       }
+
+       if (by_pos != -2)
+               i_cal_recurrence_set_by_set_pos (recr, 0, by_pos);
+}
+
+static void
+ecb_m365_add_index_from_ical (JsonBuilder *builder,
+                             gint by_pos)
+{
+       EM365WeekIndexType index = E_M365_WEEK_INDEX_UNKNOWN;
+
+       if (by_pos == 1)
+               index = E_M365_WEEK_INDEX_FIRST;
+       else if (by_pos == 2)
+               index = E_M365_WEEK_INDEX_SECOND;
+       else if (by_pos == 3)
+               index = E_M365_WEEK_INDEX_THIRD;
+       else if (by_pos == 4)
+               index = E_M365_WEEK_INDEX_FOURTH;
+       else if (by_pos == -1)
+               index = E_M365_WEEK_INDEX_LAST;
+
+       if (index != E_M365_WEEK_INDEX_UNKNOWN)
+               e_m365_recurrence_pattern_add_index (builder, index);
+}
+
+static void
+ecb_m365_set_days_of_week_to_ical (ICalRecurrence *recr,
+                                  JsonArray *days_of_week)
+{
+       gint ii, jj, sz;
+
+       if (!days_of_week)
+               return;
+
+       ii = 0;
+       sz = json_array_get_length (days_of_week);
+
+       for (jj = 0; jj < sz; jj++) {
+               ICalRecurrenceWeekday week_day;
+
+               week_day = ecb_m365_day_of_week_to_ical (e_m365_array_get_day_of_week_element (days_of_week, 
jj));
+
+               if (week_day != I_CAL_SUNDAY_WEEKDAY) {
+                       i_cal_recurrence_set_by_day (recr, ii, week_day);
+                       ii++;
+               }
+       }
+
+       i_cal_recurrence_set_by_day (recr, ii, I_CAL_RECURRENCE_ARRAY_MAX);
+}
+
+static void
+ecb_m365_add_days_of_week_from_ical (JsonBuilder *builder,
+                                    ICalRecurrence *recr)
+{
+       gint ii;
+
+       e_m365_recurrence_pattern_begin_days_of_week (builder);
+
+       for (ii = 0; ii < I_CAL_BY_DAY_SIZE; ii++) {
+               ICalRecurrenceWeekday week_day;
+               EM365DayOfWeekType m365_week_day;
+
+               week_day = i_cal_recurrence_get_by_day (recr, ii);
+
+               if (((gint) week_day) == I_CAL_RECURRENCE_ARRAY_MAX)
+                       break;
+
+               m365_week_day = ecb_m365_day_of_week_from_ical (week_day);
+
+               if (m365_week_day != E_M365_DAY_OF_WEEK_UNKNOWN)
+                       e_m365_recurrence_pattern_add_day_of_week (builder, m365_week_day);
+       }
+
+       e_m365_recurrence_pattern_end_days_of_week (builder);
+}
+
+static gboolean
+ecb_m365_get_recurrence (EM365Connection *cnc,
+                        const gchar *group_id,
+                        const gchar *folder_id,
+                        const gchar *attachments_dir,
+                        ETimezoneCache *timezone_cache,
+                        JsonObject *m365_object,
+                        ICalComponent *inout_comp,
+                        ICalPropertyKind prop_kind,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       EM365PatternedRecurrence *m365_recr;
+       EM365RecurrencePattern *m365_pattern;
+       EM365RecurrenceRange *m365_range;
+       ICalRecurrence *ical_recr;
+       ICalRecurrenceWeekday week_day;
+       gint month;
+
+       switch (i_cal_component_isa (inout_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               m365_recr = e_m365_event_get_recurrence (m365_object);
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               m365_recr = e_m365_task_get_recurrence (m365_object);
+               break;
+       default:
+               g_warn_if_reached ();
+               return FALSE;
+       }
+
+       m365_pattern = m365_recr ? e_m365_patterned_recurrence_get_pattern (m365_recr) : NULL;
+       m365_range = m365_recr ? e_m365_patterned_recurrence_get_range (m365_recr) : NULL;
+
+       if (!m365_recr || !m365_pattern || !m365_range)
+               return TRUE;
+
+       ical_recr = i_cal_recurrence_new ();
+
+       switch (e_m365_recurrence_pattern_get_type (m365_pattern)) {
+       case E_M365_RECURRENCE_PATTERN_DAILY:
+               i_cal_recurrence_set_freq (ical_recr, I_CAL_DAILY_RECURRENCE);
+               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
+               ecb_m365_set_days_of_week_to_ical (ical_recr, e_m365_recurrence_pattern_get_days_of_week 
(m365_pattern));
+               break;
+       case E_M365_RECURRENCE_PATTERN_WEEKLY:
+               i_cal_recurrence_set_freq (ical_recr, I_CAL_WEEKLY_RECURRENCE);
+               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
+
+               week_day = ecb_m365_day_of_week_to_ical (e_m365_recurrence_pattern_get_first_day_of_week 
(m365_pattern));
+
+               if (week_day != I_CAL_NO_WEEKDAY)
+                       i_cal_recurrence_set_week_start (ical_recr, week_day);
+
+               ecb_m365_set_days_of_week_to_ical (ical_recr, e_m365_recurrence_pattern_get_days_of_week 
(m365_pattern));
+               break;
+       case E_M365_RECURRENCE_PATTERN_ABSOLUTE_MONTHLY:
+               i_cal_recurrence_set_freq (ical_recr, I_CAL_MONTHLY_RECURRENCE);
+               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
+               i_cal_recurrence_set_by_month_day (ical_recr, 0, e_m365_recurrence_pattern_get_day_of_month 
(m365_pattern));
+               break;
+       case E_M365_RECURRENCE_PATTERN_RELATIVE_MONTHLY:
+               i_cal_recurrence_set_freq (ical_recr, I_CAL_MONTHLY_RECURRENCE);
+               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
+               ecb_m365_set_days_of_week_to_ical (ical_recr, e_m365_recurrence_pattern_get_days_of_week 
(m365_pattern));
+               week_day = ecb_m365_day_of_week_to_ical (e_m365_recurrence_pattern_get_first_day_of_week 
(m365_pattern));
+
+               if (week_day != I_CAL_NO_WEEKDAY)
+                       i_cal_recurrence_set_week_start (ical_recr, week_day);
+
+               ecb_m365_set_index_to_ical (ical_recr, e_m365_recurrence_pattern_get_index (m365_pattern));
+               break;
+       case E_M365_RECURRENCE_PATTERN_ABSOLUTE_YEARLY:
+               i_cal_recurrence_set_freq (ical_recr, I_CAL_YEARLY_RECURRENCE);
+               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
+               i_cal_recurrence_set_by_month_day (ical_recr, 0, e_m365_recurrence_pattern_get_day_of_month 
(m365_pattern));
+
+               month = e_m365_recurrence_pattern_get_month (m365_pattern);
+
+               if (month >= 1 && month <= 12)
+                       i_cal_recurrence_set_by_month (ical_recr, 0, month);
+               break;
+       case E_M365_RECURRENCE_PATTERN_RELATIVE_YEARLY:
+               i_cal_recurrence_set_freq (ical_recr, I_CAL_YEARLY_RECURRENCE);
+               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
+               ecb_m365_set_days_of_week_to_ical (ical_recr, e_m365_recurrence_pattern_get_days_of_week 
(m365_pattern));
+               week_day = ecb_m365_day_of_week_to_ical (e_m365_recurrence_pattern_get_first_day_of_week 
(m365_pattern));
+
+               if (week_day != I_CAL_NO_WEEKDAY)
+                       i_cal_recurrence_set_week_start (ical_recr, week_day);
+
+               ecb_m365_set_index_to_ical (ical_recr, e_m365_recurrence_pattern_get_index (m365_pattern));
+
+               month = e_m365_recurrence_pattern_get_month (m365_pattern);
+
+               if (month >= 1 && month <= 12)
+                       i_cal_recurrence_set_by_month (ical_recr, 0, month);
+               break;
+       default:
+               g_object_unref (ical_recr);
+               g_warning ("%s: Unknown pattern type: %d", G_STRFUNC, e_m365_recurrence_pattern_get_type 
(m365_pattern));
+               /* Ignore the error (in the code) and continue. */
+               return TRUE;
+       }
+
+       switch (e_m365_recurrence_range_get_type (m365_range)) {
+       case E_M365_RECURRENCE_RANGE_ENDDATE:
+               if (e_m365_recurrence_range_get_end_date (m365_range) > 0) {
+                       gint yy = 0, mm = 0, dd = 0;
+
+                       if (e_m365_date_decode (e_m365_recurrence_range_get_end_date (m365_range), &yy, &mm, 
&dd)) {
+                               ICalTime *itt;
+
+                               itt = i_cal_time_new ();
+                               i_cal_time_set_date (itt, yy, mm, dd);
+                               i_cal_time_set_is_date (itt, TRUE);
+
+                               i_cal_recurrence_set_until (ical_recr, itt);
+
+                               g_clear_object (&itt);
+                       }
+               }
+               break;
+       case E_M365_RECURRENCE_RANGE_NOEND:
+               break;
+       case E_M365_RECURRENCE_RANGE_NUMBERED:
+               i_cal_recurrence_set_count (ical_recr, e_m365_recurrence_range_get_number_of_occurrences 
(m365_range));
+               break;
+       default:
+               g_warning ("%s: Unknown range type: %d", G_STRFUNC, e_m365_recurrence_range_get_type 
(m365_range));
+               g_object_unref (ical_recr);
+               /* Ignore the error (in the code) and continue. */
+               return TRUE;
+       }
+
+       i_cal_component_take_property (inout_comp, i_cal_property_new_rrule (ical_recr));
+
+       g_object_unref (ical_recr);
+
+       return TRUE;
+}
+
+static gboolean
+ecb_m365_add_recurrence (EM365Connection *cnc,
+                        const gchar *group_id,
+                        const gchar *folder_id,
+                        ETimezoneCache *timezone_cache,
+                        ICalComponent *new_comp,
+                        ICalComponent *old_comp,
+                        ICalPropertyKind prop_kind,
+                        const gchar *m365_id,
+                        JsonBuilder *builder,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       ICalProperty *new_value, *old_value;
+       gboolean success = TRUE;
+       void (* begin_recurrence_func) (JsonBuilder *builder);
+       void (* end_recurrence_func) (JsonBuilder *builder);
+       void (* add_null_recurrence_func) (JsonBuilder *builder);
+
+       switch (i_cal_component_isa (new_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               begin_recurrence_func = e_m365_event_begin_recurrence;
+               end_recurrence_func = e_m365_event_end_recurrence;
+               add_null_recurrence_func = e_m365_event_add_null_recurrence;
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               begin_recurrence_func = e_m365_task_begin_recurrence;
+               end_recurrence_func = e_m365_task_end_recurrence;
+               add_null_recurrence_func = e_m365_task_add_null_recurrence;
+               break;
+       default:
+               g_warn_if_reached ();
+               return FALSE;
+       }
+
+       if (i_cal_component_count_properties (new_comp, prop_kind) > 1) {
+               g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_NOT_SUPPORTED,
+                       _("Microsoft 365 calendar cannot store more than one recurrence")));
+
+               return FALSE;
+       }
+
+       if (i_cal_component_count_properties (new_comp, I_CAL_RDATE_PROPERTY) > 0 ||
+           i_cal_component_count_properties (new_comp, I_CAL_EXDATE_PROPERTY) > 0 ||
+           i_cal_component_count_properties (new_comp, I_CAL_EXRULE_PROPERTY) > 0) {
+               g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_NOT_SUPPORTED,
+                       _("Microsoft 365 calendar cannot store component with RDATE, EXDATE or RRULE 
properties")));
+
+               return FALSE;
+       }
+
+       new_value = i_cal_component_get_first_property (new_comp, prop_kind);
+       old_value = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
+
+       if (!new_value && !old_value)
+               return TRUE;
+
+       if (new_value) {
+               ICalRecurrence *new_rrule;
+               gboolean same = FALSE;
+
+               new_rrule = i_cal_property_get_rrule (new_value);
+
+               if (old_value && new_rrule) {
+                       ICalRecurrence *old_rrule;
+
+                       old_rrule = i_cal_property_get_rrule (old_value);
+
+                       if (old_rrule) {
+                               gchar *new_str, *old_str;
+
+                               new_str = i_cal_recurrence_to_string (new_rrule);
+                               old_str = i_cal_recurrence_to_string (old_rrule);
+
+                               same = g_strcmp0 (new_str, old_str) == 0;
+
+                               g_free (new_str);
+                               g_free (old_str);
+                       }
+
+                       g_clear_object (&old_rrule);
+               }
+
+               if (!same && new_rrule) {
+                       EM365DayOfWeekType week_day;
+                       ICalTime *dtstart;
+                       gint by_pos, month, yy = 0, mm = 0, dd = 0;
+
+                       begin_recurrence_func (builder);
+                       e_m365_patterned_recurrence_begin_pattern (builder);
+
+                       switch (i_cal_recurrence_get_freq (new_rrule)) {
+                       case I_CAL_DAILY_RECURRENCE:
+                               e_m365_recurrence_pattern_add_type (builder, E_M365_RECURRENCE_PATTERN_DAILY);
+                               e_m365_recurrence_pattern_add_interval (builder, 
i_cal_recurrence_get_interval (new_rrule));
+                               ecb_m365_add_days_of_week_from_ical (builder, new_rrule);
+                               break;
+                       case I_CAL_WEEKLY_RECURRENCE:
+                               e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_WEEKLY);
+                               e_m365_recurrence_pattern_add_interval (builder, 
i_cal_recurrence_get_interval (new_rrule));
+
+                               week_day = ecb_m365_day_of_week_from_ical (i_cal_recurrence_get_week_start 
(new_rrule));
+
+                               if (week_day != E_M365_DAY_OF_WEEK_UNKNOWN)
+                                       e_m365_recurrence_pattern_add_first_day_of_week (builder, week_day);
+
+                               ecb_m365_add_days_of_week_from_ical (builder, new_rrule);
+                               break;
+                       case I_CAL_MONTHLY_RECURRENCE:
+                               by_pos = i_cal_recurrence_get_by_set_pos (new_rrule, 0);
+
+                               e_m365_recurrence_pattern_add_interval (builder, 
i_cal_recurrence_get_interval (new_rrule));
+
+                               if (by_pos == I_CAL_RECURRENCE_ARRAY_MAX) {
+                                       e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_ABSOLUTE_MONTHLY);
+                                       e_m365_recurrence_pattern_add_day_of_month (builder, 
i_cal_recurrence_get_by_month_day (new_rrule, 0));
+                               } else {
+                                       e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_RELATIVE_MONTHLY);
+
+                                       week_day = ecb_m365_day_of_week_from_ical 
(i_cal_recurrence_get_week_start (new_rrule));
+
+                                       if (week_day != E_M365_DAY_OF_WEEK_UNKNOWN)
+                                               e_m365_recurrence_pattern_add_first_day_of_week (builder, 
week_day);
+
+                                       ecb_m365_add_days_of_week_from_ical (builder, new_rrule);
+                                       ecb_m365_add_index_from_ical (builder, by_pos);
+                               }
+                               break;
+                       case I_CAL_YEARLY_RECURRENCE:
+                               by_pos = i_cal_recurrence_get_by_set_pos (new_rrule, 0);
+
+                               e_m365_recurrence_pattern_add_interval (builder, 
i_cal_recurrence_get_interval (new_rrule));
+
+                               month = i_cal_recurrence_get_by_month (new_rrule, 0);
+
+                               if (month >= 1 && month <= 12)
+                                       e_m365_recurrence_pattern_add_month (builder, month);
+
+                               if (by_pos == I_CAL_RECURRENCE_ARRAY_MAX) {
+                                       e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_ABSOLUTE_YEARLY);
+                                       e_m365_recurrence_pattern_add_day_of_month (builder, 
i_cal_recurrence_get_by_month_day (new_rrule, 0));
+                               } else {
+                                       e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_RELATIVE_YEARLY);
+
+                                       week_day = ecb_m365_day_of_week_from_ical 
(i_cal_recurrence_get_week_start (new_rrule));
+
+                                       if (week_day != E_M365_DAY_OF_WEEK_UNKNOWN)
+                                               e_m365_recurrence_pattern_add_first_day_of_week (builder, 
week_day);
+
+                                       ecb_m365_add_days_of_week_from_ical (builder, new_rrule);
+                                       ecb_m365_add_index_from_ical (builder, by_pos);
+                               }
+
+                               break;
+                       default:
+                               g_set_error (error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED,
+                                       _("Unknown recurrence frequency (%d)"), i_cal_recurrence_get_freq 
(new_rrule));
+
+                               success = FALSE;
+                               break;
+                       }
+
+                       e_m365_patterned_recurrence_end_pattern (builder);
+                       e_m365_patterned_recurrence_begin_range (builder);
+
+                       dtstart = i_cal_component_get_dtstart (new_comp);
+                       i_cal_time_get_date (dtstart, &yy, &mm, &dd);
+                       g_clear_object (&dtstart);
+
+                       e_m365_recurrence_range_add_start_date (builder, e_m365_date_encode (yy, mm, dd));
+
+                       if (!i_cal_recurrence_get_count (new_rrule)) {
+                               ICalTime *until;
+                               gint yy = 0, mm = 0, dd = 0;
+
+                               until = i_cal_recurrence_get_until (new_rrule);
+
+                               if (until)
+                                       i_cal_time_get_date (until, &yy, &mm, &dd);
+
+                               if (!until || yy == 0) {
+                                       e_m365_recurrence_range_add_type (builder, 
E_M365_RECURRENCE_RANGE_NOEND);
+                               } else {
+                                       e_m365_recurrence_range_add_type (builder, 
E_M365_RECURRENCE_RANGE_ENDDATE);
+                                       e_m365_recurrence_range_add_end_date (builder, e_m365_date_encode 
(yy, mm, dd));
+                               }
+
+                               g_clear_object (&until);
+                       } else {
+                               e_m365_recurrence_range_add_type (builder, E_M365_RECURRENCE_RANGE_NUMBERED);
+                               e_m365_recurrence_range_add_number_of_occurrences (builder, 
i_cal_recurrence_get_count (new_rrule));
+                       }
+
+                       e_m365_patterned_recurrence_end_range (builder);
+                       end_recurrence_func (builder);
+               }
+
+               g_clear_object (&new_rrule);
+       } else {
+               add_null_recurrence_func (builder);
+       }
+
+       g_clear_object (&new_value);
+       g_clear_object (&old_value);
+
+       return success;
+}
+
+static gboolean
+ecb_m365_get_reminder (EM365Connection *cnc,
+                      const gchar *group_id,
+                      const gchar *folder_id,
+                      const gchar *attachments_dir,
+                      ETimezoneCache *timezone_cache,
+                      EM365Event *m365_object,
+                      ICalComponent *inout_comp,
+                      ICalPropertyKind prop_kind,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+       switch (i_cal_component_isa (inout_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               if (e_m365_event_get_is_reminder_on (m365_object)) {
+                       ECalComponentAlarm *alarm;
+                       ECalComponentAlarmTrigger *trigger;
+                       ICalDuration *duration;
+
+                       duration = i_cal_duration_new_from_int (-60 * 
e_m365_event_get_reminder_minutes_before_start (m365_object));
+                       trigger = e_cal_component_alarm_trigger_new_relative 
(E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START, duration);
+                       g_object_unref (duration);
+
+                       alarm = e_cal_component_alarm_new ();
+                       e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
+                       e_cal_component_alarm_take_summary (alarm, e_cal_component_text_new 
(e_m365_event_get_subject (m365_object), NULL));
+                       e_cal_component_alarm_take_description (alarm, e_cal_component_text_new 
(e_m365_event_get_subject (m365_object), NULL));
+                       e_cal_component_alarm_take_trigger (alarm, trigger);
+
+                       i_cal_component_take_component (inout_comp, e_cal_component_alarm_get_as_component 
(alarm));
+
+                       e_cal_component_alarm_free (alarm);
+               }
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               if (e_m365_task_get_is_reminder_on (m365_object)) {
+                       EM365DateTimeWithZone *reminder_dt;
+
+                       reminder_dt = e_m365_task_get_reminder_date_time (m365_object);
+
+                       if (reminder_dt) {
+                               ECalComponentAlarm *alarm;
+                               ECalComponentAlarmTrigger *trigger;
+                               ICalTimezone *tz;
+                               ICalTime *itt;
+                               time_t tt;
+                               const gchar *zone;
+
+                               tt = e_m365_date_time_get_date_time (reminder_dt);
+                               zone = e_m365_date_time_get_time_zone (reminder_dt);
+
+                               if (zone && *zone)
+                                       zone = e_m365_tz_utils_get_ical_equivalent (zone);
+
+                               tz = zone && *zone ? e_timezone_cache_get_timezone (timezone_cache, zone) : 
NULL;
+
+                               if (!tz)
+                                       tz = i_cal_timezone_get_utc_timezone ();
+
+                               itt = i_cal_time_new_from_timet_with_zone (tt, FALSE, tz);
+                               trigger = e_cal_component_alarm_trigger_new_absolute (itt);
+                               g_object_unref (itt);
+
+                               alarm = e_cal_component_alarm_new ();
+                               e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
+                               e_cal_component_alarm_take_summary (alarm, e_cal_component_text_new 
(e_m365_task_get_subject (m365_object), NULL));
+                               e_cal_component_alarm_take_description (alarm, e_cal_component_text_new 
(e_m365_task_get_subject (m365_object), NULL));
+                               e_cal_component_alarm_take_trigger (alarm, trigger);
+
+                               i_cal_component_take_component (inout_comp, 
e_cal_component_alarm_get_as_component (alarm));
+
+                               e_cal_component_alarm_free (alarm);
+                       }
+               }
+               break;
+       default:
+               g_warn_if_reached ();
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean
+ecb_m365_add_reminder (EM365Connection *cnc,
+                      const gchar *group_id,
+                      const gchar *folder_id,
+                      ETimezoneCache *timezone_cache,
+                      ICalComponent *new_comp,
+                      ICalComponent *old_comp,
+                      ICalPropertyKind prop_kind,
+                      const gchar *m365_id,
+                      JsonBuilder *builder,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+       ICalComponent *new_value, *old_value;
+       gboolean success = TRUE;
+
+       if (i_cal_component_count_components (new_comp, I_CAL_VALARM_COMPONENT) > 1) {
+               g_propagate_error (error, ECC_ERROR_EX (E_CAL_CLIENT_ERROR_INVALID_OBJECT, _("Microsoft 365 
calendar cannot store more that one event reminder")));
+               return FALSE;
+       }
+
+       new_value = i_cal_component_get_first_component (new_comp, I_CAL_VALARM_COMPONENT);
+       old_value = old_comp ? i_cal_component_get_first_component (old_comp, I_CAL_VALARM_COMPONENT) : NULL;
+
+       if (!new_value && !old_value)
+               return TRUE;
+
+       if (new_value) {
+               ECalComponentAlarm *new_alarm;
+               ECalComponentAlarmTrigger *new_trigger;
+               ICalComponentKind kind;
+               ICalDuration *new_duration = NULL;
+               ICalTime *new_absolute_time = NULL;
+               gboolean changed = TRUE;
+
+               kind = i_cal_component_isa (new_comp);
+
+               new_alarm = e_cal_component_alarm_new_from_component (new_value);
+               new_trigger = new_alarm ? e_cal_component_alarm_get_trigger (new_alarm) : NULL;
+
+               switch (kind) {
+               case I_CAL_VEVENT_COMPONENT:
+                       success = new_trigger && e_cal_component_alarm_trigger_get_kind (new_trigger) == 
E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
+                       if (success) {
+                               new_duration = e_cal_component_alarm_trigger_get_duration (new_trigger);
+
+                               success = new_duration && i_cal_duration_as_int (new_duration) <= 0;
+                       }
+
+                       if (!success) {
+                               g_propagate_error (error, ECC_ERROR_EX (E_CAL_CLIENT_ERROR_INVALID_OBJECT, 
_("Microsoft 365 event can have only a reminder before event start")));
+                       }
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       success = new_trigger && e_cal_component_alarm_trigger_get_kind (new_trigger) == 
E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE;
+
+                       if (success) {
+                               new_absolute_time = e_cal_component_alarm_trigger_get_absolute_time 
(new_trigger);
+
+                               success = new_absolute_time != NULL;
+                       }
+
+                       if (!success) {
+                               g_propagate_error (error, ECC_ERROR_EX (E_CAL_CLIENT_ERROR_INVALID_OBJECT, 
_("Microsoft 365 task can have only a reminder with absolute time")));
+                       }
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       success = FALSE;
+                       break;
+               }
+
+               if (success && old_value && new_trigger) {
+                       ECalComponentAlarm *old_alarm;
+                       ECalComponentAlarmTrigger *old_trigger;
+
+                       old_alarm = e_cal_component_alarm_new_from_component (old_value);
+                       old_trigger = old_alarm ? e_cal_component_alarm_get_trigger (old_alarm) : NULL;
+
+                       if (old_trigger) {
+                               changed = e_cal_component_alarm_trigger_get_kind (new_trigger) != 
e_cal_component_alarm_trigger_get_kind (old_trigger);
+
+                               if (!changed) {
+                                       ICalDuration *old_duration;
+                                       ICalTime *old_absolute_time;
+
+                                       switch (kind) {
+                                       case I_CAL_VEVENT_COMPONENT:
+                                               old_duration = e_cal_component_alarm_trigger_get_duration 
(old_trigger);
+
+                                               changed = !old_duration || i_cal_duration_as_int 
(new_duration) != i_cal_duration_as_int (old_duration);
+                                               break;
+                                       case I_CAL_VTODO_COMPONENT:
+                                               old_absolute_time = 
e_cal_component_alarm_trigger_get_absolute_time (old_trigger);
+
+                                               changed = !old_absolute_time || i_cal_time_compare 
(new_absolute_time, old_absolute_time) != 0;
+                                               break;
+                                       default:
+                                               g_warn_if_reached ();
+                                               changed = FALSE;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       e_cal_component_alarm_free (old_alarm);
+               }
+
+               if (success && changed) {
+                       ICalTimezone *izone = NULL;
+                       const gchar *wzone = NULL;
+                       time_t tt;
+
+                       switch (kind) {
+                       case I_CAL_VEVENT_COMPONENT:
+                               e_m365_event_add_is_reminder_on (builder, TRUE);
+                               e_m365_event_add_reminder_minutes_before_start (builder, 
i_cal_duration_as_int (new_duration) / -60);
+                               break;
+                       case I_CAL_VTODO_COMPONENT:
+                               izone = i_cal_time_get_timezone (new_absolute_time);
+
+                               if (izone)
+                                       wzone = e_m365_tz_utils_get_msdn_equivalent 
(i_cal_timezone_get_location (izone));
+
+                               tt = i_cal_time_as_timet_with_zone (new_absolute_time, wzone ? NULL : izone);
+
+                               e_m365_task_add_is_reminder_on (builder, TRUE);
+                               e_m365_task_add_reminder_date_time (builder, tt, wzone);
+                               break;
+                       default:
+                               g_warn_if_reached ();
+                               break;
+                       }
+               }
+
+               e_cal_component_alarm_free (new_alarm);
+       } else {
+               switch (i_cal_component_isa (new_comp)) {
+               case I_CAL_VEVENT_COMPONENT:
+                       e_m365_event_add_is_reminder_on (builder, FALSE);
+                       break;
+               case I_CAL_VTODO_COMPONENT:
+                       e_m365_task_add_is_reminder_on (builder, FALSE);
+                       break;
+               default:
+                       g_warn_if_reached ();
+                       break;
+               }
+       }
+
+       g_clear_object (&new_value);
+       g_clear_object (&old_value);
+
+       return success;
+}
+
+static gboolean
+ecb_m365_add_online_meeting (EM365Connection *cnc,
+                            const gchar *group_id,
+                            const gchar *folder_id,
+                            ETimezoneCache *timezone_cache,
+                            ICalComponent *new_comp,
+                            ICalComponent *old_comp,
+                            ICalPropertyKind prop_kind,
+                            const gchar *m365_id,
+                            JsonBuilder *builder,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       /* This can be set only for new events */
+       if (old_comp)
+               return TRUE;
+
+       if (e_cal_util_component_has_x_property (new_comp, "X-M365-ONLINE-MEETING")) {
+               e_m365_event_add_is_online_meeting (builder, TRUE);
+               e_m365_event_add_online_meeting_provider (builder, 
E_M365_ONLINE_MEETING_PROVIDER_TEAMS_FOR_BUSINESS);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+ecb_m365_get_attachments (EM365Connection *cnc,
+                         const gchar *group_id,
+                         const gchar *folder_id,
+                         const gchar *attachments_dir,
+                         ETimezoneCache *timezone_cache,
+                         JsonObject *m365_object,
+                         ICalComponent *inout_comp,
+                         ICalPropertyKind prop_kind,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       GSList *attachments = NULL, *link;
+       const gchar *id;
+       gboolean success = TRUE;
+
+       switch (i_cal_component_isa (inout_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               if (!e_m365_event_get_has_attachments (m365_object))
+                       return TRUE;
+
+               id = e_m365_event_get_id (m365_object);
+
+               if (!e_m365_connection_list_event_attachments_sync (cnc, NULL,
+                       group_id, folder_id, id, "id,name,contentType,contentBytes",
+                       &attachments, cancellable, error)) {
+                       return FALSE;
+               }
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               if (!e_m365_task_get_has_attachments (m365_object))
+                       return TRUE;
+
+               id = e_m365_task_get_id (m365_object);
+
+               if (!e_m365_connection_list_task_attachments_sync (cnc, NULL,
+                       group_id, folder_id, id, "id,name,contentType,contentBytes",
+                       &attachments, cancellable, error)) {
+                       return FALSE;
+               }
+               break;
+       default:
+               g_warn_if_reached ();
+               return FALSE;
+       }
+
+       for (link = attachments; link && success; link = g_slist_next (link)) {
+               CamelStream *content_stream;
+               EM365Attachment *m365_attach = link->data;
+               gchar *filename;
+
+               if (!m365_attach || e_m365_attachment_get_data_type (m365_attach) != 
E_M365_ATTACHMENT_DATA_TYPE_FILE ||
+                   !e_m365_attachment_get_name (m365_attach))
+                       continue;
+
+               filename = g_build_filename (attachments_dir, id, e_m365_attachment_get_id (m365_attach), 
NULL);
+
+               content_stream = camel_stream_fs_new_with_name (filename, O_CREAT | O_TRUNC | O_WRONLY, 0666, 
error);
+
+               if (content_stream) {
+                       CamelMimeFilter *filter;
+                       CamelStream *filter_stream;
+                       const gchar *base64_data;
+
+                       filter_stream = camel_stream_filter_new (content_stream);
+
+                       filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
+                       camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), filter);
+                       g_object_unref (filter);
+
+                       base64_data = e_m365_file_attachment_get_content_bytes (m365_attach);
+
+                       if (base64_data && *base64_data)
+                               success = camel_stream_write (filter_stream, base64_data, strlen 
(base64_data), cancellable, error) != -1;
+
+                       camel_stream_flush (filter_stream, cancellable, NULL);
+                       g_object_unref (filter_stream);
+
+                       camel_stream_flush (content_stream, cancellable, NULL);
+                       g_object_unref (content_stream);
+
+                       if (success) {
+                               gchar *uri;
+
+                               uri = g_filename_to_uri (filename, NULL, error);
+
+                               if (uri) {
+                                       ICalAttach *ical_attach;
+                                       ICalParameter *param;
+                                       ICalProperty *prop;
+                                       gchar *enc_uri;
+                                       const gchar *tmp;
+
+                                       enc_uri = i_cal_value_encode_ical_string (uri);
+                                       ical_attach = i_cal_attach_new_from_url (enc_uri);
+                                       prop = i_cal_property_new_attach (ical_attach);
+
+                                       tmp = e_m365_attachment_get_name (m365_attach);
+
+                                       if (!tmp || !*tmp)
+                                               tmp = "attachment.dat";
+
+                                       param = i_cal_parameter_new_filename (tmp);
+                                       i_cal_property_take_parameter (prop, param);
+
+                                       tmp = e_m365_attachment_get_content_type (m365_attach);
+
+                                       if (tmp && *tmp) {
+                                               param = i_cal_parameter_new_fmttype (tmp);
+                                               i_cal_property_take_parameter (prop, param);
+                                       }
+
+                                       param = i_cal_parameter_new_x (e_m365_attachment_get_id 
(m365_attach));
+                                       i_cal_parameter_set_xname (param, "X-M365-ATTACHMENTID");
+                                       i_cal_property_take_parameter (prop, param);
+
+                                       i_cal_component_take_property (inout_comp, prop);
+
+                                       g_object_unref (ical_attach);
+                                       g_free (enc_uri);
+                                       g_free (uri);
+                               } else {
+                                       success = FALSE;
+                               }
+                       }
+               } else {
+                       success = FALSE;
+               }
+
+               g_free (filename);
+       }
+
+       g_slist_free_full (attachments, (GDestroyNotify) json_object_unref);
+
+       return success;
+}
+
+static void
+ecb_m365_extract_attachments (ICalComponent *comp,
+                             GHashTable **out_hash, /* gchar *attachment_id ~> ICalProperty * */
+                             GSList **out_slist) /* ICalProperty * */
+{
+       ICalProperty *prop;
+
+       if (!comp)
+               return;
+
+       for (prop = i_cal_component_get_first_property (comp, I_CAL_ATTACH_PROPERTY);
+            prop;
+            g_object_unref (prop), prop = i_cal_component_get_next_property (comp, I_CAL_ATTACH_PROPERTY)) {
+               if (out_slist) {
+                       *out_slist = g_slist_prepend (*out_slist, g_object_ref (prop));
+               } else if (out_hash) {
+                       gchar *attach_id;
+
+                       attach_id = i_cal_property_get_parameter_as_string (prop, "X-M365-ATTACHMENTID");
+                       g_warn_if_fail (attach_id != NULL);
+
+                       if (attach_id) {
+                               if (!*out_hash)
+                                       *out_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
g_object_unref);
+
+                               g_hash_table_insert (*out_hash, attach_id, g_object_ref (prop));
+                       }
+               } else {
+                       g_warn_if_reached ();
+               }
+       }
+
+       g_clear_object (&prop);
+
+       if (out_slist && *out_slist)
+               *out_slist = g_slist_reverse (*out_slist);
+}
+
+static gboolean
+ecb_m365_add_attachments (EM365Connection *cnc,
+                         const gchar *group_id,
+                         const gchar *folder_id,
+                         ETimezoneCache *timezone_cache,
+                         ICalComponent *new_comp,
+                         ICalComponent *old_comp,
+                         ICalPropertyKind prop_kind,
+                         const gchar *m365_id,
+                         JsonBuilder *builder,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       GSList *new_attachs = NULL;
+       GHashTable *old_attachs = NULL;
+       gboolean (* add_attachment_func) (EM365Connection *cnc,
+                                         const gchar *user_override,
+                                         const gchar *group_id,
+                                         const gchar *folder_id,
+                                         const gchar *item_id,
+                                         JsonBuilder *in_attachment,
+                                         EM365Attachment **out_attachment,
+                                         GCancellable *cancellable,
+                                         GError **error);
+       gboolean (* delete_attachment_func) (EM365Connection *cnc,
+                                            const gchar *user_override,
+                                            const gchar *group_id,
+                                            const gchar *folder_id,
+                                            const gchar *item_id,
+                                            const gchar *attachment_id,
+                                            GCancellable *cancellable,
+                                            GError **error);
+       gboolean success = TRUE;
+
+       switch (i_cal_component_isa (new_comp)) {
+       case I_CAL_VEVENT_COMPONENT:
+               add_attachment_func = e_m365_connection_add_event_attachment_sync;
+               delete_attachment_func = e_m365_connection_delete_event_attachment_sync;
+               break;
+       case I_CAL_VTODO_COMPONENT:
+               add_attachment_func = e_m365_connection_add_task_attachment_sync;
+               delete_attachment_func = e_m365_connection_delete_task_attachment_sync;
+               break;
+       default:
+               g_warn_if_reached ();
+               return FALSE;
+       }
+
+       if (!i_cal_component_count_properties (new_comp, I_CAL_ATTACH_PROPERTY) &&
+           !(old_comp ? i_cal_component_count_properties (old_comp, I_CAL_ATTACH_PROPERTY) : 0)) {
+               return TRUE;
+       }
+
+       ecb_m365_extract_attachments (new_comp, NULL, &new_attachs);
+       ecb_m365_extract_attachments (old_comp, &old_attachs, NULL);
+
+       if (new_attachs) {
+               GSList *link, *save_attachs = new_attachs;
+
+               if (old_attachs) {
+                       save_attachs = NULL;
+
+                       for (link = new_attachs; link; link = g_slist_next (link)) {
+                               ICalProperty *prop = link->data;
+                               gchar *attach_id;
+
+                               attach_id = i_cal_property_get_parameter_as_string (prop, 
"X-M365-ATTACHMENTID");
+
+                               if (!attach_id || !g_hash_table_remove (old_attachs, attach_id)) {
+                                       save_attachs = g_slist_prepend (save_attachs, g_object_ref (prop));
+                               }
+                       }
+
+                       if (save_attachs)
+                               save_attachs = g_slist_reverse (save_attachs);
+               }
+
+               for (link = save_attachs; link && success; link = g_slist_next (link)) {
+                       ICalProperty *prop = link->data;
+                       ICalAttach *attach;
+                       JsonBuilder *builder = NULL;
+
+                       attach = i_cal_property_get_attach (prop);
+
+                       if (!attach)
+                               continue;
+
+                       if (i_cal_attach_get_is_url (attach)) {
+                               const gchar *data;
+                               gchar *uri;
+
+                               data = i_cal_attach_get_url (attach);
+                               uri = i_cal_value_decode_ical_string (data);
+
+                               if (uri && g_ascii_strncasecmp (uri, "file://", 7) == 0) {
+                                       CamelStream *content_stream;
+                                       gchar *filename;
+
+                                       filename = g_filename_from_uri (uri, NULL, error);
+                                       content_stream = filename ? camel_stream_fs_new_with_name (filename, 
O_RDONLY, 0, error) : NULL;
+
+                                       if (content_stream) {
+                                               CamelMimeFilter *filter;
+                                               CamelStream *filter_stream;
+                                               CamelStream *base64_stream;
+
+                                               base64_stream = camel_stream_mem_new ();
+                                               filter_stream = camel_stream_filter_new (base64_stream);
+
+                                               filter = camel_mime_filter_basic_new 
(CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
+                                               camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), 
filter);
+                                               g_object_unref (filter);
+
+                                               success = camel_stream_write_to_stream (content_stream, 
filter_stream, cancellable, error) != -1;
+
+                                               camel_stream_flush (filter_stream, cancellable, NULL);
+                                               g_object_unref (filter_stream);
+
+                                               /* Ensure the stream is NUL-terminated, thus it can be used 
as a string */
+                                               camel_stream_write (base64_stream, "\0", 1, cancellable, 
NULL);
+
+                                               camel_stream_flush (base64_stream, cancellable, NULL);
+                                               g_object_unref (content_stream);
+
+                                               if (success) {
+                                                       GByteArray *bytes;
+
+                                                       bytes = camel_stream_mem_get_byte_array 
(CAMEL_STREAM_MEM (base64_stream));
+
+                                                       builder = json_builder_new_immutable ();
+                                                       e_m365_attachment_begin_attachment (builder, 
E_M365_ATTACHMENT_DATA_TYPE_FILE);
+                                                       e_m365_file_attachment_add_content_bytes (builder, 
(const gchar *) bytes->data);
+                                               }
+
+                                               g_object_unref (base64_stream);
+                                       } else {
+                                               success = FALSE;
+                                       }
+
+                                       g_free (filename);
+                               } else {
+                                       success = FALSE;
+
+                                       if (uri)
+                                               g_set_error (error, E_CLIENT_ERROR, 
E_CLIENT_ERROR_OTHER_ERROR, _("Cannot store attachment with URI ā€œ%sā€"), uri);
+                                       else
+                                               g_propagate_error (error, EC_ERROR_EX 
(E_CLIENT_ERROR_OTHER_ERROR, _("Failed to read attachment URI")));
+                               }
+
+                               g_free (uri);
+                       } else {
+                               const gchar *base64_data;
+
+                               base64_data = i_cal_attach_get_data (attach);
+
+                               if (base64_data) {
+                                       builder = json_builder_new_immutable ();
+                                       e_m365_attachment_begin_attachment (builder, 
E_M365_ATTACHMENT_DATA_TYPE_FILE);
+                                       e_m365_file_attachment_add_content_bytes (builder, base64_data);
+                               } else {
+                                       g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_OTHER_ERROR, 
_("Failed to get inline attachment data")));
+                               }
+                       }
+
+                       if (builder) {
+                               ICalParameter *param;
+                               const gchar *tmp;
+
+                               param = i_cal_property_get_first_parameter (prop, I_CAL_FILENAME_PARAMETER);
+
+                               if (param) {
+                                       tmp = i_cal_parameter_get_filename (param);
+
+                                       if (tmp && *tmp)
+                                               e_m365_attachment_add_name (builder, tmp);
+
+                                       g_clear_object (&param);
+                               }
+
+                               param = i_cal_property_get_first_parameter (prop, I_CAL_FMTTYPE_PARAMETER);
+
+                               if (param) {
+                                       tmp = i_cal_parameter_get_fmttype (param);
+
+                                       if (tmp && *tmp)
+                                               e_m365_attachment_add_content_type (builder, tmp);
+                                       else
+                                               e_m365_attachment_add_content_type (builder, 
"application/octet-stream");
+
+                                       g_clear_object (&param);
+                               } else {
+                                       e_m365_attachment_add_content_type (builder, 
"application/octet-stream");
+                               }
+
+                               e_m365_attachment_end_attachment (builder);
+
+                               success = add_attachment_func (cnc, NULL,
+                                       group_id, folder_id, m365_id,
+                                       builder, NULL, cancellable, error);
+
+                               g_object_unref (builder);
+                       }
+
+                       g_object_unref (attach);
+               }
+
+               if (save_attachs != new_attachs)
+                       g_slist_free_full (save_attachs, g_object_unref);
+       }
+
+       if (old_attachs && success) {
+               GHashTableIter iter;
+               gpointer key;
+
+               g_hash_table_iter_init (&iter, old_attachs);
+
+               while (g_hash_table_iter_next (&iter, &key, NULL) && success) {
+                       const gchar *attachment_id = key;
+
+                       success = delete_attachment_func (cnc, NULL,
+                               group_id, folder_id, i_cal_component_get_uid (new_comp),
+                               attachment_id, cancellable, error);
+               }
+       }
+
+       if (old_attachs)
+               g_hash_table_destroy (old_attachs);
+       g_slist_free_full (new_attachs, g_object_unref);
+
+       return success;
+}
+
+static void
+ecb_m365_get_task_status (EM365Connection *cnc,
+                         const gchar *group_id,
+                         const gchar *folder_id,
+                         const gchar *attachments_dir,
+                         ETimezoneCache *timezone_cache,
+                         EM365Task *m365_task,
+                         ICalComponent *inout_comp,
+                         ICalPropertyKind prop_kind)
+{
+       ICalPropertyStatus status = I_CAL_STATUS_NONE;
+
+       switch (e_m365_task_get_status (m365_task)) {
+       case E_M365_STATUS_NOT_STARTED:
+               break;
+       case E_M365_STATUS_IN_PROGRESS:
+       case E_M365_STATUS_WAITING_ON_OTHERS:
+               status = I_CAL_STATUS_INPROCESS;
+               break;
+       case E_M365_STATUS_COMPLETED:
+               status = I_CAL_STATUS_COMPLETED;
+               break;
+       case E_M365_STATUS_DEFERRED:
+               status = I_CAL_STATUS_CANCELLED;
+               break;
+       default:
+               break;
+       }
+
+       if (status != I_CAL_STATUS_NONE)
+               i_cal_component_take_property (inout_comp, i_cal_property_new_status (status));
+}
+
+static void
+ecb_m365_add_task_status (EM365Connection *cnc,
+                         const gchar *group_id,
+                         const gchar *folder_id,
+                         ETimezoneCache *timezone_cache,
+                         ICalComponent *new_comp,
+                         ICalComponent *old_comp,
+                         ICalPropertyKind prop_kind,
+                         JsonBuilder *builder)
+{
+       ICalProperty *new_prop, *old_prop;
+       ICalPropertyStatus new_value, old_value;
+
+       new_prop = i_cal_component_get_first_property (new_comp, prop_kind);
+       old_prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
+
+       if (!new_prop && !old_prop)
+               return;
+
+       new_value = new_prop ? i_cal_property_get_status (new_prop) : I_CAL_STATUS_NONE;
+       old_value = old_prop ? i_cal_property_get_status (old_prop) : I_CAL_STATUS_NONE;
+
+       if (new_value != old_value) {
+               EM365StatusType value = E_M365_STATUS_UNKNOWN;
+
+               switch (new_value) {
+               case I_CAL_STATUS_NONE:
+                       value = E_M365_STATUS_NOT_STARTED;
+                       break;
+               case I_CAL_STATUS_INPROCESS:
+                       value = E_M365_STATUS_IN_PROGRESS;
+                       break;
+               case I_CAL_STATUS_COMPLETED:
+                       value = E_M365_STATUS_COMPLETED;
+                       break;
+               case I_CAL_STATUS_CANCELLED:
+                       value = E_M365_STATUS_DEFERRED;
+                       break;
+               default:
+                       break;
+               }
+
+               if (value != E_M365_STATUS_UNKNOWN)
+                       e_m365_task_add_status (builder, value);
+       }
+
+       g_clear_object (&new_prop);
+       g_clear_object (&old_prop);
+}
+
+#define SIMPLE_FIELD(propknd, getfn, addfn) { propknd, FALSE, getfn, NULL, addfn, NULL }
+#define COMPLEX_FIELD(propknd, getfn, addfn) { propknd, FALSE, NULL, getfn, NULL, addfn }
+#define COMPLEX_FIELD_2(propknd, getfn, addfn) { propknd, TRUE, NULL, getfn, NULL, addfn }
+
+struct _mappings {
+       ICalPropertyKind prop_kind;
+       gboolean add_in_second_go;
+       void            (* get_simple_func)     (EM365Connection *cnc,
+                                                const gchar *group_id,
+                                                const gchar *folder_id,
+                                                const gchar *attachments_dir,
+                                                ETimezoneCache *timezone_cache,
+                                                EM365Event *m365_event,
+                                                ICalComponent *inout_comp,
+                                                ICalPropertyKind prop_kind);
+       gboolean        (* get_func)            (EM365Connection *cnc,
+                                                const gchar *group_id,
+                                                const gchar *folder_id,
+                                                const gchar *attachments_dir,
+                                                ETimezoneCache *timezone_cache,
+                                                EM365Event *m365_event,
+                                                ICalComponent *inout_comp,
+                                                ICalPropertyKind prop_kind,
+                                                GCancellable *cancellable,
+                                                GError **error);
+       void            (* add_simple_func)     (EM365Connection *cnc,
+                                                const gchar *group_id,
+                                                const gchar *folder_id,
+                                                ETimezoneCache *timezone_cache,
+                                                ICalComponent *new_comp,
+                                                ICalComponent *old_comp, /* nullable */
+                                                ICalPropertyKind prop_kind,
+                                                JsonBuilder *builder);
+       gboolean        (* add_func)            (EM365Connection *cnc,
+                                                const gchar *group_id,
+                                                const gchar *folder_id,
+                                                ETimezoneCache *timezone_cache,
+                                                ICalComponent *new_comp,
+                                                ICalComponent *old_comp, /* nullable */
+                                                ICalPropertyKind prop_kind,
+                                                const gchar *m365_id,
+                                                JsonBuilder *builder,
+                                                GCancellable *cancellable,
+                                                GError **error);
+} event_mappings[] = {
+       SIMPLE_FIELD    (I_CAL_UID_PROPERTY,            ecb_m365_get_uid,               NULL),
+       SIMPLE_FIELD    (I_CAL_CREATED_PROPERTY,        ecb_m365_get_date_time,         NULL),
+       SIMPLE_FIELD    (I_CAL_LASTMODIFIED_PROPERTY,   ecb_m365_get_date_time,         NULL),
+       SIMPLE_FIELD    (I_CAL_DTSTART_PROPERTY,        ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
+       SIMPLE_FIELD    (I_CAL_DTEND_PROPERTY,          ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
+       SIMPLE_FIELD    (I_CAL_CATEGORIES_PROPERTY,     ecb_m365_get_categories,        
ecb_m365_add_categories),
+       SIMPLE_FIELD    (I_CAL_SUMMARY_PROPERTY,        ecb_m365_get_subject,           ecb_m365_add_subject),
+       SIMPLE_FIELD    (I_CAL_DESCRIPTION_PROPERTY,    ecb_m365_get_body,              ecb_m365_add_body),
+       SIMPLE_FIELD    (I_CAL_CLASS_PROPERTY,          ecb_m365_get_sensitivity,       
ecb_m365_add_sensitivity),
+       SIMPLE_FIELD    (I_CAL_TRANSP_PROPERTY,         ecb_m365_get_show_as,           ecb_m365_add_show_as),
+       SIMPLE_FIELD    (I_CAL_LOCATION_PROPERTY,       ecb_m365_get_location,          
ecb_m365_add_location),
+       SIMPLE_FIELD    (I_CAL_ORGANIZER_PROPERTY,      ecb_m365_get_organizer,         
ecb_m365_add_organizer),
+       SIMPLE_FIELD    (I_CAL_ATTENDEE_PROPERTY,       ecb_m365_get_attendees,         
ecb_m365_add_attendees),
+       SIMPLE_FIELD    (I_CAL_PRIORITY_PROPERTY,       ecb_m365_get_importance,        
ecb_m365_add_importance),
+       SIMPLE_FIELD    (I_CAL_STATUS_PROPERTY,         ecb_m365_get_event_status,      NULL),
+       COMPLEX_FIELD   (I_CAL_RRULE_PROPERTY,          ecb_m365_get_recurrence,        
ecb_m365_add_recurrence),
+       COMPLEX_FIELD   (I_CAL_X_PROPERTY,              ecb_m365_get_reminder,          
ecb_m365_add_reminder),
+       COMPLEX_FIELD   (I_CAL_X_PROPERTY,              NULL,                           
ecb_m365_add_online_meeting),
+       COMPLEX_FIELD_2 (I_CAL_ATTACH_PROPERTY,         ecb_m365_get_attachments,       
ecb_m365_add_attachments)
+}, task_mappings[] = {
+       SIMPLE_FIELD    (I_CAL_UID_PROPERTY,            ecb_m365_get_uid,               NULL),
+       SIMPLE_FIELD    (I_CAL_CREATED_PROPERTY,        ecb_m365_get_date_time,         NULL),
+       SIMPLE_FIELD    (I_CAL_LASTMODIFIED_PROPERTY,   ecb_m365_get_date_time,         NULL),
+       SIMPLE_FIELD    (I_CAL_DTSTART_PROPERTY,        ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
+       SIMPLE_FIELD    (I_CAL_DUE_PROPERTY,            ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
+       SIMPLE_FIELD    (I_CAL_COMPLETED_PROPERTY,      ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
+       SIMPLE_FIELD    (I_CAL_CATEGORIES_PROPERTY,     ecb_m365_get_categories,        
ecb_m365_add_categories),
+       SIMPLE_FIELD    (I_CAL_SUMMARY_PROPERTY,        ecb_m365_get_subject,           ecb_m365_add_subject),
+       SIMPLE_FIELD    (I_CAL_DESCRIPTION_PROPERTY,    ecb_m365_get_body,              ecb_m365_add_body),
+       SIMPLE_FIELD    (I_CAL_CLASS_PROPERTY,          ecb_m365_get_sensitivity,       
ecb_m365_add_sensitivity),
+       SIMPLE_FIELD    (I_CAL_STATUS_PROPERTY,         ecb_m365_get_task_status,       
ecb_m365_add_task_status),
+       COMPLEX_FIELD   (I_CAL_RRULE_PROPERTY,          ecb_m365_get_recurrence,        
ecb_m365_add_recurrence),
+       COMPLEX_FIELD   (I_CAL_X_PROPERTY,              ecb_m365_get_reminder,          
ecb_m365_add_reminder),
+       COMPLEX_FIELD_2 (I_CAL_ATTACH_PROPERTY,         ecb_m365_get_attachments,       
ecb_m365_add_attachments)
+};
+
+static const struct _mappings *
+ecb_m365_get_mappings_for_kind (ICalComponentKind kind,
+                               guint *out_n_elements)
+{
+       if (kind == I_CAL_VEVENT_COMPONENT) {
+               *out_n_elements = G_N_ELEMENTS (event_mappings);
+               return event_mappings;
+       }
+
+       if (kind == I_CAL_VTODO_COMPONENT) {
+               *out_n_elements = G_N_ELEMENTS (task_mappings);
+               return task_mappings;
+       }
+
+       g_warn_if_reached ();
+
+       return NULL;
+}
+
+ICalComponent *
+e_cal_backend_m365_utils_json_to_ical (EM365Connection *cnc,
+                                      const gchar *group_id,
+                                      const gchar *folder_id,
+                                      const gchar *attachments_dir,
+                                      ETimezoneCache *timezone_cache,
+                                      ICalComponentKind kind,
+                                      JsonObject *m365_object,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+       const struct _mappings *mappings;
+       ICalComponent *icomp = NULL;
+       guint ii, n_elements = 0;
+       gboolean success = TRUE;
+
+       g_return_val_if_fail (m365_object != NULL, NULL);
+
+       mappings = ecb_m365_get_mappings_for_kind (kind, &n_elements);
+       g_return_val_if_fail (mappings != NULL, NULL);
+
+       if (kind == I_CAL_VEVENT_COMPONENT)
+               icomp = i_cal_component_new_vevent ();
+       else if (kind == I_CAL_VTODO_COMPONENT)
+               icomp = i_cal_component_new_vtodo ();
+       else
+               g_warn_if_reached ();
+
+       if (!icomp)
+               return NULL;
+
+       for (ii = 0; success && ii < n_elements; ii++) {
+               if (mappings[ii].get_simple_func) {
+                       mappings[ii].get_simple_func (cnc,
+                               group_id,
+                               folder_id,
+                               attachments_dir,
+                               timezone_cache,
+                               m365_object,
+                               icomp,
+                               mappings[ii].prop_kind);
+               } else if (mappings[ii].get_func) {
+                       success = mappings[ii].get_func (cnc,
+                               group_id,
+                               folder_id,
+                               attachments_dir,
+                               timezone_cache,
+                               m365_object,
+                               icomp,
+                               mappings[ii].prop_kind,
+                               cancellable,
+                               error);
+               }
+       }
+
+       if (!success)
+               g_clear_object (&icomp);
+
+       return icomp;
+}
+
+JsonBuilder *
+e_cal_backend_m365_utils_ical_to_json (EM365Connection *cnc,
+                                      const gchar *group_id,
+                                      const gchar *folder_id,
+                                      ETimezoneCache *timezone_cache,
+                                      ICalComponentKind kind,
+                                      ICalComponent *new_comp,
+                                      ICalComponent *old_comp, /* nullable */
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+       const struct _mappings *mappings;
+       JsonBuilder *builder;
+       guint ii, n_elements = 0;
+       gboolean success = TRUE;
+
+       g_return_val_if_fail (new_comp != NULL, NULL);
+
+       mappings = ecb_m365_get_mappings_for_kind (kind, &n_elements);
+       g_return_val_if_fail (mappings != NULL, NULL);
+
+       builder = json_builder_new_immutable ();
+       e_m365_json_begin_object_member (builder, NULL);
+
+       for (ii = 0; success && ii < n_elements; ii++) {
+               if (mappings[ii].add_simple_func) {
+                       mappings[ii].add_simple_func (cnc,
+                               group_id,
+                               folder_id,
+                               timezone_cache,
+                               new_comp,
+                               old_comp,
+                               mappings[ii].prop_kind,
+                               builder);
+               } else if (!mappings[ii].add_in_second_go && mappings[ii].add_func) {
+                       success = mappings[ii].add_func (cnc,
+                               group_id,
+                               folder_id,
+                               timezone_cache,
+                               new_comp,
+                               old_comp,
+                               mappings[ii].prop_kind,
+                               NULL,
+                               builder,
+                               cancellable,
+                               error);
+               }
+       }
+
+       e_m365_json_end_object_member (builder);
+
+       if (!success)
+               g_clear_object (&builder);
+
+       return builder;
+}
+
+gboolean
+e_cal_backend_m365_utils_ical_to_json_2nd_go (EM365Connection *cnc,
+                                             const gchar *group_id,
+                                             const gchar *folder_id,
+                                             ETimezoneCache *timezone_cache,
+                                             ICalComponentKind kind,
+                                             ICalComponent *new_comp,
+                                             ICalComponent *old_comp, /* nullable */
+                                             const gchar *m365_id,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+       const struct _mappings *mappings;
+       guint ii, n_elements = 0;
+       gboolean success = TRUE;
+
+       g_return_val_if_fail (new_comp != NULL, FALSE);
+       g_return_val_if_fail (m365_id != NULL, FALSE);
+
+       mappings = ecb_m365_get_mappings_for_kind (kind, &n_elements);
+       g_return_val_if_fail (mappings != NULL, FALSE);
+
+       for (ii = 0; success && ii < n_elements; ii++) {
+               if (mappings[ii].add_in_second_go && mappings[ii].add_func) {
+                       success = mappings[ii].add_func (cnc,
+                               group_id,
+                               folder_id,
+                               timezone_cache,
+                               new_comp,
+                               old_comp,
+                               mappings[ii].prop_kind,
+                               m365_id,
+                               NULL,
+                               cancellable,
+                               error);
+               }
+       }
+
+       return success;
+}
diff --git a/src/Microsoft365/calendar/e-cal-backend-m365-utils.h 
b/src/Microsoft365/calendar/e-cal-backend-m365-utils.h
new file mode 100644
index 00000000..658d6460
--- /dev/null
+++ b/src/Microsoft365/calendar/e-cal-backend-m365-utils.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2020 Red Hat (www.redhat.com)
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef E_CAL_BACKEND_M365_UTILS_H
+#define E_CAL_BACKEND_M365_UTILS_H
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <libecal/libecal.h>
+
+#include "common/e-m365-connection.h"
+
+G_BEGIN_DECLS
+
+ICalComponent *
+e_cal_backend_m365_utils_json_to_ical  (EM365Connection *cnc,
+                                        const gchar *group_id,
+                                        const gchar *folder_id,
+                                        const gchar *attachments_dir,
+                                        ETimezoneCache *timezone_cache,
+                                        ICalComponentKind kind,
+                                        JsonObject *m365_object,
+                                        GCancellable *cancellable,
+                                        GError **error);
+JsonBuilder *
+e_cal_backend_m365_utils_ical_to_json  (EM365Connection *cnc,
+                                        const gchar *group_id,
+                                        const gchar *folder_id,
+                                        ETimezoneCache *timezone_cache,
+                                        ICalComponentKind kind,
+                                        ICalComponent *new_comp,
+                                        ICalComponent *old_comp, /* nullable */
+                                        GCancellable *cancellable,
+                                        GError **error);
+gboolean
+e_cal_backend_m365_utils_ical_to_json_2nd_go
+                                       (EM365Connection *cnc,
+                                        const gchar *group_id,
+                                        const gchar *folder_id,
+                                        ETimezoneCache *timezone_cache,
+                                        ICalComponentKind kind,
+                                        ICalComponent *new_comp,
+                                        ICalComponent *old_comp, /* nullable */
+                                        const gchar *m365_id,
+                                        GCancellable *cancellable,
+                                        GError **error);
+
+G_END_DECLS
+
+#endif /* E_CAL_BACKEND_M365_UTILS_H */
diff --git a/src/Microsoft365/calendar/e-cal-backend-m365.c b/src/Microsoft365/calendar/e-cal-backend-m365.c
index 6f125dfc..4802be90 100644
--- a/src/Microsoft365/calendar/e-cal-backend-m365.c
+++ b/src/Microsoft365/calendar/e-cal-backend-m365.c
@@ -19,6 +19,7 @@
 #include "common/e-m365-tz-utils.h"
 #include "common/e-source-m365-folder.h"
 
+#include "e-cal-backend-m365-utils.h"
 #include "e-cal-backend-m365.h"
 
 #define EC_ERROR(_code) e_client_error_create (_code, NULL)
@@ -118,2516 +119,6 @@ ecb_m365_split_extra (gchar *inout_extra,
                *out_ical_comp = enter;
 }
 
-static void
-ecb_m365_get_uid (ECalBackendM365 *cbm365,
-                 JsonObject *m365_object,
-                 ICalComponent *inout_comp,
-                 ICalPropertyKind prop_kind)
-{
-       const gchar *id;
-
-       switch (i_cal_component_isa (inout_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               id = e_m365_event_get_id (m365_object);
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               id = e_m365_task_get_id (m365_object);
-               break;
-       default:
-               g_warn_if_reached ();
-               return;
-       }
-
-       i_cal_component_set_uid (inout_comp, id);
-}
-
-static void
-ecb_m365_get_date_time (ECalBackendM365 *cbm365,
-                       JsonObject *m365_object,
-                       ICalComponent *inout_comp,
-                       ICalPropertyKind prop_kind)
-{
-       time_t tt = (time_t) 0;
-
-       if (prop_kind == I_CAL_CREATED_PROPERTY) {
-               switch (i_cal_component_isa (inout_comp)) {
-               case I_CAL_VEVENT_COMPONENT:
-                       tt = e_m365_event_get_created_date_time (m365_object);
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       tt = e_m365_task_get_created_date_time (m365_object);
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       return;
-               }
-       } else if (prop_kind == I_CAL_LASTMODIFIED_PROPERTY) {
-               switch (i_cal_component_isa (inout_comp)) {
-               case I_CAL_VEVENT_COMPONENT:
-                       tt = e_m365_event_get_last_modified_date_time (m365_object);
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       tt = e_m365_task_get_last_modified_date_time (m365_object);
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       return;
-               }
-       } else {
-               g_warn_if_reached ();
-       }
-
-       if (tt > (time_t) 0) {
-               ICalProperty *prop;
-               ICalTime *itt;
-
-               itt = i_cal_time_new_from_timet_with_zone (tt, FALSE, i_cal_timezone_get_utc_timezone ());
-
-               if (prop_kind == I_CAL_CREATED_PROPERTY)
-                       prop = i_cal_property_new_created (itt);
-               else /* I_CAL_LASTMODIFIED_PROPERTY */
-                       prop = i_cal_property_new_lastmodified (itt);
-
-               i_cal_component_take_property (inout_comp, prop);
-
-               g_clear_object (&itt);
-       }
-}
-
-static ICalTimezone *
-ecb_m365_get_timezone_sync (ECalBackendM365 *cbm365,
-                           const gchar *tzid)
-{
-       ICalTimezone *zone;
-       ECalCache *cal_cache;
-
-       if (!tzid)
-               return NULL;
-
-       cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbm365));
-
-       if (!cal_cache)
-               return NULL;
-
-       zone = e_cal_cache_resolve_timezone_cb (tzid, cal_cache, NULL, NULL);
-
-       g_object_unref (cal_cache);
-
-       return zone;
-}
-
-static void
-ecb_m365_get_date_time_zone (ECalBackendM365 *cbm365,
-                            JsonObject *m365_object,
-                            ICalComponent *inout_comp,
-                            ICalPropertyKind prop_kind)
-{
-       EM365DateTimeWithZone *value;
-       ICalTimezone *tz;
-       ICalTime *itt;
-       time_t tt;
-       const gchar *tzid, *zone;
-       gboolean is_date;
-
-       if (prop_kind == I_CAL_DTSTART_PROPERTY) {
-               switch (i_cal_component_isa (inout_comp)) {
-               case I_CAL_VEVENT_COMPONENT:
-                       value = e_m365_event_get_start (m365_object);
-                       tzid = e_m365_event_get_original_start_timezone (m365_object);
-                       is_date = e_m365_event_get_is_all_day (m365_object);
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       value = e_m365_task_get_start_date_time (m365_object);
-                       tzid = "UTC";
-                       is_date = TRUE;
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       return;
-               }
-       } else if (prop_kind == I_CAL_DTEND_PROPERTY) {
-               value = e_m365_event_get_end (m365_object);
-               tzid = e_m365_event_get_original_end_timezone (m365_object);
-               is_date = e_m365_event_get_is_all_day (m365_object);
-       } else if (prop_kind == I_CAL_COMPLETED_PROPERTY) {
-               value = e_m365_task_get_completed_date_time (m365_object);
-               tzid = "UTC";
-               is_date = TRUE;
-       } else if (prop_kind == I_CAL_DUE_PROPERTY) {
-               value = e_m365_task_get_due_date_time (m365_object);
-               tzid = "UTC";
-               is_date = TRUE;
-       } else {
-               g_warn_if_reached ();
-               return;
-       }
-
-       if (!value)
-               return;
-
-       tt = e_m365_date_time_get_date_time (value);
-       zone = e_m365_date_time_get_time_zone (value);
-
-       if (zone && *zone)
-               zone = e_m365_tz_utils_get_ical_equivalent (zone);
-
-       tz = zone && *zone ? ecb_m365_get_timezone_sync (cbm365, zone) : NULL;
-
-       if (!tz)
-               tz = i_cal_timezone_get_utc_timezone ();
-
-       itt = i_cal_time_new_from_timet_with_zone (tt, is_date, tz);
-
-       tzid = e_m365_tz_utils_get_ical_equivalent (tzid);
-
-       if (!tzid)
-               tzid = "UTC";
-
-       tz = ecb_m365_get_timezone_sync (cbm365, tzid);
-
-       if (tz && !is_date)
-               i_cal_time_convert_to_zone_inplace (itt, tz);
-
-       if (prop_kind == I_CAL_DTSTART_PROPERTY)
-               i_cal_component_set_dtstart (inout_comp, itt);
-       else if (prop_kind == I_CAL_DTEND_PROPERTY)
-               i_cal_component_set_dtend (inout_comp, itt);
-       else if (prop_kind == I_CAL_COMPLETED_PROPERTY)
-               i_cal_component_take_property (inout_comp, i_cal_property_new_completed (itt));
-       else /* if (prop_kind == I_CAL_DUE_PROPERTY) */
-               i_cal_component_set_due (inout_comp, itt);
-
-       g_clear_object (&itt);
-}
-
-static void
-ecb_m365_add_date_time_zone (ECalBackendM365 *cbm365,
-                            ICalComponent *new_comp,
-                            ICalComponent *old_comp,
-                            ICalPropertyKind prop_kind,
-                            JsonBuilder *builder)
-{
-       ICalProperty *new_prop;
-       ICalParameter *new_param;
-       ICalTime *old_value, *new_value;
-       const gchar *new_tzid = NULL;
-       void (* add_func) (JsonBuilder *builder, time_t date_time, const gchar *zone) = NULL;
-       gboolean same = FALSE;
-
-       if (prop_kind == I_CAL_DTSTART_PROPERTY) {
-               new_value = i_cal_component_get_dtstart (new_comp);
-               old_value = old_comp ? i_cal_component_get_dtstart (old_comp) : NULL;
-
-               switch (i_cal_component_isa (new_comp)) {
-               case I_CAL_VEVENT_COMPONENT:
-                       add_func = e_m365_event_add_start;
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       add_func = e_m365_task_add_start_date_time;
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       return;
-               }
-       } else if (prop_kind == I_CAL_DTEND_PROPERTY) {
-               new_value = i_cal_component_get_dtend (new_comp);
-               old_value = old_comp ? i_cal_component_get_dtend (old_comp) : NULL;
-               add_func = e_m365_event_add_end;
-       } else if (prop_kind == I_CAL_COMPLETED_PROPERTY) {
-               ICalProperty *new_prop, *old_prop;
-
-               new_prop = i_cal_component_get_first_property (new_comp, prop_kind);
-               old_prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
-               new_value = new_prop ? i_cal_property_get_completed (new_prop) : NULL;
-               old_value = old_prop ? i_cal_property_get_completed (old_prop) : NULL;
-               add_func = e_m365_task_add_completed_date_time;
-
-               g_clear_object (&new_prop);
-               g_clear_object (&old_prop);
-       } else if (prop_kind == I_CAL_DUE_PROPERTY) {
-               new_value = i_cal_component_get_due (new_comp);
-               old_value = old_comp ? i_cal_component_get_due (old_comp) : NULL;
-               add_func = e_m365_task_add_due_date_time;
-       } else {
-               g_warn_if_reached ();
-               return;
-       }
-
-       if (!new_value && !old_value)
-               return;
-
-       new_prop = i_cal_component_get_first_property (new_comp, prop_kind);
-       new_param = new_prop ? i_cal_property_get_first_parameter (new_prop, I_CAL_TZID_PARAMETER) : NULL;
-
-       if (new_param)
-               new_tzid = i_cal_parameter_get_tzid (new_param);
-
-       if (new_value && old_value) {
-               same = i_cal_time_compare (new_value, old_value) == 0;
-
-               if (same) {
-                       ICalProperty *old_prop;
-                       ICalParameter *old_param;
-                       const gchar *old_tzid;
-
-                       old_prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
-                       old_param = old_prop ? i_cal_property_get_first_parameter (old_prop, 
I_CAL_TZID_PARAMETER) : NULL;
-                       old_tzid = old_param ? i_cal_parameter_get_tzid (old_param) : NULL;
-
-                       same = g_strcmp0 (old_tzid, new_tzid) == 0;
-
-                       g_clear_object (&old_param);
-                       g_clear_object (&old_prop);
-               }
-       }
-
-       if (!same) {
-               ICalTimezone *izone = NULL;
-               const gchar *wzone = NULL;
-               time_t tt;
-
-               if (new_tzid) {
-                       izone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbm365), new_tzid);
-
-                       if (izone)
-                               wzone = e_m365_tz_utils_get_msdn_equivalent (i_cal_timezone_get_location 
(izone));
-               }
-
-               tt = i_cal_time_as_timet_with_zone (new_value, wzone ? NULL : izone);
-
-               add_func (builder, tt, wzone);
-       }
-
-       g_clear_object (&new_prop);
-       g_clear_object (&new_param);
-       g_clear_object (&new_value);
-       g_clear_object (&old_value);
-}
-
-static void
-ecb_m365_get_categories (ECalBackendM365 *cbm365,
-                        JsonObject *m365_object,
-                        ICalComponent *inout_comp,
-                        ICalPropertyKind prop_kind)
-{
-       JsonArray *categories;
-
-       switch (i_cal_component_isa (inout_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               categories = e_m365_event_get_categories (m365_object);
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               categories = e_m365_task_get_categories (m365_object);
-               break;
-       default:
-               g_warn_if_reached ();
-               return;
-       }
-
-       if (categories) {
-               GString *categories_str = NULL;
-               guint ii, len;
-
-               len = json_array_get_length (categories);
-
-               for (ii = 0; ii < len; ii++) {
-                       const gchar *category;
-
-                       category = json_array_get_string_element (categories, ii);
-
-                       if (category && *category) {
-                               gchar *ical_str = i_cal_value_encode_ical_string (category);
-
-                               if (ical_str && *ical_str) {
-                                       if (!categories_str) {
-                                               categories_str = g_string_new (ical_str);
-                                       } else {
-                                               g_string_append_c (categories_str, ',');
-                                               g_string_append (categories_str, ical_str);
-                                       }
-                               }
-
-                               g_free (ical_str);
-                       }
-               }
-
-               if (categories_str) {
-                       i_cal_component_take_property (inout_comp, i_cal_property_new_categories 
(categories_str->str));
-
-                       g_string_free (categories_str, TRUE);
-               }
-       }
-}
-
-static void
-ecb_m365_extract_categories (ICalComponent *comp,
-                            GHashTable **out_hash, /* gchar * ~> NULL */
-                            GSList **out_slist) /* gchar * */
-{
-       ICalProperty *prop;
-
-       if (!comp)
-               return;
-
-       for (prop = i_cal_component_get_first_property (comp, I_CAL_CATEGORIES_PROPERTY);
-            prop;
-            g_object_unref (prop), prop = i_cal_component_get_next_property (comp, 
I_CAL_CATEGORIES_PROPERTY)) {
-               const gchar *categories;
-
-               categories = i_cal_property_get_categories (prop);
-
-               if (categories && *categories) {
-                       if (out_hash && !*out_hash)
-                               *out_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
-                       if (strchr (categories, ',')) {
-                               gchar **strv;
-                               guint ii;
-
-                               strv = g_strsplit (categories, ",", -1);
-
-                               for (ii = 0; strv[ii]; ii++) {
-                                       gchar *category = g_strchomp (strv[ii]);
-
-                                       if (*category) {
-                                               if (out_hash) {
-                                                       g_hash_table_insert (*out_hash, category, NULL);
-                                               } else if (out_slist) {
-                                                       *out_slist = g_slist_prepend (*out_slist, category);
-                                               } else {
-                                                       g_warn_if_reached ();
-                                                       g_free (category);
-                                               }
-                                       } else {
-                                               g_free (category);
-                                       }
-                               }
-
-                               g_free (strv);
-                       } else if (out_hash) {
-                               g_hash_table_insert (*out_hash, g_strchomp (g_strdup (categories)), NULL);
-                       } else if (out_slist) {
-                               *out_slist = g_slist_prepend (*out_slist, g_strchomp (g_strdup (categories)));
-                       } else {
-                               g_warn_if_reached ();
-                       }
-               }
-       }
-
-       g_clear_object (&prop);
-
-       if (out_slist && *out_slist)
-               *out_slist = g_slist_reverse (*out_slist);
-}
-
-static void
-ecb_m365_add_categories (ECalBackendM365 *cbm365,
-                        ICalComponent *new_comp,
-                        ICalComponent *old_comp,
-                        ICalPropertyKind prop_kind,
-                        JsonBuilder *builder)
-{
-       GHashTable *old_value = NULL;
-       GSList *new_value = NULL;
-       void (* begin_categories_func) (JsonBuilder *builder);
-       void (* end_categories_func) (JsonBuilder *builder);
-       void (* add_category_func) (JsonBuilder *builder, const gchar *category);
-
-       switch (i_cal_component_isa (new_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               begin_categories_func = e_m365_event_begin_categories;
-               end_categories_func = e_m365_event_end_categories;
-               add_category_func = e_m365_event_add_category;
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               begin_categories_func = e_m365_task_begin_categories;
-               end_categories_func = e_m365_task_end_categories;
-               add_category_func = e_m365_task_add_category;
-               break;
-       default:
-               g_warn_if_reached ();
-               return;
-       }
-
-       ecb_m365_extract_categories (new_comp, NULL, &new_value);
-       ecb_m365_extract_categories (old_comp, &old_value, NULL);
-
-       if (!new_value && !old_value)
-               return;
-
-       if (new_value) {
-               GSList *link;
-               gboolean same = FALSE;
-
-               if (old_value && g_hash_table_size (old_value) == g_slist_length (new_value)) {
-                       same = TRUE;
-
-                       for (link = new_value; link && same; link = g_slist_next (link)) {
-                               const gchar *category = link->data;
-
-                               same = g_hash_table_contains (old_value, category);
-                       }
-               }
-
-               if (!same) {
-                       begin_categories_func (builder);
-
-                       for (link = new_value; link; link = g_slist_next (link)) {
-                               const gchar *category = link->data;
-
-                               add_category_func (builder, category);
-                       }
-
-                       end_categories_func (builder);
-               }
-       } else {
-               begin_categories_func (builder);
-               end_categories_func (builder);
-       }
-
-       if (new_value)
-               g_slist_free_full (new_value, g_free);
-       if (old_value)
-               g_hash_table_destroy (old_value);
-}
-
-static void
-ecb_m365_get_subject (ECalBackendM365 *cbm365,
-                     EM365Event *m365_object,
-                     ICalComponent *inout_comp,
-                     ICalPropertyKind prop_kind)
-{
-       const gchar *subject;
-
-       switch (i_cal_component_isa (inout_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               subject = e_m365_event_get_subject (m365_object);
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               subject = e_m365_task_get_subject (m365_object);
-               break;
-       default:
-               g_warn_if_reached ();
-               return;
-       }
-
-       if (subject)
-               i_cal_component_set_summary (inout_comp, subject);
-}
-
-static void
-ecb_m365_add_subject (ECalBackendM365 *cbm365,
-                     ICalComponent *new_comp,
-                     ICalComponent *old_comp,
-                     ICalPropertyKind prop_kind,
-                     JsonBuilder *builder)
-{
-       const gchar *new_value, *old_value;
-
-       new_value = i_cal_component_get_summary (new_comp);
-       old_value = old_comp ? i_cal_component_get_summary (old_comp) : NULL;
-
-       if (g_strcmp0 (new_value, old_value) != 0) {
-               switch (i_cal_component_isa (new_comp)) {
-               case I_CAL_VEVENT_COMPONENT:
-                       e_m365_event_add_subject (builder, new_value ? new_value : "");
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       e_m365_task_add_subject (builder, new_value ? new_value : "");
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       return;
-               }
-       }
-}
-
-static void
-ecb_m365_get_body (ECalBackendM365 *cbm365,
-                  JsonObject *m365_object,
-                  ICalComponent *inout_comp,
-                  ICalPropertyKind prop_kind)
-{
-       EM365ItemBody *value;
-       const gchar *content;
-
-       switch (i_cal_component_isa (inout_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               value = e_m365_event_get_body (m365_object);
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               value = e_m365_task_get_body (m365_object);
-               break;
-       default:
-               g_warn_if_reached ();
-               return;
-       }
-
-       content = value ? e_m365_item_body_get_content (value) : NULL;
-
-       if (content && *content && strcmp (content, "\r\n") != 0)
-               i_cal_component_set_description (inout_comp, content);
-}
-
-static void
-ecb_m365_add_body (ECalBackendM365 *cbm365,
-                  ICalComponent *new_comp,
-                  ICalComponent *old_comp,
-                  ICalPropertyKind prop_kind,
-                  JsonBuilder *builder)
-{
-       const gchar *new_value, *old_value;
-
-       new_value = i_cal_component_get_description (new_comp);
-       old_value = old_comp ? i_cal_component_get_description (old_comp) : NULL;
-
-       if (g_strcmp0 (new_value, old_value) != 0) {
-               switch (i_cal_component_isa (new_comp)) {
-               case I_CAL_VEVENT_COMPONENT:
-                       e_m365_event_add_body (builder, E_M365_ITEM_BODY_CONTENT_TYPE_TEXT, new_value);
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       e_m365_task_add_body (builder, E_M365_ITEM_BODY_CONTENT_TYPE_TEXT, new_value);
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       return;
-               }
-       }
-}
-
-static void
-ecb_m365_get_sensitivity (ECalBackendM365 *cbm365,
-                         JsonObject *m365_object,
-                         ICalComponent *inout_comp,
-                         ICalPropertyKind prop_kind)
-{
-       EM365SensitivityType value;
-       ICalProperty_Class cls = I_CAL_CLASS_NONE;
-
-       switch (i_cal_component_isa (inout_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               value = e_m365_event_get_sensitivity (m365_object);
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               value = e_m365_task_get_sensitivity (m365_object);
-               break;
-       default:
-               g_warn_if_reached ();
-               return;
-       }
-
-       if (value == E_M365_SENSITIVITY_NORMAL)
-               cls = I_CAL_CLASS_PUBLIC;
-       else if (value == E_M365_SENSITIVITY_PERSONAL || value == E_M365_SENSITIVITY_PRIVATE)
-               cls = I_CAL_CLASS_PRIVATE;
-       else if (value == E_M365_SENSITIVITY_CONFIDENTIAL)
-               cls = I_CAL_CLASS_CONFIDENTIAL;
-
-       if (cls != I_CAL_CLASS_NONE)
-               i_cal_component_take_property (inout_comp, i_cal_property_new_class (cls));
-}
-
-static void
-ecb_m365_add_sensitivity (ECalBackendM365 *cbm365,
-                         ICalComponent *new_comp,
-                         ICalComponent *old_comp,
-                         ICalPropertyKind prop_kind,
-                         JsonBuilder *builder)
-{
-       ICalProperty_Class new_value = I_CAL_CLASS_NONE, old_value = I_CAL_CLASS_NONE;
-       ICalProperty *prop;
-
-       prop = i_cal_component_get_first_property (new_comp, prop_kind);
-
-       if (prop) {
-               new_value = i_cal_property_get_class (prop);
-               g_clear_object (&prop);
-       }
-
-       prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
-
-       if (prop) {
-               old_value = i_cal_property_get_class (prop);
-               g_clear_object (&prop);
-       }
-
-       if (new_value != old_value) {
-               EM365SensitivityType value = E_M365_SENSITIVITY_NOT_SET;
-
-               if (new_value == I_CAL_CLASS_PUBLIC)
-                       value = E_M365_SENSITIVITY_NORMAL;
-               else if (new_value == I_CAL_CLASS_PRIVATE)
-                       value = E_M365_SENSITIVITY_PRIVATE;
-               else if (new_value == I_CAL_CLASS_CONFIDENTIAL)
-                       value = E_M365_SENSITIVITY_CONFIDENTIAL;
-
-               switch (i_cal_component_isa (new_comp)) {
-               case I_CAL_VEVENT_COMPONENT:
-                       e_m365_event_add_sensitivity (builder, value);
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       e_m365_task_add_sensitivity (builder, value);
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       return;
-               }
-       }
-}
-
-static void
-ecb_m365_get_show_as (ECalBackendM365 *cbm365,
-                     EM365Event *m365_event,
-                     ICalComponent *inout_comp,
-                     ICalPropertyKind prop_kind)
-{
-       EM365FreeBusyStatusType value;
-       ICalPropertyTransp transp = I_CAL_TRANSP_NONE;
-
-       value = e_m365_event_get_show_as (m365_event);
-
-       if (value == E_M365_FREE_BUSY_STATUS_FREE)
-               transp = I_CAL_TRANSP_TRANSPARENT;
-       else if (value == E_M365_FREE_BUSY_STATUS_BUSY)
-               transp = I_CAL_TRANSP_OPAQUE;
-
-       if (transp != I_CAL_TRANSP_NONE)
-               i_cal_component_take_property (inout_comp, i_cal_property_new_transp (transp));
-}
-
-static void
-ecb_m365_add_show_as (ECalBackendM365 *cbm365,
-                     ICalComponent *new_comp,
-                     ICalComponent *old_comp,
-                     ICalPropertyKind prop_kind,
-                     JsonBuilder *builder)
-{
-       ICalPropertyTransp new_value = I_CAL_TRANSP_NONE, old_value = I_CAL_TRANSP_NONE;
-       ICalProperty *prop;
-
-       prop = i_cal_component_get_first_property (new_comp, prop_kind);
-
-       if (prop) {
-               new_value = i_cal_property_get_transp (prop);
-               g_clear_object (&prop);
-       }
-
-       prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
-
-       if (prop) {
-               old_value = i_cal_property_get_transp (prop);
-               g_clear_object (&prop);
-       }
-
-       if (new_value != old_value) {
-               EM365FreeBusyStatusType value = E_M365_FREE_BUSY_STATUS_NOT_SET;
-
-               if (new_value == I_CAL_TRANSP_TRANSPARENT)
-                       value = E_M365_FREE_BUSY_STATUS_FREE;
-               else if (new_value == I_CAL_TRANSP_OPAQUE)
-                       value = E_M365_FREE_BUSY_STATUS_BUSY;
-
-               e_m365_event_add_show_as (builder, value);
-       }
-}
-
-static void
-ecb_m365_get_location (ECalBackendM365 *cbm365,
-                      EM365Event *m365_event,
-                      ICalComponent *inout_comp,
-                      ICalPropertyKind prop_kind)
-{
-       EM365Location *value;
-       const gchar *tmp;
-
-       value = e_m365_event_get_location (m365_event);
-
-       if (!value)
-               return;
-
-       tmp = e_m365_location_get_display_name (value);
-
-       if (tmp && *tmp)
-               i_cal_component_set_location (inout_comp, tmp);
-}
-
-static void
-ecb_m365_add_location (ECalBackendM365 *cbm365,
-                      ICalComponent *new_comp,
-                      ICalComponent *old_comp,
-                      ICalPropertyKind prop_kind,
-                      JsonBuilder *builder)
-{
-       const gchar *new_value, *old_value;
-
-       new_value = i_cal_component_get_location (new_comp);
-       old_value = old_comp ? i_cal_component_get_location (old_comp) : NULL;
-
-       if (g_strcmp0 (new_value, old_value) != 0) {
-               if (new_value && *new_value) {
-                       e_m365_event_begin_location (builder);
-                       e_m365_location_add_display_name (builder, new_value);
-                       e_m365_event_end_location (builder);
-               } else {
-                       e_m365_event_add_null_location (builder);
-               }
-       }
-}
-
-static void
-ecb_m365_get_organizer (ECalBackendM365 *cbm365,
-                       EM365Event *m365_event,
-                       ICalComponent *inout_comp,
-                       ICalPropertyKind prop_kind)
-{
-       EM365Recipient *value;
-       JsonArray *attendees;
-       const gchar *name;
-       const gchar *address;
-
-       value = e_m365_event_get_organizer (m365_event);
-
-       if (!value)
-               return;
-
-       /* Include the organizer only if there is at least one attendee */
-       attendees = e_m365_event_get_attendees (m365_event);
-
-       if (!attendees || !json_array_get_length (attendees))
-               return;
-
-       name = e_m365_recipient_get_name (value);
-       address = e_m365_recipient_get_address (value);
-
-       if (address && *address) {
-               ECalComponentOrganizer *organizer;
-               gchar *mailto_addr;
-
-               mailto_addr = g_strconcat ("mailto:";, address, NULL);
-               organizer = e_cal_component_organizer_new ();
-               e_cal_component_organizer_set_value (organizer, mailto_addr);
-               g_free (mailto_addr);
-
-               if (name && *name)
-                       e_cal_component_organizer_set_cn (organizer, name);
-
-               i_cal_component_take_property (inout_comp, e_cal_component_organizer_get_as_property 
(organizer));
-               e_cal_component_organizer_free (organizer);
-       }
-}
-
-static const gchar *
-ecb_m365_strip_mailto (const gchar *value)
-{
-       if (value && g_ascii_strncasecmp (value, "mailto:";, 7) == 0)
-               return value + 7;
-
-       return value;
-}
-
-static void
-ecb_m365_add_organizer (ECalBackendM365 *cbm365,
-                       ICalComponent *new_comp,
-                       ICalComponent *old_comp,
-                       ICalPropertyKind prop_kind,
-                       JsonBuilder *builder)
-{
-       ECalComponentOrganizer *new_value = NULL, *old_value = NULL;
-       ICalProperty *prop;
-
-       prop = i_cal_component_get_first_property (new_comp, prop_kind);
-
-       if (prop) {
-               new_value = e_cal_component_organizer_new_from_property (prop);
-               g_clear_object (&prop);
-       }
-
-       prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
-
-       if (prop) {
-               old_value = e_cal_component_organizer_new_from_property (prop);
-               g_clear_object (&prop);
-       }
-
-       if (new_value != old_value && (
-           g_strcmp0 (new_value ? e_cal_component_organizer_get_cn (new_value) : NULL,
-                      old_value ? e_cal_component_organizer_get_cn (old_value) : NULL) != 0 ||
-           g_strcmp0 (new_value ? ecb_m365_strip_mailto (e_cal_component_organizer_get_value (new_value)) : 
NULL,
-                      old_value ? ecb_m365_strip_mailto (e_cal_component_organizer_get_value (old_value)) : 
NULL) != 0)) {
-               if (new_value) {
-                       e_m365_event_add_organizer (builder,
-                                                   e_cal_component_organizer_get_cn (new_value),
-                                                   ecb_m365_strip_mailto 
(e_cal_component_organizer_get_value (new_value)));
-               } else {
-                       e_m365_event_add_null_organizer (builder);
-               }
-       }
-
-       e_cal_component_organizer_free (new_value);
-       e_cal_component_organizer_free (old_value);
-}
-
-static void
-ecb_m365_get_attendees (ECalBackendM365 *cbm365,
-                       EM365Event *m365_event,
-                       ICalComponent *inout_comp,
-                       ICalPropertyKind prop_kind)
-{
-       JsonArray *array;
-       guint ii, sz;
-
-       array = e_m365_event_get_attendees (m365_event);
-
-       if (!array)
-               return;
-
-       sz = json_array_get_length (array);
-
-       for (ii = 0; ii < sz; ii++) {
-               EM365Attendee *m365_attendee;
-               EM365ResponseStatus *m365_status;
-               EM365AttendeeType m365_att_type;
-               EM365EmailAddress *m365_address;
-               ECalComponentAttendee *e_attendee;
-               ICalParameterRole role = I_CAL_ROLE_NONE;
-               gchar *mailto_addr;
-
-               m365_attendee = json_array_get_object_element (array, ii);
-
-               if (!m365_attendee)
-                       continue;
-
-               m365_address = e_m365_attendee_get_email_address (m365_attendee);
-
-               if (!m365_address || !e_m365_email_address_get_address (m365_address))
-                       continue;
-
-               e_attendee = e_cal_component_attendee_new ();
-
-               mailto_addr = g_strconcat ("mailto:";, e_m365_email_address_get_address (m365_address), NULL);
-               e_cal_component_attendee_set_value (e_attendee, mailto_addr);
-               g_free (mailto_addr);
-
-               if (e_m365_email_address_get_name (m365_address))
-                       e_cal_component_attendee_set_cn (e_attendee, e_m365_email_address_get_name 
(m365_address));
-
-               m365_status = e_m365_attendee_get_status (m365_attendee);
-
-               if (m365_status) {
-                       EM365ResponseType m365_response;
-                       ICalParameterPartstat partstat = I_CAL_PARTSTAT_NONE;
-
-                       m365_response = e_m365_response_status_get_response (m365_status);
-
-                       if (m365_response == E_M365_RESPONSE_TENTATIVELY_ACCEPTED)
-                               partstat = I_CAL_PARTSTAT_TENTATIVE;
-                       else if (m365_response == E_M365_RESPONSE_ACCEPTED)
-                               partstat = I_CAL_PARTSTAT_ACCEPTED;
-                       else if (m365_response == E_M365_RESPONSE_DECLINED)
-                               partstat = I_CAL_PARTSTAT_DECLINED;
-                       else if (m365_response == E_M365_RESPONSE_NOT_RESPONDED)
-                               partstat = I_CAL_PARTSTAT_NEEDSACTION;
-
-                       if (partstat != I_CAL_PARTSTAT_NONE) {
-                               time_t tt;
-
-                               e_cal_component_attendee_set_partstat (e_attendee, partstat);
-
-                               tt = e_m365_response_status_get_time (m365_status);
-
-                               if (tt > (time_t) 0) {
-                                       ECalComponentParameterBag *params;
-                                       ICalParameter *param;
-                                       gchar *tmp;
-
-                                       tmp = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) tt);
-                                       params = e_cal_component_attendee_get_parameter_bag (e_attendee);
-
-                                       param = i_cal_parameter_new_x (tmp);
-                                       i_cal_parameter_set_xname (param, "X-M365-STATUS-TIME");
-
-                                       e_cal_component_parameter_bag_take (params, param);
-
-                                       g_free (tmp);
-                               }
-                       }
-               }
-
-               m365_att_type = e_m365_attendee_get_type (m365_attendee);
-
-               if (m365_att_type == E_M365_ATTENDEE_REQUIRED) {
-                       role = I_CAL_ROLE_REQPARTICIPANT;
-                       e_cal_component_attendee_set_cutype (e_attendee, I_CAL_CUTYPE_INDIVIDUAL);
-               } else if (m365_att_type == E_M365_ATTENDEE_OPTIONAL) {
-                       role = I_CAL_ROLE_OPTPARTICIPANT;
-                       e_cal_component_attendee_set_cutype (e_attendee, I_CAL_CUTYPE_INDIVIDUAL);
-               } else if (m365_att_type == E_M365_ATTENDEE_RESOURCE) {
-                       e_cal_component_attendee_set_cutype (e_attendee, I_CAL_CUTYPE_RESOURCE);
-               }
-
-               if (role != I_CAL_ROLE_NONE)
-                       e_cal_component_attendee_set_role (e_attendee, role);
-
-               i_cal_component_take_property (inout_comp, e_cal_component_attendee_get_as_property 
(e_attendee));
-
-               e_cal_component_attendee_free (e_attendee);
-       }
-}
-
-static void
-ecb_m365_extract_attendees (ICalComponent *comp,
-                           GHashTable **out_hash, /* const gchar *ECalComponentAttendee::value ~> 
ECalComponentAttendee * */
-                           GSList **out_slist) /* ECalComponentAttendee * */
-{
-       ICalProperty *prop;
-
-       if (!comp)
-               return;
-
-       for (prop = i_cal_component_get_first_property (comp, I_CAL_ATTENDEE_PROPERTY);
-            prop;
-            g_object_unref (prop), prop = i_cal_component_get_next_property (comp, I_CAL_ATTENDEE_PROPERTY)) 
{
-               ECalComponentAttendee *attendee;
-
-               attendee = e_cal_component_attendee_new_from_property (prop);
-
-               if (attendee && e_cal_component_attendee_get_value (attendee)) {
-                       if (out_hash) {
-                               if (!*out_hash)
-                                       *out_hash = g_hash_table_new_full (camel_strcase_hash, 
camel_strcase_equal, NULL, e_cal_component_attendee_free);
-
-                               g_hash_table_insert (*out_hash, (gpointer) e_cal_component_attendee_get_value 
(attendee), attendee);
-                       } else if (out_slist) {
-                               *out_slist = g_slist_prepend (*out_slist, attendee);
-                       } else {
-                               g_warn_if_reached ();
-                               e_cal_component_attendee_free (attendee);
-                       }
-               } else {
-                       e_cal_component_attendee_free (attendee);
-               }
-       }
-
-       g_clear_object (&prop);
-
-       if (out_slist && *out_slist)
-               *out_slist = g_slist_reverse (*out_slist);
-}
-
-static void
-ecb_m365_add_attendees (ECalBackendM365 *cbm365,
-                       ICalComponent *new_comp,
-                       ICalComponent *old_comp,
-                       ICalPropertyKind prop_kind,
-                       JsonBuilder *builder)
-{
-       GHashTable *old_value = NULL;
-       GSList *new_value = NULL;
-
-       ecb_m365_extract_attendees (new_comp, NULL, &new_value);
-       ecb_m365_extract_attendees (old_comp, &old_value, NULL);
-
-       if (!new_value && !old_value)
-               return;
-
-       if (new_value) {
-               GSList *link;
-               gboolean same = FALSE;
-
-               if (old_value && g_hash_table_size (old_value) == g_slist_length (new_value)) {
-                       same = TRUE;
-
-                       for (link = new_value; link && same; link = g_slist_next (link)) {
-                               ECalComponentAttendee *new_att = link->data, *old_att;
-
-                               old_att = g_hash_table_lookup (old_value, e_cal_component_attendee_get_value 
(new_att));
-
-                               same = old_att && e_cal_component_attendee_get_value (old_att) && 
e_cal_component_attendee_get_value (new_att) &&
-                                       g_ascii_strcasecmp (e_cal_component_attendee_get_value (old_att), 
e_cal_component_attendee_get_value (new_att)) == 0 &&
-                                       g_strcmp0 (e_cal_component_attendee_get_cn (old_att), 
e_cal_component_attendee_get_cn (new_att)) == 0 &&
-                                       e_cal_component_attendee_get_partstat (old_att) == 
e_cal_component_attendee_get_partstat (new_att) &&
-                                       e_cal_component_attendee_get_cutype (old_att) == 
e_cal_component_attendee_get_cutype (new_att) &&
-                                       e_cal_component_attendee_get_role (old_att) == 
e_cal_component_attendee_get_role (new_att);
-                       }
-               }
-
-               if (!same) {
-                       e_m365_event_begin_attendees (builder);
-
-                       for (link = new_value; link; link = g_slist_next (link)) {
-                               ECalComponentAttendee *attendee = link->data;
-                               EM365AttendeeType att_type;
-                               EM365ResponseType response = E_M365_RESPONSE_NONE;
-                               time_t response_time = (time_t) 0;
-                               ICalParameterPartstat partstat;
-                               const gchar *address;
-
-                               address = ecb_m365_strip_mailto (e_cal_component_attendee_get_value 
(attendee));
-
-                               if (e_cal_component_attendee_get_cutype (attendee) == I_CAL_CUTYPE_RESOURCE)
-                                       att_type = E_M365_ATTENDEE_RESOURCE;
-                               else if (e_cal_component_attendee_get_role (attendee) == 
I_CAL_ROLE_REQPARTICIPANT ||
-                                        e_cal_component_attendee_get_role (attendee) == I_CAL_ROLE_CHAIR)
-                                       att_type = E_M365_ATTENDEE_REQUIRED;
-                               else if (e_cal_component_attendee_get_role (attendee) == 
I_CAL_ROLE_OPTPARTICIPANT)
-                                       att_type = E_M365_ATTENDEE_OPTIONAL;
-                               else /* Fallback */
-                                       att_type = E_M365_ATTENDEE_REQUIRED;
-
-                               partstat = e_cal_component_attendee_get_partstat (attendee);
-
-                               if (partstat == I_CAL_PARTSTAT_TENTATIVE)
-                                       response = E_M365_RESPONSE_TENTATIVELY_ACCEPTED;
-                               else if (partstat == I_CAL_PARTSTAT_ACCEPTED)
-                                       response = E_M365_RESPONSE_ACCEPTED;
-                               else if (partstat == I_CAL_PARTSTAT_DECLINED)
-                                       response = E_M365_RESPONSE_DECLINED;
-                               else if (partstat == I_CAL_PARTSTAT_NEEDSACTION)
-                                       response = E_M365_RESPONSE_NOT_RESPONDED;
-
-                               if (response != E_M365_RESPONSE_NONE) {
-                                       ECalComponentParameterBag *params;
-                                       guint ii, sz;
-
-                                       params = e_cal_component_attendee_get_parameter_bag (attendee);
-                                       sz = e_cal_component_parameter_bag_get_count (params);
-
-                                       for (ii = 0; ii < sz; ii++) {
-                                               ICalParameter *param;
-
-                                               param = e_cal_component_parameter_bag_get (params, ii);
-
-                                               if (param && i_cal_parameter_isa (param) == I_CAL_X_PARAMETER 
&&
-                                                   i_cal_parameter_get_xname (param) &&
-                                                   g_ascii_strcasecmp (i_cal_parameter_get_xname (param), 
"X-M365-STATUS-TIME") == 0) {
-                                                       const gchar *xvalue;
-
-                                                       xvalue = i_cal_parameter_get_xvalue (param);
-
-                                                       if (xvalue && *xvalue) {
-                                                               gint64 value;
-
-                                                               value = g_ascii_strtoll (xvalue, NULL, 10);
-
-                                                               if (value)
-                                                                       response_time = (time_t) value;
-                                                       }
-                                               }
-                                       }
-                               }
-
-                               e_m365_event_add_attendee (builder, att_type, response, response_time, 
e_cal_component_attendee_get_cn (attendee), address);
-                       }
-
-                       e_m365_event_end_attendees (builder);
-               }
-       } else {
-               e_m365_event_add_null_attendees (builder);
-       }
-
-       if (new_value)
-               g_slist_free_full (new_value, e_cal_component_attendee_free);
-       if (old_value)
-               g_hash_table_destroy (old_value);
-}
-
-static void
-ecb_m365_get_importance (ECalBackendM365 *cbm365,
-                        EM365Event *m365_event,
-                        ICalComponent *inout_comp,
-                        ICalPropertyKind prop_kind)
-{
-       EM365ImportanceType value;
-       ICalProperty *prop = NULL;
-
-       value = e_m365_event_get_importance (m365_event);
-
-       if (value == E_M365_IMPORTANCE_LOW)
-               prop = i_cal_property_new_priority (9);
-       else if (value == E_M365_IMPORTANCE_NORMAL)
-               prop = i_cal_property_new_priority (5);
-       else if (value == E_M365_IMPORTANCE_HIGH)
-               prop = i_cal_property_new_priority (1);
-
-       if (prop)
-               i_cal_component_take_property (inout_comp, prop);
-}
-
-static void
-ecb_m365_add_importance (ECalBackendM365 *cbm365,
-                        ICalComponent *new_comp,
-                        ICalComponent *old_comp,
-                        ICalPropertyKind prop_kind,
-                        JsonBuilder *builder)
-{
-       gint old_value = -1, new_value = -1;
-       ICalProperty *prop;
-
-       prop = i_cal_component_get_first_property (new_comp, prop_kind);
-
-       if (prop) {
-               new_value = i_cal_property_get_priority (prop);
-               g_clear_object (&prop);
-       }
-
-       prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
-
-       if (prop) {
-               old_value = i_cal_property_get_priority (prop);
-               g_clear_object (&prop);
-       }
-
-       if (new_value != old_value) {
-               EM365ImportanceType value = E_M365_IMPORTANCE_NOT_SET;
-
-               if (new_value >= 1 && new_value <= 4) {
-                       value = E_M365_IMPORTANCE_HIGH;
-               } else if (new_value == 5) {
-                       value = E_M365_IMPORTANCE_NORMAL;
-               } else if (new_value >= 6 && new_value <= 9) {
-                       value = E_M365_IMPORTANCE_LOW;
-               }
-
-               e_m365_event_add_importance (builder, value);
-       }
-}
-
-static void
-ecb_m365_get_event_status (ECalBackendM365 *cbm365,
-                          EM365Event *m365_event,
-                          ICalComponent *inout_comp,
-                          ICalPropertyKind prop_kind)
-{
-       ICalPropertyStatus status = I_CAL_STATUS_NONE;
-
-       if (e_m365_event_get_is_cancelled (m365_event)) {
-               status = I_CAL_STATUS_CANCELLED;
-       } else {
-               EM365ResponseStatus *response_status;
-
-               response_status = e_m365_event_get_response_status (m365_event);
-
-               if (response_status) {
-                       EM365ResponseType response;
-
-                       response = e_m365_response_status_get_response (response_status);
-
-                       if (response == E_M365_RESPONSE_TENTATIVELY_ACCEPTED)
-                               status = I_CAL_STATUS_TENTATIVE;
-                       else if (response == E_M365_RESPONSE_ACCEPTED)
-                               status = I_CAL_STATUS_CONFIRMED;
-                       else if (response == E_M365_RESPONSE_DECLINED)
-                               status = I_CAL_STATUS_CANCELLED;
-                       else if (response == E_M365_RESPONSE_NOT_RESPONDED)
-                               status = I_CAL_STATUS_NEEDSACTION;
-               }
-       }
-
-       if (status != I_CAL_STATUS_NONE)
-               i_cal_component_take_property (inout_comp, i_cal_property_new_status (status));
-}
-
-static ICalRecurrenceWeekday
-ecb_m365_day_of_week_to_ical (EM365DayOfWeekType dow)
-{
-       switch (dow) {
-       case E_M365_DAY_OF_WEEK_SUNDAY:
-               return I_CAL_SUNDAY_WEEKDAY;
-       case E_M365_DAY_OF_WEEK_MONDAY:
-               return I_CAL_MONDAY_WEEKDAY;
-       case E_M365_DAY_OF_WEEK_TUESDAY:
-               return I_CAL_TUESDAY_WEEKDAY;
-       case E_M365_DAY_OF_WEEK_WEDNESDAY:
-               return I_CAL_WEDNESDAY_WEEKDAY;
-       case E_M365_DAY_OF_WEEK_THURSDAY:
-               return I_CAL_THURSDAY_WEEKDAY;
-       case E_M365_DAY_OF_WEEK_FRIDAY:
-               return I_CAL_FRIDAY_WEEKDAY;
-       case E_M365_DAY_OF_WEEK_SATURDAY:
-               return I_CAL_SATURDAY_WEEKDAY;
-       default:
-               break;
-       }
-
-       return I_CAL_NO_WEEKDAY;
-}
-
-static EM365DayOfWeekType
-ecb_m365_day_of_week_from_ical (ICalRecurrenceWeekday dow)
-{
-       switch (dow) {
-       case I_CAL_SUNDAY_WEEKDAY:
-               return E_M365_DAY_OF_WEEK_SUNDAY;
-               break;
-       case I_CAL_MONDAY_WEEKDAY:
-               return E_M365_DAY_OF_WEEK_MONDAY;
-               break;
-       case I_CAL_TUESDAY_WEEKDAY:
-               return E_M365_DAY_OF_WEEK_TUESDAY;
-               break;
-       case I_CAL_WEDNESDAY_WEEKDAY:
-               return E_M365_DAY_OF_WEEK_WEDNESDAY;
-               break;
-       case I_CAL_THURSDAY_WEEKDAY:
-               return E_M365_DAY_OF_WEEK_THURSDAY;
-               break;
-       case I_CAL_FRIDAY_WEEKDAY:
-               return E_M365_DAY_OF_WEEK_FRIDAY;
-               break;
-       case I_CAL_SATURDAY_WEEKDAY:
-               return E_M365_DAY_OF_WEEK_SATURDAY;
-               break;
-       default:
-               break;
-       }
-
-       return E_M365_DAY_OF_WEEK_UNKNOWN;
-}
-
-static void
-ecb_m365_set_index_to_ical (ICalRecurrence *recr,
-                           EM365WeekIndexType index)
-{
-       gint by_pos = -2;
-
-       switch (index) {
-       case E_M365_WEEK_INDEX_FIRST:
-               by_pos = 1;
-               break;
-       case E_M365_WEEK_INDEX_SECOND:
-               by_pos = 2;
-               break;
-       case E_M365_WEEK_INDEX_THIRD:
-               by_pos = 3;
-               break;
-       case E_M365_WEEK_INDEX_FOURTH:
-               by_pos = 4;
-               break;
-       case E_M365_WEEK_INDEX_LAST:
-               by_pos = -1;
-               break;
-       default:
-               break;
-       }
-
-       if (by_pos != -2)
-               i_cal_recurrence_set_by_set_pos (recr, 0, by_pos);
-}
-
-static void
-ecb_m365_add_index_from_ical (JsonBuilder *builder,
-                             gint by_pos)
-{
-       EM365WeekIndexType index = E_M365_WEEK_INDEX_UNKNOWN;
-
-       if (by_pos == 1)
-               index = E_M365_WEEK_INDEX_FIRST;
-       else if (by_pos == 2)
-               index = E_M365_WEEK_INDEX_SECOND;
-       else if (by_pos == 3)
-               index = E_M365_WEEK_INDEX_THIRD;
-       else if (by_pos == 4)
-               index = E_M365_WEEK_INDEX_FOURTH;
-       else if (by_pos == -1)
-               index = E_M365_WEEK_INDEX_LAST;
-
-       if (index != E_M365_WEEK_INDEX_UNKNOWN)
-               e_m365_recurrence_pattern_add_index (builder, index);
-}
-
-static void
-ecb_m365_set_days_of_week_to_ical (ICalRecurrence *recr,
-                                  JsonArray *days_of_week)
-{
-       gint ii, jj, sz;
-
-       if (!days_of_week)
-               return;
-
-       ii = 0;
-       sz = json_array_get_length (days_of_week);
-
-       for (jj = 0; jj < sz; jj++) {
-               ICalRecurrenceWeekday week_day;
-
-               week_day = ecb_m365_day_of_week_to_ical (e_m365_array_get_day_of_week_element (days_of_week, 
jj));
-
-               if (week_day != I_CAL_SUNDAY_WEEKDAY) {
-                       i_cal_recurrence_set_by_day (recr, ii, week_day);
-                       ii++;
-               }
-       }
-
-       i_cal_recurrence_set_by_day (recr, ii, I_CAL_RECURRENCE_ARRAY_MAX);
-}
-
-static void
-ecb_m365_add_days_of_week_from_ical (JsonBuilder *builder,
-                                    ICalRecurrence *recr)
-{
-       gint ii;
-
-       e_m365_recurrence_pattern_begin_days_of_week (builder);
-
-       for (ii = 0; ii < I_CAL_BY_DAY_SIZE; ii++) {
-               ICalRecurrenceWeekday week_day;
-               EM365DayOfWeekType m365_week_day;
-
-               week_day = i_cal_recurrence_get_by_day (recr, ii);
-
-               if (((gint) week_day) == I_CAL_RECURRENCE_ARRAY_MAX)
-                       break;
-
-               m365_week_day = ecb_m365_day_of_week_from_ical (week_day);
-
-               if (m365_week_day != E_M365_DAY_OF_WEEK_UNKNOWN)
-                       e_m365_recurrence_pattern_add_day_of_week (builder, m365_week_day);
-       }
-
-       e_m365_recurrence_pattern_end_days_of_week (builder);
-}
-
-static gboolean
-ecb_m365_get_recurrence (ECalBackendM365 *cbm365,
-                        JsonObject *m365_object,
-                        ICalComponent *inout_comp,
-                        ICalPropertyKind prop_kind,
-                        GCancellable *cancellable,
-                        GError **error)
-{
-       EM365PatternedRecurrence *m365_recr;
-       EM365RecurrencePattern *m365_pattern;
-       EM365RecurrenceRange *m365_range;
-       ICalRecurrence *ical_recr;
-       ICalRecurrenceWeekday week_day;
-       gint month;
-
-       switch (i_cal_component_isa (inout_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               m365_recr = e_m365_event_get_recurrence (m365_object);
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               m365_recr = e_m365_task_get_recurrence (m365_object);
-               break;
-       default:
-               g_warn_if_reached ();
-               return FALSE;
-       }
-
-       m365_pattern = m365_recr ? e_m365_patterned_recurrence_get_pattern (m365_recr) : NULL;
-       m365_range = m365_recr ? e_m365_patterned_recurrence_get_range (m365_recr) : NULL;
-
-       if (!m365_recr || !m365_pattern || !m365_range)
-               return TRUE;
-
-       ical_recr = i_cal_recurrence_new ();
-
-       switch (e_m365_recurrence_pattern_get_type (m365_pattern)) {
-       case E_M365_RECURRENCE_PATTERN_DAILY:
-               i_cal_recurrence_set_freq (ical_recr, I_CAL_DAILY_RECURRENCE);
-               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
-               ecb_m365_set_days_of_week_to_ical (ical_recr, e_m365_recurrence_pattern_get_days_of_week 
(m365_pattern));
-               break;
-       case E_M365_RECURRENCE_PATTERN_WEEKLY:
-               i_cal_recurrence_set_freq (ical_recr, I_CAL_WEEKLY_RECURRENCE);
-               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
-
-               week_day = ecb_m365_day_of_week_to_ical (e_m365_recurrence_pattern_get_first_day_of_week 
(m365_pattern));
-
-               if (week_day != I_CAL_NO_WEEKDAY)
-                       i_cal_recurrence_set_week_start (ical_recr, week_day);
-
-               ecb_m365_set_days_of_week_to_ical (ical_recr, e_m365_recurrence_pattern_get_days_of_week 
(m365_pattern));
-               break;
-       case E_M365_RECURRENCE_PATTERN_ABSOLUTE_MONTHLY:
-               i_cal_recurrence_set_freq (ical_recr, I_CAL_MONTHLY_RECURRENCE);
-               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
-               i_cal_recurrence_set_by_month_day (ical_recr, 0, e_m365_recurrence_pattern_get_day_of_month 
(m365_pattern));
-               break;
-       case E_M365_RECURRENCE_PATTERN_RELATIVE_MONTHLY:
-               i_cal_recurrence_set_freq (ical_recr, I_CAL_MONTHLY_RECURRENCE);
-               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
-               ecb_m365_set_days_of_week_to_ical (ical_recr, e_m365_recurrence_pattern_get_days_of_week 
(m365_pattern));
-               week_day = ecb_m365_day_of_week_to_ical (e_m365_recurrence_pattern_get_first_day_of_week 
(m365_pattern));
-
-               if (week_day != I_CAL_NO_WEEKDAY)
-                       i_cal_recurrence_set_week_start (ical_recr, week_day);
-
-               ecb_m365_set_index_to_ical (ical_recr, e_m365_recurrence_pattern_get_index (m365_pattern));
-               break;
-       case E_M365_RECURRENCE_PATTERN_ABSOLUTE_YEARLY:
-               i_cal_recurrence_set_freq (ical_recr, I_CAL_YEARLY_RECURRENCE);
-               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
-               i_cal_recurrence_set_by_month_day (ical_recr, 0, e_m365_recurrence_pattern_get_day_of_month 
(m365_pattern));
-
-               month = e_m365_recurrence_pattern_get_month (m365_pattern);
-
-               if (month >= 1 && month <= 12)
-                       i_cal_recurrence_set_by_month (ical_recr, 0, month);
-               break;
-       case E_M365_RECURRENCE_PATTERN_RELATIVE_YEARLY:
-               i_cal_recurrence_set_freq (ical_recr, I_CAL_YEARLY_RECURRENCE);
-               i_cal_recurrence_set_interval (ical_recr, e_m365_recurrence_pattern_get_interval 
(m365_pattern));
-               ecb_m365_set_days_of_week_to_ical (ical_recr, e_m365_recurrence_pattern_get_days_of_week 
(m365_pattern));
-               week_day = ecb_m365_day_of_week_to_ical (e_m365_recurrence_pattern_get_first_day_of_week 
(m365_pattern));
-
-               if (week_day != I_CAL_NO_WEEKDAY)
-                       i_cal_recurrence_set_week_start (ical_recr, week_day);
-
-               ecb_m365_set_index_to_ical (ical_recr, e_m365_recurrence_pattern_get_index (m365_pattern));
-
-               month = e_m365_recurrence_pattern_get_month (m365_pattern);
-
-               if (month >= 1 && month <= 12)
-                       i_cal_recurrence_set_by_month (ical_recr, 0, month);
-               break;
-       default:
-               g_object_unref (ical_recr);
-               g_warning ("%s: Unknown pattern type: %d", G_STRFUNC, e_m365_recurrence_pattern_get_type 
(m365_pattern));
-               /* Ignore the error (in the code) and continue. */
-               return TRUE;
-       }
-
-       switch (e_m365_recurrence_range_get_type (m365_range)) {
-       case E_M365_RECURRENCE_RANGE_ENDDATE:
-               if (e_m365_recurrence_range_get_end_date (m365_range) > 0) {
-                       gint yy = 0, mm = 0, dd = 0;
-
-                       if (e_m365_date_decode (e_m365_recurrence_range_get_end_date (m365_range), &yy, &mm, 
&dd)) {
-                               ICalTime *itt;
-
-                               itt = i_cal_time_new ();
-                               i_cal_time_set_date (itt, yy, mm, dd);
-                               i_cal_time_set_is_date (itt, TRUE);
-
-                               i_cal_recurrence_set_until (ical_recr, itt);
-
-                               g_clear_object (&itt);
-                       }
-               }
-               break;
-       case E_M365_RECURRENCE_RANGE_NOEND:
-               break;
-       case E_M365_RECURRENCE_RANGE_NUMBERED:
-               i_cal_recurrence_set_count (ical_recr, e_m365_recurrence_range_get_number_of_occurrences 
(m365_range));
-               break;
-       default:
-               g_warning ("%s: Unknown range type: %d", G_STRFUNC, e_m365_recurrence_range_get_type 
(m365_range));
-               g_object_unref (ical_recr);
-               /* Ignore the error (in the code) and continue. */
-               return TRUE;
-       }
-
-       i_cal_component_take_property (inout_comp, i_cal_property_new_rrule (ical_recr));
-
-       g_object_unref (ical_recr);
-
-       return TRUE;
-}
-
-static gboolean
-ecb_m365_add_recurrence (ECalBackendM365 *cbm365,
-                        ICalComponent *new_comp,
-                        ICalComponent *old_comp,
-                        ICalPropertyKind prop_kind,
-                        const gchar *m365_id,
-                        JsonBuilder *builder,
-                        GCancellable *cancellable,
-                        GError **error)
-{
-       ICalProperty *new_value, *old_value;
-       gboolean success = TRUE;
-       void (* begin_recurrence_func) (JsonBuilder *builder);
-       void (* end_recurrence_func) (JsonBuilder *builder);
-       void (* add_null_recurrence_func) (JsonBuilder *builder);
-
-       switch (i_cal_component_isa (new_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               begin_recurrence_func = e_m365_event_begin_recurrence;
-               end_recurrence_func = e_m365_event_end_recurrence;
-               add_null_recurrence_func = e_m365_event_add_null_recurrence;
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               begin_recurrence_func = e_m365_task_begin_recurrence;
-               end_recurrence_func = e_m365_task_end_recurrence;
-               add_null_recurrence_func = e_m365_task_add_null_recurrence;
-               break;
-       default:
-               g_warn_if_reached ();
-               return FALSE;
-       }
-
-       if (i_cal_component_count_properties (new_comp, prop_kind) > 1) {
-               g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_NOT_SUPPORTED,
-                       _("Microsoft 365 calendar cannot store more than one recurrence")));
-
-               return FALSE;
-       }
-
-       if (i_cal_component_count_properties (new_comp, I_CAL_RDATE_PROPERTY) > 0 ||
-           i_cal_component_count_properties (new_comp, I_CAL_EXDATE_PROPERTY) > 0 ||
-           i_cal_component_count_properties (new_comp, I_CAL_EXRULE_PROPERTY) > 0) {
-               g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_NOT_SUPPORTED,
-                       _("Microsoft 365 calendar cannot store component with RDATE, EXDATE or RRULE 
properties")));
-
-               return FALSE;
-       }
-
-       new_value = i_cal_component_get_first_property (new_comp, prop_kind);
-       old_value = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
-
-       if (!new_value && !old_value)
-               return TRUE;
-
-       if (new_value) {
-               ICalRecurrence *new_rrule;
-               gboolean same = FALSE;
-
-               new_rrule = i_cal_property_get_rrule (new_value);
-
-               if (old_value && new_rrule) {
-                       ICalRecurrence *old_rrule;
-
-                       old_rrule = i_cal_property_get_rrule (old_value);
-
-                       if (old_rrule) {
-                               gchar *new_str, *old_str;
-
-                               new_str = i_cal_recurrence_to_string (new_rrule);
-                               old_str = i_cal_recurrence_to_string (old_rrule);
-
-                               same = g_strcmp0 (new_str, old_str) == 0;
-
-                               g_free (new_str);
-                               g_free (old_str);
-                       }
-
-                       g_clear_object (&old_rrule);
-               }
-
-               if (!same && new_rrule) {
-                       EM365DayOfWeekType week_day;
-                       ICalTime *dtstart;
-                       gint by_pos, month, yy = 0, mm = 0, dd = 0;
-
-                       begin_recurrence_func (builder);
-                       e_m365_patterned_recurrence_begin_pattern (builder);
-
-                       switch (i_cal_recurrence_get_freq (new_rrule)) {
-                       case I_CAL_DAILY_RECURRENCE:
-                               e_m365_recurrence_pattern_add_type (builder, E_M365_RECURRENCE_PATTERN_DAILY);
-                               e_m365_recurrence_pattern_add_interval (builder, 
i_cal_recurrence_get_interval (new_rrule));
-                               ecb_m365_add_days_of_week_from_ical (builder, new_rrule);
-                               break;
-                       case I_CAL_WEEKLY_RECURRENCE:
-                               e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_WEEKLY);
-                               e_m365_recurrence_pattern_add_interval (builder, 
i_cal_recurrence_get_interval (new_rrule));
-
-                               week_day = ecb_m365_day_of_week_from_ical (i_cal_recurrence_get_week_start 
(new_rrule));
-
-                               if (week_day != E_M365_DAY_OF_WEEK_UNKNOWN)
-                                       e_m365_recurrence_pattern_add_first_day_of_week (builder, week_day);
-
-                               ecb_m365_add_days_of_week_from_ical (builder, new_rrule);
-                               break;
-                       case I_CAL_MONTHLY_RECURRENCE:
-                               by_pos = i_cal_recurrence_get_by_set_pos (new_rrule, 0);
-
-                               e_m365_recurrence_pattern_add_interval (builder, 
i_cal_recurrence_get_interval (new_rrule));
-
-                               if (by_pos == I_CAL_RECURRENCE_ARRAY_MAX) {
-                                       e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_ABSOLUTE_MONTHLY);
-                                       e_m365_recurrence_pattern_add_day_of_month (builder, 
i_cal_recurrence_get_by_month_day (new_rrule, 0));
-                               } else {
-                                       e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_RELATIVE_MONTHLY);
-
-                                       week_day = ecb_m365_day_of_week_from_ical 
(i_cal_recurrence_get_week_start (new_rrule));
-
-                                       if (week_day != E_M365_DAY_OF_WEEK_UNKNOWN)
-                                               e_m365_recurrence_pattern_add_first_day_of_week (builder, 
week_day);
-
-                                       ecb_m365_add_days_of_week_from_ical (builder, new_rrule);
-                                       ecb_m365_add_index_from_ical (builder, by_pos);
-                               }
-                               break;
-                       case I_CAL_YEARLY_RECURRENCE:
-                               by_pos = i_cal_recurrence_get_by_set_pos (new_rrule, 0);
-
-                               e_m365_recurrence_pattern_add_interval (builder, 
i_cal_recurrence_get_interval (new_rrule));
-
-                               month = i_cal_recurrence_get_by_month (new_rrule, 0);
-
-                               if (month >= 1 && month <= 12)
-                                       e_m365_recurrence_pattern_add_month (builder, month);
-
-                               if (by_pos == I_CAL_RECURRENCE_ARRAY_MAX) {
-                                       e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_ABSOLUTE_YEARLY);
-                                       e_m365_recurrence_pattern_add_day_of_month (builder, 
i_cal_recurrence_get_by_month_day (new_rrule, 0));
-                               } else {
-                                       e_m365_recurrence_pattern_add_type (builder, 
E_M365_RECURRENCE_PATTERN_RELATIVE_YEARLY);
-
-                                       week_day = ecb_m365_day_of_week_from_ical 
(i_cal_recurrence_get_week_start (new_rrule));
-
-                                       if (week_day != E_M365_DAY_OF_WEEK_UNKNOWN)
-                                               e_m365_recurrence_pattern_add_first_day_of_week (builder, 
week_day);
-
-                                       ecb_m365_add_days_of_week_from_ical (builder, new_rrule);
-                                       ecb_m365_add_index_from_ical (builder, by_pos);
-                               }
-
-                               break;
-                       default:
-                               g_set_error (error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED,
-                                       _("Unknown recurrence frequency (%d)"), i_cal_recurrence_get_freq 
(new_rrule));
-
-                               success = FALSE;
-                               break;
-                       }
-
-                       e_m365_patterned_recurrence_end_pattern (builder);
-                       e_m365_patterned_recurrence_begin_range (builder);
-
-                       dtstart = i_cal_component_get_dtstart (new_comp);
-                       i_cal_time_get_date (dtstart, &yy, &mm, &dd);
-                       g_clear_object (&dtstart);
-
-                       e_m365_recurrence_range_add_start_date (builder, e_m365_date_encode (yy, mm, dd));
-
-                       if (!i_cal_recurrence_get_count (new_rrule)) {
-                               ICalTime *until;
-                               gint yy = 0, mm = 0, dd = 0;
-
-                               until = i_cal_recurrence_get_until (new_rrule);
-
-                               if (until)
-                                       i_cal_time_get_date (until, &yy, &mm, &dd);
-
-                               if (!until || yy == 0) {
-                                       e_m365_recurrence_range_add_type (builder, 
E_M365_RECURRENCE_RANGE_NOEND);
-                               } else {
-                                       e_m365_recurrence_range_add_type (builder, 
E_M365_RECURRENCE_RANGE_ENDDATE);
-                                       e_m365_recurrence_range_add_end_date (builder, e_m365_date_encode 
(yy, mm, dd));
-                               }
-
-                               g_clear_object (&until);
-                       } else {
-                               e_m365_recurrence_range_add_type (builder, E_M365_RECURRENCE_RANGE_NUMBERED);
-                               e_m365_recurrence_range_add_number_of_occurrences (builder, 
i_cal_recurrence_get_count (new_rrule));
-                       }
-
-                       e_m365_patterned_recurrence_end_range (builder);
-                       end_recurrence_func (builder);
-               }
-
-               g_clear_object (&new_rrule);
-       } else {
-               add_null_recurrence_func (builder);
-       }
-
-       g_clear_object (&new_value);
-       g_clear_object (&old_value);
-
-       return success;
-}
-
-static gboolean
-ecb_m365_get_reminder (ECalBackendM365 *cbm365,
-                      EM365Event *m365_object,
-                      ICalComponent *inout_comp,
-                      ICalPropertyKind prop_kind,
-                      GCancellable *cancellable,
-                      GError **error)
-{
-       switch (i_cal_component_isa (inout_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               if (e_m365_event_get_is_reminder_on (m365_object)) {
-                       ECalComponentAlarm *alarm;
-                       ECalComponentAlarmTrigger *trigger;
-                       ICalDuration *duration;
-
-                       duration = i_cal_duration_new_from_int (-60 * 
e_m365_event_get_reminder_minutes_before_start (m365_object));
-                       trigger = e_cal_component_alarm_trigger_new_relative 
(E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START, duration);
-                       g_object_unref (duration);
-
-                       alarm = e_cal_component_alarm_new ();
-                       e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
-                       e_cal_component_alarm_take_summary (alarm, e_cal_component_text_new 
(e_m365_event_get_subject (m365_object), NULL));
-                       e_cal_component_alarm_take_description (alarm, e_cal_component_text_new 
(e_m365_event_get_subject (m365_object), NULL));
-                       e_cal_component_alarm_take_trigger (alarm, trigger);
-
-                       i_cal_component_take_component (inout_comp, e_cal_component_alarm_get_as_component 
(alarm));
-
-                       e_cal_component_alarm_free (alarm);
-               }
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               if (e_m365_task_get_is_reminder_on (m365_object)) {
-                       EM365DateTimeWithZone *reminder_dt;
-
-                       reminder_dt = e_m365_task_get_reminder_date_time (m365_object);
-
-                       if (reminder_dt) {
-                               ECalComponentAlarm *alarm;
-                               ECalComponentAlarmTrigger *trigger;
-                               ICalTimezone *tz;
-                               ICalTime *itt;
-                               time_t tt;
-                               const gchar *zone;
-
-                               tt = e_m365_date_time_get_date_time (reminder_dt);
-                               zone = e_m365_date_time_get_time_zone (reminder_dt);
-
-                               if (zone && *zone)
-                                       zone = e_m365_tz_utils_get_ical_equivalent (zone);
-
-                               tz = zone && *zone ? ecb_m365_get_timezone_sync (cbm365, zone) : NULL;
-
-                               if (!tz)
-                                       tz = i_cal_timezone_get_utc_timezone ();
-
-                               itt = i_cal_time_new_from_timet_with_zone (tt, FALSE, tz);
-                               trigger = e_cal_component_alarm_trigger_new_absolute (itt);
-                               g_object_unref (itt);
-
-                               alarm = e_cal_component_alarm_new ();
-                               e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
-                               e_cal_component_alarm_take_summary (alarm, e_cal_component_text_new 
(e_m365_task_get_subject (m365_object), NULL));
-                               e_cal_component_alarm_take_description (alarm, e_cal_component_text_new 
(e_m365_task_get_subject (m365_object), NULL));
-                               e_cal_component_alarm_take_trigger (alarm, trigger);
-
-                               i_cal_component_take_component (inout_comp, 
e_cal_component_alarm_get_as_component (alarm));
-
-                               e_cal_component_alarm_free (alarm);
-                       }
-               }
-               break;
-       default:
-               g_warn_if_reached ();
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static gboolean
-ecb_m365_add_reminder (ECalBackendM365 *cbm365,
-                      ICalComponent *new_comp,
-                      ICalComponent *old_comp,
-                      ICalPropertyKind prop_kind,
-                      const gchar *m365_id,
-                      JsonBuilder *builder,
-                      GCancellable *cancellable,
-                      GError **error)
-{
-       ICalComponent *new_value, *old_value;
-       gboolean success = TRUE;
-
-       if (i_cal_component_count_components (new_comp, I_CAL_VALARM_COMPONENT) > 1) {
-               g_propagate_error (error, ECC_ERROR_EX (E_CAL_CLIENT_ERROR_INVALID_OBJECT, _("Microsoft 365 
calendar cannot store more that one event reminder")));
-               return FALSE;
-       }
-
-       new_value = i_cal_component_get_first_component (new_comp, I_CAL_VALARM_COMPONENT);
-       old_value = old_comp ? i_cal_component_get_first_component (old_comp, I_CAL_VALARM_COMPONENT) : NULL;
-
-       if (!new_value && !old_value)
-               return TRUE;
-
-       if (new_value) {
-               ECalComponentAlarm *new_alarm;
-               ECalComponentAlarmTrigger *new_trigger;
-               ICalComponentKind kind;
-               ICalDuration *new_duration = NULL;
-               ICalTime *new_absolute_time = NULL;
-               gboolean changed = TRUE;
-
-               kind = i_cal_component_isa (new_comp);
-
-               new_alarm = e_cal_component_alarm_new_from_component (new_value);
-               new_trigger = new_alarm ? e_cal_component_alarm_get_trigger (new_alarm) : NULL;
-
-               switch (kind) {
-               case I_CAL_VEVENT_COMPONENT:
-                       success = new_trigger && e_cal_component_alarm_trigger_get_kind (new_trigger) == 
E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
-                       if (success) {
-                               new_duration = e_cal_component_alarm_trigger_get_duration (new_trigger);
-
-                               success = new_duration && i_cal_duration_as_int (new_duration) <= 0;
-                       }
-
-                       if (!success) {
-                               g_propagate_error (error, ECC_ERROR_EX (E_CAL_CLIENT_ERROR_INVALID_OBJECT, 
_("Microsoft 365 event can have only a reminder before event start")));
-                       }
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       success = new_trigger && e_cal_component_alarm_trigger_get_kind (new_trigger) == 
E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE;
-
-                       if (success) {
-                               new_absolute_time = e_cal_component_alarm_trigger_get_absolute_time 
(new_trigger);
-
-                               success = new_absolute_time != NULL;
-                       }
-
-                       if (!success) {
-                               g_propagate_error (error, ECC_ERROR_EX (E_CAL_CLIENT_ERROR_INVALID_OBJECT, 
_("Microsoft 365 task can have only a reminder with absolute time")));
-                       }
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       success = FALSE;
-                       break;
-               }
-
-               if (success && old_value && new_trigger) {
-                       ECalComponentAlarm *old_alarm;
-                       ECalComponentAlarmTrigger *old_trigger;
-
-                       old_alarm = e_cal_component_alarm_new_from_component (old_value);
-                       old_trigger = old_alarm ? e_cal_component_alarm_get_trigger (old_alarm) : NULL;
-
-                       if (old_trigger) {
-                               changed = e_cal_component_alarm_trigger_get_kind (new_trigger) != 
e_cal_component_alarm_trigger_get_kind (old_trigger);
-
-                               if (!changed) {
-                                       ICalDuration *old_duration;
-                                       ICalTime *old_absolute_time;
-
-                                       switch (kind) {
-                                       case I_CAL_VEVENT_COMPONENT:
-                                               old_duration = e_cal_component_alarm_trigger_get_duration 
(old_trigger);
-
-                                               changed = !old_duration || i_cal_duration_as_int 
(new_duration) != i_cal_duration_as_int (old_duration);
-                                               break;
-                                       case I_CAL_VTODO_COMPONENT:
-                                               old_absolute_time = 
e_cal_component_alarm_trigger_get_absolute_time (old_trigger);
-
-                                               changed = !old_absolute_time || i_cal_time_compare 
(new_absolute_time, old_absolute_time) != 0;
-                                               break;
-                                       default:
-                                               g_warn_if_reached ();
-                                               changed = FALSE;
-                                               break;
-                                       }
-                               }
-                       }
-
-                       e_cal_component_alarm_free (old_alarm);
-               }
-
-               if (success && changed) {
-                       ICalTimezone *izone = NULL;
-                       const gchar *wzone = NULL;
-                       time_t tt;
-
-                       switch (kind) {
-                       case I_CAL_VEVENT_COMPONENT:
-                               e_m365_event_add_is_reminder_on (builder, TRUE);
-                               e_m365_event_add_reminder_minutes_before_start (builder, 
i_cal_duration_as_int (new_duration) / -60);
-                               break;
-                       case I_CAL_VTODO_COMPONENT:
-                               izone = i_cal_time_get_timezone (new_absolute_time);
-
-                               if (izone)
-                                       wzone = e_m365_tz_utils_get_msdn_equivalent 
(i_cal_timezone_get_location (izone));
-
-                               tt = i_cal_time_as_timet_with_zone (new_absolute_time, wzone ? NULL : izone);
-
-                               e_m365_task_add_is_reminder_on (builder, TRUE);
-                               e_m365_task_add_reminder_date_time (builder, tt, wzone);
-                               break;
-                       default:
-                               g_warn_if_reached ();
-                               break;
-                       }
-               }
-
-               e_cal_component_alarm_free (new_alarm);
-       } else {
-               switch (i_cal_component_isa (new_comp)) {
-               case I_CAL_VEVENT_COMPONENT:
-                       e_m365_event_add_is_reminder_on (builder, FALSE);
-                       break;
-               case I_CAL_VTODO_COMPONENT:
-                       e_m365_task_add_is_reminder_on (builder, FALSE);
-                       break;
-               default:
-                       g_warn_if_reached ();
-                       break;
-               }
-       }
-
-       g_clear_object (&new_value);
-       g_clear_object (&old_value);
-
-       return success;
-}
-
-static gboolean
-ecb_m365_get_attachments (ECalBackendM365 *cbm365,
-                         JsonObject *m365_object,
-                         ICalComponent *inout_comp,
-                         ICalPropertyKind prop_kind,
-                         GCancellable *cancellable,
-                         GError **error)
-{
-       GSList *attachments = NULL, *link;
-       const gchar *id;
-       gboolean success = TRUE;
-
-       switch (i_cal_component_isa (inout_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               if (!e_m365_event_get_has_attachments (m365_object))
-                       return TRUE;
-
-               id = e_m365_event_get_id (m365_object);
-
-               if (!e_m365_connection_list_event_attachments_sync (cbm365->priv->cnc, NULL,
-                       cbm365->priv->group_id, cbm365->priv->folder_id, id, 
"id,name,contentType,contentBytes",
-                       &attachments, cancellable, error)) {
-                       return FALSE;
-               }
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               if (!e_m365_task_get_has_attachments (m365_object))
-                       return TRUE;
-
-               id = e_m365_task_get_id (m365_object);
-
-               if (!e_m365_connection_list_task_attachments_sync (cbm365->priv->cnc, NULL,
-                       cbm365->priv->group_id, cbm365->priv->folder_id, id, 
"id,name,contentType,contentBytes",
-                       &attachments, cancellable, error)) {
-                       return FALSE;
-               }
-               break;
-       default:
-               g_warn_if_reached ();
-               return FALSE;
-       }
-
-       for (link = attachments; link && success; link = g_slist_next (link)) {
-               CamelStream *content_stream;
-               EM365Attachment *m365_attach = link->data;
-               gchar *filename;
-
-               if (!m365_attach || e_m365_attachment_get_data_type (m365_attach) != 
E_M365_ATTACHMENT_DATA_TYPE_FILE ||
-                   !e_m365_attachment_get_name (m365_attach))
-                       continue;
-
-               filename = g_build_filename (cbm365->priv->attachments_dir, id, e_m365_attachment_get_id 
(m365_attach), NULL);
-
-               content_stream = camel_stream_fs_new_with_name (filename, O_CREAT | O_TRUNC | O_WRONLY, 0666, 
error);
-
-               if (content_stream) {
-                       CamelMimeFilter *filter;
-                       CamelStream *filter_stream;
-                       const gchar *base64_data;
-
-                       filter_stream = camel_stream_filter_new (content_stream);
-
-                       filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_BASE64_DEC);
-                       camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), filter);
-                       g_object_unref (filter);
-
-                       base64_data = e_m365_file_attachment_get_content_bytes (m365_attach);
-
-                       if (base64_data && *base64_data)
-                               success = camel_stream_write (filter_stream, base64_data, strlen 
(base64_data), cancellable, error) != -1;
-
-                       camel_stream_flush (filter_stream, cancellable, NULL);
-                       g_object_unref (filter_stream);
-
-                       camel_stream_flush (content_stream, cancellable, NULL);
-                       g_object_unref (content_stream);
-
-                       if (success) {
-                               gchar *uri;
-
-                               uri = g_filename_to_uri (filename, NULL, error);
-
-                               if (uri) {
-                                       ICalAttach *ical_attach;
-                                       ICalParameter *param;
-                                       ICalProperty *prop;
-                                       gchar *enc_uri;
-                                       const gchar *tmp;
-
-                                       enc_uri = i_cal_value_encode_ical_string (uri);
-                                       ical_attach = i_cal_attach_new_from_url (enc_uri);
-                                       prop = i_cal_property_new_attach (ical_attach);
-
-                                       tmp = e_m365_attachment_get_name (m365_attach);
-
-                                       if (!tmp || !*tmp)
-                                               tmp = "attachment.dat";
-
-                                       param = i_cal_parameter_new_filename (tmp);
-                                       i_cal_property_take_parameter (prop, param);
-
-                                       tmp = e_m365_attachment_get_content_type (m365_attach);
-
-                                       if (tmp && *tmp) {
-                                               param = i_cal_parameter_new_fmttype (tmp);
-                                               i_cal_property_take_parameter (prop, param);
-                                       }
-
-                                       param = i_cal_parameter_new_x (e_m365_attachment_get_id 
(m365_attach));
-                                       i_cal_parameter_set_xname (param, "X-M365-ATTACHMENTID");
-                                       i_cal_property_take_parameter (prop, param);
-
-                                       i_cal_component_take_property (inout_comp, prop);
-
-                                       g_object_unref (ical_attach);
-                                       g_free (enc_uri);
-                                       g_free (uri);
-                               } else {
-                                       success = FALSE;
-                               }
-                       }
-               } else {
-                       success = FALSE;
-               }
-
-               g_free (filename);
-       }
-
-       g_slist_free_full (attachments, (GDestroyNotify) json_object_unref);
-
-       return success;
-}
-
-static void
-ecb_m365_extract_attachments (ICalComponent *comp,
-                             GHashTable **out_hash, /* gchar *attachment_id ~> ICalProperty * */
-                             GSList **out_slist) /* ICalProperty * */
-{
-       ICalProperty *prop;
-
-       if (!comp)
-               return;
-
-       for (prop = i_cal_component_get_first_property (comp, I_CAL_ATTACH_PROPERTY);
-            prop;
-            g_object_unref (prop), prop = i_cal_component_get_next_property (comp, I_CAL_ATTACH_PROPERTY)) {
-               if (out_slist) {
-                       *out_slist = g_slist_prepend (*out_slist, g_object_ref (prop));
-               } else if (out_hash) {
-                       gchar *attach_id;
-
-                       attach_id = i_cal_property_get_parameter_as_string (prop, "X-M365-ATTACHMENTID");
-                       g_warn_if_fail (attach_id != NULL);
-
-                       if (attach_id) {
-                               if (!*out_hash)
-                                       *out_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
g_object_unref);
-
-                               g_hash_table_insert (*out_hash, attach_id, g_object_ref (prop));
-                       }
-               } else {
-                       g_warn_if_reached ();
-               }
-       }
-
-       g_clear_object (&prop);
-
-       if (out_slist && *out_slist)
-               *out_slist = g_slist_reverse (*out_slist);
-}
-
-static gboolean
-ecb_m365_add_attachments (ECalBackendM365 *cbm365,
-                         ICalComponent *new_comp,
-                         ICalComponent *old_comp,
-                         ICalPropertyKind prop_kind,
-                         const gchar *m365_id,
-                         JsonBuilder *builder,
-                         GCancellable *cancellable,
-                         GError **error)
-{
-       GSList *new_attachs = NULL;
-       GHashTable *old_attachs = NULL;
-       gboolean (* add_attachment_func) (EM365Connection *cnc,
-                                         const gchar *user_override,
-                                         const gchar *group_id,
-                                         const gchar *folder_id,
-                                         const gchar *item_id,
-                                         JsonBuilder *in_attachment,
-                                         EM365Attachment **out_attachment,
-                                         GCancellable *cancellable,
-                                         GError **error);
-       gboolean (* delete_attachment_func) (EM365Connection *cnc,
-                                            const gchar *user_override,
-                                            const gchar *group_id,
-                                            const gchar *folder_id,
-                                            const gchar *item_id,
-                                            const gchar *attachment_id,
-                                            GCancellable *cancellable,
-                                            GError **error);
-       gboolean success = TRUE;
-
-       switch (i_cal_component_isa (new_comp)) {
-       case I_CAL_VEVENT_COMPONENT:
-               add_attachment_func = e_m365_connection_add_event_attachment_sync;
-               delete_attachment_func = e_m365_connection_delete_event_attachment_sync;
-               break;
-       case I_CAL_VTODO_COMPONENT:
-               add_attachment_func = e_m365_connection_add_task_attachment_sync;
-               delete_attachment_func = e_m365_connection_delete_task_attachment_sync;
-               break;
-       default:
-               g_warn_if_reached ();
-               return FALSE;
-       }
-
-       if (!i_cal_component_count_properties (new_comp, I_CAL_ATTACH_PROPERTY) &&
-           !(old_comp ? i_cal_component_count_properties (old_comp, I_CAL_ATTACH_PROPERTY) : 0)) {
-               return TRUE;
-       }
-
-       ecb_m365_extract_attachments (new_comp, NULL, &new_attachs);
-       ecb_m365_extract_attachments (old_comp, &old_attachs, NULL);
-
-       if (new_attachs) {
-               GSList *link, *save_attachs = new_attachs;
-
-               if (old_attachs) {
-                       save_attachs = NULL;
-
-                       for (link = new_attachs; link; link = g_slist_next (link)) {
-                               ICalProperty *prop = link->data;
-                               gchar *attach_id;
-
-                               attach_id = i_cal_property_get_parameter_as_string (prop, 
"X-M365-ATTACHMENTID");
-
-                               if (!attach_id || !g_hash_table_remove (old_attachs, attach_id)) {
-                                       save_attachs = g_slist_prepend (save_attachs, g_object_ref (prop));
-                               }
-                       }
-
-                       if (save_attachs)
-                               save_attachs = g_slist_reverse (save_attachs);
-               }
-
-               for (link = save_attachs; link && success; link = g_slist_next (link)) {
-                       ICalProperty *prop = link->data;
-                       ICalAttach *attach;
-                       JsonBuilder *builder = NULL;
-
-                       attach = i_cal_property_get_attach (prop);
-
-                       if (!attach)
-                               continue;
-
-                       if (i_cal_attach_get_is_url (attach)) {
-                               const gchar *data;
-                               gchar *uri;
-
-                               data = i_cal_attach_get_url (attach);
-                               uri = i_cal_value_decode_ical_string (data);
-
-                               if (uri && g_ascii_strncasecmp (uri, "file://", 7) == 0) {
-                                       CamelStream *content_stream;
-                                       gchar *filename;
-
-                                       filename = g_filename_from_uri (uri, NULL, error);
-                                       content_stream = filename ? camel_stream_fs_new_with_name (filename, 
O_RDONLY, 0, error) : NULL;
-
-                                       if (content_stream) {
-                                               CamelMimeFilter *filter;
-                                               CamelStream *filter_stream;
-                                               CamelStream *base64_stream;
-
-                                               base64_stream = camel_stream_mem_new ();
-                                               filter_stream = camel_stream_filter_new (base64_stream);
-
-                                               filter = camel_mime_filter_basic_new 
(CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
-                                               camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), 
filter);
-                                               g_object_unref (filter);
-
-                                               success = camel_stream_write_to_stream (content_stream, 
filter_stream, cancellable, error) != -1;
-
-                                               camel_stream_flush (filter_stream, cancellable, NULL);
-                                               g_object_unref (filter_stream);
-
-                                               /* Ensure the stream is NUL-terminated, thus it can be used 
as a string */
-                                               camel_stream_write (base64_stream, "\0", 1, cancellable, 
NULL);
-
-                                               camel_stream_flush (base64_stream, cancellable, NULL);
-                                               g_object_unref (content_stream);
-
-                                               if (success) {
-                                                       GByteArray *bytes;
-
-                                                       bytes = camel_stream_mem_get_byte_array 
(CAMEL_STREAM_MEM (base64_stream));
-
-                                                       builder = json_builder_new_immutable ();
-                                                       e_m365_attachment_begin_attachment (builder, 
E_M365_ATTACHMENT_DATA_TYPE_FILE);
-                                                       e_m365_file_attachment_add_content_bytes (builder, 
(const gchar *) bytes->data);
-                                               }
-
-                                               g_object_unref (base64_stream);
-                                       } else {
-                                               success = FALSE;
-                                       }
-
-                                       g_free (filename);
-                               } else {
-                                       success = FALSE;
-
-                                       if (uri)
-                                               g_set_error (error, E_CLIENT_ERROR, 
E_CLIENT_ERROR_OTHER_ERROR, _("Cannot store attachment with URI ā€œ%sā€"), uri);
-                                       else
-                                               g_propagate_error (error, EC_ERROR_EX 
(E_CLIENT_ERROR_OTHER_ERROR, _("Failed to read attachment URI")));
-                               }
-
-                               g_free (uri);
-                       } else {
-                               const gchar *base64_data;
-
-                               base64_data = i_cal_attach_get_data (attach);
-
-                               if (base64_data) {
-                                       builder = json_builder_new_immutable ();
-                                       e_m365_attachment_begin_attachment (builder, 
E_M365_ATTACHMENT_DATA_TYPE_FILE);
-                                       e_m365_file_attachment_add_content_bytes (builder, base64_data);
-                               } else {
-                                       g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_OTHER_ERROR, 
_("Failed to get inline attachment data")));
-                               }
-                       }
-
-                       if (builder) {
-                               ICalParameter *param;
-                               const gchar *tmp;
-
-                               param = i_cal_property_get_first_parameter (prop, I_CAL_FILENAME_PARAMETER);
-
-                               if (param) {
-                                       tmp = i_cal_parameter_get_filename (param);
-
-                                       if (tmp && *tmp)
-                                               e_m365_attachment_add_name (builder, tmp);
-
-                                       g_clear_object (&param);
-                               }
-
-                               param = i_cal_property_get_first_parameter (prop, I_CAL_FMTTYPE_PARAMETER);
-
-                               if (param) {
-                                       tmp = i_cal_parameter_get_fmttype (param);
-
-                                       if (tmp && *tmp)
-                                               e_m365_attachment_add_content_type (builder, tmp);
-                                       else
-                                               e_m365_attachment_add_content_type (builder, 
"application/octet-stream");
-
-                                       g_clear_object (&param);
-                               } else {
-                                       e_m365_attachment_add_content_type (builder, 
"application/octet-stream");
-                               }
-
-                               e_m365_attachment_end_attachment (builder);
-
-                               success = add_attachment_func (cbm365->priv->cnc, NULL,
-                                       cbm365->priv->group_id, cbm365->priv->folder_id, m365_id,
-                                       builder, NULL, cancellable, error);
-
-                               g_object_unref (builder);
-                       }
-
-                       g_object_unref (attach);
-               }
-
-               if (save_attachs != new_attachs)
-                       g_slist_free_full (save_attachs, g_object_unref);
-       }
-
-       if (old_attachs && success) {
-               GHashTableIter iter;
-               gpointer key;
-
-               g_hash_table_iter_init (&iter, old_attachs);
-
-               while (g_hash_table_iter_next (&iter, &key, NULL) && success) {
-                       const gchar *attachment_id = key;
-
-                       success = delete_attachment_func (cbm365->priv->cnc, NULL,
-                               cbm365->priv->group_id, cbm365->priv->folder_id, i_cal_component_get_uid 
(new_comp),
-                               attachment_id, cancellable, error);
-               }
-       }
-
-       if (old_attachs)
-               g_hash_table_destroy (old_attachs);
-       g_slist_free_full (new_attachs, g_object_unref);
-
-       return success;
-}
-
-static void
-ecb_m365_get_task_status (ECalBackendM365 *cbm365,
-                         EM365Task *m365_task,
-                         ICalComponent *inout_comp,
-                         ICalPropertyKind prop_kind)
-{
-       ICalPropertyStatus status = I_CAL_STATUS_NONE;
-
-       switch (e_m365_task_get_status (m365_task)) {
-       case E_M365_STATUS_NOT_STARTED:
-               break;
-       case E_M365_STATUS_IN_PROGRESS:
-       case E_M365_STATUS_WAITING_ON_OTHERS:
-               status = I_CAL_STATUS_INPROCESS;
-               break;
-       case E_M365_STATUS_COMPLETED:
-               status = I_CAL_STATUS_COMPLETED;
-               break;
-       case E_M365_STATUS_DEFERRED:
-               status = I_CAL_STATUS_CANCELLED;
-               break;
-       default:
-               break;
-       }
-
-       if (status != I_CAL_STATUS_NONE)
-               i_cal_component_take_property (inout_comp, i_cal_property_new_status (status));
-}
-
-static void
-ecb_m365_add_task_status (ECalBackendM365 *cbm365,
-                         ICalComponent *new_comp,
-                         ICalComponent *old_comp,
-                         ICalPropertyKind prop_kind,
-                         JsonBuilder *builder)
-{
-       ICalProperty *new_prop, *old_prop;
-       ICalPropertyStatus new_value, old_value;
-
-       new_prop = i_cal_component_get_first_property (new_comp, prop_kind);
-       old_prop = old_comp ? i_cal_component_get_first_property (old_comp, prop_kind) : NULL;
-
-       if (!new_prop && !old_prop)
-               return;
-
-       new_value = new_prop ? i_cal_property_get_status (new_prop) : I_CAL_STATUS_NONE;
-       old_value = old_prop ? i_cal_property_get_status (old_prop) : I_CAL_STATUS_NONE;
-
-       if (new_value != old_value) {
-               EM365StatusType value = E_M365_STATUS_UNKNOWN;
-
-               switch (new_value) {
-               case I_CAL_STATUS_NONE:
-                       value = E_M365_STATUS_NOT_STARTED;
-                       break;
-               case I_CAL_STATUS_INPROCESS:
-                       value = E_M365_STATUS_IN_PROGRESS;
-                       break;
-               case I_CAL_STATUS_COMPLETED:
-                       value = E_M365_STATUS_COMPLETED;
-                       break;
-               case I_CAL_STATUS_CANCELLED:
-                       value = E_M365_STATUS_DEFERRED;
-                       break;
-               default:
-                       break;
-               }
-
-               if (value != E_M365_STATUS_UNKNOWN)
-                       e_m365_task_add_status (builder, value);
-       }
-
-       g_clear_object (&new_prop);
-       g_clear_object (&old_prop);
-}
-
-#define SIMPLE_FIELD(propknd, getfn, addfn) { propknd, FALSE, getfn, NULL, addfn, NULL }
-#define COMPLEX_FIELD(propknd, getfn, addfn) { propknd, FALSE, NULL, getfn, NULL, addfn }
-#define COMPLEX_FIELD_2(propknd, getfn, addfn) { propknd, TRUE, NULL, getfn, NULL, addfn }
-
-struct _mappings {
-       ICalPropertyKind prop_kind;
-       gboolean add_in_second_go;
-       void            (* get_simple_func)     (ECalBackendM365 *cbm365,
-                                                EM365Event *m365_event,
-                                                ICalComponent *inout_comp,
-                                                ICalPropertyKind prop_kind);
-       gboolean        (* get_func)            (ECalBackendM365 *cbm365,
-                                                EM365Event *m365_event,
-                                                ICalComponent *inout_comp,
-                                                ICalPropertyKind prop_kind,
-                                                GCancellable *cancellable,
-                                                GError **error);
-       void            (* add_simple_func)     (ECalBackendM365 *cbm365,
-                                                ICalComponent *new_comp,
-                                                ICalComponent *old_comp, /* nullable */
-                                                ICalPropertyKind prop_kind,
-                                                JsonBuilder *builder);
-       gboolean        (* add_func)            (ECalBackendM365 *cbm365,
-                                                ICalComponent *new_comp,
-                                                ICalComponent *old_comp, /* nullable */
-                                                ICalPropertyKind prop_kind,
-                                                const gchar *m365_id,
-                                                JsonBuilder *builder,
-                                                GCancellable *cancellable,
-                                                GError **error);
-} event_mappings[] = {
-       SIMPLE_FIELD    (I_CAL_UID_PROPERTY,            ecb_m365_get_uid,               NULL),
-       SIMPLE_FIELD    (I_CAL_CREATED_PROPERTY,        ecb_m365_get_date_time,         NULL),
-       SIMPLE_FIELD    (I_CAL_LASTMODIFIED_PROPERTY,   ecb_m365_get_date_time,         NULL),
-       SIMPLE_FIELD    (I_CAL_DTSTART_PROPERTY,        ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
-       SIMPLE_FIELD    (I_CAL_DTEND_PROPERTY,          ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
-       SIMPLE_FIELD    (I_CAL_CATEGORIES_PROPERTY,     ecb_m365_get_categories,        
ecb_m365_add_categories),
-       SIMPLE_FIELD    (I_CAL_SUMMARY_PROPERTY,        ecb_m365_get_subject,           ecb_m365_add_subject),
-       SIMPLE_FIELD    (I_CAL_DESCRIPTION_PROPERTY,    ecb_m365_get_body,              ecb_m365_add_body),
-       SIMPLE_FIELD    (I_CAL_CLASS_PROPERTY,          ecb_m365_get_sensitivity,       
ecb_m365_add_sensitivity),
-       SIMPLE_FIELD    (I_CAL_TRANSP_PROPERTY,         ecb_m365_get_show_as,           ecb_m365_add_show_as),
-       SIMPLE_FIELD    (I_CAL_LOCATION_PROPERTY,       ecb_m365_get_location,          
ecb_m365_add_location),
-       SIMPLE_FIELD    (I_CAL_ORGANIZER_PROPERTY,      ecb_m365_get_organizer,         
ecb_m365_add_organizer),
-       SIMPLE_FIELD    (I_CAL_ATTENDEE_PROPERTY,       ecb_m365_get_attendees,         
ecb_m365_add_attendees),
-       SIMPLE_FIELD    (I_CAL_PRIORITY_PROPERTY,       ecb_m365_get_importance,        
ecb_m365_add_importance),
-       SIMPLE_FIELD    (I_CAL_STATUS_PROPERTY,         ecb_m365_get_event_status,      NULL),
-       COMPLEX_FIELD   (I_CAL_RRULE_PROPERTY,          ecb_m365_get_recurrence,        
ecb_m365_add_recurrence),
-       COMPLEX_FIELD   (I_CAL_X_PROPERTY,              ecb_m365_get_reminder,          
ecb_m365_add_reminder),
-       COMPLEX_FIELD_2 (I_CAL_ATTACH_PROPERTY,         ecb_m365_get_attachments,       
ecb_m365_add_attachments)
-}, task_mappings[] = {
-       SIMPLE_FIELD    (I_CAL_UID_PROPERTY,            ecb_m365_get_uid,               NULL),
-       SIMPLE_FIELD    (I_CAL_CREATED_PROPERTY,        ecb_m365_get_date_time,         NULL),
-       SIMPLE_FIELD    (I_CAL_LASTMODIFIED_PROPERTY,   ecb_m365_get_date_time,         NULL),
-       SIMPLE_FIELD    (I_CAL_DTSTART_PROPERTY,        ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
-       SIMPLE_FIELD    (I_CAL_DUE_PROPERTY,            ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
-       SIMPLE_FIELD    (I_CAL_COMPLETED_PROPERTY,      ecb_m365_get_date_time_zone,    
ecb_m365_add_date_time_zone),
-       SIMPLE_FIELD    (I_CAL_CATEGORIES_PROPERTY,     ecb_m365_get_categories,        
ecb_m365_add_categories),
-       SIMPLE_FIELD    (I_CAL_SUMMARY_PROPERTY,        ecb_m365_get_subject,           ecb_m365_add_subject),
-       SIMPLE_FIELD    (I_CAL_DESCRIPTION_PROPERTY,    ecb_m365_get_body,              ecb_m365_add_body),
-       SIMPLE_FIELD    (I_CAL_CLASS_PROPERTY,          ecb_m365_get_sensitivity,       
ecb_m365_add_sensitivity),
-       SIMPLE_FIELD    (I_CAL_STATUS_PROPERTY,         ecb_m365_get_task_status,       
ecb_m365_add_task_status),
-       COMPLEX_FIELD   (I_CAL_RRULE_PROPERTY,          ecb_m365_get_recurrence,        
ecb_m365_add_recurrence),
-       COMPLEX_FIELD   (I_CAL_X_PROPERTY,              ecb_m365_get_reminder,          
ecb_m365_add_reminder),
-       COMPLEX_FIELD_2 (I_CAL_ATTACH_PROPERTY,         ecb_m365_get_attachments,       
ecb_m365_add_attachments)
-};
-
-static const struct _mappings *
-ecb_m365_get_mappings_for_backend (ECalBackendM365 *cbm365,
-                                  guint *out_n_elements)
-{
-       ICalComponentKind kind;
-
-       kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbm365));
-
-       if (kind == I_CAL_VEVENT_COMPONENT) {
-               *out_n_elements = G_N_ELEMENTS (event_mappings);
-               return event_mappings;
-       }
-
-       if (kind == I_CAL_VTODO_COMPONENT) {
-               *out_n_elements = G_N_ELEMENTS (task_mappings);
-               return task_mappings;
-       }
-
-       g_warn_if_reached ();
-
-       return NULL;
-}
-
 static gchar *
 ecb_m365_join_to_extra (const gchar *change_key,
                        const gchar *ical_comp)
@@ -2644,41 +135,17 @@ ecb_m365_json_to_ical (ECalBackendM365 *cbm365,
                       GCancellable *cancellable,
                       GError **error)
 {
-       const struct _mappings *mappings;
-       ICalComponent *icomp = NULL;
-       ICalComponentKind kind;
-       guint ii, n_elements = 0;
-       gboolean success = TRUE;
-
        g_return_val_if_fail (m365_object != NULL, NULL);
 
-       mappings = ecb_m365_get_mappings_for_backend (cbm365, &n_elements);
-       g_return_val_if_fail (mappings != NULL, NULL);
-
-       kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbm365));
-
-       if (kind == I_CAL_VEVENT_COMPONENT)
-               icomp = i_cal_component_new_vevent ();
-       else if (kind == I_CAL_VTODO_COMPONENT)
-               icomp = i_cal_component_new_vtodo ();
-       else
-               g_warn_if_reached ();
-
-       if (!icomp)
-               return NULL;
-
-       for (ii = 0; success && ii < n_elements; ii++) {
-               if (mappings[ii].get_simple_func) {
-                       mappings[ii].get_simple_func (cbm365, m365_object, icomp, mappings[ii].prop_kind);
-               } else if (mappings[ii].get_func) {
-                       success = mappings[ii].get_func (cbm365, m365_object, icomp, mappings[ii].prop_kind, 
cancellable, error);
-               }
-       }
-
-       if (!success)
-               g_clear_object (&icomp);
-
-       return icomp;
+       return e_cal_backend_m365_utils_json_to_ical (cbm365->priv->cnc,
+               cbm365->priv->group_id,
+               cbm365->priv->folder_id,
+               cbm365->priv->attachments_dir,
+               E_TIMEZONE_CACHE (cbm365),
+               e_cal_backend_get_kind (E_CAL_BACKEND (cbm365)),
+               m365_object,
+               cancellable,
+               error);
 }
 
 static ECalMetaBackendInfo *
@@ -2716,33 +183,17 @@ ecb_m365_ical_to_json_locked (ECalBackendM365 *cbm365,
                              GCancellable *cancellable,
                              GError **error)
 {
-       const struct _mappings *mappings;
-       JsonBuilder *builder;
-       guint ii, n_elements = 0;
-       gboolean success = TRUE;
-
        g_return_val_if_fail (new_comp != NULL, NULL);
 
-       mappings = ecb_m365_get_mappings_for_backend (cbm365, &n_elements);
-       g_return_val_if_fail (mappings != NULL, NULL);
-
-       builder = json_builder_new_immutable ();
-       e_m365_json_begin_object_member (builder, NULL);
-
-       for (ii = 0; success && ii < n_elements; ii++) {
-               if (mappings[ii].add_simple_func) {
-                       mappings[ii].add_simple_func (cbm365, new_comp, old_comp, mappings[ii].prop_kind, 
builder);
-               } else if (!mappings[ii].add_in_second_go && mappings[ii].add_func) {
-                       success = mappings[ii].add_func (cbm365, new_comp, old_comp, mappings[ii].prop_kind, 
NULL, builder, cancellable, error);
-               }
-       }
-
-       e_m365_json_end_object_member (builder);
-
-       if (!success)
-               g_clear_object (&builder);
-
-       return builder;
+       return e_cal_backend_m365_utils_ical_to_json (cbm365->priv->cnc,
+               cbm365->priv->group_id,
+               cbm365->priv->folder_id,
+               E_TIMEZONE_CACHE (cbm365),
+               e_cal_backend_get_kind (E_CAL_BACKEND (cbm365)),
+               new_comp,
+               old_comp,
+               cancellable,
+               error);
 }
 
 static gboolean
@@ -2753,22 +204,19 @@ ecb_m365_ical_to_json_2nd_go_locked (ECalBackendM365 *cbm365,
                                     GCancellable *cancellable,
                                     GError **error)
 {
-       const struct _mappings *mappings;
-       guint ii, n_elements = 0;
-       gboolean success = TRUE;
-
        g_return_val_if_fail (new_comp != NULL, FALSE);
-
-       mappings = ecb_m365_get_mappings_for_backend (cbm365, &n_elements);
-       g_return_val_if_fail (mappings != NULL, FALSE);
-
-       for (ii = 0; success && ii < n_elements; ii++) {
-               if (mappings[ii].add_in_second_go && mappings[ii].add_func) {
-                       success = mappings[ii].add_func (cbm365, new_comp, old_comp, mappings[ii].prop_kind, 
m365_id, NULL, cancellable, error);
-               }
-       }
-
-       return success;
+       g_return_val_if_fail (m365_id != NULL, FALSE);
+
+       return e_cal_backend_m365_utils_ical_to_json_2nd_go (cbm365->priv->cnc,
+               cbm365->priv->group_id,
+               cbm365->priv->folder_id,
+               E_TIMEZONE_CACHE (cbm365),
+               e_cal_backend_get_kind (E_CAL_BACKEND (cbm365)),
+               new_comp,
+               old_comp,
+               m365_id,
+               cancellable,
+               error);
 }
 
 static gboolean
diff --git a/src/Microsoft365/common/e-m365-connection.c b/src/Microsoft365/common/e-m365-connection.c
index 889dc670..02664874 100644
--- a/src/Microsoft365/common/e-m365-connection.c
+++ b/src/Microsoft365/common/e-m365-connection.c
@@ -3854,7 +3854,7 @@ gboolean
 e_m365_connection_create_event_sync (EM365Connection *cnc,
                                     const gchar *user_override, /* for which user, NULL to use the account 
user */
                                     const gchar *group_id, /* nullable, then the default group is used */
-                                    const gchar *calendar_id,
+                                    const gchar *calendar_id, /* nullable, then the default user calendar is 
used */
                                     JsonBuilder *event,
                                     EM365Event **out_created_event,
                                     GCancellable *cancellable,
@@ -3869,13 +3869,18 @@ e_m365_connection_create_event_sync (EM365Connection *cnc,
        g_return_val_if_fail (event != NULL, FALSE);
        g_return_val_if_fail (out_created_event != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
-               group_id ? "calendarGroups" : "calendars",
-               group_id,
-               group_id ? "calendars" : NULL,
-               "", calendar_id,
-               "", "events",
-               NULL);
+       if (calendar_id) {
+               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+                       group_id ? "calendarGroups" : "calendars",
+                       group_id,
+                       group_id ? "calendars" : NULL,
+                       "", calendar_id,
+                       "", "events",
+                       NULL);
+       } else {
+               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, "me",
+                       "events", NULL, NULL, NULL);
+       }
 
        message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
 
@@ -4405,7 +4410,7 @@ gboolean
 e_m365_connection_add_event_attachment_sync (EM365Connection *cnc,
                                             const gchar *user_override, /* for which user, NULL to use the 
account user */
                                             const gchar *group_id, /* nullable, then the default group is 
used */
-                                            const gchar *calendar_id,
+                                            const gchar *calendar_id, /* nullable, then the default user 
calendar is used */
                                             const gchar *event_id,
                                             JsonBuilder *in_attachment,
                                             EM365Attachment **out_attachment, /* nullable */
@@ -4421,15 +4426,23 @@ e_m365_connection_add_event_attachment_sync (EM365Connection *cnc,
        g_return_val_if_fail (event_id != NULL, FALSE);
        g_return_val_if_fail (in_attachment != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
-               group_id ? "calendarGroups" : "calendars",
-               group_id,
-               group_id ? "calendars" : NULL,
-               "", calendar_id,
-               "", "events",
-               "", event_id,
-               "", "attachments",
-               NULL);
+       if (calendar_id) {
+               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+                       group_id ? "calendarGroups" : "calendars",
+                       group_id,
+                       group_id ? "calendars" : NULL,
+                       "", calendar_id,
+                       "", "events",
+                       "", event_id,
+                       "", "attachments",
+                       NULL);
+       } else {
+               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, "me",
+                       "events", NULL, NULL,
+                       "", event_id,
+                       "", "attachments",
+                       NULL);
+       }
 
        message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, out_attachment ? CSM_DEFAULT : 
CSM_DISABLE_RESPONSE, error);
 


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