[evolution-ews/wip/mcrha/office365] Camel: Implement store's get_folder_info() for user folders
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews/wip/mcrha/office365] Camel: Implement store's get_folder_info() for user folders
- Date: Tue, 16 Jun 2020 17:00:39 +0000 (UTC)
commit 115051ef543b53a6786ec9bae15bf63fec9bd41c
Author: Milan Crha <mcrha redhat com>
Date: Tue Jun 16 18:59:59 2020 +0200
Camel: Implement store's get_folder_info() for user folders
src/Office365/camel/CMakeLists.txt | 2 +
src/Office365/camel/camel-o365-store-summary.c | 598 ++++++++++++++++++---
src/Office365/camel/camel-o365-store-summary.h | 36 +-
src/Office365/camel/camel-o365-store.c | 461 +++++++++++++---
src/Office365/camel/camel-o365-utils.c | 93 ++++
src/Office365/camel/camel-o365-utils.h | 29 +
src/Office365/common/e-o365-connection.c | 215 +++++++-
src/Office365/common/e-o365-connection.h | 29 +-
src/Office365/common/e-o365-enums.h | 2 +-
.../evolution/e-mail-config-o365-backend.c | 2 +-
10 files changed, 1311 insertions(+), 156 deletions(-)
---
diff --git a/src/Office365/camel/CMakeLists.txt b/src/Office365/camel/CMakeLists.txt
index 65b471e1..64167043 100644
--- a/src/Office365/camel/CMakeLists.txt
+++ b/src/Office365/camel/CMakeLists.txt
@@ -14,6 +14,8 @@ set(SOURCES
camel-o365-store-summary.h
camel-o365-transport.c
camel-o365-transport.h
+ camel-o365-utils.c
+ camel-o365-utils.h
)
add_library(cameloffice365 MODULE
diff --git a/src/Office365/camel/camel-o365-store-summary.c b/src/Office365/camel/camel-o365-store-summary.c
index 068b4637..367af65b 100644
--- a/src/Office365/camel/camel-o365-store-summary.c
+++ b/src/Office365/camel/camel-o365-store-summary.c
@@ -24,25 +24,78 @@
#define STORE_GROUP_NAME "##storepriv##"
#define DATA_VERSION 1
-#define LOCK(summary) g_rec_mutex_lock (&(summary->priv->property_lock))
-#define UNLOCK(summary) g_rec_mutex_unlock (&(summary->priv->property_lock))
+#define LOCK(_summary) g_rec_mutex_lock (&(_summary->priv->property_lock))
+#define UNLOCK(_summary) g_rec_mutex_unlock (&(_summary->priv->property_lock))
struct _CamelO365StoreSummaryPrivate {
GRecMutex property_lock;
- gchar *path;
+ gchar *filename;
GKeyFile *key_file;
GFileMonitor *monitor_delete;
gboolean dirty;
/* Note: We use the *same* strings in both of these hash tables, and
- * only id_fname_hash has g_free() hooked up as the destructor func.
- * So entries must always be removed from fname_id_hash *first*. */
- GHashTable *id_fname_hash; /* id ~> folder name */
- GHashTable *fname_id_hash; /* folder name ~> id */
+ * only id_full_name_hash has g_free() hooked up as the destructor func.
+ * So entries must always be removed from full_name_id_hash *first*. */
+ GHashTable *id_full_name_hash; /* id ~> folder full name */
+ GHashTable *full_name_id_hash; /* folder full name ~> id */
};
G_DEFINE_TYPE_WITH_PRIVATE (CamelO365StoreSummary, camel_o365_store_summary, G_TYPE_OBJECT)
+static gchar *
+o365_store_summary_encode_folder_name (const gchar *display_name)
+{
+ GString *encoded;
+ const gchar *pos;
+
+ if (!display_name || !*display_name)
+ return NULL;
+
+ encoded = g_string_sized_new (strlen (display_name) + 4);
+
+ for (pos = display_name; *pos; pos++) {
+ if (strchr ("%?/", *pos))
+ g_string_append_printf (encoded, "%%%02x", *pos);
+ else
+ g_string_append_c (encoded, *pos);
+ }
+
+ return g_string_free (encoded, FALSE);
+}
+
+static gchar *
+o365_store_summary_decode_folder_name (gchar *pathpart)
+{
+ gchar *pos, *write_pos;
+
+ if (!pathpart || !*pathpart)
+ return pathpart;
+
+ pos = pathpart;
+ write_pos = pathpart;
+
+ while (*pos) {
+ if (*pos == '%' &&
+ g_ascii_isxdigit (pos[1]) &&
+ g_ascii_isxdigit (pos[2])) {
+ *write_pos = (g_ascii_xdigit_value (pos[1]) << 4) + g_ascii_xdigit_value (pos[2]);
+
+ pos += 2;
+ } else if (write_pos != pos) {
+ *write_pos = *pos;
+ }
+
+ pos++;
+ write_pos++;
+ }
+
+ if (write_pos != pos)
+ *write_pos = 0;
+
+ return pathpart;
+}
+
static void
camel_o365_store_summary_migrate_data_locked (CamelO365StoreSummary *store_summary,
gint from_version)
@@ -97,10 +150,10 @@ o365_store_summary_finalize (GObject *object)
CamelO365StoreSummary *store_summary = CAMEL_O365_STORE_SUMMARY (object);
g_rec_mutex_clear (&store_summary->priv->property_lock);
- g_hash_table_destroy (store_summary->priv->id_fname_hash);
- g_hash_table_destroy (store_summary->priv->fname_id_hash);
+ g_hash_table_destroy (store_summary->priv->full_name_id_hash);
+ g_hash_table_destroy (store_summary->priv->id_full_name_hash);
g_key_file_free (store_summary->priv->key_file);
- g_free (store_summary->priv->path);
+ g_free (store_summary->priv->filename);
/* Chain up to parent's method. */
G_OBJECT_CLASS (camel_o365_store_summary_parent_class)->finalize (object);
@@ -121,25 +174,25 @@ camel_o365_store_summary_init (CamelO365StoreSummary *store_summary)
{
store_summary->priv = camel_o365_store_summary_get_instance_private (store_summary);
store_summary->priv->key_file = g_key_file_new ();
- store_summary->priv->id_fname_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- store_summary->priv->fname_id_hash = g_hash_table_new (g_str_hash, g_str_equal); /* shared data with
'id_fname_hash' */
+ store_summary->priv->id_full_name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
g_free);
+ store_summary->priv->full_name_id_hash = g_hash_table_new (g_str_hash, g_str_equal); /* shared data
with 'id_full_name_hash' */
g_rec_mutex_init (&store_summary->priv->property_lock);
}
CamelO365StoreSummary *
-camel_o365_store_summary_new (const gchar *path)
+camel_o365_store_summary_new (const gchar *filename)
{
CamelO365StoreSummary *store_summary;
GError *error = NULL;
GFile *file;
- g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (filename != NULL, NULL);
- file = g_file_new_for_path (path);
+ file = g_file_new_for_path (filename);
store_summary = g_object_new (CAMEL_TYPE_O365_STORE_SUMMARY, NULL);
- store_summary->priv->path = g_strdup (path);
+ store_summary->priv->filename = g_strdup (filename);
store_summary->priv->monitor_delete = g_file_monitor_file (file, G_FILE_MONITOR_SEND_MOVED, NULL,
&error);
if (!error) {
@@ -167,9 +220,14 @@ camel_o365_store_summary_load (CamelO365StoreSummary *store_summary,
LOCK (store_summary);
- success = g_key_file_load_from_file (store_summary->priv->key_file, store_summary->priv->path,
G_KEY_FILE_NONE, &local_error);
+ g_hash_table_remove_all (store_summary->priv->full_name_id_hash);
+ g_hash_table_remove_all (store_summary->priv->id_full_name_hash);
- if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+ store_summary->priv->dirty = FALSE;
+
+ success = g_key_file_load_from_file (store_summary->priv->key_file, store_summary->priv->filename,
G_KEY_FILE_NONE, &local_error);
+
+ if (g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
g_key_file_set_integer (store_summary->priv->key_file, STORE_GROUP_NAME, "Version",
DATA_VERSION);
g_clear_error (&local_error);
@@ -182,7 +240,9 @@ camel_o365_store_summary_load (CamelO365StoreSummary *store_summary,
version = g_key_file_get_integer (store_summary->priv->key_file, STORE_GROUP_NAME, "Version",
NULL);
if (version && version < DATA_VERSION)
- camel_o365_store_summary_migrate_data_locked (store_summary, version);
+ camel_o365_store_summary_migrate_data_locked (store_summary, version);
+
+ camel_o365_store_summary_rebuild_hashes (store_summary);
}
UNLOCK (store_summary);
@@ -194,14 +254,14 @@ gboolean
camel_o365_store_summary_save (CamelO365StoreSummary *store_summary,
GError **error)
{
- gboolean success;
+ gboolean success = TRUE;
g_return_val_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary), FALSE);
LOCK (store_summary);
if (store_summary->priv->dirty) {
- success = g_key_file_save_to_file (store_summary->priv->key_file, store_summary->priv->path,
error);
+ success = g_key_file_save_to_file (store_summary->priv->key_file,
store_summary->priv->filename, error);
if (success)
store_summary->priv->dirty = FALSE;
@@ -219,13 +279,13 @@ camel_o365_store_summary_clear (CamelO365StoreSummary *store_summary)
LOCK (store_summary);
- store_summary->priv->dirty = g_hash_table_size (store_summary->priv->id_fname_hash) > 0;
+ store_summary->priv->dirty = g_hash_table_size (store_summary->priv->id_full_name_hash) > 0;
g_key_file_free (store_summary->priv->key_file);
store_summary->priv->key_file = g_key_file_new ();
- g_hash_table_remove_all (store_summary->priv->fname_id_hash);
- g_hash_table_remove_all (store_summary->priv->id_fname_hash);
+ g_hash_table_remove_all (store_summary->priv->full_name_id_hash);
+ g_hash_table_remove_all (store_summary->priv->id_full_name_hash);
UNLOCK (store_summary);
}
@@ -246,6 +306,109 @@ camel_o365_store_summary_unlock (CamelO365StoreSummary *store_summary)
UNLOCK (store_summary);
}
+static void
+o365_store_summary_build_full_name (const gchar *id,
+ GHashTable *id_folder_name,
+ GHashTable *id_parent_id,
+ GHashTable *covered,
+ GString *inout_full_name)
+{
+ const gchar *parent_id;
+
+ g_return_if_fail (id != NULL);
+
+ if (g_hash_table_contains (covered, id))
+ return;
+
+ g_hash_table_insert (covered, (gpointer) id, NULL);
+
+ parent_id = g_hash_table_lookup (id_parent_id, id);
+
+ if (parent_id && *parent_id && g_hash_table_contains (id_folder_name, parent_id))
+ o365_store_summary_build_full_name (parent_id, id_folder_name, id_parent_id, covered,
inout_full_name);
+
+ if (inout_full_name->len)
+ g_string_append_c (inout_full_name, '/');
+
+ g_string_append (inout_full_name, g_hash_table_lookup (id_folder_name, id));
+}
+
+void
+camel_o365_store_summary_rebuild_hashes (CamelO365StoreSummary *store_summary)
+{
+ GHashTable *id_folder_name;
+ GHashTable *id_parent_id;
+ gchar **groups;
+ gint ii;
+
+ g_return_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary));
+
+ LOCK (store_summary);
+
+ g_hash_table_remove_all (store_summary->priv->full_name_id_hash);
+ g_hash_table_remove_all (store_summary->priv->id_full_name_hash);
+
+ id_folder_name = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+ id_parent_id = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
+
+ groups = g_key_file_get_groups (store_summary->priv->key_file, NULL);
+
+ for (ii = 0; groups[ii]; ii++) {
+ const gchar *group = groups[ii];
+
+ if (g_ascii_strcasecmp (group, STORE_GROUP_NAME) != 0 &&
+ g_key_file_has_key (store_summary->priv->key_file, group, "DisplayName", NULL)) {
+ gchar *display_name, *folder_name;
+
+ display_name = g_key_file_get_string (store_summary->priv->key_file, group,
"DisplayName", NULL);
+ folder_name = o365_store_summary_encode_folder_name (display_name);
+
+ g_hash_table_insert (id_folder_name, (gpointer) group, folder_name);
+ g_hash_table_insert (id_parent_id, (gpointer) group,
+ camel_o365_store_summary_dup_folder_parent_id (store_summary, group));
+
+ g_free (display_name);
+ }
+ }
+
+ if (g_hash_table_size (id_folder_name)) {
+ GHashTable *covered;
+ GHashTableIter iter;
+ gpointer key;
+
+ covered = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_iter_init (&iter, id_folder_name);
+
+ while (g_hash_table_iter_next (&iter, &key, NULL)) {
+ const gchar *id = key;
+ GString *full_name_str;
+
+ g_hash_table_remove_all (covered);
+
+ full_name_str = g_string_sized_new (16);
+
+ o365_store_summary_build_full_name (id, id_folder_name, id_parent_id, covered,
full_name_str);
+
+ if (full_name_str->len) {
+ gchar *id_dup = g_strdup (id);
+ gchar *full_name = g_string_free (full_name_str, FALSE);
+
+ g_hash_table_insert (store_summary->priv->id_full_name_hash, id_dup,
full_name);
+ g_hash_table_insert (store_summary->priv->full_name_id_hash, full_name,
id_dup);
+ } else {
+ g_string_free (full_name_str, TRUE);
+ }
+ }
+
+ g_hash_table_destroy (covered);
+ }
+
+ g_strfreev (groups);
+
+ UNLOCK (store_summary);
+}
+
void
camel_o365_store_summary_set_delta_link (CamelO365StoreSummary *store_summary,
const gchar *delta_link)
@@ -281,8 +444,53 @@ camel_o365_store_summary_dup_delta_link (CamelO365StoreSummary *store_summary)
return value;
}
+gboolean
+camel_o365_store_summary_has_folder (CamelO365StoreSummary *store_summary,
+ const gchar *id)
+{
+ gboolean has;
+
+ g_return_val_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary), FALSE);
+ g_return_val_if_fail (id != NULL, FALSE);
+
+ LOCK (store_summary);
+
+ has = g_hash_table_contains (store_summary->priv->id_full_name_hash, id);
+
+ UNLOCK (store_summary);
+
+ return has;
+}
+
+void
+camel_o365_store_summary_remove_folder (CamelO365StoreSummary *store_summary,
+ const gchar *id)
+{
+ const gchar *full_name;
+
+ g_return_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary));
+ g_return_if_fail (id != NULL);
+
+ LOCK (store_summary);
+
+ full_name = g_hash_table_lookup (store_summary->priv->id_full_name_hash, id);
+
+ if (full_name) {
+ g_hash_table_remove (store_summary->priv->full_name_id_hash, full_name);
+ g_hash_table_remove (store_summary->priv->id_full_name_hash, id);
+
+ store_summary->priv->dirty = store_summary->priv->dirty ||
+ g_key_file_has_group (store_summary->priv->key_file, id);
+
+ g_key_file_remove_group (store_summary->priv->key_file, id, NULL);
+ }
+
+ UNLOCK (store_summary);
+}
+
void
camel_o365_store_summary_set_folder (CamelO365StoreSummary *store_summary,
+ gboolean with_hashes_update,
const gchar *id,
const gchar *parent_id,
const gchar *display_name,
@@ -301,7 +509,6 @@ camel_o365_store_summary_set_folder (CamelO365StoreSummary *store_summary,
LOCK (store_summary);
- camel_o365_store_summary_set_folder_display_name (store_summary, id, display_name);
camel_o365_store_summary_set_folder_parent_id (store_summary, id, parent_id);
camel_o365_store_summary_set_folder_total_count (store_summary, id, total_count);
camel_o365_store_summary_set_folder_unread_count (store_summary, id, unread_count);
@@ -322,6 +529,9 @@ camel_o365_store_summary_set_folder (CamelO365StoreSummary *store_summary,
changed = TRUE;
}
+ /* Set display name as the last, because it updates internal hashes and depends on the stored data */
+ camel_o365_store_summary_set_folder_display_name (store_summary, id, display_name,
with_hashes_update);
+
if (changed)
store_summary->priv->dirty = TRUE;
@@ -331,8 +541,9 @@ camel_o365_store_summary_set_folder (CamelO365StoreSummary *store_summary,
gboolean
camel_o365_store_summary_get_folder (CamelO365StoreSummary *store_summary,
const gchar *id,
- gchar **out_parent_id,
+ gchar **out_full_name,
gchar **out_display_name,
+ gchar **out_parent_id,
gint32 *out_total_count,
gint32 *out_unread_count,
guint32 *out_flags,
@@ -350,6 +561,9 @@ camel_o365_store_summary_get_folder (CamelO365StoreSummary *store_summary,
found = g_key_file_has_group (store_summary->priv->key_file, id);
if (found) {
+ if (out_full_name)
+ *out_full_name = g_strdup (g_hash_table_lookup
(store_summary->priv->id_full_name_hash, id));
+
if (out_display_name)
*out_display_name = g_key_file_get_string (store_summary->priv->key_file, id,
"DisplayName", NULL);
@@ -380,79 +594,231 @@ camel_o365_store_summary_get_folder (CamelO365StoreSummary *store_summary,
return found;
}
-void
-camel_o365_store_summary_set_folder_parent_id (CamelO365StoreSummary *store_summary,
- const gchar *id,
- const gchar *parent_id)
+gchar *
+camel_o365_store_summary_dup_folder_full_name (CamelO365StoreSummary *store_summary,
+ const gchar *id)
{
- g_return_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary));
- g_return_if_fail (id != NULL);
+ gchar *value = NULL;
- LOCK (store_summary);
+ if (!camel_o365_store_summary_get_folder (store_summary, id, &value, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL))
+ value = NULL;
- if (parent_id && *parent_id) {
- gchar *current_parent_id;
+ return value;
+}
- current_parent_id = g_key_file_get_string (store_summary->priv->key_file, id, "ParentId",
NULL);
+typedef struct _IdFullNameData {
+ gchar *id;
+ gchar *full_name;
+} IdFullNameData;
- if (g_strcmp0 (current_parent_id, parent_id) != 0) {
- g_key_file_set_string (store_summary->priv->key_file, id, "ParentId", parent_id);
- store_summary->priv->dirty = TRUE;
- }
+static IdFullNameData *
+id_full_name_data_new (gchar *id,
+ gchar *full_name)
+{
+ IdFullNameData *ifnd;
- g_free (current_parent_id);
- } else if (g_key_file_has_key (store_summary->priv->key_file, id, "ParentId", NULL)) {
- g_key_file_remove_key (store_summary->priv->key_file, id, "ParentId", NULL);
+ ifnd = g_slice_new (IdFullNameData);
+ ifnd->id = id;
+ ifnd->full_name = full_name;
+
+ return ifnd;
+}
+
+static void
+id_full_name_data_free (gpointer ptr)
+{
+ IdFullNameData *ifnd = ptr;
+
+ if (ifnd) {
+ g_free (ifnd->id);
+ g_free (ifnd->full_name);
+ g_slice_free (IdFullNameData, ifnd);
+ }
+}
+
+typedef struct _RemovePrefixedData {
+ GHashTable *full_name_id_hash;
+ const gchar *prefix;
+ gint prefix_len;
+ GSList *removed; /* IdFullNameData * */
+} RemovePrefixedData;
+
+static gboolean
+o365_remove_prefixed_cb (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ RemovePrefixedData *rpd = user_data;
+ gchar *id = key, *full_name = value;
+
+ g_return_val_if_fail (rpd != NULL, FALSE);
+ g_return_val_if_fail (full_name != NULL, FALSE);
+
+ if (g_str_has_prefix (full_name, rpd->prefix) &&
+ (!full_name[rpd->prefix_len] || full_name[rpd->prefix_len] == '/')) {
+ g_hash_table_remove (rpd->full_name_id_hash, full_name);
+
+ rpd->removed = g_slist_prepend (rpd->removed, id_full_name_data_new (id, full_name));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+o365_store_summary_build_new_full_name (const gchar *old_full_name,
+ const gchar *new_display_name)
+{
+ gchar *encoded;
+ GString *full_name;
+ const gchar *last_slash;
+
+ g_return_val_if_fail (old_full_name != NULL, NULL);
+ g_return_val_if_fail (new_display_name != NULL, NULL);
+
+ last_slash = strrchr (old_full_name, '/');
+ encoded = o365_store_summary_encode_folder_name (new_display_name);
+ full_name = g_string_sized_new ((last_slash ? (last_slash - old_full_name) : 0) + strlen (encoded) +
2);
+
+ if (last_slash)
+ g_string_append_len (full_name, old_full_name, last_slash - old_full_name + 1);
+
+ g_string_append (full_name, encoded);
+
+ g_free (encoded);
+
+ return g_string_free (full_name, FALSE);
+}
+
+gboolean
+camel_o365_store_summary_set_folder_display_name (CamelO365StoreSummary *store_summary,
+ const gchar *id,
+ const gchar *display_name,
+ gboolean with_hashes_update)
+{
+ gchar *current_display_name;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary), FALSE);
+ g_return_val_if_fail (id != NULL, FALSE);
+ g_return_val_if_fail (display_name != NULL, FALSE);
+
+ LOCK (store_summary);
+
+ current_display_name = g_key_file_get_string (store_summary->priv->key_file, id, "DisplayName", NULL);
+
+ if (g_strcmp0 (current_display_name, display_name) != 0) {
+ const gchar *old_full_name;
+
+ g_key_file_set_string (store_summary->priv->key_file, id, "DisplayName", display_name);
store_summary->priv->dirty = TRUE;
+
+ changed = TRUE;
+
+ if (with_hashes_update) {
+ old_full_name = g_hash_table_lookup (store_summary->priv->id_full_name_hash, id);
+
+ if (old_full_name) {
+ RemovePrefixedData rpd;
+ gchar *new_full_name;
+ gint diff;
+ GSList *link;
+
+ rpd.full_name_id_hash = store_summary->priv->full_name_id_hash;
+ rpd.prefix = old_full_name;
+ rpd.prefix_len = strlen (old_full_name);
+ rpd.removed = NULL;
+
+ g_hash_table_foreach_remove (store_summary->priv->id_full_name_hash,
o365_remove_prefixed_cb, &rpd);
+
+ new_full_name = o365_store_summary_build_new_full_name (old_full_name,
display_name);
+ diff = strlen (new_full_name) - rpd.prefix_len;
+
+ for (link = rpd.removed; link; link = g_slist_next (link)) {
+ IdFullNameData *ifnd = link->data;
+ GString *fixed_full_name_str;
+ gchar *fixed_full_name;
+ gint old_full_name_len;
+
+ old_full_name_len = strlen (ifnd->full_name);
+ fixed_full_name_str = g_string_sized_new (old_full_name_len + diff +
2);
+
+ g_string_append (fixed_full_name_str, new_full_name);
+
+ if (old_full_name_len > rpd.prefix_len)
+ g_string_append (fixed_full_name_str, ifnd->full_name +
rpd.prefix_len);
+
+ fixed_full_name = g_string_free (fixed_full_name_str, FALSE);
+
+ g_hash_table_insert (store_summary->priv->id_full_name_hash,
ifnd->id, fixed_full_name);
+ g_hash_table_insert (store_summary->priv->full_name_id_hash,
fixed_full_name, ifnd->id);
+
+ /* To not be freed by id_full_name_data_free() below */
+ ifnd->id = NULL;
+ }
+
+ g_slist_free_full (rpd.removed, id_full_name_data_free);
+ g_free (new_full_name);
+ }
+ }
}
+ g_free (current_display_name);
+
UNLOCK (store_summary);
+
+ return changed;
}
gchar *
-camel_o365_store_summary_dup_folder_parent_id (CamelO365StoreSummary *store_summary,
- const gchar *id)
+camel_o365_store_summary_dup_folder_display_name (CamelO365StoreSummary *store_summary,
+ const gchar *id)
{
gchar *value = NULL;
- if (!camel_o365_store_summary_get_folder (store_summary, id, &value, NULL, NULL, NULL, NULL, NULL,
NULL, NULL))
+ if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, &value, NULL, NULL, NULL, NULL,
NULL, NULL, NULL))
value = NULL;
return value;
}
void
-camel_o365_store_summary_set_folder_display_name (CamelO365StoreSummary *store_summary,
- const gchar *id,
- const gchar *display_name)
+camel_o365_store_summary_set_folder_parent_id (CamelO365StoreSummary *store_summary,
+ const gchar *id,
+ const gchar *parent_id)
{
- const gchar *current_display_name;
-
g_return_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary));
g_return_if_fail (id != NULL);
- g_return_if_fail (display_name != NULL);
LOCK (store_summary);
- current_display_name = g_hash_table_lookup (store_summary->priv->id_fname_hash, id);
+ if (parent_id && *parent_id) {
+ gchar *current_parent_id;
- if (g_strcmp0 (current_display_name, display_name) != 0) {
- g_key_file_set_string (store_summary->priv->key_file, id, "DisplayName", display_name);
- store_summary->priv->dirty = TRUE;
+ current_parent_id = g_key_file_get_string (store_summary->priv->key_file, id, "ParentId",
NULL);
+
+ if (g_strcmp0 (current_parent_id, parent_id) != 0) {
+ g_key_file_set_string (store_summary->priv->key_file, id, "ParentId", parent_id);
+ store_summary->priv->dirty = TRUE;
+ }
- // TODO: update hashes on display name change
+ g_free (current_parent_id);
+ } else if (g_key_file_has_key (store_summary->priv->key_file, id, "ParentId", NULL)) {
+ g_key_file_remove_key (store_summary->priv->key_file, id, "ParentId", NULL);
+ store_summary->priv->dirty = TRUE;
}
UNLOCK (store_summary);
}
gchar *
-camel_o365_store_summary_dup_folder_display_name (CamelO365StoreSummary *store_summary,
- const gchar *id)
+camel_o365_store_summary_dup_folder_parent_id (CamelO365StoreSummary *store_summary,
+ const gchar *id)
{
gchar *value = NULL;
- if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, &value, NULL, NULL, NULL, NULL,
NULL, NULL))
+ if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, &value, NULL, NULL, NULL,
NULL, NULL, NULL))
value = NULL;
return value;
@@ -482,7 +848,7 @@ camel_o365_store_summary_get_folder_total_count (CamelO365StoreSummary *store_su
{
gint32 value = 0;
- if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, &value, NULL, NULL, NULL,
NULL, NULL))
+ if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, &value, NULL, NULL,
NULL, NULL, NULL))
value = 0;
return value;
@@ -512,7 +878,7 @@ camel_o365_store_summary_get_folder_unread_count (CamelO365StoreSummary *store_s
{
gint32 value = 0;
- if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, &value, NULL, NULL,
NULL, NULL))
+ if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, &value, NULL,
NULL, NULL, NULL))
value = 0;
return value;
@@ -542,7 +908,7 @@ camel_o365_store_summary_get_folder_flags (CamelO365StoreSummary *store_summary,
{
guint32 value = 0;
- if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, &value, NULL,
NULL, NULL))
+ if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, NULL, &value,
NULL, NULL, NULL))
value = 0;
return value;
@@ -554,7 +920,7 @@ camel_o365_store_summary_get_folder_kind (CamelO365StoreSummary *store_summary,
{
EO365FolderKind value = E_O365_FOLDER_KIND_UNKNOWN;
- if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, NULL, &value,
NULL, NULL))
+ if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, NULL, NULL,
&value, NULL, NULL))
value = E_O365_FOLDER_KIND_UNKNOWN;
return value;
@@ -566,7 +932,7 @@ camel_o365_store_summary_get_folder_is_foreign (CamelO365StoreSummary *store_sum
{
gboolean value = FALSE;
- if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, NULL, NULL,
&value, NULL))
+ if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, &value, NULL))
value = FALSE;
return value;
@@ -578,8 +944,108 @@ camel_o365_store_summary_get_folder_is_public (CamelO365StoreSummary *store_summ
{
gboolean value = FALSE;
- if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, &value))
+ if (!camel_o365_store_summary_get_folder (store_summary, id, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, &value))
value = FALSE;
return value;
}
+
+CamelFolderInfo *
+camel_o365_store_summary_build_folder_info_for_id (CamelO365StoreSummary *store_summary,
+ const gchar *id)
+{
+ CamelFolderInfo *info;
+ gchar *full_name = NULL;
+ gchar *display_name = NULL;
+ gint32 total_count = 0;
+ gint32 unread_count = 0;
+ guint32 flags = 0;
+
+ g_return_val_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary), NULL);
+ g_return_val_if_fail (id != NULL, NULL);
+
+ LOCK (store_summary);
+
+ if (camel_o365_store_summary_get_folder (store_summary, id, &full_name, &display_name, NULL,
&total_count, &unread_count, &flags, NULL, NULL, NULL)) {
+ info = camel_folder_info_new ();
+ info->full_name = full_name;
+ info->display_name = display_name;
+ info->flags = flags;
+ info->unread = unread_count;
+ info->total = total_count;
+ } else {
+ info = NULL;
+ }
+
+ UNLOCK (store_summary);
+
+ return info;
+}
+
+typedef struct _GatherInfosData {
+ CamelO365StoreSummary *store_summary;
+ GPtrArray *folder_infos;
+ const gchar *prefix;
+ gint prefix_len;
+ gboolean recursive;
+} GatherInfosData;
+
+static void
+o365_store_summary_gather_folder_infos (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ const gchar *id = key, *full_name = value;
+ GatherInfosData *gid = user_data;
+
+ g_return_if_fail (full_name != NULL);
+ g_return_if_fail (gid != NULL);
+
+ if (!gid->prefix_len || (g_str_has_prefix (full_name, gid->prefix) &&
+ full_name[gid->prefix_len] == '/')) {
+ const gchar *without_prefix = full_name + gid->prefix_len + (gid->prefix_len > 0 ? 1 : 0);
+
+ if (gid->recursive || !strchr (without_prefix, '/')) {
+ CamelFolderInfo *info;
+
+ info = camel_o365_store_summary_build_folder_info_for_id (gid->store_summary, id);
+
+ if (info)
+ g_ptr_array_add (gid->folder_infos, info);
+ else
+ g_warning ("%s: Failed to build folder info for id:'%s' full_name:'%s'",
G_STRFUNC, id, full_name);
+ }
+ }
+}
+
+CamelFolderInfo *
+camel_o365_store_summary_build_folder_info (CamelO365StoreSummary *store_summary,
+ const gchar *top,
+ gboolean recursive)
+{
+ CamelFolderInfo *info = NULL;
+ GatherInfosData gid;
+
+ g_return_val_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary), NULL);
+
+ if (!top)
+ top = "";
+
+ LOCK (store_summary);
+
+ gid.store_summary = store_summary;
+ gid.folder_infos = g_ptr_array_new ();
+ gid.prefix = top;
+ gid.prefix_len = strlen (top);
+ gid.recursive = recursive;
+
+ g_hash_table_foreach (store_summary->priv->id_full_name_hash, o365_store_summary_gather_folder_infos,
&gid);
+
+ info = camel_folder_info_build (gid.folder_infos, NULL, '/', TRUE);
+
+ UNLOCK (store_summary);
+
+ g_ptr_array_free (gid.folder_infos, TRUE);
+
+ return info;
+}
diff --git a/src/Office365/camel/camel-o365-store-summary.h b/src/Office365/camel/camel-o365-store-summary.h
index 6a24463d..48cd4570 100644
--- a/src/Office365/camel/camel-o365-store-summary.h
+++ b/src/Office365/camel/camel-o365-store-summary.h
@@ -59,7 +59,7 @@ struct _CamelO365StoreSummaryClass {
GType camel_o365_store_summary_get_type (void);
CamelO365StoreSummary *
- camel_o365_store_summary_new (const gchar *path);
+ camel_o365_store_summary_new (const gchar *filename);
gboolean camel_o365_store_summary_load (CamelO365StoreSummary *store_summary,
GError **error);
gboolean camel_o365_store_summary_save (CamelO365StoreSummary *store_summary,
@@ -67,10 +67,16 @@ gboolean camel_o365_store_summary_save (CamelO365StoreSummary *store_summary,
void camel_o365_store_summary_clear (CamelO365StoreSummary *store_summary);
void camel_o365_store_summary_lock (CamelO365StoreSummary *store_summary);
void camel_o365_store_summary_unlock (CamelO365StoreSummary *store_summary);
+void camel_o365_store_summary_rebuild_hashes (CamelO365StoreSummary *store_summary);
void camel_o365_store_summary_set_delta_link (CamelO365StoreSummary *store_summary,
const gchar *delta_link);
gchar * camel_o365_store_summary_dup_delta_link (CamelO365StoreSummary *store_summary);
+gboolean camel_o365_store_summary_has_folder (CamelO365StoreSummary *store_summary,
+ const gchar *id);
+void camel_o365_store_summary_remove_folder (CamelO365StoreSummary *store_summary,
+ const gchar *id);
void camel_o365_store_summary_set_folder (CamelO365StoreSummary *store_summary,
+ gboolean with_hashes_update,
const gchar *id,
const gchar *parent_id,
const gchar *display_name,
@@ -82,26 +88,31 @@ void camel_o365_store_summary_set_folder (CamelO365StoreSummary
*store_summary,
gboolean is_public);
gboolean camel_o365_store_summary_get_folder (CamelO365StoreSummary *store_summary,
const gchar *id,
- gchar **out_parent_id,
+ gchar **out_full_name,
gchar **out_display_name,
+ gchar **out_parent_id,
gint32 *out_total_count,
gint32 *out_unread_count,
guint32 *out_flags,
EO365FolderKind *out_kind,
gboolean *out_is_foreign,
gboolean *out_is_public);
-void camel_o365_store_summary_set_folder_parent_id
+gchar * camel_o365_store_summary_dup_folder_full_name
+ (CamelO365StoreSummary *store_summary,
+ const gchar *id);
+gboolean camel_o365_store_summary_set_folder_display_name
(CamelO365StoreSummary *store_summary,
const gchar *id,
- const gchar *parent_id);
-gchar * camel_o365_store_summary_dup_folder_parent_id
+ const gchar *display_name,
+ gboolean with_hashes_update);
+gchar * camel_o365_store_summary_dup_folder_display_name
(CamelO365StoreSummary *store_summary,
const gchar *id);
-void camel_o365_store_summary_set_folder_display_name
+void camel_o365_store_summary_set_folder_parent_id
(CamelO365StoreSummary *store_summary,
const gchar *id,
- const gchar *display_name);
-gchar * camel_o365_store_summary_dup_folder_display_name
+ const gchar *parent_id);
+gchar * camel_o365_store_summary_dup_folder_parent_id
(CamelO365StoreSummary *store_summary,
const gchar *id);
void camel_o365_store_summary_set_folder_total_count
@@ -133,6 +144,15 @@ gboolean camel_o365_store_summary_get_folder_is_foreign
gboolean camel_o365_store_summary_get_folder_is_public
(CamelO365StoreSummary *store_summary,
const gchar *id);
+CamelFolderInfo *
+ camel_o365_store_summary_build_folder_info_for_id
+ (CamelO365StoreSummary *store_summary,
+ const gchar *id);
+CamelFolderInfo *
+ camel_o365_store_summary_build_folder_info
+ (CamelO365StoreSummary *store_summary,
+ const gchar *top,
+ gboolean recursive);
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 7efb4f1d..bb17054b 100644
--- a/src/Office365/camel/camel-o365-store.c
+++ b/src/Office365/camel/camel-o365-store.c
@@ -21,12 +21,21 @@
#include <glib/gstdio.h>
#include "common/camel-o365-settings.h"
+#include "common/e-o365-connection.h"
+#include "common/e-o365-json-utils.h"
+#include "camel-o365-store-summary.h"
+#include "camel-o365-utils.h"
#include "camel-o365-store.h"
+#define LOCK(_store) g_rec_mutex_lock (&(_store->priv->property_lock))
+#define UNLOCK(_store) g_rec_mutex_unlock (&(_store->priv->property_lock))
+
struct _CamelO365StorePrivate {
GRecMutex property_lock;
gchar *storage_path;
+ CamelO365StoreSummary *summary;
+ EO365Connection *cnc;
};
static void camel_o365_store_initable_init (GInitableIface *iface);
@@ -45,47 +54,6 @@ G_DEFINE_TYPE_WITH_CODE (CamelO365Store, camel_o365_store, CAMEL_TYPE_OFFLINE_ST
G_IMPLEMENT_INTERFACE (CAMEL_TYPE_SUBSCRIBABLE, camel_o365_subscribable_init)
G_ADD_PRIVATE (CamelO365Store))
-static void
-o365_store_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_CONNECTABLE:
- camel_network_service_set_connectable (
- CAMEL_NETWORK_SERVICE (object),
- g_value_get_object (value));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
-static void
-o365_store_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- switch (property_id) {
- case PROP_CONNECTABLE:
- g_value_take_object (
- value,
- camel_network_service_ref_connectable (
- CAMEL_NETWORK_SERVICE (object)));
- return;
- case PROP_HOST_REACHABLE:
- g_value_set_boolean (
- value,
- camel_network_service_get_host_reachable (
- CAMEL_NETWORK_SERVICE (object)));
- return;
- }
-
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-}
-
static gboolean
o365_store_construct (CamelService *service,
CamelSession *session,
@@ -93,21 +61,21 @@ o365_store_construct (CamelService *service,
GError **error)
{
CamelO365Store *o365_store;
- gchar /**summary_file,*/ *session_storage_path;
+ gchar *summary_file, *session_storage_path;
guint32 store_flags;
+ GError *local_error = NULL;
o365_store = (CamelO365Store *) service;
store_flags = camel_store_get_flags (CAMEL_STORE (o365_store));
- /* Disable virtual trash and junk folders. Office365 has real
- * folders for that */
+ /* Disable virtual trash and junk folders. Office365 has real folders for that */
store_flags &= ~(CAMEL_STORE_VTRASH | CAMEL_STORE_VJUNK);
store_flags |= CAMEL_STORE_REAL_JUNK_FOLDER;
camel_store_set_flags (CAMEL_STORE (o365_store), store_flags);
- /*storage path*/
session_storage_path = g_strdup (camel_service_get_user_cache_dir (service));
+
if (!session_storage_path) {
g_set_error (
error, CAMEL_STORE_ERROR,
@@ -119,11 +87,15 @@ o365_store_construct (CamelService *service,
o365_store->priv->storage_path = session_storage_path;
g_mkdir_with_parents (o365_store->priv->storage_path, 0700);
- /*summary_file = g_build_filename (o365_store->storage_path, "folder-tree", NULL);
- o365_store->summary = camel_o365_store_summary_new (summary_file);
- camel_o365_store_summary_load (o365_store->summary, NULL);
- g_free (summary_file);*/
+ summary_file = g_build_filename (o365_store->priv->storage_path, "folder-tree", NULL);
+ o365_store->priv->summary = camel_o365_store_summary_new (summary_file);
+
+ if (!camel_o365_store_summary_load (o365_store->priv->summary, &local_error))
+ g_warning ("%s: Failed to load store summary '%s': %s", G_STRFUNC, summary_file, local_error
? local_error->message : "Unknown error");
+
+ g_clear_error (&local_error);
+ g_free (summary_file);
return TRUE;
}
@@ -183,12 +155,30 @@ o365_store_get_name (CamelService *service,
return name;
}
+static EO365Connection *
+o365_store_ref_connection (CamelO365Store *o365_store)
+{
+ EO365Connection *cnc = NULL;
+
+ g_return_val_if_fail (CAMEL_IS_O365_STORE (o365_store), NULL);
+
+ LOCK (o365_store);
+
+ if (o365_store->priv->cnc)
+ cnc = g_object_ref (o365_store->priv->cnc);
+
+ UNLOCK (o365_store);
+
+ return cnc;
+}
+
static gboolean
o365_store_connect_sync (CamelService *service,
GCancellable *cancellable,
GError **error)
{
- /*CamelO365Store *o365_store;*/
+ CamelO365Store *o365_store;
+ EO365Connection *cnc;
gboolean success = FALSE;
/* Chain up to parent's method. */
@@ -198,19 +188,29 @@ o365_store_connect_sync (CamelService *service,
if (camel_service_get_connection_status (service) == CAMEL_SERVICE_DISCONNECTED)
return FALSE;
- /*o365_store = CAMEL_O365_STORE (service);
+ o365_store = CAMEL_O365_STORE (service);
+ cnc = o365_store_ref_connection (o365_store);
+
+ if (!cnc) {
+ LOCK (o365_store);
+
+ o365_store->priv->cnc = camel_o365_utils_new_connection (service, NULL);
+
+ UNLOCK (o365_store);
+ }
- g_signal_connect_swapped (
- o365_settings,
- "notify::listen-notifications",
- G_CALLBACK (camel_o365_store_listen_notifications_cb),
- o365_store);
+ if (cnc) {
+ CamelSession *session;
- g_signal_connect_swapped (
- o365_settings,
- "notify::check-all",
- G_CALLBACK (camel_o365_store_check_all_cb),
- o365_store);*/
+ session = camel_service_ref_session (service);
+
+ success = camel_session_authenticate_sync (session, service, "Office365", cancellable, error);
+
+ g_clear_object (&session);
+ g_clear_object (&cnc);
+ } else {
+ g_set_error_literal (error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, _("Failed
to create connection"));
+ }
return success;
}
@@ -221,12 +221,22 @@ o365_store_disconnect_sync (CamelService *service,
GCancellable *cancellable,
GError **error)
{
- /*CamelO365Store *o365_store = CAMEL_O365_STORE (service);
+ CamelO365Store *o365_store = CAMEL_O365_STORE (service);
+ EO365Connection *cnc;
+ gboolean success = TRUE;
- g_mutex_lock (&o365_store->priv->connection_lock);
- o365_store_unset_connection_locked (o365_store, TRUE);
- g_mutex_unlock (&o365_store->priv->connection_lock);*/
+ cnc = o365_store_ref_connection (o365_store);
+
+ if (cnc) {
+ success = e_o365_connection_disconnect_sync (cnc, cancellable, error);
+
+ g_clear_object (&cnc);
+ }
+ if (!success)
+ return FALSE;
+
+ /* Chain up to parent's method. */
return CAMEL_SERVICE_CLASS (camel_o365_store_parent_class)->disconnect_sync (service, clean,
cancellable, error);
}
@@ -237,11 +247,29 @@ o365_store_authenticate_sync (CamelService *service,
GError **error)
{
CamelAuthenticationResult result;
- /*CamelO365Store *o365_store;
-
- o365_store = CAMEL_O365_STORE (service);*/
+ EO365Connection *cnc;
+
+ cnc = o365_store_ref_connection (CAMEL_O365_STORE (service));
+
+ if (!cnc)
+ return CAMEL_AUTHENTICATION_ERROR;
+
+ switch (e_o365_connection_authenticate_sync (cnc, cancellable, error)) {
+ case E_SOURCE_AUTHENTICATION_ERROR:
+ case E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED:
+ default:
+ result = CAMEL_AUTHENTICATION_ERROR;
+ break;
+ case E_SOURCE_AUTHENTICATION_ACCEPTED:
+ result = CAMEL_AUTHENTICATION_ACCEPTED;
+ break;
+ case E_SOURCE_AUTHENTICATION_REJECTED:
+ case E_SOURCE_AUTHENTICATION_REQUIRED:
+ result = CAMEL_AUTHENTICATION_REJECTED;
+ break;
+ }
- result = CAMEL_AUTHENTICATION_ERROR;
+ g_clear_object (&cnc);
return result;
}
@@ -288,10 +316,296 @@ camel_o365_store_maybe_disconnect (CamelO365Store *store,
#endif
}
+static void
+o365_store_save_summary_locked (CamelO365StoreSummary *summary,
+ const gchar *where)
+{
+ GError *error = NULL;
+
+ if (!camel_o365_store_summary_save (summary, &error))
+ g_warning ("%s: Failed to save store summary: %s", where, error ? error->message : "Unknown
error");
+
+ g_clear_error (&error);
+}
+
+typedef struct _FolderRenamedData {
+ gchar *id;
+ gchar *old_name;
+} FolderRenamedData;
+
+static FolderRenamedData *
+folder_renamed_data_new (gchar *id,
+ gchar *old_name)
+{
+ FolderRenamedData *frd;
+
+ frd = g_slice_new (FolderRenamedData);
+ frd->id = id;
+ frd->old_name = old_name;
+
+ return frd;
+}
+
+static void
+folder_renamed_data_free (gpointer ptr)
+{
+ FolderRenamedData *frd = ptr;
+
+ if (frd) {
+ g_free (frd->id);
+ g_free (frd->old_name);
+ g_slice_free (FolderRenamedData, frd);
+ }
+}
+
+typedef struct _FoldersDeltaData {
+ CamelO365Store *o365_store;
+ GSList *added_ids; /* gchar *, folder ids */
+ GSList *renamed_data; /* FolderRenamedData * */
+ GSList *removed_fis; /* CamelFolderInfo * */
+} FoldersDeltaData;
+
+static gboolean
+camel_o365_got_folders_delta_cb (EO365Connection *cnc,
+ const GSList *results, /* JsonObject * - the returned objects from the
server */
+ gpointer user_data, /* expects GSList **, aka pointer to a GSList *, where
it copies the 'results' */
+ GCancellable *cancellable,
+ GError **error)
+{
+ FoldersDeltaData *fdd = user_data;
+ GSList *link;
+
+ g_return_val_if_fail (fdd, FALSE);
+
+ LOCK (fdd->o365_store);
+
+ for (link = (GSList *) results; link; link = g_slist_next (link)) {
+ JsonObject *object = link->data;
+ const gchar *id = e_o365_mail_folder_get_id (object);
+
+ if (e_o365_delta_is_removed_object (object)) {
+ CamelFolderInfo *info;
+
+ info = camel_o365_store_summary_build_folder_info_for_id
(fdd->o365_store->priv->summary, id);
+
+ if (info)
+ fdd->removed_fis = g_slist_prepend (fdd->removed_fis, info);
+
+ camel_o365_store_summary_remove_folder (fdd->o365_store->priv->summary, id);
+ } else {
+ gchar *old_full_name = NULL;
+ guint32 flags;
+
+ if (camel_o365_store_summary_has_folder (fdd->o365_store->priv->summary, id))
+ old_full_name = camel_o365_store_summary_dup_folder_full_name
(fdd->o365_store->priv->summary, id);
+
+ flags = e_o365_mail_folder_get_child_folder_count (object) ?
CAMEL_STORE_INFO_FOLDER_CHILDREN : CAMEL_STORE_INFO_FOLDER_NOCHILDREN;
+
+ camel_o365_store_summary_set_folder (fdd->o365_store->priv->summary, FALSE, id,
+ e_o365_mail_folder_get_parent_folder_id (object),
+ e_o365_mail_folder_get_display_name (object),
+ e_o365_mail_folder_get_total_item_count (object),
+ e_o365_mail_folder_get_unread_item_count (object),
+ flags, E_O365_FOLDER_KIND_MAIL, FALSE, FALSE);
+
+ if (old_full_name)
+ fdd->renamed_data = g_slist_prepend (fdd->renamed_data,
folder_renamed_data_new (g_strdup (id), old_full_name));
+ else
+ fdd->added_ids = g_slist_prepend (fdd->added_ids, g_strdup (id));
+ }
+ }
+
+ UNLOCK (fdd->o365_store);
+
+ return TRUE;
+}
+
+static CamelFolderInfo *
+o365_get_folder_info_sync (CamelStore *store,
+ const gchar *top,
+ guint32 flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelO365Store *o365_store;
+ CamelFolderInfo *fi;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (CAMEL_IS_O365_STORE (store), NULL);
+
+ o365_store = CAMEL_O365_STORE (store);
+
+ if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (o365_store))) {
+ gboolean refresh_online;
+
+ refresh_online = !(flags & CAMEL_STORE_FOLDER_INFO_FAST) ||
+ (flags & CAMEL_STORE_FOLDER_INFO_REFRESH) != 0;
+
+ if (!refresh_online) {
+ gchar *delta_link;
+
+ LOCK (o365_store);
+
+ delta_link = camel_o365_store_summary_dup_delta_link (o365_store->priv->summary);
+ refresh_online = !delta_link || !*delta_link;
+ g_free (delta_link);
+
+ UNLOCK (o365_store);
+ }
+
+ if (refresh_online) {
+ EO365Connection *cnc;
+
+ cnc = o365_store_ref_connection (o365_store);
+
+ if (cnc) {
+ FoldersDeltaData fdd;
+ gchar *old_delta_link, *new_delta_link = NULL;
+
+ LOCK (o365_store);
+
+ old_delta_link = camel_o365_store_summary_dup_delta_link
(o365_store->priv->summary);
+
+ UNLOCK (o365_store);
+
+ fdd.o365_store = o365_store;
+ fdd.added_ids = NULL;
+ fdd.renamed_data = NULL;
+ fdd.removed_fis = NULL;
+
+ success = e_o365_connection_get_mail_folders_delta_sync (cnc, NULL, NULL,
old_delta_link, 0,
+ camel_o365_got_folders_delta_cb, &fdd, &new_delta_link, cancellable,
error);
+
+ if (success) {
+ CamelSubscribable *subscribable = CAMEL_SUBSCRIBABLE (o365_store);
+ CamelFolderInfo *info;
+ GSList *link;
+
+ LOCK (o365_store);
+
+ camel_o365_store_summary_set_delta_link (o365_store->priv->summary,
new_delta_link);
+ o365_store_save_summary_locked (o365_store->priv->summary, G_STRFUNC);
+
+ fdd.added_ids = g_slist_reverse (fdd.added_ids);
+ fdd.renamed_data = g_slist_reverse (fdd.renamed_data);
+ fdd.removed_fis = g_slist_reverse (fdd.removed_fis);
+
+ if (fdd.added_ids || fdd.renamed_data || fdd.removed_fis)
+ camel_o365_store_summary_rebuild_hashes
(o365_store->priv->summary);
+
+ for (link = fdd.removed_fis; link; link = g_slist_next (link)) {
+ info = link->data;
+
+ camel_subscribable_folder_unsubscribed (subscribable, info);
+ camel_store_folder_deleted (store, info);
+ }
+
+ for (link = fdd.added_ids; link; link = g_slist_next (link)) {
+ const gchar *id = link->data;
+
+ info = camel_o365_store_summary_build_folder_info_for_id
(o365_store->priv->summary, id);
+
+ if (info) {
+ camel_store_folder_created (store, info);
+ camel_subscribable_folder_subscribed (subscribable,
info);
+ camel_folder_info_free (info);
+ }
+ }
+
+ for (link = fdd.renamed_data; link; link = g_slist_next (link)) {
+ const FolderRenamedData *frd = link->data;
+
+ info = camel_o365_store_summary_build_folder_info_for_id
(o365_store->priv->summary, frd->id);
+
+ if (info) {
+ camel_store_folder_renamed (store, frd->old_name,
info);
+ camel_folder_info_free (info);
+ }
+ }
+
+ UNLOCK (o365_store);
+ }
+
+ g_slist_free_full (fdd.added_ids, g_free);
+ g_slist_free_full (fdd.renamed_data, folder_renamed_data_free);
+ g_slist_free_full (fdd.removed_fis, (GDestroyNotify) camel_folder_info_free);
+
+ g_clear_object (&cnc);
+ g_free (old_delta_link);
+ g_free (new_delta_link);
+ }
+ }
+ }
+
+ if (success) {
+ LOCK (o365_store);
+
+ fi = camel_o365_store_summary_build_folder_info (o365_store->priv->summary, top, (flags &
CAMEL_STORE_FOLDER_INFO_RECURSIVE) != 0);
+
+ UNLOCK (o365_store);
+ } else {
+ fi = NULL;
+ }
+
+ return fi;
+}
+
+static void
+o365_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CONNECTABLE:
+ camel_network_service_set_connectable (
+ CAMEL_NETWORK_SERVICE (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+o365_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CONNECTABLE:
+ g_value_take_object (
+ value,
+ camel_network_service_ref_connectable (
+ CAMEL_NETWORK_SERVICE (object)));
+ return;
+ case PROP_HOST_REACHABLE:
+ g_value_set_boolean (
+ value,
+ camel_network_service_get_host_reachable (
+ CAMEL_NETWORK_SERVICE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
static void
o365_store_dispose (GObject *object)
{
- /*CamelO365Store *o365_store = CAMEL_O365_STORE (object);*/
+ CamelO365Store *o365_store = CAMEL_O365_STORE (object);
+
+ LOCK (o365_store);
+
+ if (o365_store->priv->summary) {
+ o365_store_save_summary_locked (o365_store->priv->summary, G_STRFUNC);
+ g_clear_object (&o365_store->priv->summary);
+ }
+
+ g_clear_object (&o365_store->priv->cnc);
+
+ UNLOCK (o365_store);
/* Chain up to parent's method. */
G_OBJECT_CLASS (camel_o365_store_parent_class)->dispose (object);
@@ -315,7 +629,7 @@ camel_o365_store_class_init (CamelO365StoreClass *class)
{
GObjectClass *object_class;
CamelServiceClass *service_class;
- /*CamelStoreClass *store_class;*/
+ CamelStoreClass *store_class;
object_class = G_OBJECT_CLASS (class);
object_class->set_property = o365_store_set_property;
@@ -342,13 +656,16 @@ camel_o365_store_class_init (CamelO365StoreClass *class)
service_class->connect_sync = o365_store_connect_sync;
service_class->disconnect_sync = o365_store_disconnect_sync;
service_class->authenticate_sync = o365_store_authenticate_sync;
-#if 0
+
store_class = CAMEL_STORE_CLASS (class);
+#if 0
store_class->get_folder_sync = o365_get_folder_sync;
store_class->create_folder_sync = o365_create_folder_sync;
store_class->delete_folder_sync = o365_delete_folder_sync;
store_class->rename_folder_sync = o365_rename_folder_sync;
+#endif
store_class->get_folder_info_sync = o365_get_folder_info_sync;
+#if 0
store_class->initial_setup_sync = o365_initial_setup_sync;
store_class->get_trash_folder_sync = o365_get_trash_folder_sync;
store_class->get_junk_folder_sync = o365_get_junk_folder_sync;
diff --git a/src/Office365/camel/camel-o365-utils.c b/src/Office365/camel/camel-o365-utils.c
new file mode 100644
index 00000000..0d96872f
--- /dev/null
+++ b/src/Office365/camel/camel-o365-utils.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-ews-config.h"
+
+#include <libemail-engine/libemail-engine.h>
+
+#include "camel-o365-utils.h"
+
+/* Unref with g_object_unref() when done with it */
+static ESource *
+camel_o365_utils_ref_corresponding_source (CamelService *service,
+ GCancellable *cancellable)
+{
+ ESourceRegistry *registry = NULL;
+ CamelSession *session;
+ ESource *source = NULL;
+
+ g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
+
+ session = camel_service_ref_session (service);
+ if (E_IS_MAIL_SESSION (session)) {
+ registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
+ if (registry)
+ g_object_ref (registry);
+ }
+
+ g_clear_object (&session);
+
+ if (!registry)
+ registry = e_source_registry_new_sync (cancellable, NULL);
+
+ if (registry) {
+ source = e_source_registry_ref_source (registry, camel_service_get_uid (service));
+
+ if (source) {
+ ESource *parent;
+
+ parent = e_source_registry_find_extension (registry, source,
E_SOURCE_EXTENSION_COLLECTION);
+
+ g_clear_object (&source);
+ source = parent;
+ }
+ }
+
+ g_clear_object (®istry);
+
+ return source;
+}
+
+EO365Connection *
+camel_o365_utils_new_connection (CamelService *service,
+ GCancellable *cancellable)
+{
+ CamelSettings *settings;
+ EO365Connection *cnc;
+ ESource *source;
+
+ g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
+
+ source = camel_o365_utils_ref_corresponding_source (service, cancellable);
+
+ if (!source)
+ return NULL;
+
+ settings = camel_service_ref_settings (service);
+
+ cnc = e_o365_connection_new (source, CAMEL_O365_SETTINGS (settings));
+
+ e_binding_bind_property (
+ service, "proxy-resolver",
+ cnc, "proxy-resolver",
+ G_BINDING_SYNC_CREATE);
+
+ g_clear_object (&settings);
+ g_clear_object (&source);
+
+ return cnc;
+}
diff --git a/src/Office365/camel/camel-o365-utils.h b/src/Office365/camel/camel-o365-utils.h
new file mode 100644
index 00000000..dfca2254
--- /dev/null
+++ b/src/Office365/camel/camel-o365-utils.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2020 Red Hat (www.redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CAMEL_O365_UTILS_H
+#define CAMEL_O365_UTILS_H
+
+#include <camel/camel.h>
+
+#include "common/e-o365-connection.h"
+
+EO365Connection *
+ camel_o365_utils_new_connection (CamelService *service,
+ GCancellable *cancellable);
+
+#endif /* CAMEL_O365_UTILS_H */
diff --git a/src/Office365/common/e-o365-connection.c b/src/Office365/common/e-o365-connection.c
index ddb731b0..ca1fc975 100644
--- a/src/Office365/common/e-o365-connection.c
+++ b/src/Office365/common/e-o365-connection.c
@@ -45,6 +45,10 @@ struct _EO365ConnectionPrivate {
GProxyResolver *proxy_resolver;
ESoupAuthBearer *bearer_auth;
+ gboolean ssl_info_set;
+ gchar *ssl_certificate_pem;
+ GTlsCertificateFlags ssl_certificate_errors;
+
gchar *hash_key; /* in the opened connections hash */
/* How many microseconds to wait, until can execute a new request.
@@ -489,6 +493,7 @@ o365_connection_finalize (GObject *object)
EO365Connection *cnc = E_O365_CONNECTION (object);
g_rec_mutex_clear (&cnc->priv->property_lock);
+ g_clear_pointer (&cnc->priv->ssl_certificate_pem, g_free);
g_free (cnc->priv->hash_key);
/* Chain up to parent's method. */
@@ -827,6 +832,35 @@ o365_connection_request_cancelled_cb (GCancellable *cancellable,
e_flag_set (flag);
}
+static void
+o365_connection_extract_ssl_data (EO365Connection *cnc,
+ SoupMessage *message)
+{
+ GTlsCertificate *certificate = NULL;
+
+ g_return_if_fail (E_IS_O365_CONNECTION (cnc));
+ g_return_if_fail (SOUP_IS_MESSAGE (message));
+
+ LOCK (cnc);
+
+ g_clear_pointer (&cnc->priv->ssl_certificate_pem, g_free);
+ cnc->priv->ssl_info_set = FALSE;
+
+ g_object_get (G_OBJECT (message),
+ "tls-certificate", &certificate,
+ "tls-errors", &cnc->priv->ssl_certificate_errors,
+ NULL);
+
+ if (certificate) {
+ g_object_get (certificate, "certificate-pem", &cnc->priv->ssl_certificate_pem, NULL);
+ cnc->priv->ssl_info_set = TRUE;
+
+ g_object_unref (certificate);
+ }
+
+ UNLOCK (cnc);
+}
+
/* An example error response:
{
@@ -863,8 +897,10 @@ o365_connection_extract_error (JsonNode *node,
if (!code && !message)
return FALSE;
- if (!status_code || !SOUP_STATUS_IS_SUCCESSFUL (status_code))
+ if (!status_code || SOUP_STATUS_IS_SUCCESSFUL (status_code))
status_code = SOUP_STATUS_MALFORMED;
+ else if (g_strcmp0 (code, "ErrorInvalidUser") == 0)
+ status_code = SOUP_STATUS_UNAUTHORIZED;
if (code && message)
g_set_error (error, SOUP_HTTP_ERROR, status_code, "%s: %s", code, message);
@@ -974,6 +1010,10 @@ o365_connection_send_request_sync (EO365Connection *cnc,
soup_session = cnc->priv->soup_session ? g_object_ref (cnc->priv->soup_session) : NULL;
+ g_clear_pointer (&cnc->priv->ssl_certificate_pem, g_free);
+ cnc->priv->ssl_certificate_errors = 0;
+ cnc->priv->ssl_info_set = FALSE;
+
UNLOCK (cnc);
if (soup_session &&
@@ -992,6 +1032,8 @@ o365_connection_send_request_sync (EO365Connection *cnc,
/*
https://docs.microsoft.com/en-us/graph/best-practices-concept#handling-expected-errors */
message->status_code == SOUP_STATUS_SERVICE_UNAVAILABLE) {
need_retry = TRUE;
+ } else if (message->status_code == SOUP_STATUS_SSL_FAILED) {
+ o365_connection_extract_ssl_data (cnc, message);
}
if (need_retry) {
@@ -1204,6 +1246,8 @@ e_o365_construct_uri (EO365Connection *cnc,
if (*name && value) {
g_string_append_c (uri, first_param ? '?' : '&');
+ first_param = FALSE;
+
g_string_append (uri, name);
g_string_append_c (uri, '=');
@@ -1227,6 +1271,9 @@ e_o365_construct_uri (EO365Connection *cnc,
}
typedef struct _EO365ResponseData {
+ EO365ConnectionCallFunc func;
+ gpointer func_user_data;
+ gboolean read_only_once; /* To be able to just try authentication */
GSList **out_items; /* JsonObject * */
gchar **out_delta_link; /* set only if available and not NULL */
} EO365ResponseData;
@@ -1245,6 +1292,8 @@ e_o365_read_valued_response_cb (EO365Connection *cnc,
JsonObject *object;
JsonArray *value;
const gchar *delta_link;
+ GSList *items = NULL;
+ gboolean can_continue = TRUE;
guint ii, len;
g_return_val_if_fail (response_data != NULL, FALSE);
@@ -1254,7 +1303,8 @@ e_o365_read_valued_response_cb (EO365Connection *cnc,
object = json_node_get_object (node);
g_return_val_if_fail (object != NULL, FALSE);
- *out_next_link = g_strdup (e_o365_json_get_string_member (object, "@odata.nextLink", NULL));
+ if (!response_data->read_only_once)
+ *out_next_link = g_strdup (e_o365_json_get_string_member (object, "@odata.nextLink", NULL));
delta_link = e_o365_json_get_string_member (object, "@odata.deltaLink", NULL);
@@ -1274,11 +1324,160 @@ e_o365_read_valued_response_cb (EO365Connection *cnc,
if (JSON_NODE_HOLDS_OBJECT (elem)) {
JsonObject *elem_object = json_node_get_object (elem);
- if (elem_object)
- *response_data->out_items = g_slist_prepend (*response_data->out_items,
json_object_ref (elem_object));
+ if (elem_object) {
+ if (response_data->out_items)
+ *response_data->out_items = g_slist_prepend
(*response_data->out_items, json_object_ref (elem_object));
+ else
+ items = g_slist_prepend (items, json_object_ref (elem_object));
+ }
}
}
+ if (response_data->func)
+ can_continue = response_data->func (cnc, items, response_data->func_user_data, cancellable,
error);
+
+ g_slist_free_full (items, (GDestroyNotify) json_object_unref);
+
+ return can_continue;
+}
+
+gboolean
+e_o365_connection_get_ssl_error_details (EO365Connection *cnc,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors)
+{
+ g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (out_certificate_pem != NULL, FALSE);
+ g_return_val_if_fail (out_certificate_errors != NULL, FALSE);
+
+ LOCK (cnc);
+
+ if (!cnc->priv->ssl_info_set) {
+ UNLOCK (cnc);
+ return FALSE;
+ }
+
+ *out_certificate_pem = g_strdup (cnc->priv->ssl_certificate_pem);
+ *out_certificate_errors = cnc->priv->ssl_certificate_errors;
+
+ UNLOCK (cnc);
+
+ return TRUE;
+}
+
+ESourceAuthenticationResult
+e_o365_connection_authenticate_sync (EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESourceAuthenticationResult result = E_SOURCE_AUTHENTICATION_ERROR;
+ EO365ResponseData rd;
+ SoupMessage *message;
+ gchar *uri;
+ gboolean success;
+ GSList *folders = NULL;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), result);
+
+ /* Just pick an inexpensive operation */
+ uri = e_o365_construct_uri (cnc, TRUE, NULL, E_O365_API_V1_0, NULL,
+ "mailFolders",
+ NULL,
+ "$select", "displayName",
+ "$top", "1",
+ NULL);
+
+ message = soup_message_new (SOUP_METHOD_GET, uri);
+
+ if (!message) {
+ g_set_error (error, SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, _("Malformed URI: ā%sā"), uri);
+ g_free (uri);
+
+ return FALSE;
+ }
+
+ g_free (uri);
+
+ memset (&rd, 0, sizeof (EO365ResponseData));
+
+ rd.read_only_once = TRUE;
+ rd.out_items = &folders;
+
+ success = o365_connection_send_request_sync (cnc, message, e_o365_read_valued_response_cb, &rd,
cancellable, &local_error);
+
+ if (success) {
+ result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+ } else {
+ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
+ local_error->domain = G_IO_ERROR;
+ local_error->code = G_IO_ERROR_CANCELLED;
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+ ESoupAuthBearer *bearer;
+
+ bearer = e_o365_connection_ref_bearer_auth (cnc);
+
+ if (bearer)
+ result = E_SOURCE_AUTHENTICATION_REJECTED;
+ else
+ result = E_SOURCE_AUTHENTICATION_REQUIRED;
+
+ g_clear_object (&bearer);
+ g_clear_error (&local_error);
+ }
+
+ if (local_error) {
+ g_propagate_error (error, local_error);
+ local_error = NULL;
+ }
+ }
+
+ g_slist_free_full (folders, (GDestroyNotify) json_object_unref);
+ g_clear_object (&message);
+ g_clear_error (&local_error);
+
+ return result;
+}
+
+gboolean
+e_o365_connection_disconnect_sync (EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+
+ LOCK (cnc);
+
+ soup_session_abort (cnc->priv->soup_session);
+
+ UNLOCK (cnc);
+
+ return TRUE;
+}
+
+/* This can be used as a EO365ConnectionCallFunc function, it only
+ copies items of 'results' into 'user_data', which is supposed
+ to be a pointer to a GSList *. */
+gboolean
+e_o365_connection_call_gather_into_slist (EO365Connection *cnc,
+ const GSList *results, /* JsonObject * - the returned objects from
the server */
+ gpointer user_data, /* expects GSList **, aka pointer to a GSList
*, where it copies the 'results' */
+ GCancellable *cancellable,
+ GError **error)
+{
+ GSList **out_results = user_data, *link;
+
+ g_return_val_if_fail (out_results != NULL, FALSE);
+
+ for (link = (GSList *) results; link; link = g_slist_next (link)) {
+ JsonObject *obj = link->data;
+
+ if (obj)
+ *out_results = g_slist_prepend (*out_results, json_object_ref (obj));
+ }
+
return TRUE;
}
@@ -1333,8 +1532,9 @@ e_o365_connection_get_mail_folders_delta_sync (EO365Connection *cnc,
const gchar *select, /* fields to select, nullable */
const gchar *delta_link, /* previous delta link */
guint max_page_size, /* 0 for default by the server */
+ EO365ConnectionCallFunc func, /* function to call with each
result set */
+ gpointer func_user_data, /* user data passed into the 'func' */
gchar **out_delta_link,
- GSList **out_folders, /* JsonObject * - the returned
mailFolder objects */
GCancellable *cancellable,
GError **error)
{
@@ -1344,7 +1544,7 @@ e_o365_connection_get_mail_folders_delta_sync (EO365Connection *cnc,
g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
g_return_val_if_fail (out_delta_link != NULL, FALSE);
- g_return_val_if_fail (out_folders != NULL, FALSE);
+ g_return_val_if_fail (func != NULL, FALSE);
if (delta_link)
message = soup_message_new (SOUP_METHOD_GET, delta_link);
@@ -1382,7 +1582,8 @@ e_o365_connection_get_mail_folders_delta_sync (EO365Connection *cnc,
memset (&rd, 0, sizeof (EO365ResponseData));
- rd.out_items = out_folders;
+ rd.func = func;
+ rd.func_user_data = func_user_data;
rd.out_delta_link = out_delta_link;
success = o365_connection_send_request_sync (cnc, message, e_o365_read_valued_response_cb, &rd,
cancellable, error);
diff --git a/src/Office365/common/e-o365-connection.h b/src/Office365/common/e-o365-connection.h
index a21a8558..aeba478f 100644
--- a/src/Office365/common/e-o365-connection.h
+++ b/src/Office365/common/e-o365-connection.h
@@ -51,6 +51,13 @@ typedef struct _EO365Connection EO365Connection;
typedef struct _EO365ConnectionClass EO365ConnectionClass;
typedef struct _EO365ConnectionPrivate EO365ConnectionPrivate;
+/* Returns whether can continue */
+typedef gboolean (* EO365ConnectionCallFunc) (EO365Connection *cnc,
+ const GSList *results, /* JsonObject * - the returned
objects from the server */
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error);
+
struct _EO365Connection {
GObject parent;
EO365ConnectionPrivate *priv;
@@ -94,6 +101,25 @@ ESoupAuthBearer *
void e_o365_connection_set_bearer_auth
(EO365Connection *cnc,
ESoupAuthBearer *bearer_auth);
+gboolean e_o365_connection_get_ssl_error_details
+ (EO365Connection *cnc,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors);
+ESourceAuthenticationResult
+ e_o365_connection_authenticate_sync
+ (EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error);
+gboolean e_o365_connection_disconnect_sync
+ (EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error);
+gboolean e_o365_connection_call_gather_into_slist
+ (EO365Connection *cnc,
+ const GSList *results, /* JsonObject * - the returned
objects from the server */
+ gpointer user_data, /* expects GSList **, aka pointer to a
GSList *, where it copies the 'results' */
+ GCancellable *cancellable,
+ GError **error);
gboolean e_o365_connection_list_folders_sync
(EO365Connection *cnc,
const gchar *user_override, /* for which user, NULL to use
the account user */
@@ -108,8 +134,9 @@ gboolean e_o365_connection_get_mail_folders_delta_sync
const gchar *select, /* fields to select, nullable */
const gchar *delta_link, /* previous delta link */
guint max_page_size, /* 0 for default by the server */
+ EO365ConnectionCallFunc func, /* function to call with each
result set */
+ gpointer func_user_data, /* user data passed into the 'func'
*/
gchar **out_delta_link,
- GSList **out_folders, /* JsonObject * - the returned
mailFolder objects */
GCancellable *cancellable,
GError **error);
diff --git a/src/Office365/common/e-o365-enums.h b/src/Office365/common/e-o365-enums.h
index b2718c74..130a289c 100644
--- a/src/Office365/common/e-o365-enums.h
+++ b/src/Office365/common/e-o365-enums.h
@@ -24,7 +24,7 @@ G_BEGIN_DECLS
typedef enum {
E_O365_FOLDER_KIND_UNKNOWN,
- E_O365_FOLDER_KIND_MAILBOX,
+ E_O365_FOLDER_KIND_MAIL,
E_O365_FOLDER_KIND_CALENDAR,
E_O365_FOLDER_KIND_CONTACTS,
E_O365_FOLDER_KIND_SEARCH,
diff --git a/src/Office365/evolution/e-mail-config-o365-backend.c
b/src/Office365/evolution/e-mail-config-o365-backend.c
index 7faba8c0..eedad28a 100644
--- a/src/Office365/evolution/e-mail-config-o365-backend.c
+++ b/src/Office365/evolution/e-mail-config-o365-backend.c
@@ -103,7 +103,7 @@ test_clicked_cb (GtkButton *button,
g_return_if_fail (cnc != NULL);
//success = e_o365_connection_list_folders_sync (cnc, NULL, NULL, NULL, &folders, NULL, &error);
- success = e_o365_connection_get_mail_folders_delta_sync (cnc, NULL, NULL, delta_link, 0,
&new_delta_link, &folders, NULL, &error);
+ success = e_o365_connection_get_mail_folders_delta_sync (cnc, NULL, NULL, delta_link, 0,
e_o365_connection_call_gather_into_slist, &folders, &new_delta_link, NULL, &error);
if (success) {
g_free (delta_link);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]