[evolution-ews] Bug 777021 - Use server-side search for 'body-contains' when online



commit 19d3783ed9a90b5e6bf8f33a2aca7099ba3ba6a3
Author: Milan Crha <mcrha redhat com>
Date:   Fri Jan 13 14:06:11 2017 +0100

    Bug 777021 - Use server-side search for 'body-contains' when online

 src/addressbook/e-book-backend-ews.c    |    7 +-
 src/camel/CMakeLists.txt                |    2 +
 src/camel/camel-ews-folder.c            |   24 ++-
 src/camel/camel-ews-search.c            |  534 ++++++++++++++++++++++++++
 src/camel/camel-ews-search.h            |   83 ++++
 src/server/e-ews-connection.c           |   47 +++-
 src/server/e-ews-connection.h           |    2 +
 src/server/e-ews-query-to-restriction.c |  621 ++++++++++++++++++-------------
 src/server/e-ews-query-to-restriction.h |   12 +-
 9 files changed, 1073 insertions(+), 259 deletions(-)
---
diff --git a/src/addressbook/e-book-backend-ews.c b/src/addressbook/e-book-backend-ews.c
index 71aa14e..7f0aab3 100644
--- a/src/addressbook/e-book-backend-ews.c
+++ b/src/addressbook/e-book-backend-ews.c
@@ -2041,17 +2041,20 @@ e_book_backend_ews_get_contact_list (EBookBackend *backend,
                GSList *items = NULL;
                EwsFolderId *fid = NULL;
                gboolean includes_last_item;
+               gboolean any_applicable;
 
                fid = g_new0 (EwsFolderId, 1);
                fid->id = g_strdup (priv->folder_id);
                fid->is_distinguished_id = FALSE;
 
+               any_applicable = e_ews_query_check_applicable (query, E_EWS_FOLDER_TYPE_CONTACTS);
+
                e_ews_connection_find_folder_items_sync (
                        priv->cnc, EWS_PRIORITY_MEDIUM,
-                       fid, "IdOnly", NULL, NULL, query,
+                       fid, "IdOnly", NULL, NULL, query, NULL,
                        E_EWS_FOLDER_TYPE_CONTACTS,
                        &includes_last_item,
-                       &items, (EwsConvertQueryCallback) (e_ews_query_to_restriction),
+                       &items, any_applicable ? e_ews_query_to_restriction : NULL,
                        cancellable, &error);
 
                /*we have got Id for items lets fetch them using getitem operation*/
diff --git a/src/camel/CMakeLists.txt b/src/camel/CMakeLists.txt
index 817eef1..469eb0f 100644
--- a/src/camel/CMakeLists.txt
+++ b/src/camel/CMakeLists.txt
@@ -19,6 +19,8 @@ set(SOURCES
        camel-ews-message-info.c
        camel-ews-message-info.h
        camel-ews-private.h
+       camel-ews-search.c
+       camel-ews-search.h
        camel-ews-store-summary.c
        camel-ews-store-summary.h
        camel-ews-store.c
diff --git a/src/camel/camel-ews-folder.c b/src/camel/camel-ews-folder.c
index f1a0f53..a7d4c57 100644
--- a/src/camel/camel-ews-folder.c
+++ b/src/camel/camel-ews-folder.c
@@ -50,6 +50,7 @@ which needs to be better organized via functions */
 
 #include "camel-ews-folder.h"
 #include "camel-ews-private.h"
+#include "camel-ews-search.h"
 #include "camel-ews-store.h"
 #include "camel-ews-summary.h"
 #include "camel-ews-utils.h"
@@ -835,6 +836,7 @@ ews_folder_search_by_expression (CamelFolder *folder,
                                  GError **error)
 {
        CamelEwsFolder *ews_folder;
+       CamelEwsSearch *ews_search;
        CamelEwsFolderPrivate *priv;
        GPtrArray *matches;
 
@@ -843,9 +845,15 @@ ews_folder_search_by_expression (CamelFolder *folder,
 
        g_mutex_lock (&priv->search_lock);
 
+       ews_search = CAMEL_EWS_SEARCH (ews_folder->search);
+
        camel_folder_search_set_folder (ews_folder->search, folder);
+       camel_ews_search_set_cancellable_and_error (ews_search, cancellable, error);
+
        matches = camel_folder_search_search (ews_folder->search, expression, NULL, cancellable, error);
 
+       camel_ews_search_set_cancellable_and_error (ews_search, NULL, NULL);
+
        g_mutex_unlock (&priv->search_lock);
 
        return matches;
@@ -858,6 +866,7 @@ ews_folder_count_by_expression (CamelFolder *folder,
                                 GError **error)
 {
        CamelEwsFolder *ews_folder;
+       CamelEwsSearch *ews_search;
        CamelEwsFolderPrivate *priv;
        guint32 matches;
 
@@ -866,9 +875,15 @@ ews_folder_count_by_expression (CamelFolder *folder,
 
        g_mutex_lock (&priv->search_lock);
 
+       ews_search = CAMEL_EWS_SEARCH (ews_folder->search);
+
        camel_folder_search_set_folder (ews_folder->search, folder);
+       camel_ews_search_set_cancellable_and_error (ews_search, cancellable, error);
+
        matches = camel_folder_search_count (ews_folder->search, expression, cancellable, error);
 
+       camel_ews_search_set_cancellable_and_error (ews_search, NULL, NULL);
+
        g_mutex_unlock (&priv->search_lock);
 
        return matches;
@@ -882,6 +897,7 @@ ews_folder_search_by_uids (CamelFolder *folder,
                            GError **error)
 {
        CamelEwsFolder *ews_folder;
+       CamelEwsSearch *ews_search;
        CamelEwsFolderPrivate *priv;
        GPtrArray *matches;
 
@@ -893,9 +909,15 @@ ews_folder_search_by_uids (CamelFolder *folder,
 
        g_mutex_lock (&priv->search_lock);
 
+       ews_search = CAMEL_EWS_SEARCH (ews_folder->search);
+
        camel_folder_search_set_folder (ews_folder->search, folder);
+       camel_ews_search_set_cancellable_and_error (ews_search, cancellable, error);
+
        matches = camel_folder_search_search (ews_folder->search, expression, uids, cancellable, error);
 
+       camel_ews_search_set_cancellable_and_error (ews_search, NULL, NULL);
+
        g_mutex_unlock (&priv->search_lock);
 
        return matches;
@@ -1541,7 +1563,7 @@ camel_ews_folder_new (CamelStore *store,
                g_object_unref (settings);
        }
 
-       ews_folder->search = camel_folder_search_new ();
+       ews_folder->search = camel_ews_search_new (CAMEL_EWS_STORE (store));
        if (!ews_folder->search) {
                g_object_unref (folder);
                return NULL;
diff --git a/src/camel/camel-ews-search.c b/src/camel/camel-ews-search.c
new file mode 100644
index 0000000..8f22817
--- /dev/null
+++ b/src/camel/camel-ews-search.c
@@ -0,0 +1,534 @@
+/*
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "evolution-ews-config.h"
+
+#include <string.h>
+#include <camel/camel.h>
+#include <camel/camel-search-private.h>
+#include <e-util/e-util.h>
+
+#include "server/e-ews-query-to-restriction.h"
+
+#include "camel-ews-folder.h"
+#include "camel-ews-search.h"
+
+#define CAMEL_EWS_SEARCH_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), CAMEL_TYPE_EWS_SEARCH, CamelEwsSearchPrivate))
+
+struct _CamelEwsSearchPrivate {
+       GWeakRef ews_store;
+       gint *local_data_search; /* not NULL, if testing whether all used headers are all locally available */
+
+       GCancellable *cancellable; /* not referenced */
+       GError **error; /* not referenced */
+};
+
+enum {
+       PROP_0,
+       PROP_STORE
+};
+
+G_DEFINE_TYPE (
+       CamelEwsSearch,
+       camel_ews_search,
+       CAMEL_TYPE_FOLDER_SEARCH)
+
+static void
+ews_search_set_property (GObject *object,
+                        guint property_id,
+                        const GValue *value,
+                        GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_STORE:
+                       camel_ews_search_set_store (
+                               CAMEL_EWS_SEARCH (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ews_search_get_property (GObject *object,
+                        guint property_id,
+                        GValue *value,
+                        GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_STORE:
+                       g_value_take_object (
+                               value,
+                               camel_ews_search_ref_store (
+                               CAMEL_EWS_SEARCH (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+ews_search_dispose (GObject *object)
+{
+       CamelEwsSearchPrivate *priv;
+
+       priv = CAMEL_EWS_SEARCH_GET_PRIVATE (object);
+
+       g_weak_ref_set (&priv->ews_store, NULL);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (camel_ews_search_parent_class)->dispose (object);
+}
+
+static void
+ews_search_finalize (GObject *object)
+{
+       CamelEwsSearchPrivate *priv;
+
+       priv = CAMEL_EWS_SEARCH_GET_PRIVATE (object);
+
+       g_weak_ref_clear (&priv->ews_store);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (camel_ews_search_parent_class)->finalize (object);
+}
+
+static CamelSExpResult *
+ews_search_result_match_all (CamelSExp *sexp,
+                            CamelFolderSearch *search)
+{
+       CamelSExpResult *result;
+
+       g_return_val_if_fail (search != NULL, NULL);
+
+       if (camel_folder_search_get_current_message_info (search)) {
+               result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
+               result->value.boolean = TRUE;
+       } else {
+               GPtrArray *summary;
+               gint ii;
+
+               summary = camel_folder_search_get_summary (search);
+
+               result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_ARRAY_PTR);
+               result->value.ptrarray = g_ptr_array_new ();
+
+               for (ii = 0; summary && ii < summary->len; ii++) {
+                       g_ptr_array_add (
+                               result->value.ptrarray,
+                               (gpointer) summary->pdata[ii]);
+               }
+       }
+
+       return result;
+}
+
+static CamelSExpResult *
+ews_search_result_match_none (CamelSExp *sexp,
+                             CamelFolderSearch *search)
+{
+       CamelSExpResult *result;
+
+       g_return_val_if_fail (search != NULL, NULL);
+
+       if (camel_folder_search_get_current_message_info (search)) {
+               result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
+               result->value.boolean = FALSE;
+       } else {
+               result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_ARRAY_PTR);
+               result->value.ptrarray = g_ptr_array_new ();
+       }
+
+       return result;
+}
+
+static GPtrArray *
+ews_search_items_to_ptr_array (const GSList *items)
+{
+       GPtrArray *matches = NULL;
+       const GSList *link;
+
+       for (link = items; link; link = g_slist_next (link)) {
+               EEwsItem *item = link->data;
+               const EwsId *id;
+
+               if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR)
+                       continue;
+
+               id = e_ews_item_get_id (item);
+               if (!id || !id->id)
+                       continue;
+
+               if (!matches)
+                       matches = g_ptr_array_new ();
+
+               g_ptr_array_add (matches, (gpointer) camel_pstring_strdup (id->id));
+       }
+
+       return matches;
+}
+
+static CamelSExpResult *
+ews_search_process_criteria (CamelSExp *sexp,
+                            CamelFolderSearch *search,
+                            CamelEwsStore *ews_store,
+                            const GPtrArray *words)
+{
+       CamelSExpResult *result;
+       CamelEwsSearch *ews_search = CAMEL_EWS_SEARCH (search);
+       CamelEwsFolder *ews_folder;
+       GPtrArray *uids = NULL;
+       GError *local_error = NULL;
+
+       ews_folder = CAMEL_EWS_FOLDER (camel_folder_search_get_folder (search));
+
+       /* Sanity check. */
+       g_return_val_if_fail (ews_folder != NULL, NULL);
+
+       if (ews_folder != NULL) {
+               EEwsConnection *connection = NULL;
+               gchar *folder_id = NULL;
+               gboolean can_search;
+
+               /* there should always be one, held by one of the callers of this function */
+               g_warn_if_fail (ews_store != NULL);
+
+               can_search = ews_store != NULL && words != NULL;
+
+               if (can_search) {
+                       folder_id = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary,
+                               camel_folder_get_full_name (CAMEL_FOLDER (ews_folder)));
+                       if (!folder_id)
+                               can_search = FALSE;
+               }
+
+               if (can_search) {
+                       connection = camel_ews_store_ref_connection (ews_store);
+                       if (!connection)
+                               can_search = FALSE;
+               }
+
+               if (can_search) {
+                       EwsFolderId *fid;
+                       GSList *found_items = NULL;
+                       gboolean includes_last_item = FALSE;
+                       GString *expression;
+                       guint ii;
+
+                       fid = e_ews_folder_id_new (folder_id, NULL, FALSE);
+                       expression = g_string_new ("");
+
+                       if (words->len >= 2)
+                               g_string_append (expression, "(and ");
+
+                       for (ii = 0; ii < words->len; ii++) {
+                               GString *word;
+
+                               word = e_str_replace_string (g_ptr_array_index (words, ii), "\"", "\\\"");
+
+                               g_string_append (expression, "(body-contains \"");
+                               g_string_append (expression, word->str);
+                               g_string_append (expression, "\")");
+
+                               g_string_free (word, TRUE);
+                       }
+
+                       /* Close the 'and' */
+                       if (words->len >= 2)
+                               g_string_append (expression, ")");
+
+                       if (e_ews_connection_find_folder_items_sync (
+                               connection, EWS_PRIORITY_MEDIUM,
+                               fid, "IdOnly", NULL, NULL, expression->str, NULL,
+                               E_EWS_FOLDER_TYPE_MAILBOX, &includes_last_item, &found_items,
+                               e_ews_query_to_restriction,
+                               ews_search->priv->cancellable, &local_error)) {
+
+                               uids = ews_search_items_to_ptr_array (found_items);
+                       }
+
+                       g_slist_free_full (found_items, g_object_unref);
+                       g_string_free (expression, TRUE);
+                       e_ews_folder_id_free (fid);
+               }
+
+               g_free (folder_id);
+       }
+
+       /* Sanity check. */
+       g_warn_if_fail (
+               ((uids != NULL) && (local_error == NULL)) ||
+               ((uids == NULL) && (local_error != NULL)));
+
+       if (local_error != NULL)
+               g_propagate_error (ews_search->priv->error, local_error);
+
+       if (!uids) {
+               /* Make like we've got an empty result */
+               uids = g_ptr_array_new ();
+       }
+
+       if (camel_folder_search_get_current_message_info (search)) {
+               result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
+               result->value.boolean = (uids && uids->len > 0);
+       } else {
+               result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_ARRAY_PTR);
+               result->value.ptrarray = g_ptr_array_ref (uids);
+       }
+
+       g_ptr_array_unref (uids);
+
+       return result;
+}
+
+static GPtrArray *
+ews_search_gather_words (CamelSExpResult **argv,
+                        gint from_index,
+                        gint argc)
+{
+       GPtrArray *ptrs;
+       GHashTable *words_hash;
+       GHashTableIter iter;
+       gpointer key, value;
+       gint ii, jj;
+
+       g_return_val_if_fail (argv != 0, NULL);
+
+       words_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+       for (ii = from_index; ii < argc; ii++) {
+               struct _camel_search_words *words;
+
+               if (argv[ii]->type != CAMEL_SEXP_RES_STRING)
+                       continue;
+
+               /* Handle multiple search words within a single term. */
+               words = camel_search_words_split ((const guchar *) argv[ii]->value.string);
+
+               for (jj = 0; jj < words->len; jj++) {
+                       const gchar *word = words->words[jj]->word;
+
+                       g_hash_table_insert (words_hash, g_strdup (word), NULL);
+               }
+
+               camel_search_words_free (words);
+       }
+
+       ptrs = g_ptr_array_new_full (g_hash_table_size (words_hash), g_free);
+
+       g_hash_table_iter_init (&iter, words_hash);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               g_ptr_array_add (ptrs, g_strdup (key));
+       }
+
+       if (ptrs->len == 0) {
+               g_ptr_array_free (ptrs, TRUE);
+               ptrs = NULL;
+       }
+
+       g_hash_table_destroy (words_hash);
+
+       return ptrs;
+}
+
+static CamelSExpResult *
+ews_search_body_contains (CamelSExp *sexp,
+                         gint argc,
+                         CamelSExpResult **argv,
+                         CamelFolderSearch *search)
+{
+       CamelEwsSearch *ews_search = CAMEL_EWS_SEARCH (search);
+       CamelEwsStore *ews_store;
+       CamelSExpResult *result;
+       GPtrArray *words;
+
+       /* Always do body-search server-side */
+       if (ews_search->priv->local_data_search) {
+               *ews_search->priv->local_data_search = -1;
+               return ews_search_result_match_none (sexp, search);
+       }
+
+       /* Match everything if argv = [""] */
+       if (argc == 1 && argv[0]->value.string[0] == '\0')
+               return ews_search_result_match_all (sexp, search);
+
+       /* Match nothing if empty argv or empty summary. */
+       if (argc == 0 || camel_folder_search_get_summary_empty (search))
+               return ews_search_result_match_none (sexp, search);
+
+       ews_store = camel_ews_search_ref_store (CAMEL_EWS_SEARCH (search));
+
+       /* This will be NULL if we're offline. Search from cache. */
+       if (!ews_store) {
+               /* Chain up to parent's method. */
+               return CAMEL_FOLDER_SEARCH_CLASS (camel_ews_search_parent_class)->
+                       body_contains (sexp, argc, argv, search);
+       }
+
+       words = ews_search_gather_words (argv, 0, argc);
+
+       result = ews_search_process_criteria (sexp, search, ews_store, words);
+
+       g_ptr_array_free (words, TRUE);
+       g_object_unref (ews_store);
+
+       return result;
+}
+
+static void
+camel_ews_search_class_init (CamelEwsSearchClass *class)
+{
+       GObjectClass *object_class;
+       CamelFolderSearchClass *search_class;
+
+       g_type_class_add_private (class, sizeof (CamelEwsSearchPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = ews_search_set_property;
+       object_class->get_property = ews_search_get_property;
+       object_class->dispose = ews_search_dispose;
+       object_class->finalize = ews_search_finalize;
+
+       search_class = CAMEL_FOLDER_SEARCH_CLASS (class);
+       search_class->body_contains = ews_search_body_contains;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_STORE,
+               g_param_spec_object (
+                       "store",
+                       "EWS Store",
+                       "EWS Store for server-side searches",
+                       CAMEL_TYPE_EWS_STORE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+camel_ews_search_init (CamelEwsSearch *search)
+{
+       search->priv = CAMEL_EWS_SEARCH_GET_PRIVATE (search);
+       search->priv->local_data_search = NULL;
+
+       g_weak_ref_init (&search->priv->ews_store, NULL);
+}
+
+/**
+ * camel_ews_search_new:
+ * @ews_store: a #CamelEwsStore to which the search belongs
+ *
+ * Returns a new #CamelEwsSearch instance.
+ *
+ * Returns: a new #CamelEwsSearch
+ *
+ * Since: 3.24
+ **/
+CamelFolderSearch *
+camel_ews_search_new (CamelEwsStore *ews_store)
+{
+       g_return_val_if_fail (CAMEL_IS_EWS_STORE (ews_store), NULL);
+
+       return g_object_new (
+               CAMEL_TYPE_EWS_SEARCH,
+               "store", ews_store,
+               NULL);
+}
+
+/**
+ * camel_ews_search_ref_store:
+ * @search: a #CamelEwsSearch
+ *
+ * Returns a #CamelEwsStore to use for server-side searches,
+ * or %NULL when the store is offline.
+ *
+ * The returned #CamelEwsStore is referenced for thread-safety and
+ * must be unreferenced with g_object_unref() when finished with it.
+ *
+ * Returns: a #CamelEwsStore, or %NULL
+ *
+ * Since: 3.24
+ **/
+CamelEwsStore *
+camel_ews_search_ref_store (CamelEwsSearch *search)
+{
+       CamelEwsStore *ews_store;
+
+       g_return_val_if_fail (CAMEL_IS_EWS_SEARCH (search), NULL);
+
+       ews_store = g_weak_ref_get (&search->priv->ews_store);
+
+       if (ews_store && !camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store)))
+               g_clear_object (&ews_store);
+
+       return ews_store;
+}
+
+/**
+ * camel_ews_search_set_store:
+ * @search: a #CamelEwsSearch
+ * @ews_store: a #CamelEwsStore, or %NULL
+ *
+ * Sets a #CamelEwsStore to use for server-side searches. Generally
+ * this is set for the duration of a single search when online, and then
+ * reset to %NULL.
+ *
+ * Since: 3.24
+ **/
+void
+camel_ews_search_set_store (CamelEwsSearch *search,
+                           CamelEwsStore *ews_store)
+{
+       g_return_if_fail (CAMEL_IS_EWS_SEARCH (search));
+
+       if (ews_store != NULL)
+               g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store));
+
+       g_weak_ref_set (&search->priv->ews_store, ews_store);
+
+       g_object_notify (G_OBJECT (search), "store");
+}
+
+/**
+ * camel_ews_search_set_cancellable_and_error:
+ * @search: a #CamelEwsSearch
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Sets @cancellable and @error to use for server-side searches. This way
+ * the search can return accurate errors and be eventually cancelled by
+ * a user.
+ *
+ * Note: The caller is responsible to keep alive both @cancellable and @error
+ * for the whole run of the search and reset them both to NULL after
+ * the search is finished.
+ *
+ * Since: 3.24
+ **/
+void
+camel_ews_search_set_cancellable_and_error (CamelEwsSearch *search,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       g_return_if_fail (CAMEL_IS_EWS_SEARCH (search));
+
+       if (cancellable)
+               g_return_if_fail (G_IS_CANCELLABLE (cancellable));
+
+       search->priv->cancellable = cancellable;
+       search->priv->error = error;
+}
diff --git a/src/camel/camel-ews-search.h b/src/camel/camel-ews-search.h
new file mode 100644
index 0000000..1d8152e
--- /dev/null
+++ b/src/camel/camel-ews-search.h
@@ -0,0 +1,83 @@
+/*
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAMEL_EWS_SEARCH_H
+#define CAMEL_EWS_SEARCH_H
+
+#include <camel/camel.h>
+
+#include "camel-ews-store.h"
+
+/* Standard GObject macros */
+#define CAMEL_TYPE_EWS_SEARCH \
+       (camel_ews_search_get_type ())
+#define CAMEL_EWS_SEARCH(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), CAMEL_TYPE_EWS_SEARCH, CamelEwsSearch))
+#define CAMEL_EWS_SEARCH_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), CAMEL_TYPE_EWS_SEARCH, CamelEwsSearchClass))
+#define CAMEL_IS_EWS_SEARCH(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), CAMEL_TYPE_EWS_SEARCH))
+#define CAMEL_IS_EWS_SEARCH_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), CAMEL_TYPE_EWS_SEARCH))
+#define CAMEL_EWS_SEARCH_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), CAMEL_TYPE_EWS_SEARCH, CamelEwsSearchClass))
+
+G_BEGIN_DECLS
+
+typedef struct _CamelEwsSearch CamelEwsSearch;
+typedef struct _CamelEwsSearchClass CamelEwsSearchClass;
+typedef struct _CamelEwsSearchPrivate CamelEwsSearchPrivate;
+
+/**
+ * CamelEwsSearch:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.24
+ **/
+struct _CamelEwsSearch {
+       /*< private >*/
+       CamelFolderSearch parent;
+       CamelEwsSearchPrivate *priv;
+};
+
+struct _CamelEwsSearchClass {
+       CamelFolderSearchClass parent_class;
+
+       /* Padding for future expansion */
+       gpointer reserved[20];
+};
+
+GType          camel_ews_search_get_type       (void) G_GNUC_CONST;
+CamelFolderSearch *
+               camel_ews_search_new            (CamelEwsStore *ews_store);
+CamelEwsStore *
+               camel_ews_search_ref_store      (CamelEwsSearch *search);
+void           camel_ews_search_set_store      (CamelEwsSearch *search,
+                                                CamelEwsStore *ews_store);
+void           camel_ews_search_set_cancellable_and_error
+                                               (CamelEwsSearch *search,
+                                                GCancellable *cancellable,
+                                                GError **error);
+
+G_END_DECLS
+
+#endif /* CAMEL_EWS_SEARCH_H */
diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c
index 40a9fbe..23dc779 100644
--- a/src/server/e-ews-connection.c
+++ b/src/server/e-ews-connection.c
@@ -3914,6 +3914,27 @@ ews_append_folder_ids_to_msg (ESoapMessage *msg,
        }
 }
 
+static void
+ews_connection_write_only_ids_restriction (ESoapMessage *msg,
+                                          GPtrArray *only_ids)
+{
+       guint ii;
+
+       g_return_if_fail (E_IS_SOAP_MESSAGE (msg));
+       g_return_if_fail (only_ids && only_ids->len);
+
+       for (ii = 0; ii < only_ids->len; ii++) {
+               const gchar *itemid = g_ptr_array_index (only_ids, ii);
+
+               e_soap_message_start_element (msg, "IsEqualTo", NULL, NULL);
+               e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", 
"item:ItemId");
+               e_soap_message_start_element (msg, "FieldURIOrConstant", NULL, NULL);
+               e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", 
itemid);
+               e_soap_message_end_element (msg); /* FieldURIOrConstant */
+               e_soap_message_end_element (msg); /* IsEqualTo */
+       }
+}
+
 /**
  * e_ews_connection_find_folder_items:
  * @cnc: The EWS Connection
@@ -3923,6 +3944,7 @@ ews_append_folder_ids_to_msg (ESoapMessage *msg,
  * @add_props: Specify any additional properties to be fetched
  * @sort_order: Specific sorting order for items
  * @query: evo query based on which items will be fetched
+ * @only_ids: (element-type utf8) (nullable): a gchar * with item IDs, to check with only; can be %NULL
  * @type: type of folder
  * @convert_query_cb: a callback method to convert query to ews restiction
  * @cancellable: a GCancellable to monitor cancelled operations
@@ -3937,6 +3959,7 @@ e_ews_connection_find_folder_items (EEwsConnection *cnc,
                                     const EEwsAdditionalProps *add_props,
                                     EwsSortOrder *sort_order,
                                     const gchar *query,
+                                   GPtrArray *only_ids, /* element-type utf8 */
                                     EEwsFolderType type,
                                     EwsConvertQueryCallback convert_query_cb,
                                     GCancellable *cancellable,
@@ -3968,9 +3991,28 @@ e_ews_connection_find_folder_items (EEwsConnection *cnc,
        e_soap_message_end_element (msg);
 
        /*write restriction message based on query*/
-       if (convert_query_cb)
+       if (convert_query_cb) {
+               e_soap_message_start_element (msg, "Restriction", "messages", NULL);
+
+               if (only_ids && only_ids->len) {
+                       e_soap_message_start_element (msg, "And", "messages", NULL);
+                       e_soap_message_start_element (msg, "Or", "messages", NULL);
+                       ews_connection_write_only_ids_restriction (msg, only_ids);
+                       e_soap_message_end_element (msg); /* Or */
+               }
+
                convert_query_cb (msg, query, type);
 
+               if (only_ids && only_ids->len)
+                       e_soap_message_end_element (msg); /* And */
+
+               e_soap_message_end_element (msg); /* Restriction */
+       } else if (only_ids && only_ids->len) {
+               e_soap_message_start_element (msg, "Restriction", "messages", NULL);
+               ews_connection_write_only_ids_restriction (msg, only_ids);
+               e_soap_message_end_element (msg);
+       }
+
        if (sort_order)
                ews_write_sort_order_to_msg (msg, sort_order);
 
@@ -4037,6 +4079,7 @@ e_ews_connection_find_folder_items_sync (EEwsConnection *cnc,
                                          const EEwsAdditionalProps *add_props,
                                          EwsSortOrder *sort_order,
                                          const gchar *query,
+                                        GPtrArray *only_ids, /* element-type utf8 */
                                          EEwsFolderType type,
                                          gboolean *includes_last_item,
                                          GSList **items,
@@ -4055,7 +4098,7 @@ e_ews_connection_find_folder_items_sync (EEwsConnection *cnc,
        e_ews_connection_find_folder_items (
                cnc, pri, fid, default_props,
                add_props, sort_order, query,
-               type, convert_query_cb, NULL,
+               only_ids, type, convert_query_cb, NULL,
                e_async_closure_callback, closure);
 
        result = e_async_closure_wait (closure);
diff --git a/src/server/e-ews-connection.h b/src/server/e-ews-connection.h
index c8e35b6..a5a409a 100644
--- a/src/server/e-ews-connection.h
+++ b/src/server/e-ews-connection.h
@@ -496,6 +496,7 @@ void                e_ews_connection_find_folder_items
                                                 const EEwsAdditionalProps *add_props,
                                                 EwsSortOrder *sort_order,
                                                 const gchar *query,
+                                                GPtrArray *only_ids, /* element-type utf8 */
                                                 EEwsFolderType type,
                                                 EwsConvertQueryCallback convert_query_cb,
                                                 GCancellable *cancellable,
@@ -515,6 +516,7 @@ gboolean    e_ews_connection_find_folder_items_sync
                                                 const EEwsAdditionalProps *add_props,
                                                 EwsSortOrder *sort_order,
                                                 const gchar *query,
+                                                GPtrArray *only_ids, /* element-type utf8 */
                                                 EEwsFolderType type,
                                                 gboolean *includes_last_item,
                                                 GSList **items,
diff --git a/src/server/e-ews-query-to-restriction.c b/src/server/e-ews-query-to-restriction.c
index 1042c5f..d7bca3f 100644
--- a/src/server/e-ews-query-to-restriction.c
+++ b/src/server/e-ews-query-to-restriction.c
@@ -33,86 +33,6 @@
 
 #define d(x) x
 
-#define WRITE_CONTAINS_MESSAGE(msg, mode, compare, uri, val) \
-       G_STMT_START { \
-               e_soap_message_start_element (msg, "Contains", NULL, NULL); \
-               e_soap_message_add_attribute (msg, "ContainmentMode", mode, NULL, NULL); \
-               e_soap_message_add_attribute (msg, "ContainmentComparison", compare, NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", 
uri); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", 
val); \
-               e_soap_message_end_element (msg); \
-       } G_STMT_END
-
-#define WRITE_CONTAINS_MESSAGE_INDEXED(msg, mode, compare, uri, index, val) \
-       G_STMT_START { \
-               e_soap_message_start_element (msg, "Contains", NULL, NULL); \
-               e_soap_message_add_attribute (msg, "ContainmentMode", mode, NULL, NULL); \
-               e_soap_message_add_attribute (msg, "ContainmentComparison", compare, NULL, NULL); \
-               e_soap_message_start_element (msg, "IndexedFieldURI", NULL, NULL); \
-               e_soap_message_add_attribute (msg, "FieldURI", uri, NULL, NULL); \
-               e_soap_message_add_attribute (msg, "FieldIndex", index, NULL, NULL); \
-               e_soap_message_end_element (msg); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", 
val); \
-               e_soap_message_end_element (msg); \
-       } G_STMT_END
-
-#define WRITE_EXISTS_MESSAGE(msg, uri) \
-       G_STMT_START { \
-               e_soap_message_start_element (msg, "Exists", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", 
uri);\
-               e_soap_message_end_element (msg); \
-       } G_STMT_END
-
-#define WRITE_GREATER_THAN_OR_EQUAL_TO_MESSAGE(msg, uri, val) \
-       G_STMT_START { \
-               e_soap_message_start_element (msg, "IsGreaterThanOrEqualTo", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", 
uri); \
-               e_soap_message_start_element (msg, "FieldURIOrConstant", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", 
val); \
-               e_soap_message_end_element (msg); \
-               e_soap_message_end_element (msg); \
-       } G_STMT_END
-
-#define WRITE_LESS_THAN_OR_EQUAL_TO_MESSAGE(msg, uri, val) \
-       G_STMT_START { \
-               e_soap_message_start_element (msg, "IsLessThanOrEqualTo", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", 
uri); \
-               e_soap_message_start_element (msg, "FieldURIOrConstant", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", 
val); \
-               e_soap_message_end_element (msg); \
-               e_soap_message_end_element (msg); \
-       } G_STMT_END
-
-#define WRITE_GREATER_THAN_MESSAGE(msg, uri, val) \
-       G_STMT_START { \
-               e_soap_message_start_element (msg, "IsGreaterThan", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", 
uri); \
-               e_soap_message_start_element (msg, "FieldURIOrConstant", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", 
val); \
-               e_soap_message_end_element (msg); \
-               e_soap_message_end_element (msg); \
-       } G_STMT_END
-
-#define WRITE_LESS_THAN_MESSAGE(msg, uri, val) \
-       G_STMT_START { \
-               e_soap_message_start_element (msg, "IsLessThan", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", 
uri); \
-               e_soap_message_start_element (msg, "FieldURIOrConstant", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", 
val); \
-               e_soap_message_end_element (msg); \
-               e_soap_message_end_element (msg); \
-       } G_STMT_END
-
-#define WRITE_IS_EQUAL_TO_MESSAGE(msg, uri, val) \
-       G_STMT_START { \
-               e_soap_message_start_element (msg, "IsEqualTo", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", 
uri); \
-               e_soap_message_start_element (msg, "FieldURIOrConstant", NULL, NULL); \
-               e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", 
val); \
-               e_soap_message_end_element (msg); \
-               e_soap_message_end_element (msg); \
-       } G_STMT_END
-
 typedef enum {
        MATCH_CONTAINS,
        MATCH_IS,
@@ -218,6 +138,175 @@ struct EmailIndex {
        {"EmailAddress3"}
 };
 
+typedef struct _EvalContext {
+       ESoapMessage *msg; /* NULL when just checking whether any applied */
+       gboolean any_applicable;
+} EvalContext;
+
+static void
+ews_restriction_write_contains_message (EvalContext *ctx,
+                                       const gchar *mode,
+                                       const gchar *compare,
+                                       const gchar *uri,
+                                       const gchar *val)
+{
+       g_return_if_fail (ctx != NULL);
+
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+               return;
+       }
+
+       e_soap_message_start_element (ctx->msg, "Contains", NULL, NULL);
+       e_soap_message_add_attribute (ctx->msg, "ContainmentMode", mode, NULL, NULL);
+       e_soap_message_add_attribute (ctx->msg, "ContainmentComparison", compare, NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "FieldURI", NULL, NULL, "FieldURI", 
uri);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "Constant", NULL, NULL, "Value", val);
+       e_soap_message_end_element (ctx->msg);
+}
+
+static void
+ews_restriction_write_contains_message_indexed (EvalContext *ctx,
+                                               const gchar *mode,
+                                               const gchar *compare,
+                                               const gchar *uri,
+                                               const gchar *index,
+                                               const gchar *val)
+{
+       g_return_if_fail (ctx != NULL);
+
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+               return;
+       }
+
+       e_soap_message_start_element (ctx->msg, "Contains", NULL, NULL);
+       e_soap_message_add_attribute (ctx->msg, "ContainmentMode", mode, NULL, NULL);
+       e_soap_message_add_attribute (ctx->msg, "ContainmentComparison", compare, NULL, NULL);
+       e_soap_message_start_element (ctx->msg, "IndexedFieldURI", NULL, NULL);
+       e_soap_message_add_attribute (ctx->msg, "FieldURI", uri, NULL, NULL);
+       e_soap_message_add_attribute (ctx->msg, "FieldIndex", index, NULL, NULL);
+       e_soap_message_end_element (ctx->msg);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "Constant", NULL, NULL, "Value", val);
+       e_soap_message_end_element (ctx->msg);
+}
+
+static void
+ews_restriction_write_exists_message (EvalContext *ctx,
+                                     const gchar *uri)
+{
+       g_return_if_fail (ctx != NULL);
+
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+               return;
+       }
+
+       e_soap_message_start_element (ctx->msg, "Exists", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "FieldURI", NULL, NULL, "FieldURI", 
uri);
+       e_soap_message_end_element (ctx->msg);
+}
+
+static void
+ews_restriction_write_greater_than_or_equal_to_message (EvalContext *ctx,
+                                                       const gchar *uri,
+                                                       const gchar *val)
+{
+       g_return_if_fail (ctx != NULL);
+
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+               return;
+       }
+
+       e_soap_message_start_element (ctx->msg, "IsGreaterThanOrEqualTo", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "FieldURI", NULL, NULL, "FieldURI", 
uri);
+       e_soap_message_start_element (ctx->msg, "FieldURIOrConstant", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "Constant", NULL, NULL, "Value", val);
+       e_soap_message_end_element (ctx->msg);
+       e_soap_message_end_element (ctx->msg);
+}
+
+static void
+ews_restriction_write_less_than_or_equal_to_message (EvalContext *ctx,
+                                                    const gchar *uri,
+                                                    const gchar *val)
+{
+       g_return_if_fail (ctx != NULL);
+
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+               return;
+       }
+
+       e_soap_message_start_element (ctx->msg, "IsLessThanOrEqualTo", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "FieldURI", NULL, NULL, "FieldURI", 
uri);
+       e_soap_message_start_element (ctx->msg, "FieldURIOrConstant", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "Constant", NULL, NULL, "Value", val);
+       e_soap_message_end_element (ctx->msg);
+       e_soap_message_end_element (ctx->msg);
+}
+
+static void
+ews_restriction_write_greater_than_message (EvalContext *ctx,
+                                           const gchar *uri,
+                                           const gchar *val)
+{
+       g_return_if_fail (ctx != NULL);
+
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+               return;
+       }
+
+       e_soap_message_start_element (ctx->msg, "IsGreaterThan", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "FieldURI", NULL, NULL, "FieldURI", 
uri);
+       e_soap_message_start_element (ctx->msg, "FieldURIOrConstant", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "Constant", NULL, NULL, "Value", val);
+       e_soap_message_end_element (ctx->msg);
+       e_soap_message_end_element (ctx->msg);
+}
+
+static void
+ews_restriction_write_less_than_message (EvalContext *ctx,
+                                        const gchar *uri,
+                                        const gchar *val)
+{
+       g_return_if_fail (ctx != NULL);
+
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+               return;
+       }
+
+       e_soap_message_start_element (ctx->msg, "IsLessThan", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "FieldURI", NULL, NULL, "FieldURI", 
uri);
+       e_soap_message_start_element (ctx->msg, "FieldURIOrConstant", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "Constant", NULL, NULL, "Value", val);
+       e_soap_message_end_element (ctx->msg);
+       e_soap_message_end_element (ctx->msg);
+}
+
+static void
+ews_restriction_write_is_equal_to_message (EvalContext *ctx,
+                                          const gchar *uri,
+                                          const gchar *val)
+{
+       g_return_if_fail (ctx != NULL);
+
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+               return;
+       }
+
+       e_soap_message_start_element (ctx->msg, "IsEqualTo", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "FieldURI", NULL, NULL, "FieldURI", 
uri);
+       e_soap_message_start_element (ctx->msg, "FieldURIOrConstant", NULL, NULL);
+       e_ews_message_write_string_parameter_with_attribute (ctx->msg, "Constant", NULL, NULL, "Value", val);
+       e_soap_message_end_element (ctx->msg);
+       e_soap_message_end_element (ctx->msg);
+}
+
 static ESExpResult *
 e_ews_implement_contact_contains (ESExp *f,
                                   gint argc,
@@ -226,78 +315,85 @@ e_ews_implement_contact_contains (ESExp *f,
                                   match_type type)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argc > 1 && argv[0]->type == ESEXP_RES_STRING) {
                const gchar *field;
                field = argv[0]->value.string;
 
                if (argv[1]->type == ESEXP_RES_STRING && argv[1]->value.string != NULL) {
-                       gchar *mode = NULL;
+                       const gchar *mode = NULL;
 
                        if (type == MATCH_CONTAINS || type == MATCH_ENDS_WITH)
-                               mode = g_strdup ("Substring");
+                               mode = "Substring";
                        else if (type == MATCH_BEGINS_WITH)
-                               mode = g_strdup ("Prefixed");
+                               mode = "Prefixed";
                        else if (type == MATCH_IS)
-                               mode = g_strdup ("FullString");
+                               mode = "FullString";
                        else
-                               mode = g_strdup ("Substring");
+                               mode = "Substring";
 
                        if (!strcmp (field, "full_name")) {
                                gint n = 0;
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               e_soap_message_start_element (msg, "Or", NULL, NULL);
-                               while (n < G_N_ELEMENTS (contact_field)) {
-                                       if ((contact_field[n].flag == CONTACT_NAME) && 
(!contact_field[n].indexed)) {
-                                               WRITE_CONTAINS_MESSAGE (msg, mode, "IgnoreCase", 
contact_field[n].field_uri, value);
+                               if (!ctx->msg) {
+                                       ctx->any_applicable = TRUE;
+                               } else {
+                                       e_soap_message_start_element (ctx->msg, "Or", NULL, NULL);
+                                       while (n < G_N_ELEMENTS (contact_field)) {
+                                               if ((contact_field[n].flag == CONTACT_NAME) && 
(!contact_field[n].indexed)) {
+                                                       ews_restriction_write_contains_message (ctx, mode, 
"IgnoreCase", contact_field[n].field_uri, value);
+                                               }
+                                               n++;
                                        }
-                                       n++;
+                                       e_soap_message_end_element (ctx->msg); /* Or */
                                }
-                               e_soap_message_end_element (msg);
-
                        } else if (!strcmp (field, "x-evolution-any-field")) {
                                gint n = 0;
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               e_soap_message_start_element (msg, "Or", NULL, NULL);
-                               while (n < G_N_ELEMENTS (contact_field)) {
-                                       if (!contact_field[n].indexed) {
-                                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", 
contact_field[n].field_uri, value);
-                                       } else if (contact_field[n].flag == CONTACT_EMAIL && 
contact_field[n].indexed) {
-                                               gint i = 0;
-                                               while (i < G_N_ELEMENTS (email_index)) {
-                                                       WRITE_CONTAINS_MESSAGE_INDEXED (msg, "Substring", 
"IgnoreCase", "contacts:EmailAddress", email_index[i].field_index, value);
-                                                       i++;
+                               if (!ctx->msg) {
+                                       ctx->any_applicable = TRUE;
+                               } else {
+                                       e_soap_message_start_element (ctx->msg, "Or", NULL, NULL);
+                                       while (n < G_N_ELEMENTS (contact_field)) {
+                                               if (!contact_field[n].indexed) {
+                                                       ews_restriction_write_contains_message (ctx, 
"Substring", "IgnoreCase", contact_field[n].field_uri, value);
+                                               } else if (contact_field[n].flag == CONTACT_EMAIL && 
contact_field[n].indexed) {
+                                                       gint i = 0;
+                                                       while (i < G_N_ELEMENTS (email_index)) {
+                                                               
ews_restriction_write_contains_message_indexed (ctx, "Substring", "IgnoreCase", "contacts:EmailAddress", 
email_index[i].field_index, value);
+                                                               i++;
+                                                       }
                                                }
+                                               n++;
                                        }
-                                       n++;
+                                       e_soap_message_end_element (ctx->msg); /* Or */
                                }
-                               e_soap_message_end_element (msg);
                        } else if (!strcmp (field, "email")) {
                                const gchar *value;
                                gint n = 0;
                                value = argv[1]->value.string;
 
-                               e_soap_message_start_element (msg, "Or", NULL, NULL);
-                               while (n < G_N_ELEMENTS (email_index)) {
-                                       WRITE_CONTAINS_MESSAGE_INDEXED (msg, mode, "IgnoreCase", 
"contacts:EmailAddress", email_index[n].field_index, value);
-                                       n++;
+                               if (!ctx->msg) {
+                                       ctx->any_applicable = TRUE;
+                               } else {
+                                       e_soap_message_start_element (ctx->msg, "Or", NULL, NULL);
+                                       while (n < G_N_ELEMENTS (email_index)) {
+                                               ews_restriction_write_contains_message_indexed (ctx, mode, 
"IgnoreCase", "contacts:EmailAddress", email_index[n].field_index, value);
+                                               n++;
+                                       }
+                                       e_soap_message_end_element (ctx->msg); /* Or */
                                }
-                               e_soap_message_end_element (msg);
                        } else if (!strcmp (field, "category_list")) {
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               WRITE_CONTAINS_MESSAGE (msg, mode, "IgnoreCase", "item:Categories", value);
+                               ews_restriction_write_contains_message (ctx, mode, "IgnoreCase", 
"item:Categories", value);
                        }
-
-                       g_free (mode);
                }
        }
 
@@ -314,36 +410,61 @@ e_ews_func_and_or_not (ESExp *f,
                        match_type type)
 {
        ESExpResult *r, *r1;
-       ESoapMessage *msg;
-       gint i;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
+       ESoapMessage *used_msg;
+       gboolean was_any_applicable;
+       gint ii, n_applicable = 0;
+       const gchar *elem_name = NULL;
 
-       /* "and" and "or" expects atleast two arguments */
+       /* "And" and "Or" expect at least two arguments */
 
        if (argc == 0)
                goto result;
 
+       was_any_applicable = ctx->any_applicable;
+       used_msg = ctx->msg;
+       ctx->msg = NULL;
+
+       for (ii = 0; ii < argc; ii++) {
+               ctx->any_applicable = FALSE;
+
+               r1 = e_sexp_term_eval (f, argv[ii]);
+               e_sexp_result_free (f, r1);
+
+               if (ctx->any_applicable)
+                       n_applicable++;
+       }
+
+       ctx->msg = used_msg;
+
+       if (!ctx->msg || !n_applicable) {
+               ctx->any_applicable = n_applicable > 0 || was_any_applicable;
+               goto result;
+       }
+
        if (type == MATCH_AND) {
-               if (argc >= 2)
-                       e_soap_message_start_element (msg, "And", NULL, NULL);
+               if (n_applicable >= 2)
+                       elem_name = "And";
 
        } else if (type == MATCH_OR) {
-               if (argc >= 2)
-                       e_soap_message_start_element (msg, "Or", NULL, NULL);
+               if (n_applicable >= 2)
+                       elem_name = "Or";
 
        } else if (type == MATCH_NOT)
-               e_soap_message_start_element (msg, "Not", NULL, NULL);
+               elem_name = "Not";
+
+       if (elem_name)
+               e_soap_message_start_element (ctx->msg, elem_name, NULL, NULL);
 
-       for (i = 0; i < argc; i++) {
-               r1 = e_sexp_term_eval (f, argv[i]);
+       for (ii = 0; ii < argc; ii++) {
+               r1 = e_sexp_term_eval (f, argv[ii]);
                e_sexp_result_free (f, r1);
        }
 
-       if (argc >= 2 || type == MATCH_NOT)
-               e_soap_message_end_element (msg);
+       if (elem_name)
+               e_soap_message_end_element (ctx->msg);
 
-result:
+ result:
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
 
        return r;
@@ -356,9 +477,7 @@ calendar_func_contains (ESExp *f,
                         gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argc > 1 && argv[0]->type == ESEXP_RES_STRING) {
                const gchar *field;
@@ -369,60 +488,68 @@ calendar_func_contains (ESExp *f,
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", "item:Subject", 
value);
+                               ews_restriction_write_contains_message (ctx, "Substring", "IgnoreCase", 
"item:Subject", value);
                        } else if (!g_strcmp0 (field, "description")) {
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", "item:Body", value);
+                               ews_restriction_write_contains_message (ctx, "Substring", "IgnoreCase", 
"item:Body", value);
                        } else if (!g_strcmp0 (field, "location")) {
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", "calendar:Location", 
value);
+                               ews_restriction_write_contains_message (ctx, "Substring", "IgnoreCase", 
"calendar:Location", value);
                        } else if (!g_strcmp0 (field, "attendee")) {
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               e_soap_message_start_element (msg, "Or", NULL, NULL);
-                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", 
"calendar:RequiredAttendees", value);
-                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", 
"calendar:OptionalAttendees", value);
-                               e_soap_message_end_element (msg);
+                               if (!ctx->msg) {
+                                       ctx->any_applicable = TRUE;
+                               } else {
+                                       e_soap_message_start_element (ctx->msg, "Or", NULL, NULL);
+                                       ews_restriction_write_contains_message (ctx, "Substring", 
"IgnoreCase", "calendar:RequiredAttendees", value);
+                                       ews_restriction_write_contains_message (ctx, "Substring", 
"IgnoreCase", "calendar:OptionalAttendees", value);
+                                       e_soap_message_end_element (ctx->msg);
+                               }
                        } else if (!g_strcmp0 (field, "organizer")) {
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", "calendar:Organizer", 
value);
+                               ews_restriction_write_contains_message (ctx, "Substring", "IgnoreCase", 
"calendar:Organizer", value);
                        } else if (!g_strcmp0 (field, "classification")) {
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", "item:Sensitivity", 
value);
+                               ews_restriction_write_contains_message (ctx, "Substring", "IgnoreCase", 
"item:Sensitivity", value);
                        } else if (!g_strcmp0 (field, "priority")) {
                                const gchar *value;
                                value = argv[1]->value.string;
 
-                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", "item:Importance", 
value);
+                               ews_restriction_write_contains_message (ctx, "Substring", "IgnoreCase", 
"item:Importance", value);
                        } else if (!g_strcmp0 (field, "any")) {
                                const gchar *value;
                                gint n = 0;
                                value = argv[1]->value.string;
 
-                               e_soap_message_start_element (msg, "Or", NULL, NULL);
-                               while (n < G_N_ELEMENTS (calendar_field)) {
-                                       if (calendar_field[n].any_field) {
-                                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", 
calendar_field[n].field_uri, value);
+                               if (!ctx->msg) {
+                                       ctx->any_applicable = TRUE;
+                               } else {
+                                       e_soap_message_start_element (ctx->msg, "Or", NULL, NULL);
+                                       while (n < G_N_ELEMENTS (calendar_field)) {
+                                               if (calendar_field[n].any_field) {
+                                                       ews_restriction_write_contains_message (ctx, 
"Substring", "IgnoreCase", calendar_field[n].field_uri, value);
+                                               }
+                                               n++;
                                        }
-                                       n++;
-                               }
-                               n = 0;
-                               while (n < G_N_ELEMENTS (item_field)) {
-                                       if (item_field[n].any_field) {
-                                               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", 
item_field[n].field_uri, value);
+                                       n = 0;
+                                       while (n < G_N_ELEMENTS (item_field)) {
+                                               if (item_field[n].any_field) {
+                                                       ews_restriction_write_contains_message (ctx, 
"Substring", "IgnoreCase", item_field[n].field_uri, value);
+                                               }
+                                               n++;
                                        }
-                                       n++;
+                                       e_soap_message_end_element (ctx->msg);
                                }
-                               e_soap_message_end_element (msg);
                        }
                }
        }
