[evolution-ews/wip/mcrha/office365: 40/50] Add event-related functions and fill few stubs in the calendar backend code




commit fa7113b629a305379ae0cd8b0d151a71b3f556f3
Author: Milan Crha <mcrha redhat com>
Date:   Fri Jul 17 14:28:41 2020 +0200

    Add event-related functions and fill few stubs in the calendar backend code

 src/Office365/addressbook/e-book-backend-o365.c    |  13 +-
 .../calendar/e-cal-backend-o365-factory.c          |  17 +-
 src/Office365/calendar/e-cal-backend-o365.c        | 296 +++++++++--
 src/Office365/common/e-o365-connection.c           | 559 ++++++++++++++++++++-
 src/Office365/common/e-o365-connection.h           |  94 ++++
 src/Office365/common/e-o365-json-utils.c           |  52 +-
 src/Office365/common/e-o365-json-utils.h           |  10 +-
 7 files changed, 971 insertions(+), 70 deletions(-)
---
diff --git a/src/Office365/addressbook/e-book-backend-o365.c b/src/Office365/addressbook/e-book-backend-o365.c
index 724b2682..43224979 100644
--- a/src/Office365/addressbook/e-book-backend-o365.c
+++ b/src/Office365/addressbook/e-book-backend-o365.c
@@ -23,10 +23,6 @@
 #include <glib.h>
 #include <glib/gi18n-lib.h>
 
-#define LIBICAL_GLIB_UNSTABLE_API
-#include <libical-glib/libical-glib.h>
-#undef LIBICAL_GLIB_UNSTABLE_API
-
 #include <libedata-book/libedata-book.h>
 
 #include "common/camel-o365-settings.h"
@@ -47,9 +43,6 @@
 #define EC_ERROR_EX(_code,_msg) e_client_error_create (_code, _msg)
 #define EBC_ERROR_EX(_code,_msg) e_book_client_error_create (_code, _msg)
 
-#define EBB_O365_DATA_VERSION 1
-#define EBB_O365_DATA_VERSION_KEY "o365-data-version"
-
 #define LOCK(_bb) g_rec_mutex_lock (&_bb->priv->property_lock)
 #define UNLOCK(_bb) g_rec_mutex_unlock (&_bb->priv->property_lock)
 
@@ -1797,11 +1790,7 @@ ebb_o365_search_sync (EBookMetaBackend *meta_backend,
        /*ebb_o365_update_cache_for_expression (E_BOOK_BACKEND_O365 (meta_backend), expr, cancellable, 
NULL);*/
 
        /* Chain up to parent's method */
-       if (!E_BOOK_META_BACKEND_CLASS (e_book_backend_o365_parent_class)->search_sync (meta_backend, expr, 
meta_contact,
-               out_contacts, cancellable, error))
-               return FALSE;
-
-       return TRUE;
+       return E_BOOK_META_BACKEND_CLASS (e_book_backend_o365_parent_class)->search_sync (meta_backend, expr, 
meta_contact, out_contacts, cancellable, error);
 }
 
 static gboolean
diff --git a/src/Office365/calendar/e-cal-backend-o365-factory.c 
b/src/Office365/calendar/e-cal-backend-o365-factory.c
index 3711559b..bcada3e7 100644
--- a/src/Office365/calendar/e-cal-backend-o365-factory.c
+++ b/src/Office365/calendar/e-cal-backend-o365-factory.c
@@ -49,20 +49,9 @@ GType e_cal_backend_o365_events_factory_get_type (void);
 GType e_cal_backend_o365_journal_factory_get_type (void);
 GType e_cal_backend_o365_todos_factory_get_type (void);
 
-G_DEFINE_DYNAMIC_TYPE (
-       ECalBackendO365EventsFactory,
-       e_cal_backend_o365_events_factory,
-       E_TYPE_CAL_BACKEND_FACTORY)
-
-G_DEFINE_DYNAMIC_TYPE (
-       ECalBackendO365JournalFactory,
-       e_cal_backend_o365_journal_factory,
-       E_TYPE_CAL_BACKEND_FACTORY)
-
-G_DEFINE_DYNAMIC_TYPE (
-       ECalBackendO365TodosFactory,
-       e_cal_backend_o365_todos_factory,
-       E_TYPE_CAL_BACKEND_FACTORY)
+G_DEFINE_DYNAMIC_TYPE (ECalBackendO365EventsFactory, e_cal_backend_o365_events_factory, 
E_TYPE_CAL_BACKEND_FACTORY)
+G_DEFINE_DYNAMIC_TYPE (ECalBackendO365JournalFactory, e_cal_backend_o365_journal_factory, 
E_TYPE_CAL_BACKEND_FACTORY)
+G_DEFINE_DYNAMIC_TYPE (ECalBackendO365TodosFactory, e_cal_backend_o365_todos_factory, 
E_TYPE_CAL_BACKEND_FACTORY)
 
 static void
 e_cal_backend_o365_events_factory_class_init (ECalBackendFactoryClass *class)
diff --git a/src/Office365/calendar/e-cal-backend-o365.c b/src/Office365/calendar/e-cal-backend-o365.c
index 74442e00..142dfde7 100644
--- a/src/Office365/calendar/e-cal-backend-o365.c
+++ b/src/Office365/calendar/e-cal-backend-o365.c
@@ -24,6 +24,7 @@
 #include <libecal/libecal.h>
 
 #include "common/camel-o365-settings.h"
