aboutsummaryrefslogtreecommitdiffstats
path: root/camel/camel-folder-search.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/camel-folder-search.c')
-rw-r--r--camel/camel-folder-search.c320
1 files changed, 73 insertions, 247 deletions
diff --git a/camel/camel-folder-search.c b/camel/camel-folder-search.c
index d9702706f9..993de40801 100644
--- a/camel/camel-folder-search.c
+++ b/camel/camel-folder-search.c
@@ -275,14 +275,7 @@ camel_folder_search_set_folder(CamelFolderSearch *search, CamelFolder *folder)
void
camel_folder_search_set_summary(CamelFolderSearch *search, GPtrArray *summary)
{
- int i;
-
search->summary = summary;
- if (search->summary_hash)
- g_hash_table_destroy(search->summary_hash);
- search->summary_hash = g_hash_table_new(g_str_hash, g_str_equal);
- for (i=0;i<summary->len;i++)
- g_hash_table_insert(search->summary_hash, (char *)camel_message_info_uid(summary->pdata[i]), summary->pdata[i]);
}
/**
@@ -290,19 +283,15 @@ camel_folder_search_set_summary(CamelFolderSearch *search, GPtrArray *summary)
* @search:
* @index:
*
- * Set the index representing the contents of all messages
+ * Set the index (ibex) representing the contents of all messages
* in this folder. If this is not set, then the folder implementation
* should sub-class the CamelFolderSearch and provide its own
* body-contains function.
**/
void
-camel_folder_search_set_body_index(CamelFolderSearch *search, CamelIndex *index)
+camel_folder_search_set_body_index(CamelFolderSearch *search, ibex *index)
{
- if (search->body_index)
- camel_object_unref((CamelObject *)search->body_index);
search->body_index = index;
- if (index)
- camel_object_ref((CamelObject *)index);
}
/**
@@ -621,9 +610,8 @@ check_header(struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolder
char *headername;
const char *header = NULL;
char strbuf[32];
- int i, j;
+ int i;
camel_search_t type = CAMEL_SEARCH_TYPE_ASIS;
- struct _camel_search_words *words;
/* only a subset of headers are supported .. */
headername = argv[0]->value.string;
@@ -653,21 +641,9 @@ check_header(struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolder
if (header) {
/* performs an OR of all words */
for (i=1;i<argc && !truth;i++) {
- if (argv[i]->type == ESEXP_RES_STRING) {
- if (argv[i]->value.string[0] == 0) {
- truth = TRUE;
- } else if (how == CAMEL_SEARCH_MATCH_CONTAINS) {
- /* doesn't make sense to split words on anything but contains i.e. we can't have an ending match different words */
- words = camel_search_words_split(argv[i]->value.string);
- truth = TRUE;
- for (j=0;j<words->len && truth;j++) {
- truth = camel_search_header_match(header, words->words[j]->word, how, type, NULL);
- }
- camel_search_words_free(words);
- } else {
- truth = camel_search_header_match(header, argv[i]->value.string, how, type, NULL);
- }
- }
+ if (argv[i]->type == ESEXP_RES_STRING)
+ truth = camel_search_header_match(header, argv[i]->value.string,
+ how, type, NULL);
}
}
}
@@ -736,261 +712,111 @@ g_lib_sux_htor(char *key, int value, struct _glib_sux_donkeys *fuckup)
g_ptr_array_add(fuckup->uids, key);
}
-/* and, only store duplicates */
-static void
-g_lib_sux_htand(char *key, int value, struct _glib_sux_donkeys *fuckup)
-{
- if (value == fuckup->count)
- g_ptr_array_add(fuckup->uids, key);
-}
-
static int
-match_message_index(CamelIndex *idx, const char *uid, const char *match, CamelException *ex)
-{
- CamelIndexCursor *wc, *nc;
- const char *word, *name;
- int truth = FALSE;
-
- wc = camel_index_words(idx);
- if (wc) {
- while (!truth && (word = camel_index_cursor_next(wc))) {
- if (camel_ustrstrcase(word,match) != NULL) {
- /* perf: could have the wc cursor return the name cursor */
- nc = camel_index_find(idx, word);
- if (nc) {
- while (!truth && (name = camel_index_cursor_next(nc)))
- truth = strcmp(name, uid) == 0;
- camel_object_unref((CamelObject *)nc);
- }
- }
- }
- camel_object_unref((CamelObject *)wc);
- }
-
- return truth;
-}
-
-/*
- "one two" "three" "four five"
-
- one and two
-or
- three
-or
- four and five
-*/
-
-/* returns messages which contain all words listed in words */
-static GPtrArray *
-match_words_index(CamelFolderSearch *search, struct _camel_search_words *words, CamelException *ex)
-{
- GPtrArray *result = g_ptr_array_new();
- GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
- struct _glib_sux_donkeys lambdafoo;
- CamelIndexCursor *wc, *nc;
- const char *word, *name;
- CamelMessageInfo *mi;
- int i;
-
- /* we can have a maximum of 32 words, as we use it as the AND mask */
-
- wc = camel_index_words(search->body_index);
- if (wc) {
- while ((word = camel_index_cursor_next(wc))) {
- for (i=0;i<words->len;i++) {
- if (camel_ustrstrcase(word, words->words[i]->word) != NULL) {
- /* perf: could have the wc cursor return the name cursor */
- nc = camel_index_find(search->body_index, word);
- if (nc) {
- while ((name = camel_index_cursor_next(nc))) {
- mi = g_hash_table_lookup(search->summary_hash, name);
- if (mi) {
- int mask;
- const char *uid = camel_message_info_uid(mi);
-
- mask = ((int)g_hash_table_lookup(ht, uid)) | (1<<i);
- g_hash_table_insert(ht, (char *)uid, (void *)mask);
- }
- }
- camel_object_unref((CamelObject *)nc);
- }
- }
- }
- }
- camel_object_unref((CamelObject *)wc);
-
- lambdafoo.uids = result;
- lambdafoo.count = (1<<words->len) - 1;
- g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htand, &lambdafoo);
- g_hash_table_destroy(ht);
- }
-
- return result;
-}
-
-static gboolean
-match_words_1message (CamelDataWrapper *object, struct _camel_search_words *words, guint32 *mask)
+match_message(CamelFolder *folder, const char *uid, regex_t *pattern, CamelException *ex)
{
- CamelDataWrapper *containee;
- int truth = FALSE;
- int parts, i;
-
- containee = camel_medium_get_content_object (CAMEL_MEDIUM (object));
-
- if (containee == NULL)
- return FALSE;
-
- /* using the object types is more accurate than using the mime/types */
- if (CAMEL_IS_MULTIPART (containee)) {
- parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
- for (i = 0; i < parts && truth == FALSE; i++) {
- CamelDataWrapper *part = (CamelDataWrapper *)camel_multipart_get_part (CAMEL_MULTIPART (containee), i);
- if (part)
- truth = match_words_1message(part, words, mask);
- }
- } else if (CAMEL_IS_MIME_MESSAGE (containee)) {
- /* for messages we only look at its contents */
- truth = match_words_1message((CamelDataWrapper *)containee, words, mask);
- } else if (header_content_type_is(CAMEL_DATA_WRAPPER (containee)->mime_type, "text", "*")) {
- /* for all other text parts, we look inside, otherwise we dont care */
- CamelStreamMem *mem = (CamelStreamMem *)camel_stream_mem_new ();
-
- /* FIXME: The match should be part of a stream op */
- camel_data_wrapper_write_to_stream (containee, CAMEL_STREAM (mem));
- camel_stream_write (CAMEL_STREAM (mem), "", 1);
- for (i=0;i<words->len;i++) {
- /* FIXME: This is horridly slow, and should use a real search algorithm */
- if (camel_ustrstrcase(mem->buffer->data, words->words[i]->word) != NULL) {
- *mask |= (1<<i);
- /* shortcut a match */
- if (*mask == (1<<(words->len))-1)
- return TRUE;
- }
- }
- camel_object_unref (CAMEL_OBJECT (mem));
- }
-
- return truth;
-}
-
-static gboolean
-match_words_message(CamelFolder *folder, const char *uid, struct _camel_search_words *words, CamelException *ex)
-{
- guint32 mask;
CamelMimeMessage *msg;
- int truth;
+ int truth = FALSE;
msg = camel_folder_get_message(folder, uid, ex);
- if (msg) {
- mask = 0;
- truth = match_words_1message((CamelDataWrapper *)msg, words, &mask);
+ if (!camel_exception_is_set(ex) && msg!=NULL) {
+ truth = camel_search_message_body_contains((CamelDataWrapper *)msg, pattern);
camel_object_unref((CamelObject *)msg);
} else {
camel_exception_clear(ex);
- truth = FALSE;
}
-
return truth;
}
-static GPtrArray *
-match_words_messages(CamelFolderSearch *search, struct _camel_search_words *words, CamelException *ex)
-{
- int i;
- GPtrArray *matches = g_ptr_array_new();
-
- if (search->body_index) {
- GPtrArray *indexed;
- struct _camel_search_words *simple;
-
- simple = camel_search_words_simple(words);
- indexed = match_words_index(search, simple, ex);
- camel_search_words_free(simple);
-
- for (i=0;i<indexed->len;i++) {
- const char *uid = g_ptr_array_index(indexed, i);
-
- if (match_words_message(search->folder, uid, words, ex))
- g_ptr_array_add(matches, (char *)uid);
- }
-
- g_ptr_array_free(indexed, TRUE);
- } else {
- for (i=0;i<search->summary->len;i++) {
- CamelMessageInfo *info = g_ptr_array_index(search->summary, i);
- const char *uid = camel_message_info_uid(info);
-
- if (match_words_message(search->folder, uid, words, ex))
- g_ptr_array_add(matches, (char *)uid);
- }
- }
-
- return matches;
-}
-
static ESExpResult *
search_body_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolderSearch *search)
{
- int i, j;
- CamelException *ex = search->priv->ex;
- struct _camel_search_words *words;
ESExpResult *r;
- struct _glib_sux_donkeys lambdafoo;
+ int i, j;
+ regex_t pattern;
- if (search->current) {
+ if (search->current) {
int truth = FALSE;
- if (argc == 1 && argv[0]->value.string[0] == 0) {
+ if (argc == 1 && argv[0]->value.string[0] == 0 && search->folder) {
truth = TRUE;
- } else {
+ } else if (search->body_index) {
for (i=0;i<argc && !truth;i++) {
if (argv[i]->type == ESEXP_RES_STRING) {
- words = camel_search_words_split(argv[i]->value.string);
- truth = TRUE;
- if ((words->type & CAMEL_SEARCH_WORD_COMPLEX) == 0 && search->body_index) {
- for (j=0;j<words->len && truth;j++)
- truth = match_message_index(search->body_index, camel_message_info_uid(search->current), words->words[j]->word, ex);
- } else {
- /* TODO: cache current message incase of multiple body search terms */
- truth = match_words_message(search->folder, camel_message_info_uid(search->current), words, ex);
- }
- camel_search_words_free(words);
+ truth = ibex_find_name(search->body_index, (char *)camel_message_info_uid(search->current),
+ argv[i]->value.string);
+ } else {
+ e_sexp_resultv_free(f, argc, argv);
+ e_sexp_fatal_error(f, _("Invalid type in body-contains, expecting string"));
}
}
+ } else if (search->folder) {
+ /* we do a 'slow' direct search */
+ if (camel_search_build_match_regex(&pattern, CAMEL_SEARCH_MATCH_ICASE, argc, argv, search->priv->ex) == 0) {
+ truth = match_message(search->folder, camel_message_info_uid(search->current), &pattern, search->priv->ex);
+ regfree(&pattern);
+ }
+ } else {
+ g_warning("Cannot perform indexed body query with no index or folder set");
}
r = e_sexp_result_new(f, ESEXP_RES_BOOL);
r->value.bool = truth;
} else {
r = e_sexp_result_new(f, ESEXP_RES_ARRAY_PTR);
- r->value.ptrarray = g_ptr_array_new();
- if (argc == 1 && argv[0]->value.string[0] == 0) {
+ if (argc == 1 && argv[0]->value.string[0] == 0 && search->folder) {
+ /* optimise the match "" case - match everything */
+ r->value.ptrarray = g_ptr_array_new();
for (i=0;i<search->summary->len;i++) {
CamelMessageInfo *info = g_ptr_array_index(search->summary, i);
-
g_ptr_array_add(r->value.ptrarray, (char *)camel_message_info_uid(info));
}
- } else {
- GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
- GPtrArray *matches;
-
- for (i=0;i<argc;i++) {
- if (argv[i]->type == ESEXP_RES_STRING) {
- words = camel_search_words_split(argv[i]->value.string);
- if ((words->type & CAMEL_SEARCH_WORD_COMPLEX) == 0 && search->body_index) {
- matches = match_words_index(search, words, ex);
+ } else if (search->body_index) {
+ if (argc==1) {
+ /* common case */
+ r->value.ptrarray = ibex_find(search->body_index, argv[0]->value.string);
+ } else {
+ GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
+ GPtrArray *pa;
+ struct _glib_sux_donkeys lambdafoo;
+
+ /* this sux, perform an or operation on the result(s) of each word */
+ for (i=0;i<argc;i++) {
+ if (argv[i]->type == ESEXP_RES_STRING) {
+ pa = ibex_find(search->body_index, argv[i]->value.string);
+ for (j=0;j<pa->len;j++) {
+ g_hash_table_insert(ht, g_ptr_array_index(pa, j), (void *)1);
+ }
+ g_ptr_array_free(pa, FALSE);
} else {
- matches = match_words_messages(search, words, ex);
+ e_sexp_result_free(f, r);
+ e_sexp_resultv_free(f, argc, argv);
+ e_sexp_fatal_error(f, _("Invalid type in body-contains, expecting string"));
}
- for (j=0;j<matches->len;j++)
- g_hash_table_insert(ht, matches->pdata[j], matches->pdata[j]);
- g_ptr_array_free(matches, TRUE);
- camel_search_words_free(words);
}
+ lambdafoo.uids = g_ptr_array_new();
+ g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htor, &lambdafoo);
+ r->value.ptrarray = lambdafoo.uids;
+ g_hash_table_destroy(ht);
+ }
+ } else if (search->folder) {
+ /* do a slow search */
+ r->value.ptrarray = g_ptr_array_new();
+ if (camel_search_build_match_regex(&pattern, CAMEL_SEARCH_MATCH_ICASE, argc, argv, search->priv->ex) == 0) {
+ if (search->summary) {
+ for (i=0;i<search->summary->len;i++) {
+ CamelMessageInfo *info = g_ptr_array_index(search->summary, i);
+
+ if (match_message(search->folder, camel_message_info_uid(info), &pattern, search->priv->ex))
+ g_ptr_array_add(r->value.ptrarray, (char *)camel_message_info_uid(info));
+ }
+ } /* else? we could always get the summary from the folder, but then
+ we need to free it later somehow */
+ regfree(&pattern);
}
- lambdafoo.uids = r->value.ptrarray;
- g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htor, &lambdafoo);
- g_hash_table_destroy(ht);
+ } else {
+ g_warning("Cannot perform indexed body query with no index or folder set");
+ r->value.ptrarray = g_ptr_array_new();
}
}