[evolution-data-server] Bug 777431 - [IMAPx] Gmail's BODY search returns only whole word matches
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug 777431 - [IMAPx] Gmail's BODY search returns only whole word matches
- Date: Wed, 18 Jan 2017 10:13:08 +0000 (UTC)
commit a268ea96d534bdd201147808fada96b1ac67b617
Author: Milan Crha <mcrha redhat com>
Date: Wed Jan 18 11:13:34 2017 +0100
Bug 777431 - [IMAPx] Gmail's BODY search returns only whole word matches
src/camel/camel-folder-search.c | 73 +++++++++++++++++++++---
src/camel/camel-folder-search.h | 5 ++
src/camel/camel-folder.c | 53 +++++++++++++----
src/camel/camel-folder.h | 4 +
src/camel/providers/imapx/camel-imapx-search.c | 64 +++++++++++++++++++++
src/camel/providers/imapx/camel-imapx-store.c | 10 ++--
src/camel/providers/imapx/camel-imapx-store.h | 2 +
7 files changed, 186 insertions(+), 25 deletions(-)
---
diff --git a/src/camel/camel-folder-search.c b/src/camel/camel-folder-search.c
index 617bf45..e3bb857 100644
--- a/src/camel/camel-folder-search.c
+++ b/src/camel/camel-folder-search.c
@@ -84,6 +84,8 @@ struct _CamelFolderSearchPrivate {
* This saves us having to perform more complex UID life cycle management
* and the overhead from the additional refs/unrefs it would require. */
GPtrArray *owned_pstrings;
+
+ gboolean only_cached_messages;
};
typedef enum {
@@ -278,12 +280,28 @@ fill_thread_table (CamelFolderThreadNode *root,
}
static CamelMimeMessage *
+search_get_message_sync (CamelFolderSearch *search,
+ CamelFolder *folder,
+ const gchar *uid,
+ GCancellable *cancellable)
+{
+ g_return_val_if_fail (CAMEL_IS_FOLDER_SEARCH (search), NULL);
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ if (camel_folder_search_get_only_cached_messages (search))
+ return camel_folder_get_message_cached (folder, uid, cancellable);
+
+ return camel_folder_get_message_sync (folder, uid, cancellable, NULL);
+}
+
+static CamelMimeMessage *
get_current_message (CamelFolderSearch *search)
{
if (!search || !search->priv->folder || !search->priv->current)
return NULL;
- return camel_folder_get_message_sync (search->priv->folder, camel_message_info_get_uid
(search->priv->current), search->priv->cancellable, NULL);
+ return search_get_message_sync (search, search->priv->folder, camel_message_info_get_uid
(search->priv->current), search->priv->cancellable);
}
static CamelSExpResult *
@@ -593,7 +611,8 @@ match_words_1message (CamelDataWrapper *object,
}
static gboolean
-match_words_message (CamelFolder *folder,
+match_words_message (CamelFolderSearch *search,
+ CamelFolder *folder,
const gchar *uid,
struct _camel_search_words *words,
GCancellable *cancellable,
@@ -606,7 +625,7 @@ match_words_message (CamelFolder *folder,
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return truth;
- msg = camel_folder_get_message_sync (folder, uid, cancellable, NULL);
+ msg = search_get_message_sync (search, folder, uid, cancellable);
if (msg) {
mask = 0;
truth = match_words_1message ((CamelDataWrapper *) msg, words, &mask, cancellable);
@@ -639,7 +658,7 @@ match_words_messages (CamelFolderSearch *search,
for (i = 0; i < indexed->len && !g_cancellable_is_cancelled (cancellable); i++) {
const gchar *uid = g_ptr_array_index (indexed, i);
- if (match_words_message (
+ if (match_words_message (search,
search->priv->folder, uid, words,
cancellable, error))
g_ptr_array_add (matches, (gchar *) uid);
@@ -652,7 +671,7 @@ match_words_messages (CamelFolderSearch *search,
for (i = 0; i < v->len && !g_cancellable_is_cancelled (cancellable); i++) {
gchar *uid = g_ptr_array_index (v, i);
- if (match_words_message (
+ if (match_words_message (search,
search->priv->folder, uid, words,
cancellable, error))
g_ptr_array_add (matches, (gchar *) uid);
@@ -1093,7 +1112,7 @@ folder_search_body_contains (CamelSExp *sexp,
error);
} else {
/* TODO: cache current message incase of multiple body search
terms */
- truth = match_words_message (
+ truth = match_words_message (search,
search->priv->folder,
camel_message_info_get_uid (search->priv->current),
words,
@@ -1195,8 +1214,7 @@ folder_search_body_regex (CamelSExp *sexp,
for (i = 0; i < v->len && !g_cancellable_is_cancelled (search->priv->cancellable);
i++) {
gchar *uid = g_ptr_array_index (v, i);
- message = camel_folder_get_message_sync (
- search->priv->folder, uid, search->priv->cancellable, NULL);
+ message = search_get_message_sync (search, search->priv->folder, uid,
search->priv->cancellable);
if (message) {
if (camel_search_message_body_contains ((CamelDataWrapper *) message,
&pattern)) {
g_ptr_array_add (r->value.ptrarray, uid);
@@ -1713,6 +1731,7 @@ camel_folder_search_init (CamelFolderSearch *search)
{
search->priv = CAMEL_FOLDER_SEARCH_GET_PRIVATE (search);
search->priv->sexp = camel_sexp_new ();
+ search->priv->only_cached_messages = FALSE;
}
/**
@@ -1734,6 +1753,44 @@ camel_folder_search_new (void)
}
/**
+ * camel_folder_search_set_only_cached_messages:
+ * @search: a #CamelFolderSearch
+ * @only_cached_messages: a value to set
+ *
+ * Sets whether only locally cached messages can be searched. The default
+ * value is %FALSE, which means that when a message is required and it is
+ * not available locally, then it is downloaded from the server, if possible.
+ *
+ * Since: 3.24
+ **/
+void
+camel_folder_search_set_only_cached_messages (CamelFolderSearch *search,
+ gboolean only_cached_messages)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER_SEARCH (search));
+
+ search->priv->only_cached_messages = only_cached_messages;
+}
+
+/**
+ * camel_folder_search_get_only_cached_messages:
+ * @search: a #CamelFolderSearch
+ *
+ * Returns: Whether only cached messages can be searched. See
+ * camel_folder_search_set_only_cached_messages() for more
+ * information what it means.
+ *
+ * Since: 3.24
+ **/
+gboolean
+camel_folder_search_get_only_cached_messages (CamelFolderSearch *search)
+{
+ g_return_val_if_fail (CAMEL_IS_FOLDER_SEARCH (search), FALSE);
+
+ return search->priv->only_cached_messages;
+}
+
+/**
* camel_folder_search_set_current_message_info:
* @search: a #CamelFolderSearch
* @info: (nullable): a #CamelMessageInfo
diff --git a/src/camel/camel-folder-search.h b/src/camel/camel-folder-search.h
index fe86b8b..7d4cc3d 100644
--- a/src/camel/camel-folder-search.h
+++ b/src/camel/camel-folder-search.h
@@ -260,6 +260,11 @@ struct _CamelFolderSearchClass {
GType camel_folder_search_get_type (void) G_GNUC_CONST;
CamelFolderSearch *
camel_folder_search_new (void);
+void camel_folder_search_set_only_cached_messages
+ (CamelFolderSearch *search,
+ gboolean only_cached_messages);
+gboolean camel_folder_search_get_only_cached_messages
+ (CamelFolderSearch *search);
void camel_folder_search_set_current_message_info
(CamelFolderSearch *search,
CamelMessageInfo *info);
diff --git a/src/camel/camel-folder.c b/src/camel/camel-folder.c
index 82ed860..85b6dbc 100644
--- a/src/camel/camel-folder.c
+++ b/src/camel/camel-folder.c
@@ -3090,7 +3090,7 @@ camel_folder_get_message_sync (CamelFolder *folder,
GError **error)
{
CamelFolderClass *class;
- CamelMimeMessage *message = NULL;
+ CamelMimeMessage *message;
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
g_return_val_if_fail (message_uid != NULL, NULL);
@@ -3106,21 +3106,14 @@ camel_folder_get_message_sync (CamelFolder *folder,
message_uid, camel_service_get_display_name (CAMEL_SERVICE (camel_folder_get_parent_store
(folder))),
camel_folder_get_full_name (folder));
- if (class->get_message_cached) {
- /* Return cached message, if available locally; this should
- * not do any network I/O, only check if message is already
- * downloaded and return it quicker, not being blocked by
- * the folder's lock. Returning NULL is not considered as
- * an error, it just means that the message is still
- * to-be-downloaded. */
- message = class->get_message_cached (
- folder, message_uid, cancellable);
- }
+ message = camel_folder_get_message_cached (folder, message_uid, cancellable);
if (message == NULL) {
/* Recover from a dropped connection, unless we're offline. */
- if (!folder_maybe_connect_sync (folder, cancellable, error))
+ if (!folder_maybe_connect_sync (folder, cancellable, error)) {
+ camel_operation_pop_message (cancellable);
return NULL;
+ }
camel_folder_lock (folder);
@@ -3162,6 +3155,42 @@ camel_folder_get_message_sync (CamelFolder *folder,
return message;
}
+/**
+ * camel_folder_get_message_cached:
+ * @folder: a #CamelFolder
+ * @message_uid: the message UID
+ * @cancellable: optional #GCancellable object, or %NULL
+ *
+ * Gets the message corresponding to @message_uid from the @folder cache,
+ * if available locally. This should not do any network I/O, only check
+ * if message is already downloaded and return it quickly, not being
+ * blocked by the folder's lock. Returning NULL is not considered as
+ * an error, it just means that the message is still to-be-downloaded.
+ *
+ * Note: This function is called automatically within camel_folder_get_message_sync().
+ *
+ * Returns: (transfer full) (nullable): a cached #CamelMimeMessage corresponding
+ * to the requested UID
+ *
+ * Since: 3.24
+ **/
+CamelMimeMessage *
+camel_folder_get_message_cached (CamelFolder *folder,
+ const gchar *message_uid,
+ GCancellable *cancellable)
+{
+ CamelFolderClass *class;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (message_uid != NULL, NULL);
+
+ class = CAMEL_FOLDER_GET_CLASS (folder);
+ if (!class->get_message_cached)
+ return NULL;
+
+ return class->get_message_cached (folder, message_uid, cancellable);
+}
+
/* Helper for camel_folder_get_message() */
static void
folder_get_message_thread (GTask *task,
diff --git a/src/camel/camel-folder.h b/src/camel/camel-folder.h
index 1246281..a20e183 100644
--- a/src/camel/camel-folder.h
+++ b/src/camel/camel-folder.h
@@ -451,6 +451,10 @@ CamelMimeMessage *
camel_folder_get_message_finish (CamelFolder *folder,
GAsyncResult *result,
GError **error);
+CamelMimeMessage *
+ camel_folder_get_message_cached (CamelFolder *folder,
+ const gchar *message_uid,
+ GCancellable *cancellable);
CamelFolderQuotaInfo *
camel_folder_get_quota_info_sync
(CamelFolder *folder,
diff --git a/src/camel/providers/imapx/camel-imapx-search.c b/src/camel/providers/imapx/camel-imapx-search.c
index e24d1e8..29245d9 100644
--- a/src/camel/providers/imapx/camel-imapx-search.c
+++ b/src/camel/providers/imapx/camel-imapx-search.c
@@ -351,6 +351,7 @@ imapx_search_body_contains (CamelSExp *sexp,
CamelSExpResult *result;
GString *criteria;
GPtrArray *words;
+ gboolean is_gmail;
/* Always do body-search server-side */
if (imapx_search->priv->local_data_search) {
@@ -391,10 +392,73 @@ imapx_search_body_contains (CamelSExp *sexp,
result = imapx_search_process_criteria (sexp, search, imapx_store, criteria, "BODY", words,
G_STRFUNC);
+ is_gmail = camel_imapx_store_is_gmail_server (imapx_store);
+
g_string_free (criteria, TRUE);
g_ptr_array_free (words, TRUE);
g_object_unref (imapx_store);
+ if (is_gmail && result && (result->type == CAMEL_SEXP_RES_ARRAY_PTR || (result->type ==
CAMEL_SEXP_RES_BOOL && !result->value.boolean))) {
+ /* Gmail returns BODY matches on whole words only, which is not it should be,
+ thus try also locally cached messages to provide any better results. */
+ gboolean was_only_cached_messages;
+ CamelSExpResult *cached_result;
+
+ was_only_cached_messages = camel_folder_search_get_only_cached_messages (search);
+ camel_folder_search_set_only_cached_messages (search, TRUE);
+
+ cached_result = CAMEL_FOLDER_SEARCH_CLASS (camel_imapx_search_parent_class)->body_contains
(sexp, argc, argv, search);
+
+ camel_folder_search_set_only_cached_messages (search, was_only_cached_messages);
+
+ if (cached_result && cached_result->type == result->type) {
+ if (result->type == CAMEL_SEXP_RES_BOOL) {
+ result->value.boolean = cached_result->value.boolean;
+ } else {
+ /* Merge the two UID arrays */
+ GHashTable *merge;
+ GHashTableIter iter;
+ GPtrArray *array;
+ gpointer key;
+ guint ii;
+
+ /* UID-s are strings from the string pool, thus can be compared directly */
+ merge = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ array = result->value.ptrarray;
+ for (ii = 0; array && ii < array->len; ii++) {
+ gpointer uid = g_ptr_array_index (array, ii);
+
+ if (uid)
+ g_hash_table_insert (merge, uid, NULL);
+ }
+
+ array = cached_result->value.ptrarray;
+ for (ii = 0; array && ii < array->len; ii++) {
+ gpointer uid = g_ptr_array_index (array, ii);
+
+ if (uid)
+ g_hash_table_insert (merge, uid, NULL);
+ }
+
+ array = g_ptr_array_new_full (g_hash_table_size (merge), (GDestroyNotify)
camel_pstring_free);
+
+ g_hash_table_iter_init (&iter, merge);
+ while (g_hash_table_iter_next (&iter, &key, NULL)) {
+ g_ptr_array_add (array, (gpointer) camel_pstring_strdup (key));
+ }
+
+ g_hash_table_destroy (merge);
+
+ g_ptr_array_unref (result->value.ptrarray);
+
+ result->value.ptrarray = array;
+ }
+ }
+
+ camel_sexp_result_free (sexp, cached_result);
+ }
+
return result;
}
diff --git a/src/camel/providers/imapx/camel-imapx-store.c b/src/camel/providers/imapx/camel-imapx-store.c
index ccbf201..4bdde58 100644
--- a/src/camel/providers/imapx/camel-imapx-store.c
+++ b/src/camel/providers/imapx/camel-imapx-store.c
@@ -2298,15 +2298,15 @@ exit:
return success;
}
-static gboolean
-imapx_is_gmail_server (CamelService *service)
+gboolean
+camel_imapx_store_is_gmail_server (CamelIMAPXStore *imapx_store)
{
CamelSettings *settings;
gboolean is_gmail = FALSE;
- g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+ g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), FALSE);
- settings = camel_service_ref_settings (service);
+ settings = camel_service_ref_settings (CAMEL_SERVICE (imapx_store));
if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
gchar *host;
@@ -2576,7 +2576,7 @@ imapx_initial_setup_sync (CamelStore *store,
/* Skip changing Sent folder for GMail, because GMail stores sent messages
automatically, thus it would make doubled copies on the server. */
- if (!imapx_is_gmail_server (CAMEL_SERVICE (store))) {
+ if (!camel_imapx_store_is_gmail_server (imapx_store)) {
imapx_check_initial_setup_group (imapx_store, finfo, save_setup,
CAMEL_IMAPX_LIST_ATTR_SENT,
CAMEL_STORE_SETUP_SENT_FOLDER, NULL, NULL,
diff --git a/src/camel/providers/imapx/camel-imapx-store.h b/src/camel/providers/imapx/camel-imapx-store.h
index c9f5d2f..ce1eade 100644
--- a/src/camel/providers/imapx/camel-imapx-store.h
+++ b/src/camel/providers/imapx/camel-imapx-store.h
@@ -78,6 +78,8 @@ struct _CamelIMAPXStoreClass {
};
GType camel_imapx_store_get_type (void);
+gboolean camel_imapx_store_is_gmail_server
+ (CamelIMAPXStore *store);
CamelIMAPXConnManager *
camel_imapx_store_get_conn_manager
(CamelIMAPXStore *store);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]