[evolution-ews/wip/mcrha/office365] Read categories and sync them with mail message labels



commit d96414b4abde8edabc4ff7714ec9125782ab3edf
Author: Milan Crha <mcrha redhat com>
Date:   Mon Jun 29 18:59:02 2020 +0200

    Read categories and sync them with mail message labels

 src/Office365/camel/camel-o365-folder.c           |  92 ++++++++
 src/Office365/camel/camel-o365-store-summary.c    | 159 ++++++++++++++
 src/Office365/camel/camel-o365-store-summary.h    |  19 ++
 src/Office365/camel/camel-o365-store.c            | 250 ++++++++++++++++++++++
 src/Office365/camel/camel-o365-utils.c            |  85 ++++++++
 src/Office365/camel/camel-o365-utils.h            |   7 +
 src/Office365/common/e-o365-connection.c          |  46 +++-
 src/Office365/common/e-o365-connection.h          |   8 +-
 src/Office365/common/e-o365-json-utils.c          |  63 ++++++
 src/Office365/common/e-o365-json-utils.h          |   5 +
 src/Office365/common/e-oauth2-service-office365.c |   1 +
 11 files changed, 733 insertions(+), 2 deletions(-)
---
diff --git a/src/Office365/camel/camel-o365-folder.c b/src/Office365/camel/camel-o365-folder.c
index b51382bc..231fac41 100644
--- a/src/Office365/camel/camel-o365-folder.c
+++ b/src/Office365/camel/camel-o365-folder.c
@@ -27,6 +27,7 @@
 #include "camel-o365-folder-summary.h"
 #include "camel-o365-store.h"
 #include "camel-o365-store-summary.h"
+#include "camel-o365-utils.h"
 
 #include "camel-o365-folder.h"
 
@@ -544,6 +545,95 @@ o365_folder_get_message_sync (CamelFolder *folder,
        return message;
 }
 
+static gboolean
+o365_folder_is_system_user_flag (const gchar *name)
+{
+       if (!name)
+               return FALSE;
+
+       return g_str_equal (name, "receipt-handled") ||
+               g_str_equal (name, "$has-cal");
+}
+
+static gboolean
+o365_folder_merge_server_user_flags (CamelMessageInfo *mi,
+                                    EO365MailMessage *mail)
+{
+       CamelFolderSummary *summary;
+       JsonArray *categories;
+       GHashTable *current_labels;
+       const CamelNamedFlags *user_flags;
+       guint ii, len;
+       gboolean changed = FALSE;
+
+       summary = camel_message_info_ref_summary (mi);
+       if (summary)
+               camel_folder_summary_lock (summary);
+       camel_message_info_property_lock (mi);
+       camel_message_info_freeze_notifications (mi);
+
+       current_labels = g_hash_table_new (g_str_hash, g_str_equal);
+
+       user_flags = camel_message_info_get_user_flags (mi);
+       len = camel_named_flags_get_length (user_flags);
+
+       /* transfer camel flags to a list */
+       for (ii = 0; ii < len; ii++) {
+               const gchar *name = camel_named_flags_get (user_flags, ii);
+
+               if (!o365_folder_is_system_user_flag (name))
+                       g_hash_table_insert (current_labels, (gpointer) name, NULL);
+       }
+
+       categories = e_o365_mail_message_get_categories (mail);
+
+       if (categories) {
+               len = json_array_get_length (categories);
+
+               for (ii = 0; ii < len; ii++) {
+                       const gchar *name = json_array_get_string_element (categories, ii);
+
+                       name = camel_o365_utils_rename_label (name, TRUE);
+
+                       if (name && *name) {
+                               gchar *flag;
+
+                               flag = camel_o365_utils_encode_category_name (name);
+
+                               if (!g_hash_table_remove (current_labels, flag)) {
+                                       changed = TRUE;
+
+                                       camel_message_info_set_user_flag (mi, flag, TRUE);
+                               }
+
+                               g_free (flag);
+                       }
+               }
+       }
+
+       /* Those left here are to be removed */
+       if (g_hash_table_size (current_labels)) {
+               GHashTableIter iter;
+               gpointer key;
+
+               changed = TRUE;
+
+               g_hash_table_iter_init (&iter, current_labels);
+
+               while (g_hash_table_iter_next (&iter, &key, NULL)) {
+                       camel_message_info_set_user_flag (mi, key, FALSE);
+               }
+       }
+
+       camel_message_info_thaw_notifications (mi);
+       camel_message_info_property_unlock (mi);
+       if (summary)
+               camel_folder_summary_unlock (summary);
+       g_clear_object (&summary);
+
+       return changed;
+}
+
 static gboolean
 o365_folder_update_message_info (CamelMessageInfo *mi,
                                 EO365MailMessage *mail)