+#include "common/e-o365-connection.h"
 #include "common/e-source-o365-folder.h"
 
 #include "e-cal-backend-o365.h"
@@ -42,17 +43,21 @@
 #define ECC_ERROR(_code) e_cal_client_error_create (_code, NULL)
 #define ECC_ERROR_EX(_code, _msg) e_cal_client_error_create (_code, _msg)
 
-/* Private part of the CalBackendO365 structure */
+#define LOCK(_cb) g_rec_mutex_lock (&_cb->priv->property_lock)
+#define UNLOCK(_cb) g_rec_mutex_unlock (&_cb->priv->property_lock)
+
 struct _ECalBackendO365Private {
        GRecMutex property_lock;
-
+       EO365Connection *cnc;
+       gchar *group_id;
+       gchar *calendar_id;
        gchar *attachments_dir;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (ECalBackendO365, e_cal_backend_o365, E_TYPE_CAL_META_BACKEND)
 
 static void
-ecb_o365_convert_error_to_edc_error (GError **perror)
+ecb_o365_convert_error_to_client_error (GError **perror)
 {
        GError *error = NULL;
 
@@ -91,6 +96,82 @@ ecb_o365_convert_error_to_edc_error (GError **perror)
        }
 }
 
+static gchar *
+ecb_o365_join_to_extra (const gchar *change_key,
+                       const gchar *ical_comp)
+{
+       if (!change_key && !ical_comp)
+               return NULL;
+
+       return g_strconcat (change_key ? change_key : "", "\n", ical_comp, NULL);
+}
+
+/* Modifies inout_extra, cannot be called multiple times with the same arguments */
+static void
+ecb_o365_split_extra (gchar *inout_extra,
+                     const gchar **out_change_key,
+                     const gchar **out_ical_comp)
+{
+       gchar *enter;
+
+       if (!inout_extra)
+               return;
+
+       enter = strchr (inout_extra, '\n');
+       g_return_if_fail (enter != NULL);
+
+       *enter = '\0';
+       enter++;
+
+       if (out_change_key)
+               *out_change_key = inout_extra;
+
+       if (out_ical_comp)
+               *out_ical_comp = enter;
+}
+
+static ECalMetaBackendInfo *
+ecb_o365_json_to_ical_nfo (ECalBackendO365 *cbo365,
+                          EO365Event *event,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       return NULL;
+}
+
+static gboolean
+ecb_o365_download_event_changes_locked (ECalBackendO365 *cbo365,
+                                       const GSList *ids,
+                                       GSList **out_info_objects,
+                                       GCancellable *cancellable,
+                                       GError **error)
+{
+       GSList *events = NULL, *link;
+
+       if (!ids)
+               return TRUE;
+
+       if (!e_o365_connection_get_events_sync (cbo365->priv->cnc, NULL, cbo365->priv->group_id, 
cbo365->priv->calendar_id, ids, NULL, NULL, &events, cancellable, error))
+               return FALSE;
+
+       for (link = events; link; link = g_slist_next (link)) {
+               EO365Event *event = link->data;
+               ECalMetaBackendInfo *nfo;
+
+               if (!event)
+                       continue;
+
+               nfo = ecb_o365_json_to_ical_nfo (cbo365, event, cancellable, error);
+
+               if (nfo)
+                       *out_info_objects = g_slist_prepend (*out_info_objects, nfo);
+       }
+
+       g_slist_free_full (events, (GDestroyNotify) json_object_unref);
+
+       return TRUE;
+}
+
 static void
 ecb_o365_maybe_disconnect_sync (ECalBackendO365 *cbo365,
                                GError **in_perror,
@@ -104,22 +185,30 @@ ecb_o365_maybe_disconnect_sync (ECalBackendO365 *cbo365,
        }
 }
 
-static void
-ecb_o365_unset_connection (ECalBackendO365 *cbo365,
-                          gboolean is_disconnect)
+static gboolean
+ecb_o365_unset_connection_sync (ECalBackendO365 *cbo365,
+                               gboolean is_disconnect,
+                               GCancellable *cancellable,
+                               GError **error)
 {
-       g_return_if_fail (E_IS_CAL_BACKEND_O365 (cbo365));
+       gboolean success = TRUE;
 
-       g_rec_mutex_lock (&cbo365->priv->property_lock);
+       g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (cbo365), FALSE);
 
-       /*if (cbo365->priv->cnc) {
+       LOCK (cbo365);
+
+       if (cbo365->priv->cnc) {
                if (is_disconnect)
-                       e_o365_connection_set_disconnected_flag (cbo365->priv->cnc, TRUE);
+                       success = e_o365_connection_disconnect_sync (cbo365->priv->cnc, cancellable, error);
        }
 
-       g_clear_object (&cbo365->priv->cnc);*/
+       g_clear_object (&cbo365->priv->cnc);
+       g_clear_pointer (&cbo365->priv->group_id, g_free);
+       g_clear_pointer (&cbo365->priv->calendar_id, g_free);
+
+       UNLOCK (cbo365);
 