@@ -439,15 +566,13 @@ calendar_func_has_categories (ESExp *f,
                               gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argc == 1 && argv[0]->type == ESEXP_RES_STRING) {
                const gchar *value;
                value = argv[0]->value.string;
 
-               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", "item:Categories", value);
+               ews_restriction_write_contains_message (ctx, "Substring", "IgnoreCase", "item:Categories", 
value);
        }
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
@@ -462,13 +587,10 @@ calendar_func_has_attachment (ESExp *f,
                               gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
-       if (argc == 0) {
-               WRITE_EXISTS_MESSAGE (msg, "item:HasAttachments");
-       }
+       if (argc == 0)
+               ews_restriction_write_exists_message (ctx, "item:HasAttachments");
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
 
@@ -482,13 +604,10 @@ calendar_func_has_recurrence (ESExp *f,
                               gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
+       EvalContext *ctx = data;
 
-       msg = (ESoapMessage *) data;
-
-       if (argc == 0) {
-               WRITE_EXISTS_MESSAGE (msg, "calendar:IsRecurring");
-       }
+       if (argc == 0)
+               ews_restriction_write_exists_message (ctx, "calendar:IsRecurring");
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
 
@@ -514,11 +633,9 @@ calendar_func_occur_in_time_range (ESExp *f,
                                    gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
+       EvalContext *ctx = data;
        gchar *start, *end;
 
-       msg = (ESoapMessage *) data;
-
        if (argv[0]->type != ESEXP_RES_TIME) {
                e_sexp_fatal_error (
                        f, "occur-in-time-range? expects argument 1 "
@@ -533,13 +650,17 @@ calendar_func_occur_in_time_range (ESExp *f,
                return NULL;
        }
 
-       start = e_ews_make_timestamp (argv[0]->value.time);
-       end = e_ews_make_timestamp (argv[1]->value.time);
+       if (!ctx->msg) {
+               ctx->any_applicable = TRUE;
+       } else {
+               start = e_ews_make_timestamp (argv[0]->value.time);
+               end = e_ews_make_timestamp (argv[1]->value.time);
 
-       e_soap_message_start_element (msg, "And", NULL, NULL);
-       WRITE_GREATER_THAN_OR_EQUAL_TO_MESSAGE (msg, "calendar:Start", start);
-       WRITE_LESS_THAN_OR_EQUAL_TO_MESSAGE (msg, "calendar:End", end);
-       e_soap_message_end_element (msg);
+               e_soap_message_start_element (ctx->msg, "And", NULL, NULL);
+               ews_restriction_write_greater_than_or_equal_to_message (ctx, "calendar:Start", start);
+               ews_restriction_write_less_than_or_equal_to_message (ctx, "calendar:End", end);
+               e_soap_message_end_element (ctx->msg);
+       }
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
 
@@ -571,15 +692,13 @@ message_func_body_contains (ESExp *f,
 
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argv[0]->type == ESEXP_RES_STRING) {
                const gchar *value;
                value = argv[0]->value.string;
 
-               WRITE_CONTAINS_MESSAGE (msg, "Substring", "IgnoreCase", "item:Body", value);
+               ews_restriction_write_contains_message (ctx, "Substring", "IgnoreCase", "item:Body", value);
        }
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
@@ -596,19 +715,17 @@ common_message_func_header_contains (ESExp *f,
 
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-       gchar *mode;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
+       const gchar *mode;
 
        if (type == MATCH_CONTAINS || type == MATCH_ENDS_WITH)
-               mode = g_strdup ("Substring");
+               mode = "Substring";
        else if (type == MATCH_BEGINS_WITH)
-               mode = g_strdup ("Prefixed");
+               mode = "Prefixed";
        else if (type == MATCH_IS)
-               mode = g_strdup ("FullString");
+               mode = "FullString";
        else
-               mode = g_strdup ("Substring");
+               mode = "Substring";
 
        if (argv[0]->type == ESEXP_RES_STRING) {
                const gchar *headername;
@@ -619,23 +736,21 @@ common_message_func_header_contains (ESExp *f,
                        value = argv[1]->value.string;
 
                        if (!g_ascii_strcasecmp (headername, "subject")) {
-                               WRITE_CONTAINS_MESSAGE (msg, mode, "IgnoreCase", "item:Subject", value);
+                               ews_restriction_write_contains_message (ctx, mode, "IgnoreCase", 
"item:Subject", value);
                        } else if (!g_ascii_strcasecmp (headername, "from")) {
-                               WRITE_CONTAINS_MESSAGE (msg, mode, "IgnoreCase", "message:From", value);
+                               ews_restriction_write_contains_message (ctx, mode, "IgnoreCase", 
"message:From", value);
                        } else if (!g_ascii_strcasecmp (headername, "to")) {
-                               WRITE_CONTAINS_MESSAGE (msg, mode, "IgnoreCase", "message:ToRecipients", 
value);
+                               ews_restriction_write_contains_message (ctx, mode, "IgnoreCase", 
"message:ToRecipients", value);
                        } else if (!g_ascii_strcasecmp (headername, "cc")) {
-                               WRITE_CONTAINS_MESSAGE (msg, mode, "IgnoreCase", "message:CcRecipients", 
value);
+                               ews_restriction_write_contains_message (ctx, mode, "IgnoreCase", 
"message:CcRecipients", value);
                        } else if (!g_ascii_strcasecmp (headername, "bcc")) {
-                               WRITE_CONTAINS_MESSAGE (msg, mode, "IgnoreCase", "message:BccRecipients", 
value);
+                               ews_restriction_write_contains_message (ctx, mode, "IgnoreCase", 
"message:BccRecipients", value);
                        }
                }
        }
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
 
-       g_free (mode);
-
        return r;
 }
 
@@ -646,24 +761,22 @@ message_func_header_exists (ESExp *f,
                             gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argv[0]->type == ESEXP_RES_STRING) {
                const gchar *headername;
                headername = argv[0]->value.string;
 
                if (!g_ascii_strcasecmp (headername, "subject")) {
-                       WRITE_EXISTS_MESSAGE (msg, "item:Subject");
+                       ews_restriction_write_exists_message (ctx, "item:Subject");
                } else if (!g_ascii_strcasecmp (headername, "from")) {
-                       WRITE_EXISTS_MESSAGE (msg, "message:From");
+                       ews_restriction_write_exists_message (ctx, "message:From");
                } else if (!g_ascii_strcasecmp (headername, "to")) {
-                       WRITE_EXISTS_MESSAGE (msg, "message:ToRecipients");
+                       ews_restriction_write_exists_message (ctx, "message:ToRecipients");
                } else if (!g_ascii_strcasecmp (headername, "cc")) {
-                       WRITE_EXISTS_MESSAGE (msg, "message:CcRecipients");
+                       ews_restriction_write_exists_message (ctx, "message:CcRecipients");
                } else if (!g_ascii_strcasecmp (headername, "bcc")) {
-                       WRITE_EXISTS_MESSAGE (msg, "message:BccRecipients");
+                       ews_restriction_write_exists_message (ctx, "message:BccRecipients");
                }
        }
 
@@ -679,15 +792,13 @@ message_func_system_flag (ESExp *f,
                           gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argv[0]->type == ESEXP_RES_STRING) {
                const gchar *name;
                name = argv[0]->value.string;
                if (!g_ascii_strcasecmp (name, "Attachments")) {
-                       WRITE_EXISTS_MESSAGE (msg, "item:HasAttachments");
+                       ews_restriction_write_exists_message (ctx, "item:HasAttachments");
                } else if (!g_ascii_strcasecmp (name, "deleted") || !g_ascii_strcasecmp (name, "junk")) {
                        r = e_sexp_result_new (f, ESEXP_RES_BOOL);
                        r->value.boolean = FALSE;
@@ -783,9 +894,7 @@ func_eq (ESExp *f,
          gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argc != 2) {
                e_sexp_fatal_error (f, "two arguments are required for this operation");
@@ -794,14 +903,14 @@ func_eq (ESExp *f,
 
        if (argv[0]->type == ESEXP_RES_STRING) {
                const gchar *name;
-               gchar *field_uri = NULL;
+               const gchar *field_uri = NULL;
 
                name = argv[0]->value.string;
 
                if (!g_strcmp0 (name, "sent-date")) {
-                       field_uri = g_strdup ("item:DateTimeSent");
+                       field_uri = "item:DateTimeSent";
                } else if (!g_strcmp0 (name, "received-date")) {
-                       field_uri = g_strdup ("item:DateTimeReceived");
+                       field_uri = "item:DateTimeReceived";
                }
 
                if (field_uri && argv[1]->type == ESEXP_RES_INT && argv[1]->value.number != 0) {
@@ -810,10 +919,9 @@ func_eq (ESExp *f,
                        time = argv[1]->value.number;
                        date = e_ews_make_timestamp (time);
 
-                       WRITE_IS_EQUAL_TO_MESSAGE (msg, field_uri, date);
+                       ews_restriction_write_is_equal_to_message (ctx, field_uri, date);
                        g_free (date);
                }
-               g_free (field_uri);
        }
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
@@ -828,9 +936,7 @@ func_gt (ESExp *f,
          gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argc != 2) {
                e_sexp_fatal_error (f, "two arguments are required for this operation");
@@ -839,19 +945,19 @@ func_gt (ESExp *f,
 
        if (argv[0]->type == ESEXP_RES_STRING) {
                const gchar *name;
-               gchar *field_uri = NULL;
+               const gchar *field_uri = NULL;
                gboolean is_time = FALSE;
 
                name = argv[0]->value.string;
 
                if (!g_strcmp0 (name, "sent-date")) {
-                       field_uri = g_strdup ("item:DateTimeSent");
+                       field_uri = "item:DateTimeSent";
                        is_time = TRUE;
                } else if (!g_strcmp0 (name, "received-date")) {
-                       field_uri = g_strdup ("item:DateTimeReceived");
+                       field_uri = "item:DateTimeReceived";
                        is_time = TRUE;
                } else if (!g_strcmp0 (name, "message-size")) {
-                       field_uri = g_strdup ("item:Size");
+                       field_uri = "item:Size";
                        is_time = FALSE;
                }
 
@@ -862,7 +968,7 @@ func_gt (ESExp *f,
                                time = argv[1]->value.number;
                                date = e_ews_make_timestamp (time);
 
-                               WRITE_GREATER_THAN_MESSAGE (msg, field_uri, date);
+                               ews_restriction_write_greater_than_message (ctx, field_uri, date);
                                g_free (date);
                        } else {
                                gint value;
@@ -872,10 +978,9 @@ func_gt (ESExp *f,
                                value = value * (1024); //conver kB to Bytes.
                                g_sprintf (val_str, "%d", value);
 
-                               WRITE_GREATER_THAN_MESSAGE (msg, field_uri, val_str);
+                               ews_restriction_write_greater_than_message (ctx, field_uri, val_str);
                        }
                }
-               g_free (field_uri);
        }
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
@@ -890,9 +995,7 @@ func_lt (ESExp *f,
          gpointer data)
 {
        ESExpResult *r;
-       ESoapMessage *msg;
-
-       msg = (ESoapMessage *) data;
+       EvalContext *ctx = data;
 
        if (argc != 2) {
                e_sexp_fatal_error (f, "two arguments are required for this operation");
@@ -901,18 +1004,18 @@ func_lt (ESExp *f,
 
        if (argv[0]->type == ESEXP_RES_STRING) {
                const gchar *name;
-               gchar *field_uri = NULL;
+               const gchar *field_uri = NULL;
                gboolean is_time = FALSE;
                name = argv[0]->value.string;
 
                if (!g_strcmp0 (name, "sent-date")) {
-                       field_uri = g_strdup ("item:DateTimeSent");
+                       field_uri = "item:DateTimeSent";
                        is_time = TRUE;
                } else if (!g_strcmp0 (name, "received-date")) {
-                       field_uri = g_strdup ("item:DateTimeReceived");
+                       field_uri = "item:DateTimeReceived";
                        is_time = TRUE;
                } else if (!g_strcmp0 (name, "message-size")) {
-                       field_uri = g_strdup ("item:Size");
+                       field_uri = "item:Size";
                        is_time = FALSE;
                }
 
@@ -923,7 +1026,7 @@ func_lt (ESExp *f,
                                time = argv[1]->value.number;
                                date = e_ews_make_timestamp (time);
 
-                               WRITE_LESS_THAN_MESSAGE (msg, field_uri, date);
+                               ews_restriction_write_less_than_message (ctx, field_uri, date);
                                g_free (date);
                        } else {
                                gint value;
@@ -933,10 +1036,9 @@ func_lt (ESExp *f,
                                value = value * (1024); //conver kB to Bytes.
                                g_sprintf (val_str, "%d", value);
 
-                               WRITE_LESS_THAN_MESSAGE (msg, field_uri, val_str);
+                               ews_restriction_write_less_than_message (ctx, field_uri, val_str);
                        }
                }
-               g_free (field_uri);
        }
 
        r = e_sexp_result_new (f, ESEXP_RES_UNDEFINED);
@@ -1121,7 +1223,7 @@ static struct {
 };
 
 static void
-e_ews_convert_sexp_to_restriction (ESoapMessage *msg,
+e_ews_convert_sexp_to_restriction (EvalContext *ctx,
                                    const gchar *query,
                                    EEwsFolderType type)
 {
@@ -1136,11 +1238,11 @@ e_ews_convert_sexp_to_restriction (ESoapMessage *msg,
                        if (contact_symbols[i].immediate)
                                e_sexp_add_ifunction (
                                        sexp, 0, contact_symbols[i].name,
-                                       (ESExpIFunc *) contact_symbols[i].func, msg);
+                                       (ESExpIFunc *) contact_symbols[i].func, ctx);
                        else
                                e_sexp_add_function (
                                        sexp, 0, contact_symbols[i].name,
-                                       contact_symbols[i].func, msg);
+                                       contact_symbols[i].func, ctx);
                }
 
        } else if (type == E_EWS_FOLDER_TYPE_CALENDAR || type == E_EWS_FOLDER_TYPE_TASKS || type == 
E_EWS_FOLDER_TYPE_MEMOS) {
@@ -1148,22 +1250,22 @@ e_ews_convert_sexp_to_restriction (ESoapMessage *msg,
                        if (calendar_symbols[i].immediate)
                                e_sexp_add_ifunction (
                                        sexp, 0, calendar_symbols[i].name,
-                                       (ESExpIFunc *) calendar_symbols[i].func, msg);
+                                       (ESExpIFunc *) calendar_symbols[i].func, ctx);
                        else
                                e_sexp_add_function (
                                        sexp, 0, calendar_symbols[i].name,
-                                       calendar_symbols[i].func, msg);
+                                       calendar_symbols[i].func, ctx);
                }
        } else if (type == E_EWS_FOLDER_TYPE_MAILBOX) {
                for (i = 0; i < G_N_ELEMENTS (message_symbols); i++) {
                        if (message_symbols[i].immediate)
                                e_sexp_add_ifunction (
                                        sexp, 0, message_symbols[i].name,
-                                       (ESExpIFunc *) message_symbols[i].func, msg);
+                                       (ESExpIFunc *) message_symbols[i].func, ctx);
                        else
                                e_sexp_add_function (
                                        sexp, 0, message_symbols[i].name,
-                                       message_symbols[i].func, msg);
+                                       message_symbols[i].func, ctx);
                }
 
        }
@@ -1204,19 +1306,32 @@ e_ews_check_is_query (const gchar *query,
                return FALSE;
 }
 
+gboolean
+e_ews_query_check_applicable (const gchar *query,
+                             EEwsFolderType type)
+{
+       EvalContext ctx;
+
+       if (!e_ews_check_is_query (query, type))
+               return FALSE;
+
+       ctx.msg = NULL;
+       ctx.any_applicable = FALSE;
+
+       e_ews_convert_sexp_to_restriction (&ctx, query, type);
+
+       return ctx.any_applicable;
+}
+
 void
 e_ews_query_to_restriction (ESoapMessage *msg,
                             const gchar *query,
                             EEwsFolderType type)
 {
-       gboolean is_query;
+       EvalContext ctx;
 
-       is_query = e_ews_check_is_query (query, type);
+       ctx.msg = msg;
+       ctx.any_applicable = FALSE;
 
-       if (is_query) {
-               e_soap_message_start_element (msg, "Restriction", "messages", NULL);
-               e_ews_convert_sexp_to_restriction (msg, query, type);
-               e_soap_message_end_element (msg);
-       }
-       return;
+       e_ews_convert_sexp_to_restriction (&ctx, query, type);
 }
diff --git a/src/server/e-ews-query-to-restriction.h b/src/server/e-ews-query-to-restriction.h
index 893ec98..f3ba10c 100644
--- a/src/server/e-ews-query-to-restriction.h
+++ b/src/server/e-ews-query-to-restriction.h
@@ -20,7 +20,17 @@
  * USA
  */
 
+#ifndef E_EWS_QUERY_TO_RESTRICTION_H
+#define E_EWS_QUERY_TO_RESTRICTION_H
+
 #include "server/e-soap-message.h"
 #include "server/e-ews-folder.h"
 
-void           e_ews_query_to_restriction (ESoapMessage *msg, const gchar *query, EEwsFolderType type);
+gboolean       e_ews_query_check_applicable    (const gchar *query,
+                                                EEwsFolderType type);
+
+void           e_ews_query_to_restriction      (ESoapMessage *msg,
+                                                const gchar *query,
+                                                EEwsFolderType type);
+
+#endif /* E_EWS_QUERY_TO_RESTRICTION_H */


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