@@ -583,6 +673,8 @@ o365_folder_update_message_info (CamelMessageInfo *mi,
                changed = TRUE;
        }
 
+       changed = o365_folder_merge_server_user_flags (mi, mail) || changed;
+
        return changed;
 }
 
diff --git a/src/Office365/camel/camel-o365-store-summary.c b/src/Office365/camel/camel-o365-store-summary.c
index 246331f0..b02271bd 100644
--- a/src/Office365/camel/camel-o365-store-summary.c
+++ b/src/Office365/camel/camel-o365-store-summary.c
@@ -22,6 +22,7 @@
 #include "camel-o365-store-summary.h"
 
 #define STORE_GROUP_NAME "##storepriv##"
+#define CATEGORIES_KEY "Categories"
 #define DATA_VERSION 1
 
 #define LOCK(_summary) g_rec_mutex_lock (&(_summary->priv->property_lock))
@@ -1257,3 +1258,161 @@ camel_o365_store_summary_connect_folder_summary (CamelO365StoreSummary *store_su
        g_signal_connect_object (folder_summary, "notify::saved-count", G_CALLBACK 
(o365_store_summary_folder_count_notify_cb), store_summary, 0);
        g_signal_connect_object (folder_summary, "notify::unread-count", G_CALLBACK 
(o365_store_summary_folder_count_notify_cb), store_summary, 0);
 }