-       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+       return success;
 }
 
 static gboolean
@@ -139,17 +228,71 @@ ecb_o365_connect_sync (ECalMetaBackend *meta_backend,
 
        cbo365 = E_CAL_BACKEND_O365 (meta_backend);
 
-       g_rec_mutex_lock (&cbo365->priv->property_lock);
+       LOCK (cbo365);
 
-       /*if (cbo365->priv->cnc)*/ {
-               g_rec_mutex_unlock (&cbo365->priv->property_lock);
+       if (cbo365->priv->cnc) {
+               UNLOCK (cbo365);
 
                *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
 
                return TRUE;
+       } else {
+               EBackend *backend;
+               ESourceRegistry *registry;
+               ESource *source;
+               EO365Connection *cnc;
+               ESourceO365Folder *o365_folder_extension;
+               CamelO365Settings *o365_settings;
+               gchar *group_id;
+               gchar *calendar_id;
+
+               backend = E_BACKEND (cbo365);
+               source = e_backend_get_source (backend);
+               registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbo365));
+               o365_settings = camel_o365_settings_get_from_backend (backend, registry);
+               g_warn_if_fail (o365_settings != NULL);
+
+               o365_folder_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_O365_FOLDER);
+               group_id = e_source_o365_folder_dup_group_id (o365_folder_extension);
+               calendar_id = e_source_o365_folder_dup_id (o365_folder_extension);
+
+               if (calendar_id) {
+                       cnc = e_o365_connection_new_for_backend (backend, registry, source, o365_settings);
+
+                       *out_auth_result = e_o365_connection_authenticate_sync (cnc, NULL, 
E_O365_FOLDER_KIND_CALENDAR, group_id, calendar_id,
+                               out_certificate_pem, out_certificate_errors, cancellable, error);
+
+                       if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
+                               cbo365->priv->cnc = g_object_ref (cnc);
+
+                               g_warn_if_fail (cbo365->priv->group_id == NULL);
+                               g_warn_if_fail (cbo365->priv->calendar_id == NULL);
+
+                               g_free (cbo365->priv->group_id);
+                               cbo365->priv->group_id = group_id;
+
+                               g_free (cbo365->priv->calendar_id);
+                               cbo365->priv->calendar_id = calendar_id;
+
+                               group_id = NULL;
+                               calendar_id = NULL;
+                               success = TRUE;
+
+                               e_cal_backend_set_writable (E_CAL_BACKEND (cbo365), TRUE);
+                       }
+               } else {
+                       *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR;
+                       g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_OTHER_ERROR, _("Folder ID is 
not set")));
+               }
+
+               g_clear_object (&cnc);
+               g_free (group_id);
+               g_free (calendar_id);
        }
 
-       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+       UNLOCK (cbo365);
+
+       ecb_o365_convert_error_to_client_error (error);
 
        return success;
 }
@@ -161,9 +304,7 @@ ecb_o365_disconnect_sync (ECalMetaBackend *meta_backend,
 {
        g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (meta_backend), FALSE);
 
-       ecb_o365_unset_connection (E_CAL_BACKEND_O365 (meta_backend), TRUE);
-
-       return TRUE;
+       return ecb_o365_unset_connection_sync (E_CAL_BACKEND_O365 (meta_backend), TRUE, cancellable, error);
 }
 
 static gboolean