+
+static gchar *
+camel_o365_category_to_string (const CamelO365Category *cat)
+{
+       gchar *id, *display_name, *color = NULL, *str;
+
+       g_return_val_if_fail (cat != NULL, NULL);
+
+       id = g_uri_escape_string (cat->id, NULL, TRUE);
+       display_name = g_uri_escape_string (cat->display_name, NULL, TRUE);
+
+       if (cat->color)
+               color = g_uri_escape_string (cat->color, NULL, TRUE);
+
+       str = g_strconcat (
+               id ? id : "", "\t",
+               display_name ? display_name : "", "\t",
+               color ? color : "",
+               NULL);
+
+       g_free (id);
+       g_free (display_name);
+       g_free (color);
+
+       return str;
+}
+
+static CamelO365Category *
+camel_o365_category_from_string (const gchar *str)
+{
+       CamelO365Category *cat;
+       gchar **strv, *id, *display_name, *color;
+
+       g_return_val_if_fail (str != NULL, NULL);
+
+       strv = g_strsplit (str, "\t", -1);
+       if (!strv || !strv[0] || !strv[1]) {
+               g_strfreev (strv);
+               return NULL;
+       }
+
+       id = g_uri_unescape_string (strv[0], NULL);
+       display_name = g_uri_unescape_string (strv[1], NULL);
+       color = (strv[2] && strv[2][0]) ? g_uri_unescape_string (strv[2], NULL) : NULL;
+
+       cat = camel_o365_category_new (id, display_name, color);
+
+       g_free (id);
+       g_free (display_name);
+       g_free (color);
+       g_strfreev (strv);
+
+       return cat;
+}
+
+GHashTable * /* gchar *id ~> CamelO365Category * */
+camel_o365_store_summary_get_categories (CamelO365StoreSummary *store_summary)
+{
+       GHashTable *categories;
+       gchar **strv;
+
+       g_return_val_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary), NULL);
+
+       LOCK (store_summary);
+
+       strv = g_key_file_get_string_list (store_summary->priv->key_file, STORE_GROUP_NAME, CATEGORIES_KEY, 
NULL, NULL);
+
+       UNLOCK (store_summary);
+
+       categories = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, camel_o365_category_free);
+
+       if (strv) {
+               gint ii;
+
+               for (ii = 0; strv[ii]; ii++) {
+                       CamelO365Category *cat;
+
+                       cat = camel_o365_category_from_string (strv[ii]);
+                       if (cat)
+                               g_hash_table_insert (categories, cat->id, cat);
+               }
+
+               g_strfreev (strv);
+       }
+
+       return categories;
+}
+
+void
+camel_o365_store_summary_set_categories (CamelO365StoreSummary *store_summary,
+                                        GHashTable *categories) /* gchar *id ~> CamelO365Category * */
+{
+       GPtrArray *array;
+       GHashTableIter iter;
+       gpointer value;
+
+       g_return_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary));
+       g_return_if_fail (categories != NULL);
+
+       array = g_ptr_array_new_full (g_hash_table_size (categories), g_free);
+
+       g_hash_table_iter_init (&iter, categories);
+
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               CamelO365Category *cat = value;
+
+               if (cat) {
+                       gchar *str;
+
+                       str = camel_o365_category_to_string (cat);
+
+                       if (str)
+                               g_ptr_array_add (array, str);
+               }
+       }
+
+       LOCK (store_summary);
+
+       g_key_file_set_string_list (store_summary->priv->key_file, STORE_GROUP_NAME, CATEGORIES_KEY,
+               (const gchar * const *) array->pdata, array->len);
+
+       store_summary->priv->dirty = TRUE;
+
+       UNLOCK (store_summary);
+
+       g_ptr_array_free (array, TRUE);
+}
+
+CamelO365Category *
+camel_o365_category_new (const gchar *id,
+                        const gchar *display_name,
+                        const gchar *color)
+{
+       CamelO365Category *cat;
+
+       g_return_val_if_fail (id != NULL, NULL);
+       g_return_val_if_fail (display_name != NULL, NULL);
+
+       cat = g_slice_new0 (CamelO365Category);
+       cat->id = g_strdup (id);
+       cat->display_name = g_strdup (display_name);
+       cat->color = g_strdup (color);
+
+       return cat;
+}
+
+void
+camel_o365_category_free (gpointer ptr)
+{
+       CamelO365Category *cat = ptr;
+
+       if (cat) {
+               g_free (cat->id);
+               g_free (cat->display_name);
+               g_free (cat->color);
+               g_slice_free (CamelO365Category, cat);
+       }
+}
diff --git a/src/Office365/camel/camel-o365-store-summary.h b/src/Office365/camel/camel-o365-store-summary.h
index 670f739b..c4f3bfb3 100644
--- a/src/Office365/camel/camel-o365-store-summary.h
+++ b/src/Office365/camel/camel-o365-store-summary.h
@@ -175,6 +175,25 @@ CamelFolderInfo *
 void           camel_o365_store_summary_connect_folder_summary
                                                        (CamelO365StoreSummary *store_summary,
                                                         CamelFolderSummary *folder_summary);
+
+typedef struct _CamelO365Category {
+       gchar *id;
+       gchar *display_name;
+       gchar *color;
+} CamelO365Category;
+
+GHashTable *   camel_o365_store_summary_get_categories /* gchar *id ~> CamelO365Category * */
+                                               (CamelO365StoreSummary *store_summary);
+void           camel_o365_store_summary_set_categories
+                                               (CamelO365StoreSummary *store_summary,
+                                                GHashTable *categories); /* gchar *id ~> CamelO365Category * 
*/
+
+CamelO365Category *
+               camel_o365_category_new         (const gchar *id,
+                                                const gchar *display_name,
+                                                const gchar *color);
+void           camel_o365_category_free        (gpointer ptr); /* CamelO365Category * */
+
 G_END_DECLS
 
 #endif /* CAMEL_O365_STORE_SUMMARY_H */
diff --git a/src/Office365/camel/camel-o365-store.c b/src/Office365/camel/camel-o365-store.c
index a648b2fe..ee1474b4 100644
--- a/src/Office365/camel/camel-o365-store.c
+++ b/src/Office365/camel/camel-o365-store.c
@@ -20,6 +20,8 @@
 #include <glib/gi18n-lib.h>
 #include <glib/gstdio.h>
 
+#include <e-util/e-util.h>
+
 #include "common/camel-o365-settings.h"
 #include "common/e-o365-connection.h"
 #include "camel-o365-folder.h"
@@ -259,6 +261,246 @@ o365_store_read_default_folders (CamelO365Store *o365_store,
        return success;
 }
 
+static gboolean
+o365_store_equal_label_tag_cb (gconstpointer ptr1,
+                              gconstpointer ptr2)
+{
+       const gchar *evo_label_def = ptr1;
+       const gchar *tag = ptr2;
+       const gchar *pos;
+
+       if (!evo_label_def || !tag || !*tag)
+               return FALSE;
+
+       pos = g_strrstr (evo_label_def, tag);
+
+       return pos > evo_label_def && pos[-1] == '|' && !pos[strlen (tag)];
+}
+
+static gboolean
+o365_store_find_in_ptr_array (GPtrArray *haystack,
+                             gconstpointer needle,
+                             GEqualFunc equal_func,
+                             guint *out_index)
+{
+       guint ii;
+
+       if (!haystack)
+               return FALSE;
+
+       if (!equal_func)
+               equal_func = g_direct_equal;
+
+       for (ii = 0; ii < haystack->len; ii++) {
+               if (equal_func (haystack->pdata[ii], needle)) {
+                       if (out_index)
+                               *out_index = ii;
+
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+/* Returns whether had been done any changes */
+static gboolean
+o365_store_save_category_changes (GHashTable *old_categories, /* gchar *id ~> CamelO365Category * */
+                                 GHashTable *new_categories) /* gchar *id ~> CamelO365Category * */
+{
+       GHashTableIter iter;
+       GSettings *settings;
+       GPtrArray *evo_labels; /* gchar * (encoded label definition) */
+       gchar **strv;
+       gint ii;
+       gpointer value;
+       gboolean changed = FALSE;
+
+       if (!old_categories || !new_categories)
+               return new_categories != NULL;
+
+       evo_labels = g_ptr_array_new_full (5, g_free);
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       strv = g_settings_get_strv (settings, "labels");
+
+       for (ii = 0; strv && strv[ii]; ii++) {
+               g_ptr_array_add (evo_labels, g_strdup (strv[ii]));
+       }
+
+       g_strfreev (strv);
+
+       g_hash_table_iter_init (&iter, new_categories);
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               CamelO365Category *new_cat = value, *old_cat;
+               gchar *tag = NULL;
+
+               if (!new_cat)
+                       continue;
+
+               old_cat = g_hash_table_lookup (old_categories, new_cat->id);
+               if (old_cat) {
+                       if (g_strcmp0 (old_cat->display_name, new_cat->display_name) != 0 ||
+                           g_strcmp0 (old_cat->color, new_cat->color) != 0) {
+                               /* Old category changed name or color */
+                               tag = camel_o365_utils_encode_category_name (new_cat->display_name);
+                       }
+               } else {
+                       /* This is a new category */
+                       tag = camel_o365_utils_encode_category_name (new_cat->display_name);
+               }
+
+               if (tag && *tag) {
+                       guint index = (guint) -1;
+                       gchar *label_def;
+
+                       changed = TRUE;
+
+                       /* Sanitize value */
+                       for (ii = 0; tag[ii]; ii++) {
+                               if (tag[ii] == '|')
+                                       tag[ii] = '-';
+                       }
+
+                       if (old_cat && g_strcmp0 (old_cat->display_name, new_cat->display_name) != 0) {
+                               gchar *old_tag = camel_o365_utils_encode_category_name 
(old_cat->display_name);
+
+                               if (old_tag && *old_tag) {
+                                       if (!o365_store_find_in_ptr_array (evo_labels, old_tag, 
o365_store_equal_label_tag_cb, &index))
+                                               index = (guint) -1;
+                               }
+
+                               g_free (old_tag);
+                       }
+
+                       for (ii = 0; new_cat->display_name[ii]; ii++) {
+                               if (new_cat->display_name[ii] == '|')
+                                       new_cat->display_name[ii] = '-';
+                       }
+
+                       if (index == (guint) -1 &&
+                           !o365_store_find_in_ptr_array (evo_labels, tag, o365_store_equal_label_tag_cb, 
&index))
+                               index = (guint) -1;
+
+                       label_def = g_strconcat (new_cat->display_name, "|", new_cat->color ? new_cat->color 
: "#FF0000", "|", tag, NULL);
+
+                       if (index == (guint) -1 || index >= (gint) evo_labels->len) {
+                               g_ptr_array_add (evo_labels, label_def);
+                       } else {
+                               g_free (evo_labels->pdata[index]);
+                               evo_labels->pdata[index] = label_def;
+                       }
+               }
+
+               g_hash_table_remove (old_categories, new_cat->id);
+
+               g_free (tag);
+       }
+
+       if (g_hash_table_size (old_categories) > 0) {
+               /* Some categories had been removed */
+               changed = TRUE;
+
+               g_hash_table_iter_init (&iter, old_categories);
+               while (g_hash_table_iter_next (&iter, NULL, &value)) {
+                       CamelO365Category *old_cat = value;
+                       gchar *old_tag;
+                       guint index;
+
+                       if (!old_cat)
+                               continue;
+
+                       old_tag = camel_o365_utils_encode_category_name (old_cat->display_name);
+
+                       for (ii = 0; old_tag && old_tag[ii]; ii++) {
+                               if (old_tag[ii] == '|')
+                                       old_tag[ii] = '-';
+                       }
+
+                       if (old_tag &&
+                           o365_store_find_in_ptr_array (evo_labels, old_tag, o365_store_equal_label_tag_cb, 
&index))
+                               g_ptr_array_remove_index (evo_labels, index);
+
+                       g_free (old_tag);
+               }
+       }
+
+       if (changed) {
+               /* NULL-terminated array of strings */
+               g_ptr_array_add (evo_labels, NULL);
+
+               g_settings_set_strv (settings, "labels", (const gchar * const *) evo_labels->pdata);
+       }
+
+       g_ptr_array_free (evo_labels, TRUE);
+       g_object_unref (settings);
+
+       return changed;
+}
+
+static void
+o365_store_get_categories_cb (CamelSession *session,
+                             GCancellable *cancellable,
+                             gpointer user_data,
+                             GError **error)
+{
+       CamelO365Store *o365_store = user_data;
+       EO365Connection *cnc;
+       GSList *categories = NULL;
+
+       g_return_if_fail (CAMEL_IS_O365_STORE (o365_store));
+
+       cnc = camel_o365_store_ref_connection (o365_store);
+
+       if (!cnc)
+               return;
+
+       if (e_o365_connection_get_categories_sync (cnc, NULL, &categories, cancellable, error)) {
+               GHashTable *old_categories, *new_categories;
+               GSList *link;
+
+               new_categories = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, 
camel_o365_category_free);
+
+               for (link = categories; link; link = g_slist_next (link)) {
+                       EO365Category *category = link->data;
+                       CamelO365Category *cat;
+                       const gchar *id, *display_name, *color;
+
+                       if (!category)
+                               continue;
+
+                       id = e_o365_category_get_id (category);
+                       display_name = e_o365_category_get_display_name (category);
+                       color = e_o365_category_get_color (category);
+
+                       if (!id || !display_name)
+                               continue;
+
+                       if (display_name != camel_o365_utils_rename_label (display_name, TRUE))
+                               continue;
+
+                       cat = camel_o365_category_new (id, display_name, color);
+
+                       if (cat)
+                               g_hash_table_insert (new_categories, cat->id, cat);
+               }
+
+               g_slist_free_full (categories, (GDestroyNotify) json_object_unref);
+
+               old_categories = camel_o365_store_summary_get_categories (o365_store->priv->summary);
+
+               if (o365_store_save_category_changes (old_categories, new_categories)) {
+                       camel_o365_store_summary_set_categories (o365_store->priv->summary, new_categories);
+                       camel_o365_store_summary_save (o365_store->priv->summary, NULL);
+               }
+
+               g_hash_table_destroy (new_categories);
+               g_hash_table_destroy (old_categories);
+       }
+
+       g_object_unref (cnc);
+}
+
 static gboolean
 o365_store_connect_sync (CamelService *service,
                         GCancellable *cancellable,
@@ -293,6 +535,14 @@ o365_store_connect_sync (CamelService *service,
 
                success = camel_session_authenticate_sync (session, service, "Office365", cancellable, error);
 
+               if (success) {
+                       camel_session_submit_job (
+                               session, _("Look up Office 365 categories"),
+                               o365_store_get_categories_cb,
+                               g_object_ref (o365_store),
+                               g_object_unref);
+               }
+
                g_clear_object (&session);
                g_clear_object (&cnc);
        } else {
diff --git a/src/Office365/camel/camel-o365-utils.c b/src/Office365/camel/camel-o365-utils.c
index 0d96872f..fa6f11bb 100644
--- a/src/Office365/camel/camel-o365-utils.c
+++ b/src/Office365/camel/camel-o365-utils.c
@@ -91,3 +91,88 @@ camel_o365_utils_new_connection (CamelService *service,
 
        return cnc;
 }
+
+/* From Outlook name (which allows spaces) to Evolution name */
+gchar *
+camel_o365_utils_encode_category_name (const gchar *name)
+{
+       if (name && strchr (name, ' ')) {
+               GString *str;
+
+               str = g_string_sized_new (strlen (name) + 16);
+
+               while (*name) {
+                       if (*name == '_')
+                               g_string_append_c (str, '_');
+
+                       g_string_append_c (str, *name == ' ' ? '_' : *name);
+
+                       name++;
+               }
+
+               return g_string_free (str, FALSE);
+       }
+
+       return g_strdup (name);
+}
+
+/* From Evolution name to Outlook name (which allows spaces) */
+gchar *
+camel_o365_utils_decode_category_name (const gchar *flag)
+{
+       if (flag && strchr (flag, '_')) {
+               GString *str = g_string_sized_new (strlen (flag));
+
+               while (*flag) {
+                       if (*flag == '_') {
+                               if (flag[1] == '_') {
+                                       g_string_append_c (str, '_');
+                                       flag++;
+                               } else {
+                                       g_string_append_c (str, ' ');
+                               }
+                       } else {
+                               g_string_append_c (str, *flag);
+                       }
+
+                       flag++;
+               }
+
+               return g_string_free (str, FALSE);
+       }
+
+       return g_strdup (flag);
+}
+
+const gchar *
+camel_o365_utils_rename_label (const gchar *cat,
+                              gboolean from_cat)
+{
+       gint ii;
+
+       /* This is a mapping from Outlook categories to
+        * Evolution labels based on the standard colors */
+       const gchar *labels[] = {
+               "Red Category", "$Labelimportant",
+               "Orange Category", "$Labelwork",
+               "Green Category", "$Labelpersonal",
+               "Blue Category", "$Labeltodo",
+               "Purple Category", "$Labellater",
+               NULL, NULL
+       };
+
+       if (!cat || !*cat)
+               return "";
+
+       for (ii = 0; labels[ii]; ii += 2) {
+               if (from_cat) {
+                       if (!g_ascii_strcasecmp (cat, labels[ii]))
+                               return labels[ii + 1];
+               } else {
+                       if (!g_ascii_strcasecmp (cat, labels[ii + 1]))
+                               return labels[ii];
+               }
+       }
+
+       return cat;
+}
diff --git a/src/Office365/camel/camel-o365-utils.h b/src/Office365/camel/camel-o365-utils.h
index dfca2254..6c4c507e 100644
--- a/src/Office365/camel/camel-o365-utils.h
+++ b/src/Office365/camel/camel-o365-utils.h
@@ -25,5 +25,12 @@
 EO365Connection *
                camel_o365_utils_new_connection (CamelService *service,
                                                 GCancellable *cancellable);
+gchar *                camel_o365_utils_encode_category_name
+                                               (const gchar *name);
+gchar *                camel_o365_utils_decode_category_name
+                                               (const gchar *flag);
+const gchar *  camel_o365_utils_rename_label   (const gchar *cat,
+                                                gboolean from_cat);
+
 
 #endif /* CAMEL_O365_UTILS_H */
diff --git a/src/Office365/common/e-o365-connection.c b/src/Office365/common/e-o365-connection.c
index 31cd7791..d1e95bc3 100644
--- a/src/Office365/common/e-o365-connection.c
+++ b/src/Office365/common/e-o365-connection.c
@@ -2111,6 +2111,50 @@ e_o365_connection_call_gather_into_slist (EO365Connection *cnc,
        return TRUE;
 }
 
+/* 
https://docs.microsoft.com/en-us/graph/api/outlookuser-list-mastercategories?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_get_categories_sync (EO365Connection *cnc,
+                                      const gchar *user_override,
+                                      GSList **out_categories, /* EO365Category * */
+                                      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 (out_categories != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               "outlook",
+               "masterCategories",
+               NULL,
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       memset (&rd, 0, sizeof (EO365ResponseData));
+
+       rd.out_items = out_categories;
+
+       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/user-list-mailfolders?view=graph-rest-1.0&tabs=http */
 
 gboolean
@@ -2118,7 +2162,7 @@ e_o365_connection_list_mail_folders_sync (EO365Connection *cnc,
                                          const gchar *user_override, /* for which user, NULL to use the 
account user */
                                          const gchar *from_path, /* path for the folder to read, NULL for 
top user folder */
                                          const gchar *select, /* properties to select, nullable */
-                                         GSList **out_folders, /* JsonObject * - the returned mailFolder 
objects */
+                                         GSList **out_folders, /* EO365MailFolder * - the returned 
mailFolder objects */
                                          GCancellable *cancellable,
                                          GError **error)
 {
diff --git a/src/Office365/common/e-o365-connection.h b/src/Office365/common/e-o365-connection.h
index 4335190e..ee7ea81a 100644
--- a/src/Office365/common/e-o365-connection.h
+++ b/src/Office365/common/e-o365-connection.h
@@ -159,12 +159,18 @@ gboolean  e_o365_connection_call_gather_into_slist
                                                 gpointer user_data, /* expects GSList **, aka pointer to a 
GSList *, where it copies the 'results' */
                                                 GCancellable *cancellable,
                                                 GError **error);
+gboolean       e_o365_connection_get_categories_sync
+                                               (EO365Connection *cnc,
+                                                const gchar *user_override,
+                                                GSList **out_categories, /* EO365Category * */
+                                                GCancellable *cancellable,
+                                                GError **error);
 gboolean       e_o365_connection_list_mail_folders_sync
                                                (EO365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
                                                 const gchar *from_path, /* path for the folder to read, NULL 
for top user folder */
                                                 const gchar *select, /* properties to select, nullable */
-                                                GSList **out_folders, /* JsonObject * - the returned 
mailFolder objects */
+                                                GSList **out_folders, /* EO365MailFolder * - the returned 
mailFolder objects */
                                                 GCancellable *cancellable,
                                                 GError **error);
 gboolean       e_o365_connection_get_mail_folders_delta_sync
diff --git a/src/Office365/common/e-o365-json-utils.c b/src/Office365/common/e-o365-json-utils.c
index 7e86ea52..9b50c3f7 100644
--- a/src/Office365/common/e-o365-json-utils.c
+++ b/src/Office365/common/e-o365-json-utils.c
@@ -190,6 +190,69 @@ e_o365_delta_is_removed_object (JsonObject *object)
        return json_object_has_member (object, "@removed");
 }
 
+/* https://docs.microsoft.com/en-us/graph/api/resources/outlookcategory?view=graph-rest-1.0 */
+
+const gchar *
+e_o365_category_get_display_name (EO365Category *category)
+{
+       return e_o365_json_get_string_member (category, "displayName", NULL);
+}
+
+const gchar *
+e_o365_category_get_id (EO365Category *category)
+{
+       return e_o365_json_get_string_member (category, "id", NULL);
+}
+
+const gchar *
+e_o365_category_get_color (EO365Category *category)
+{
+       const gchar *colors_array[] = {
+               "#ff1a36", /* Red */
+               "#ff8c00", /* Orange */
+               "#f4b10b", /* Peach */
+               "#fff100", /* Yellow */
+               "#009e48", /* Green */
+               "#00b294", /* Teal */
+               "#89933f", /* Olive */
+               "#00bcf2", /* Blue */
+               "#8e69df", /* Purple */
+               "#f30092", /* Maroon */
+               "#6c7e9a", /* Steel */
+               "#425066", /* DarkSteel */
+               "#969696", /* Gray */
+               "#525552", /* DarkGray */
+               "#282828", /* Black */
+               "#a00023", /* DarkRed */
+               "#c45502", /* DarkOrange */
+               "#af7000", /* DarkPeach */
+               "#b59b02", /* DarkYellow */
+               "#176002", /* DarkGreen */
+               "#00725c", /* DarkTeal */
+               "#5c6022", /* DarkOlive */
+               "#036393", /* DarkBlue */
+               "#422f8e", /* DarkPurple */
+               "#960269"  /* DarkMaroon */
+       };
+       const gchar *color_str;
+       gchar *enptr = NULL;
+       gint color_index;
+
+       color_str = e_o365_json_get_string_member (category, "color", NULL);
+
+       if (!color_str ||
+           g_ascii_strcasecmp (color_str, "None") == 0 ||
+           g_ascii_strncasecmp (color_str, "preset", 6) != 0)
+               return NULL;
+
+       color_index = (gint) g_ascii_strtoll (color_str + 6, &enptr, 10);
+
+       if (enptr != color_str && color_index >= 0 && color_index < G_N_ELEMENTS (colors_array))
+               return colors_array[color_index];
+
+       return NULL;
+}
+
 /* https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0 */
 
 const gchar *
diff --git a/src/Office365/common/e-o365-json-utils.h b/src/Office365/common/e-o365-json-utils.h
index 1f480853..fb562900 100644
--- a/src/Office365/common/e-o365-json-utils.h
+++ b/src/Office365/common/e-o365-json-utils.h
@@ -54,6 +54,7 @@ typedef enum _EO365ItemBodyContentTypeType {
 } EO365ItemBodyContentTypeType;
 
 /* Just for better readability */
+#define EO365Category                  JsonObject
 #define EO365DateTimeWithZone          JsonObject
 #define EO365FollowupFlag              JsonObject
 #define EO365InternetMessageHeader     JsonObject
@@ -87,6 +88,10 @@ time_t               e_o365_get_date_time_offset_member      (JsonObject *object,
 
 gboolean       e_o365_delta_is_removed_object          (JsonObject *object);
 
+const gchar *  e_o365_category_get_display_name        (EO365Category *category);
+const gchar *  e_o365_category_get_id                  (EO365Category *category);
+const gchar *  e_o365_category_get_color               (EO365Category *category);
+
 const gchar *  e_o365_mail_folder_get_display_name     (EO365MailFolder *folder);
 const gchar *  e_o365_mail_folder_get_id               (EO365MailFolder *folder);
 const gchar *  e_o365_mail_folder_get_parent_folder_id (EO365MailFolder *folder);
diff --git a/src/Office365/common/e-oauth2-service-office365.c 
b/src/Office365/common/e-oauth2-service-office365.c
index aaf28744..57a05dc9 100644
--- a/src/Office365/common/e-oauth2-service-office365.c
+++ b/src/Office365/common/e-oauth2-service-office365.c
@@ -38,6 +38,7 @@
                        "Mail.ReadWrite.Shared " \
                        "Mail.Send " \
                        "Mail.Send.Shared " \
+                       "MailboxSettings.Read " \
                        "Notes.Create " \
                        "Notes.ReadWrite.All " \
                        "offline_access " \


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