@@ -180,7 +321,9 @@ ecb_o365_get_changes_sync (ECalMetaBackend *meta_backend,
 {
        ECalBackendO365 *cbo365;
        ECalCache *cal_cache;
-       gboolean success = FALSE;
+       EO365Calendar *o365_calendar = NULL;
+       gboolean changed = FALSE;
+       gboolean success = TRUE;
 
        g_return_val_if_fail (E_IS_CAL_BACKEND_O365 (meta_backend), FALSE);
        g_return_val_if_fail (out_new_sync_tag != NULL, FALSE);
@@ -198,13 +341,100 @@ ecb_o365_get_changes_sync (ECalMetaBackend *meta_backend,
        cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
        g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
 
-       g_rec_mutex_lock (&cbo365->priv->property_lock);
+       LOCK (cbo365);
+
+       if (e_o365_connection_get_calendar_folder_sync (cbo365->priv->cnc, NULL, cbo365->priv->group_id, 
cbo365->priv->calendar_id, "id,changeKey",
+               &o365_calendar, cancellable, error) && o365_calendar) {
+               changed = g_strcmp0 (last_sync_tag, e_o365_calendar_get_change_key (o365_calendar)) != 0;
+
+               if (changed)
+                       *out_new_sync_tag = g_strdup (e_o365_calendar_get_change_key (o365_calendar));
+
+               json_object_unref (o365_calendar);
+       } else {
+               success = FALSE;
+       }
+
+       if (changed) {
+               GSList *events = NULL, *link;
+               gboolean full_read;
 
-       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+               full_read = !e_cache_get_count (E_CACHE (cal_cache), E_CACHE_INCLUDE_DELETED, cancellable, 
NULL);
 
-       ecb_o365_convert_error_to_edc_error (error);
+               success = e_o365_connection_list_events_sync (cbo365->priv->cnc, NULL, 
cbo365->priv->group_id, cbo365->priv->calendar_id, NULL,
+                       full_read ? NULL : "id,changeKey", &events, cancellable, error);
+
+               if (success) {
+                       GSList *new_ids = NULL; /* const gchar *, borrowed from 'events' objects */
+                       GSList *changed_ids = NULL; /* const gchar *, borrowed from 'events' objects */
+
+                       for (link = events; link && !g_cancellable_is_cancelled (cancellable); link = 
g_slist_next (link)) {
+                               EO365Event *event = link->data;
+                               const gchar *id, *change_key;
+                               gchar *extra = NULL;
+
+                               if (!event)
+                                       continue;
+
+                               id = e_o365_event_get_id (event);
+                               change_key = e_o365_event_get_change_key (event);
+
+                               if (e_cal_cache_get_component_extra (cal_cache, id, NULL, &extra, 
cancellable, NULL)) {
+                                       const gchar *saved_change_key = NULL;
+
+                                       ecb_o365_split_extra (extra, &saved_change_key, NULL);
+
+                                       if (g_strcmp0 (saved_change_key, change_key) == 0) {
+                                               g_free (extra);
+                                               continue;
+                                       } else if (full_read) {
+                                               ECalMetaBackendInfo *nfo;
+
+                                               nfo = ecb_o365_json_to_ical_nfo (cbo365, event, cancellable, 
NULL);
+
+                                               if (nfo)
+                                                       *out_modified_objects = g_slist_prepend 
(*out_modified_objects, nfo);
+                                       } else {
+                                               changed_ids = g_slist_prepend (changed_ids, (gpointer) id);
+                                       }
+
+                                       g_free (extra);
+                               } else if (full_read) {
+                                       ECalMetaBackendInfo *nfo;
+
+                                       nfo = ecb_o365_json_to_ical_nfo (cbo365, event, cancellable, NULL);
+
+                                       if (nfo)
+                                               *out_created_objects = g_slist_prepend (*out_created_objects, 
nfo);
+                               } else {
+                                       new_ids = g_slist_prepend (new_ids, (gpointer) id);
+                               }
+                       }
+
+                       if (new_ids) {
+                               new_ids = g_slist_reverse (new_ids);
+                               success = ecb_o365_download_event_changes_locked (cbo365, new_ids, 
out_created_objects, cancellable, error);
+                       }
+
+                       if (success && changed_ids) {
+                               changed_ids = g_slist_reverse (changed_ids);
+                               success = ecb_o365_download_event_changes_locked (cbo365, changed_ids, 
out_modified_objects, cancellable, error);
+                       }
+
+                       g_slist_free (new_ids);
+                       g_slist_free (changed_ids);
+               }
+
+               g_slist_free_full (events, (GDestroyNotify) json_object_unref);
+       }
+
+       UNLOCK (cbo365);
+
+       ecb_o365_convert_error_to_client_error (error);
        ecb_o365_maybe_disconnect_sync (cbo365, error, cancellable);
 
+       g_clear_object (&cal_cache);
+
        return success;
 }
 
@@ -227,11 +457,11 @@ ecb_o365_load_component_sync (ECalMetaBackend *meta_backend,
 
        cbo365 = E_CAL_BACKEND_O365 (meta_backend);
 
-       g_rec_mutex_lock (&cbo365->priv->property_lock);
+       LOCK (cbo365);
 
-       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+       UNLOCK (cbo365);
 
-       ecb_o365_convert_error_to_edc_error (error);
+       ecb_o365_convert_error_to_client_error (error);
        ecb_o365_maybe_disconnect_sync (cbo365, error, cancellable);
 
        return success;
@@ -256,11 +486,11 @@ ecb_o365_save_component_sync (ECalMetaBackend *meta_backend,
 
        cbo365 = E_CAL_BACKEND_O365 (meta_backend);
 
-       g_rec_mutex_lock (&cbo365->priv->property_lock);
+       LOCK (cbo365);
 
-       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+       UNLOCK (cbo365);
 
-       ecb_o365_convert_error_to_edc_error (error);
+       ecb_o365_convert_error_to_client_error (error);
        ecb_o365_maybe_disconnect_sync (cbo365, error, cancellable);
 
        return success;
@@ -284,11 +514,11 @@ ecb_o365_remove_component_sync (ECalMetaBackend *meta_backend,
 
        cbo365 = E_CAL_BACKEND_O365 (meta_backend);
 
-       g_rec_mutex_lock (&cbo365->priv->property_lock);
+       LOCK (cbo365);
 
-       g_rec_mutex_unlock (&cbo365->priv->property_lock);
+       UNLOCK (cbo365);
 
-       ecb_o365_convert_error_to_edc_error (error);
+       ecb_o365_convert_error_to_client_error (error);
        ecb_o365_maybe_disconnect_sync (cbo365, error, cancellable);
 
        return success;
@@ -405,7 +635,7 @@ ecb_o365_dispose (GObject *object)
 {
        ECalBackendO365 *cbo365 = E_CAL_BACKEND_O365 (object);
 
-       ecb_o365_unset_connection (cbo365, FALSE);
+       ecb_o365_unset_connection_sync (cbo365, FALSE, NULL, NULL);
 
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_cal_backend_o365_parent_class)->dispose (object);
diff --git a/src/Office365/common/e-o365-connection.c b/src/Office365/common/e-o365-connection.c
index 5ec6528a..b2066edb 100644
--- a/src/Office365/common/e-o365-connection.c
+++ b/src/Office365/common/e-o365-connection.c
@@ -2969,7 +2969,7 @@ e_o365_connection_copy_move_mail_messages_sync (EO365Connection *cnc,
                guint total, done = 0;
 
                total = g_slist_length ((GSList *) message_ids);
-               requests = g_ptr_array_new_full (MIN (E_O365_BATCH_MAX_REQUESTS, 50), g_object_unref);
+               requests = g_ptr_array_new_full (MIN (E_O365_BATCH_MAX_REQUESTS, MIN (total, 50)), 
g_object_unref);
 
                for (link = (GSList *) message_ids; link && success; link = g_slist_next (link)) {
                        const gchar *id = link->data;
@@ -3121,7 +3121,7 @@ e_o365_connection_delete_mail_messages_sync (EO365Connection *cnc,
                guint total, done = 0;
 
                total = g_slist_length ((GSList *) message_ids);
-               requests = g_ptr_array_new_full (MIN (E_O365_BATCH_MAX_REQUESTS, 50), g_object_unref);
+               requests = g_ptr_array_new_full (MIN (E_O365_BATCH_MAX_REQUESTS, MIN (total, 50)), 
g_object_unref);
 
                for (link = (GSList *) message_ids; link && success; link = g_slist_next (link)) {
                        const gchar *id = link->data;
@@ -4133,3 +4133,558 @@ e_o365_connection_list_events_sync (EO365Connection *cnc,
 
        return success;
 }
+
+/* https://docs.microsoft.com/en-us/graph/api/user-post-events?view=graph-rest-1.0&tabs=http
+   https://docs.microsoft.com/en-us/graph/api/group-post-events?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_create_event_sync (EO365Connection *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,
+                                    JsonBuilder *event,
+                                    EO365Calendar **out_created_event,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event != NULL, FALSE);
+       g_return_val_if_fail (out_created_event != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               group_id ? "calendarGroups" : "calendars",
+               group_id,
+               group_id ? "calendars" : NULL,
+               "", calendar_id,
+               "", "events",
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       e_o365_connection_set_json_body (message, event);
+
+       success = o365_connection_send_request_sync (cnc, message, e_o365_read_json_object_response_cb, NULL, 
out_created_event, cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/event-get?view=graph-rest-1.0&tabs=http */
+
+SoupMessage *
+e_o365_connection_prepare_get_event (EO365Connection *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 *event_id,
+                                    const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise 
that zone for the returned times */
+                                    const gchar *select, /* nullable - properties to select */
+                                    GError **error)
+{
+       SoupMessage *message;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), NULL);
+       g_return_val_if_fail (calendar_id != NULL, NULL);
+       g_return_val_if_fail (event_id != NULL, NULL);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               group_id ? "calendarGroups" : "calendars",
+               group_id,
+               group_id ? "calendars" : NULL,
+               "", calendar_id,
+               "", "events",
+               "", event_id,
+               "$select", select,
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return NULL;
+       }
+
+       g_free (uri);
+
+       if (prefer_outlook_timezone && *prefer_outlook_timezone) {
+               gchar *prefer_value;
+
+               prefer_value = g_strdup_printf ("outlook.timezone=\"%s\"", prefer_outlook_timezone);
+
+               soup_message_headers_append (message->request_headers, "Prefer", prefer_value);
+
+               g_free (prefer_value);
+       }
+
+       soup_message_headers_append (message->request_headers, "Prefer", 
"outlook.body-content-type=\"text\"");
+
+       return message;
+}
+
+gboolean
+e_o365_connection_get_event_sync (EO365Connection *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 *event_id,
+                                 const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise 
that zone for the returned times */
+                                 const gchar *select, /* nullable - properties to select */
+                                 EO365Event **out_event,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event_id != NULL, FALSE);
+       g_return_val_if_fail (out_event != NULL, FALSE);
+
+       message = e_o365_connection_prepare_get_event (cnc, user_override, group_id, calendar_id, event_id, 
prefer_outlook_timezone, select, error);
+
+       if (!message)
+               return FALSE;
+
+       success = o365_connection_send_request_sync (cnc, message, e_o365_read_json_object_response_cb, NULL, 
out_event, cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+gboolean
+e_o365_connection_get_events_sync (EO365Connection *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 GSList *event_ids, /* const gchar * */
+                                  const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise 
that zone for the returned times */
+                                  const gchar *select, /* nullable - properties to select */
+                                  GSList **out_events, /* EO365Event *, in the same order as event_ids; can 
return partial list */
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event_ids != NULL, FALSE);
+       g_return_val_if_fail (out_events != NULL, FALSE);
+
+       if (g_slist_next (event_ids)) {
+               GPtrArray *requests;
+               GSList *link;
+               guint total, done = 0;
+
+               total = g_slist_length ((GSList *) event_ids);
+               requests = g_ptr_array_new_full (MIN (E_O365_BATCH_MAX_REQUESTS, MIN (total, 50)), 
g_object_unref);
+
+               for (link = (GSList *) event_ids; link && success; link = g_slist_next (link)) {
+                       const gchar *id = link->data;
+                       SoupMessage *message;
+
+                       message = e_o365_connection_prepare_get_event (cnc, user_override, group_id, 
calendar_id, id, prefer_outlook_timezone, select, error);
+
+                       if (!message) {
+                               success = FALSE;
+                               break;
+                       }
+
+                       g_ptr_array_add (requests, message);
+
+                       if (requests->len == E_O365_BATCH_MAX_REQUESTS || !link->next) {
+                               if (requests->len == 1) {
+                                       EO365Event *event = NULL;
+
+                                       success = o365_connection_send_request_sync (cnc, message, 
e_o365_read_json_object_response_cb, NULL, &event, cancellable, error);
+
+                                       if (success)
+                                               *out_events = g_slist_prepend (*out_events, event);
+                               } else {
+                                       success = e_o365_connection_batch_request_sync (cnc, E_O365_API_V1_0, 
requests, cancellable, error);
+
+                                       if (success) {
+                                               guint ii;
+
+                                               for (ii = 0; ii < requests->len && success; ii++) {
+                                                       JsonNode *node = NULL;
+
+                                                       message = requests->pdata[ii];
+                                                       success = e_o365_connection_json_node_from_message 
(message, NULL, &node, cancellable, error);
+
+                                                       if (success && node && JSON_NODE_HOLDS_OBJECT (node)) 
{
+                                                               JsonObject *response;
+
+                                                               response = json_node_get_object (node);
+
+                                                               if (response) {
+                                                                       *out_events = g_slist_prepend 
(*out_events, json_object_ref (response));
+                                                               } else {
+                                                                       success = FALSE;
+                                                               }
+                                                       } else {
+                                                               success = FALSE;
+                                                       }
+
+                                                       if (node)
+                                                               json_node_unref (node);
+                                               }
+                                       }
+                               }
+
+                               g_ptr_array_remove_range (requests, 0, requests->len);
+
+                               done += requests->len;
+
+                               camel_operation_progress (cancellable, done * 100.0 / total);
+                       }
+               }
+
+               g_ptr_array_free (requests, TRUE);
+       } else {
+               SoupMessage *message;
+
+               message = e_o365_connection_prepare_get_event (cnc, user_override, group_id, calendar_id, 
event_ids->data, prefer_outlook_timezone, select, error);
+
+               if (message) {
+                       EO365Event *event = NULL;
+
+                       success = o365_connection_send_request_sync (cnc, message, 
e_o365_read_json_object_response_cb, NULL, &event, cancellable, error);
+
+                       if (success)
+                               *out_events = g_slist_prepend (*out_events, event);
+
+                       g_clear_object (&message);
+               } else {
+                       success = FALSE;
+               }
+       }
+
+       *out_events = g_slist_reverse (*out_events);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/event-update?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_update_event_sync (EO365Connection *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 *event_id,
+                                    JsonBuilder *event,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event_id != NULL, FALSE);
+       g_return_val_if_fail (event != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               group_id ? "calendarGroups" : "calendars",
+               group_id,
+               group_id ? "calendars" : NULL,
+               "", calendar_id,
+               "", "events",
+               "", event_id,
+               NULL);
+
+       message = o365_connection_new_soup_message ("PATCH", uri, CSM_DISABLE_RESPONSE, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       e_o365_connection_set_json_body (message, event);
+
+       success = o365_connection_send_request_sync (cnc, message, NULL, e_o365_read_no_response_cb, NULL, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/event-delete?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_delete_event_sync (EO365Connection *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 *event_id,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event_id != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               group_id ? "calendarGroups" : "calendars",
+               group_id,
+               group_id ? "calendars" : NULL,
+               "", calendar_id,
+               "", "events",
+               "", event_id,
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       success = o365_connection_send_request_sync (cnc, message, NULL, e_o365_read_no_response_cb, NULL, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/event-accept?view=graph-rest-1.0&tabs=http
+   https://docs.microsoft.com/en-us/graph/api/event-tentativelyaccept?view=graph-rest-1.0&tabs=http
+   https://docs.microsoft.com/en-us/graph/api/event-decline?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_response_event_sync (EO365Connection *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 *event_id,
+                                      EO365ResponseType response, /* uses only accepted/tentatively 
accepted/declined values */
+                                      const gchar *comment, /* nullable */
+                                      gboolean send_response,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+       JsonBuilder *builder;
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event_id != NULL, FALSE);
+       g_return_val_if_fail (response == E_O365_RESPONSE_ACCEPTED || response == 
E_O365_RESPONSE_TENTATIVELY_ACCEPTED || response == E_O365_RESPONSE_DECLINED, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               group_id ? "calendarGroups" : "calendars",
+               group_id,
+               group_id ? "calendars" : NULL,
+               "", calendar_id,
+               "", "events",
+               "", event_id,
+               "", response == E_O365_RESPONSE_TENTATIVELY_ACCEPTED ? "tentativelyAccept" :
+                   response == E_O365_RESPONSE_DECLINED ? "decline" : "accept",
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DISABLE_RESPONSE, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       builder = json_builder_new_immutable ();
+
+       e_o365_json_begin_object_member (builder, NULL);
+       e_o365_json_add_nonempty_string_member (builder, "comment", comment);
+       e_o365_json_add_boolean_member (builder, "sendResponse", send_response);
+       e_o365_json_end_object_member (builder);
+
+       e_o365_connection_set_json_body (message, builder);
+
+       g_object_unref (builder);
+
+       success = o365_connection_send_request_sync (cnc, message, NULL, e_o365_read_no_response_cb, NULL, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/event-dismissreminder?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_dismiss_reminder_sync (EO365Connection *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 *event_id,
+                                        GCancellable *cancellable,
+                                        GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event_id != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               group_id ? "calendarGroups" : "calendars",
+               group_id,
+               group_id ? "calendars" : NULL,
+               "", calendar_id,
+               "", "events",
+               "", event_id,
+               "", "dismissReminder",
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       success = o365_connection_send_request_sync (cnc, message, NULL, e_o365_read_no_response_cb, NULL, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/event-list-attachments?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_list_event_attachments_sync (EO365Connection *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 *event_id,
+                                              GSList **out_attachments, /* EO365Attachment * */
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+       EO365ResponseData rd;
+       SoupMessage *message;
+       gchar *uri;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event_id != NULL, FALSE);
+       g_return_val_if_fail (out_attachments != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               group_id ? "calendarGroups" : "calendars",
+               group_id,
+               group_id ? "calendars" : NULL,
+               "", calendar_id,
+               "", "events",
+               "", event_id,
+               "", "attachments",
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       memset (&rd, 0, sizeof (EO365ResponseData));
+
+       rd.out_items = out_attachments;
+
+       success = o365_connection_send_request_sync (cnc, message, e_o365_read_valued_response_cb, NULL, &rd, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/attachment-delete?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_delete_event_attachment_sync (EO365Connection *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 *event_id,
+                                               const gchar *attachment_id,
+                                               GCancellable *cancellable,
+                                               GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (calendar_id != NULL, FALSE);
+       g_return_val_if_fail (event_id != NULL, FALSE);
+       g_return_val_if_fail (attachment_id != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               group_id ? "calendarGroups" : "calendars",
+               group_id,
+               group_id ? "calendars" : NULL,
+               "", calendar_id,
+               "", "events",
+               "", event_id,
+               "", "attachments",
+               "", attachment_id,
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       success = o365_connection_send_request_sync (cnc, message, NULL, e_o365_read_no_response_cb, NULL, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
diff --git a/src/Office365/common/e-o365-connection.h b/src/Office365/common/e-o365-connection.h
index dfcc11d9..af131551 100644
--- a/src/Office365/common/e-o365-connection.h
+++ b/src/Office365/common/e-o365-connection.h
@@ -451,6 +451,100 @@ gboolean  e_o365_connection_list_events_sync
                                                 GSList **out_events, /* EO365Event * - the returned event 
objects */
                                                 GCancellable *cancellable,
                                                 GError **error);
+gboolean       e_o365_connection_create_event_sync
+                                               (EO365Connection *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,
+                                                JsonBuilder *event,
+                                                EO365Calendar **out_created_event,
+                                                GCancellable *cancellable,
+                                                GError **error);
+SoupMessage *  e_o365_connection_prepare_get_event
+                                               (EO365Connection *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 *event_id,
+                                                const gchar *prefer_outlook_timezone, /* nullable - then 
UTC, otherwise that zone for the returned times */
+                                                const gchar *select, /* nullable - properties to select */
+                                                GError **error);
+gboolean       e_o365_connection_get_event_sync
+                                               (EO365Connection *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 *event_id,
+                                                const gchar *prefer_outlook_timezone, /* nullable - then 
UTC, otherwise that zone for the returned times */
+                                                const gchar *select, /* nullable - properties to select */
+                                                EO365Event **out_event,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_get_events_sync
+                                               (EO365Connection *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 GSList *event_ids, /* const gchar * */
+                                                const gchar *prefer_outlook_timezone, /* nullable - then 
UTC, otherwise that zone for the returned times */
+                                                const gchar *select, /* nullable - properties to select */
+                                                GSList **out_events, /* EO365Event *, in the same order as 
event_ids; can return partial list */
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_update_event_sync
+                                               (EO365Connection *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 *event_id,
+                                                JsonBuilder *event,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_delete_event_sync
+                                               (EO365Connection *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 *event_id,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_response_event_sync
+                                               (EO365Connection *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 *event_id,
+                                                EO365ResponseType response, /* uses only 
accepted/tentatively accepted/declined values */
+                                                const gchar *comment, /* nullable */
+                                                gboolean send_response,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_dismiss_reminder_sync
+                                               (EO365Connection *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 *event_id,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_list_event_attachments_sync
+                                               (EO365Connection *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 *event_id,
+                                                GSList **out_attachments, /* EO365Attachment * */
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_delete_event_attachment_sync
+                                               (EO365Connection *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 *event_id,
+                                                const gchar *attachment_id,
+                                                GCancellable *cancellable,
+                                                GError **error);
 
 G_END_DECLS
 
diff --git a/src/Office365/common/e-o365-json-utils.c b/src/Office365/common/e-o365-json-utils.c
index 50d31745..2579ea60 100644
--- a/src/Office365/common/e-o365-json-utils.c
+++ b/src/Office365/common/e-o365-json-utils.c
@@ -3013,19 +3013,19 @@ e_o365_event_get_is_cancelled (EO365Event *event)
        return e_o365_json_get_boolean_member (event, "isCancelled", FALSE);
 }
 
-void
-e_o365_event_add_is_cancelled (JsonBuilder *builder,
-                              gboolean value)
-{
-       e_o365_json_add_boolean_member (builder, "isCancelled", value);
-}
-
 gboolean
 e_o365_event_get_is_online_meeting (EO365Event *event)
 {
        return e_o365_json_get_boolean_member (event, "isOnlineMeeting", FALSE);
 }
 
+void
+e_o365_event_add_is_online_meeting (JsonBuilder *builder,
+                                   gboolean value)
+{
+       e_o365_json_add_boolean_member (builder, "isOnlineMeeting", value);
+}
+
 gboolean
 e_o365_event_get_is_organizer (EO365Event *event)
 {
@@ -3075,6 +3075,30 @@ e_o365_event_get_locations (EO365Event *event)
        return e_o365_json_get_array_member (event, "locations");
 }
 
+void
+e_o365_event_begin_locations (JsonBuilder *builder)
+{
+       e_o365_json_begin_array_member (builder, "locations");
+}
+
+void
+e_o365_event_end_locations (JsonBuilder *builder)
+{
+       e_o365_json_end_array_member (builder);
+}
+
+void
+e_o365_event_begin_locations_location (JsonBuilder *builder)
+{
+       e_o365_json_begin_object_member (builder, NULL);
+}
+
+void
+e_o365_event_end_locations_location (JsonBuilder *builder)
+{
+       e_o365_json_end_object_member (builder);
+}
+
 EO365OnlineMeetingInfo *
 e_o365_event_get_online_meeting_info (EO365Event *event)
 {
@@ -3090,6 +3114,20 @@ e_o365_event_get_online_meeting_provider (EO365Event *event)
                E_O365_ONLINE_MEETING_PROVIDER_UNKNOWN);
 }
 
+void
+e_o365_event_add_online_meeting_provider (JsonBuilder *builder,
+                                         EO365OnlineMeetingProviderType value)
+{
+       if (value == E_O365_ONLINE_MEETING_PROVIDER_NOT_SET) {
+               e_o365_json_add_null_member (builder, "onlineMeetingProvider");
+       } else {
+               o365_json_utils_add_enum_as_json (builder, "onlineMeetingProvider", value,
+                       meeting_provider_map, G_N_ELEMENTS (meeting_provider_map),
+                       E_O365_ONLINE_MEETING_PROVIDER_NOT_SET,
+                       E_O365_ONLINE_MEETING_PROVIDER_UNKNOWN);
+       }
+}
+
 const gchar *
 e_o365_event_get_online_meeting_url (EO365Event *event)
 {
diff --git a/src/Office365/common/e-o365-json-utils.h b/src/Office365/common/e-o365-json-utils.h
index 40260bf8..3cfc9c2d 100644
--- a/src/Office365/common/e-o365-json-utils.h
+++ b/src/Office365/common/e-o365-json-utils.h
@@ -855,9 +855,9 @@ gboolean    e_o365_event_get_is_all_day             (EO365Event *event);
 void           e_o365_event_add_is_all_day             (JsonBuilder *builder,
                                                         gboolean value);
 gboolean       e_o365_event_get_is_cancelled           (EO365Event *event);
-void           e_o365_event_add_is_cancelled           (JsonBuilder *builder,
-                                                        gboolean value);
 gboolean       e_o365_event_get_is_online_meeting      (EO365Event *event);
+void           e_o365_event_add_is_online_meeting      (JsonBuilder *builder,
+                                                        gboolean value);
 gboolean       e_o365_event_get_is_organizer           (EO365Event *event);
 gboolean       e_o365_event_get_is_reminder_on         (EO365Event *event);
 void           e_o365_event_add_is_reminder_on         (JsonBuilder *builder,
@@ -867,10 +867,16 @@ EO365Location *   e_o365_event_get_location               (EO365Event *event);
 void           e_o365_event_begin_location             (JsonBuilder *builder);
 void           e_o365_event_end_location               (JsonBuilder *builder);
 JsonArray *    e_o365_event_get_locations              (EO365Event *event); /* EO365Location * */
+void           e_o365_event_begin_locations            (JsonBuilder *builder);
+void           e_o365_event_end_locations              (JsonBuilder *builder);
+void           e_o365_event_begin_locations_location   (JsonBuilder *builder);
+void           e_o365_event_end_locations_location     (JsonBuilder *builder);
 EO365OnlineMeetingInfo *
                e_o365_event_get_online_meeting_info    (EO365Event *event);
 EO365OnlineMeetingProviderType
                e_o365_event_get_online_meeting_provider(EO365Event *event);
+void           e_o365_event_add_online_meeting_provider(JsonBuilder *builder,
+                                                        EO365OnlineMeetingProviderType value);
 const gchar *  e_o365_event_get_online_meeting_url     (EO365Event *event);
 EO365Recipient *e_o365_event_get_organizer             (EO365Event *event);
 void           e_o365_event_add_organizer              (JsonBuilder *builder,



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