aboutsummaryrefslogtreecommitdiffstats
path: root/camel
diff options
context:
space:
mode:
Diffstat (limited to 'camel')
-rw-r--r--camel/Makefile.am2
-rw-r--r--camel/camel-folder.c34
-rw-r--r--camel/camel-folder.h14
-rw-r--r--camel/md5-utils.c4
-rw-r--r--camel/providers/mbox/Makefile.am7
-rw-r--r--camel/providers/mbox/camel-mbox-folder.c38
-rw-r--r--camel/providers/mbox/camel-mbox-folder.h3
-rw-r--r--camel/providers/mbox/camel-mbox-parser.c12
-rw-r--r--camel/providers/mbox/camel-mbox-search.c857
-rw-r--r--camel/providers/mbox/camel-mbox-search.h10
10 files changed, 948 insertions, 33 deletions
diff --git a/camel/Makefile.am b/camel/Makefile.am
index 2db0ecf42c..d466534530 100644
--- a/camel/Makefile.am
+++ b/camel/Makefile.am
@@ -57,6 +57,7 @@ libcamel_la_SOURCES = \
camel-session.c \
camel-store.c \
camel-stream.c \
+ camel-stream-buffer.c \
camel-stream-buffered-fs.c \
camel-stream-fs.c \
camel-stream-mem.c \
@@ -98,6 +99,7 @@ libcamelinclude_HEADERS = \
camel-session.h \
camel-store.h \
camel-stream.h \
+ camel-stream-buffer.h \
camel-stream-buffered-fs.h \
camel-stream-fs.h \
camel-stream-mem.h \
diff --git a/camel/camel-folder.c b/camel/camel-folder.c
index 0930550cbf..d7366d51b4 100644
--- a/camel/camel-folder.c
+++ b/camel/camel-folder.c
@@ -1807,5 +1807,39 @@ camel_folder_get_uid_list (CamelFolder *folder, CamelException *ex)
return CF_CLASS (folder)->get_uid_list (folder, ex);
}
+/**
+ * camel_folder_has_search_capability:
+ * @folder: Folder object
+ *
+ * Checks if a folder supports searching.
+ *
+ * Return value: TRUE if the folder supports UIDs
+ **/
+gboolean
+camel_folder_has_search_capability (CamelFolder *folder, CamelException *ex)
+{
+ if (!CAMEL_IS_FOLDER(folder)) {
+ camel_exception_set (ex,
+ CAMEL_EXCEPTION_FOLDER_INVALID,
+ "Invalid folder");
+ return FALSE;
+ }
+
+ return folder->has_search_capability;
+}
+
+GList *camel_folder_search_by_expression (CamelFolder *folder,
+ const char *expression,
+ CamelException *ex)
+{
+ if (!CAMEL_IS_FOLDER(folder)) {
+ camel_exception_set (ex,
+ CAMEL_EXCEPTION_FOLDER_INVALID,
+ "Invalid folder");
+ return NULL;
+ }
+
+ return CF_CLASS (folder)->search_by_expression (folder, expression, ex);
+}
/* **** */
diff --git a/camel/camel-folder.h b/camel/camel-folder.h
index d9715c4d54..2a7b0caced 100644
--- a/camel/camel-folder.h
+++ b/camel/camel-folder.h
@@ -81,6 +81,7 @@ struct _CamelFolder
gboolean has_uid_capability;
+ gboolean has_search_capability;
};
@@ -200,6 +201,9 @@ typedef struct {
GList * (*get_uid_list) (CamelFolder *folder,
CamelException *ex);
+ gboolean (*has_search_capability) (CamelFolder *folder, CamelException *ex);
+ GList *(*search_by_expression) (CamelFolder *folder, const char *expression, CamelException *ex);
+
} CamelFolderClass;
@@ -280,10 +284,12 @@ const gchar * camel_folder_get_message_uid (CamelFolder *folder, CamelMimeMessag
CamelMimeMessage *camel_folder_get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex);
GList *camel_folder_get_uid_list (CamelFolder *folder, CamelException *ex);
-
-
-
-
+/* search api */
+gboolean camel_folder_has_search_capability (CamelFolder *folder,
+ CamelException *ex);
+GList *camel_folder_search_by_expression (CamelFolder *folder,
+ const char *expression,
+ CamelException *ex);
#ifdef __cplusplus
}
diff --git a/camel/md5-utils.c b/camel/md5-utils.c
index 1300c00f9f..2ebb1533f4 100644
--- a/camel/md5-utils.c
+++ b/camel/md5-utils.c
@@ -364,6 +364,8 @@ md5_get_digest_from_file (const gchar *filename, guchar digest[16])
gint nb_bytes_read;
FILE *fp;
+ printf("generating checksum\n");
+
md5_init (&ctx);
fp = fopen(filename, "r");
if (!fp) {
@@ -377,9 +379,11 @@ md5_get_digest_from_file (const gchar *filename, guchar digest[16])
fclose(fp);
return;
}
+
md5_final (digest, &ctx);
+ printf("checksum done\n");
}
diff --git a/camel/providers/mbox/Makefile.am b/camel/providers/mbox/Makefile.am
index be8bce9232..9b5b40ff84 100644
--- a/camel/providers/mbox/Makefile.am
+++ b/camel/providers/mbox/Makefile.am
@@ -9,7 +9,8 @@ lib_LTLIBRARIES = libcamelmbox.la
INCLUDES = -I.. -I$(srcdir)/.. -I$(includedir) \
-I$(top_srcdir)/intl \
- $(GTK_INCLUDEDIR) -I$(top_srcdir)/camel
+ $(GTK_INCLUDEDIR) -I$(top_srcdir)/camel \
+ -I$(top_srcdir)/libibex
libcamelmbox_la_SOURCES = \
camel-mbox-folder.c \
@@ -17,6 +18,7 @@ libcamelmbox_la_SOURCES = \
camel-mbox-provider.c \
camel-mbox-store.c \
camel-mbox-summary.c \
+ camel-mbox-search.c \
camel-mbox-utils.c
libcamelmboxinclude_HEADERS = \
@@ -24,10 +26,13 @@ libcamelmboxinclude_HEADERS = \
camel-mbox-parser.h \
camel-mbox-store.h \
camel-mbox-summary.h \
+ camel-mbox-search.h \
camel-mbox-utils.h
libcamelmbox_la_LDFLAGS = -version-info 0:0:0 -rpath $(libdir)
+#libcamelmbox_la_LIBADD = -L$(top_srcdir)/libibex -libex
+libcamelmbox_la_LIBADD = $(top_srcdir)/libibex/libibex.la
EXTRA_DIST =
diff --git a/camel/providers/mbox/camel-mbox-folder.c b/camel/providers/mbox/camel-mbox-folder.c
index 53da87b746..09c08d967a 100644
--- a/camel/providers/mbox/camel-mbox-folder.c
+++ b/camel/providers/mbox/camel-mbox-folder.c
@@ -45,6 +45,7 @@
#include "camel-mbox-utils.h"
#include "md5-utils.h"
#include "gmime-utils.h"
+#include "camel-mbox-search.h"
#include "camel-exception.h"
@@ -78,6 +79,7 @@ static void _copy_message_to (CamelFolder *folder, CamelMimeMessage *message, Ca
static const gchar *_get_message_uid (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex);
#endif
+static CamelFolderSummary *search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex);
static void _finalize (GtkObject *object);
@@ -112,6 +114,8 @@ camel_mbox_folder_class_init (CamelMboxFolderClass *camel_mbox_folder_class)
#endif
camel_folder_class->get_message_by_uid = _get_message_by_uid;
+ camel_folder_class->search_by_expression = search_by_expression;
+
gtk_object_class->finalize = _finalize;
}
@@ -190,6 +194,7 @@ _init_with_store (CamelFolder *folder, CamelStore *parent_store, CamelException
mbox_folder->folder_file_path = NULL;
mbox_folder->summary_file_path = NULL;
mbox_folder->folder_dir_path = NULL;
+ mbox_folder->index_file_path = NULL;
mbox_folder->internal_summary = NULL;
mbox_folder->uid_array = NULL;
@@ -352,6 +357,7 @@ _set_name (CamelFolder *folder, const gchar *name, CamelException *ex)
g_free (mbox_folder->folder_file_path);
g_free (mbox_folder->folder_dir_path);
+ g_free (mbox_folder->index_file_path);
separator = camel_store_get_separator (folder->parent_store, ex);
root_dir_path = camel_mbox_store_get_toplevel_dir (CAMEL_MBOX_STORE(folder->parent_store), ex);
@@ -363,8 +369,8 @@ _set_name (CamelFolder *folder, const gchar *name, CamelException *ex)
mbox_folder->folder_file_path = g_strdup_printf ("%s%c%s", root_dir_path, separator, folder->full_name);
mbox_folder->summary_file_path = g_strdup_printf ("%s%c%s-ev-summary", root_dir_path, separator, folder->full_name);
mbox_folder->folder_dir_path = g_strdup_printf ("%s%c%s.sdb", root_dir_path, separator, folder->full_name);
-
-
+ mbox_folder->index_file_path = g_strdup_printf ("%s%c%s.ibex", root_dir_path, separator, folder->full_name);
+
CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::set_name mbox_folder->folder_file_path is %s\n",
mbox_folder->folder_file_path);
CAMEL_LOG_FULL_DEBUG ("CamelMboxFolder::set_name mbox_folder->folder_dir_path is %s\n",
@@ -1118,26 +1124,8 @@ _get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex)
return message;
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+static CamelFolderSummary *
+search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex)
+{
+ return camel_mbox_folder_search_by_expression(folder, expression);
+}
diff --git a/camel/providers/mbox/camel-mbox-folder.h b/camel/providers/mbox/camel-mbox-folder.h
index 9fdc765103..76288d5c3c 100644
--- a/camel/providers/mbox/camel-mbox-folder.h
+++ b/camel/providers/mbox/camel-mbox-folder.h
@@ -54,7 +54,8 @@ typedef struct {
gchar *folder_file_path; /* contains the messages */
gchar *summary_file_path; /* contains the messages summary */
gchar *folder_dir_path; /* contains the subfolders */
-
+ gchar *index_file_path; /* index of body contents */
+
CamelMboxSummary *internal_summary; /* internal summary object */
GList *uid_array;
diff --git a/camel/providers/mbox/camel-mbox-parser.c b/camel/providers/mbox/camel-mbox-parser.c
index 905214811e..43a7fa3133 100644
--- a/camel/providers/mbox/camel-mbox-parser.c
+++ b/camel/providers/mbox/camel-mbox-parser.c
@@ -846,17 +846,25 @@ main (int argc, char **argv)
{
int test_file_fd;
int i;
+ int file_size;
+ int next_uid;
GArray *message_positions;
CamelMboxParserMessageInfo *message_info;
gchar tmp_buffer[50];
tmp_buffer[49] = '\0';
+ if (argc<2) {
+ printf("usage: %s mbox\n", argv[0]);
+ return 1;
+ }
+
test_file_fd = open (argv[1], O_RDONLY);
message_positions = camel_mbox_parse_file (test_file_fd,
"From ",
-
- 0,
+ 0,
+ &file_size,
+ &next_uid,
TRUE,
status,
0.05,
diff --git a/camel/providers/mbox/camel-mbox-search.c b/camel/providers/mbox/camel-mbox-search.c
new file mode 100644
index 0000000000..b46c046b6d
--- /dev/null
+++ b/camel/providers/mbox/camel-mbox-search.c
@@ -0,0 +1,857 @@
+/*
+ * Copyright 2000 HelixCode (http://www.helixcode.com).
+ *
+ * Author :
+ * Michael Zucchi <notzed@helixcode.com>
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+#include <camel/gmime-utils.h>
+#include <camel/camel-log.h>
+#include "camel/camel-mime-message.h"
+#include "camel/camel-mime-part.h"
+#include "camel/camel-stream.h"
+#include "camel/camel-stream-fs.h"
+#include "camel/camel.h"
+#include "camel-mbox-folder.h"
+
+#define HAVE_IBEX
+#ifdef HAVE_IBEX
+#include "ibex.h"
+#endif
+
+#define p(x) /* parse debug */
+#define r(x) /* run debug */
+#define d(x) /* general debug */
+
+
+/*
+
+ This is not yet complete.
+
+ The following s-exp's are supported:
+
+ list = (and list*)
+ perform an intersection of a number of lists, and return that.
+
+ bool = (and bool*)
+ perform a boolean AND of boolean values.
+
+ list = (or list*)
+ perform a union of a number of lists, returning the new list.
+
+ bool = (or bool*)
+ perform a boolean OR of boolean values.
+
+ Comparison operators:
+
+ bool = (lt int int)
+ bool = (gt int int)
+ bool = (eq int int)
+
+ bool = (lt string string)
+ bool = (gt string string)
+ bool = (eq string string)
+ Perform a comparision of 2 integers, or 2 string values.
+
+ Matching operators:
+
+ list = (contains string)
+ Returns a list of all messages containing the string in their body.
+
+ list = (match-all bool-expr)
+ Returns a list of all messages for which the bool expression is true.
+ The bool-expr is evaluated for each message in turn.
+ It is more efficient not to perform body-content comparisons inside a
+ match-all operator.
+
+ int = (date-sent)
+ Returns a time_t of the date-sent of the message.
+
+ bool = (header-contains string1 string2)
+ Returns true if the current message (inside a match-all operator)
+ has a header 'string1', which contains 'string2'
+*/
+
+static GScannerConfig scanner_config =
+{
+ (
+ " \t\r\n"
+ ) /* cset_skip_characters */,
+ (
+ G_CSET_a_2_z
+ "_"
+ G_CSET_A_2_Z
+ ) /* cset_identifier_first */,
+ (
+ G_CSET_a_2_z
+ "_0123456789-"
+ G_CSET_A_2_Z
+ G_CSET_LATINS
+ G_CSET_LATINC
+ ) /* cset_identifier_nth */,
+ ( "#\n" ) /* cpair_comment_single */,
+
+ FALSE /* case_sensitive */,
+
+ TRUE /* skip_comment_multi */,
+ TRUE /* skip_comment_single */,
+ TRUE /* scan_comment_multi */,
+ TRUE /* scan_identifier */,
+ FALSE /* scan_identifier_1char */,
+ FALSE /* scan_identifier_NULL */,
+ TRUE /* scan_symbols */,
+ FALSE /* scan_binary */,
+ TRUE /* scan_octal */,
+ TRUE /* scan_float */,
+ TRUE /* scan_hex */,
+ FALSE /* scan_hex_dollar */,
+ TRUE /* scan_string_sq */,
+ TRUE /* scan_string_dq */,
+ TRUE /* numbers_2_int */,
+ FALSE /* int_2_float */,
+ FALSE /* identifier_2_string */,
+ TRUE /* char_2_token */,
+ FALSE /* symbol_2_token */,
+ FALSE /* scope_0_fallback */,
+};
+
+
+enum _searchtermtype_t {
+ SEARCH_AND,
+ SEARCH_OR,
+ SEARCH_LT,
+ SEARCH_GT,
+ SEARCH_EQ,
+ SEARCH_CONTAINS,
+ SEARCH_DATESENT,
+ SEARCH_STRING,
+ SEARCH_INT,
+ SEARCH_FUNC,
+};
+
+struct _searchterm {
+ enum _searchtermtype_t type;
+ union {
+ char *string;
+ int number;
+ struct {
+ struct _searchterm_symbol *sym;
+ struct _searchterm **terms;
+ int termcount;
+ } func;
+ } value;
+};
+
+struct _searchcontext {
+ int whatever;
+
+ CamelFolder *folder;
+
+#ifdef HAVE_IBEX
+ ibex *index; /* index of content for this folder */
+#endif
+
+ CamelFolderSummary *summary;
+ const GArray *message_info;
+
+ CamelMessageInfo *message_current; /* when performing a (match operation */
+};
+
+enum _searchresulttype_t {
+ RESULT_ARRAY_PTR=0,
+ RESULT_INT,
+ RESULT_STRING,
+ RESULT_BOOL,
+ RESULT_UNDEFINED
+};
+
+struct _searchresult {
+ enum _searchresulttype_t type;
+ union {
+ GPtrArray *ptrarray;
+ int number;
+ char *string;
+ int bool;
+ } value;
+};
+
+/* function callbacks */
+static struct _searchresult *search_contains(struct _searchcontext *ctx, struct _searchterm *t);
+static struct _searchresult *search_matches(struct _searchcontext *ctx, struct _searchterm *t);
+static struct _searchresult *search_date_sent(struct _searchcontext *ctx, struct _searchterm *t);
+static struct _searchresult *header_contains(struct _searchcontext *ctx, struct _searchterm *t);
+
+struct _searchterm_symbol {
+ char *name;
+ int type;
+ int argtype;
+ struct _searchresult * (*func)(struct _searchcontext *ctx, struct _searchterm *t);
+} symbols[] = {
+ { "and", SEARCH_AND, 0, NULL },
+ { "or", SEARCH_OR, 0, NULL },
+ { "lt", SEARCH_LT, 1, NULL },
+ { "gt", SEARCH_GT, 1, NULL },
+ { "eq", SEARCH_EQ, 1, NULL },
+ { "contains", SEARCH_FUNC, 1, search_contains },
+ { "match-all", SEARCH_FUNC, 1, search_matches },
+ { "date-sent", SEARCH_FUNC, 1, search_date_sent },
+ { "header-contains", SEARCH_FUNC, 1, header_contains },
+};
+
+static struct _searchterm * parse_list(GScanner *gs, int gotbrace);
+static struct _searchterm * parse_value(GScanner *gs);
+
+static struct _searchresult *term_eval(struct _searchcontext *ctx, struct _searchterm *t);
+static void parse_dump_term(struct _searchterm *t, int depth);
+
+/* can you tell, i dont like glib? */
+struct _glib_sux_donkeys {
+ int count;
+ GPtrArray *uids;
+};
+
+
+/* ok, store any values that are in all sets */
+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);
+ }
+}
+
+/* or, store all unique values */
+static void
+g_lib_sux_htor(char *key, int value, struct _glib_sux_donkeys *fuckup)
+{
+ g_ptr_array_add(fuckup->uids, key);
+}
+
+static struct _searchresult *
+result_new(int type)
+{
+ struct _searchresult *r = g_malloc0(sizeof(*r));
+ r->type = type;
+ return r;
+}
+
+static void
+result_free(struct _searchresult *t)
+{
+ switch(t->type) {
+ case RESULT_ARRAY_PTR:
+ g_ptr_array_free(t->value.ptrarray, TRUE);
+ break;
+ case RESULT_BOOL:
+ case RESULT_INT:
+ break;
+ case RESULT_STRING:
+ g_free(t->value.string);
+ break;
+ case RESULT_UNDEFINED:
+ break;
+ }
+ g_free(t);
+}
+
+static struct _searchresult *search_contains(struct _searchcontext *ctx, struct _searchterm *t)
+{
+ struct _searchresult *r, *r1;
+
+ r = result_new(RESULT_UNDEFINED);
+
+ if (t->value.func.termcount>0) {
+ if (t->value.func.termcount!=1) {
+ printf("warning, only looking for first string in contains clause\n");
+ }
+ r1 = term_eval(ctx, t->value.func.terms[0]);
+ if (r1->type == RESULT_STRING) {
+ if (ctx->message_current) {
+ int truth = FALSE;
+#ifdef HAVE_IBEX
+ int i;
+ GPtrArray *array;
+
+ if (ctx->index) {
+ array = ibex_find(ctx->index, r1->value.string);
+
+ for (i=0;i<array->len;i++) {
+ if (!strcmp(g_ptr_array_index(array, i), ctx->message_current->uid)) {
+ truth = TRUE;
+ break;
+ }
+ }
+ g_ptr_array_free(array, TRUE);
+ }
+#endif
+ r->type = RESULT_BOOL;
+ r->value.bool = truth;
+ } else {
+ r->type = RESULT_ARRAY_PTR;
+#ifdef HAVE_IBEX
+ if (ctx->index) {
+ /* blah, this should probably copy the index strings? */
+ r->value.ptrarray = ibex_find(ctx->index, r1->value.string);
+ } else {
+ r->value.ptrarray = g_ptr_array_new();
+ }
+#endif
+ }
+ } else {
+ printf("you can't search for a contents of a non-string, fool\n");
+ }
+ result_free(r1);
+ }
+ return r;
+}
+
+/* run a sub-tree of commands which match on header fields etc */
+static struct _searchresult *search_matches(struct _searchcontext *ctx, struct _searchterm *t)
+{
+ int i;
+ struct _searchresult *r, *r1;
+
+ if (t->value.func.termcount == 1) {
+ r = result_new(RESULT_ARRAY_PTR);
+ r->value.ptrarray = g_ptr_array_new();
+
+ for (i=0;i<ctx->message_info->len;i++) {
+ ctx->message_current = &g_array_index(ctx->message_info, CamelMessageInfo, i);
+ r1 = term_eval(ctx, t->value.func.terms[0]);
+ if (r1->type == RESULT_BOOL) {
+ if (r1->value.bool) {
+ r(printf("adding message %s\n", ctx->message_current->uid));
+ g_ptr_array_add(r->value.ptrarray, ctx->message_current->uid);
+ }
+ } else {
+ printf("invalid syntax, matches require a single bool result\n");
+ }
+ result_free(r1);
+ }
+ ctx->message_current = NULL;
+ } else {
+ r = result_new(RESULT_UNDEFINED);
+ printf("invalid syntax, matches only allows a single bool arg\n");
+ }
+ return r;
+}
+
+/* these variable-getting things could be put into 1 function */
+static struct _searchresult *search_date_sent(struct _searchcontext *ctx, struct _searchterm *t)
+{
+ struct _searchresult *r;
+
+ if (ctx->message_current) {
+ r = result_new(RESULT_INT);
+ r->value.number = time(0);
+ /* r->value.number = ctx->current_message->date_sent;*/
+ } else {
+ r = result_new(RESULT_UNDEFINED);
+ }
+ return r;
+}
+
+/* header contains - can only be used inside a match-all construct */
+/* all headers should be inside a lookup table, so this can search
+ all header types */
+static struct _searchresult *header_contains(struct _searchcontext *ctx, struct _searchterm *t)
+{
+ struct _searchresult *r;
+
+ r(printf("executing header-contains\n"));
+
+ /* are we inside a match-all? */
+ if (ctx->message_current
+ && t->value.func.termcount == 2) {
+ char *header, *substring;
+ int truth = FALSE;
+ struct _searchresult *r1, *r2;
+
+ r1 = term_eval(ctx, t->value.func.terms[0]);
+ r2 = term_eval(ctx, t->value.func.terms[1]);
+
+ if (r1->type == RESULT_STRING
+ && r2->type == RESULT_STRING) {
+
+ header = r1->value.string;
+ substring = r2->value.string;
+ if (!strcasecmp(header, "subject")) {
+ r(printf("comparing subject: %s\n", ctx->message_current->subject));
+ if (ctx->message_current->subject)
+ truth = (strstr(ctx->message_current->subject, substring)) != NULL;
+ else
+ printf("Warning: no subject line in message?\n");
+ }
+ r(printf("header-contains %s %s = %s\n", header, substring, truth?"TRUE":"FALSE"));
+ }
+
+ result_free(r1);
+ result_free(r2);
+
+ r = result_new(RESULT_BOOL);
+ r->value.number = truth;
+ } else {
+ r = result_new(RESULT_UNDEFINED);
+ }
+ return r;
+}
+
+
+static struct _searchresult *
+term_eval(struct _searchcontext *ctx, struct _searchterm *t)
+{
+ struct _searchresult *r, *r1, *r2;
+ int i;
+
+ r(printf("eval term :\n"));
+ r(parse_dump_term(t, 0));
+
+ r = g_malloc0(sizeof(*r));
+ r->type = RESULT_UNDEFINED;
+
+ switch (t->type) {
+ case SEARCH_AND: {
+ GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
+ struct _glib_sux_donkeys lambdafoo;
+ int type=-1;
+ int bool = TRUE;
+
+ r(printf("( and\n"));
+
+ for (i=0;bool && i<t->value.func.termcount;i++) {
+ r1 = term_eval(ctx, t->value.func.terms[i]);
+ if (type == -1)
+ type = r1->type;
+ if (type != r1->type) {
+ printf("invalid types in and operation, all types must be the same\n");
+ } else if ( r1->type == RESULT_ARRAY_PTR ) {
+ char **a1;
+ int l1, j;
+
+ a1 = (char **)r1->value.ptrarray->pdata;
+ l1 = r1->value.ptrarray->len;
+ for (j=0;i<l1;j++) {
+ int n;
+ n = (int)g_hash_table_lookup(ht, a1[i]);
+ g_hash_table_insert(ht, a1[i], (void *)n+1);
+ }
+ } else if ( r1->type == RESULT_BOOL ) {
+ bool &= r1->value.bool;
+ }
+ result_free(r1);
+ }
+
+ if (type == RESULT_ARRAY_PTR) {
+ lambdafoo.count = t->value.func.termcount;
+ lambdafoo.uids = g_ptr_array_new();
+ g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htand, &lambdafoo);
+ r->type = RESULT_ARRAY_PTR;
+ r->value.ptrarray = lambdafoo.uids;
+ } else if (type == RESULT_BOOL) {
+ r->type = RESULT_BOOL;
+ r->value.bool = bool;
+ }
+
+ g_hash_table_destroy(ht);
+
+ break; }
+ case SEARCH_OR: {
+ GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
+ struct _glib_sux_donkeys lambdafoo;
+ int type = -1;
+ int bool = FALSE;
+
+ r(printf("(or \n"));
+
+ for (i=0;!bool && i<t->value.func.termcount;i++) {
+ r1 = term_eval(ctx, t->value.func.terms[i]);
+ if (type == -1)
+ type = r1->type;
+ if (r1->type != type) {
+ printf("wrong types in or operation\n");
+ } else if (r1->type == RESULT_ARRAY_PTR) {
+ char **a1;
+ int l1, j;
+
+ a1 = (char **)r1->value.ptrarray->pdata;
+ l1 = r1->value.ptrarray->len;
+ for (j=0;i<l1;j++) {
+ g_hash_table_insert(ht, a1[j], (void *)1);
+ }
+ } else if (r1->type == RESULT_BOOL) {
+ bool |= r1->value.bool;
+ }
+ result_free(r1);
+ }
+
+ if (type == RESULT_ARRAY_PTR) {
+ lambdafoo.count = t->value.func.termcount;
+ lambdafoo.uids = g_ptr_array_new();
+ g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htor, &lambdafoo);
+ r->type = RESULT_ARRAY_PTR;
+ r->value.ptrarray = lambdafoo.uids;
+ } else if (type == RESULT_BOOL) {
+ r->type = RESULT_BOOL;
+ r->value.bool = bool;
+ }
+ g_hash_table_destroy(ht);
+
+ break; }
+ case SEARCH_LT:
+ r(printf("(lt \n"));
+ if (t->value.func.termcount == 2) {
+ r1 = term_eval(ctx, t->value.func.terms[0]);
+ r2 = term_eval(ctx, t->value.func.terms[1]);
+ if (r1->type != r2->type) {
+ printf("error, invalid types in compare\n");
+ } else if (r1->type == RESULT_INT) {
+ r->type = RESULT_BOOL;
+ r->value.bool = r1->value.number < r2->value.number;
+ } else if (r1->type == RESULT_STRING) {
+ r->type = RESULT_BOOL;
+ r->value.bool = strcmp(r1->value.string, r2->value.string) < 0;
+ }
+ }
+ break;
+ case SEARCH_GT:
+ r(printf("(gt \n"));
+ if (t->value.func.termcount == 2) {
+ r1 = term_eval(ctx, t->value.func.terms[0]);
+ r2 = term_eval(ctx, t->value.func.terms[1]);
+ if (r1->type != r2->type) {
+ printf("error, invalid types in compare\n");
+ } else if (r1->type == RESULT_INT) {
+ r->type = RESULT_BOOL;
+ r->value.bool = r1->value.number > r2->value.number;
+ } else if (r1->type == RESULT_STRING) {
+ r->type = RESULT_BOOL;
+ r->value.bool = strcmp(r1->value.string, r2->value.string) > 0;
+ }
+ }
+ break;
+ case SEARCH_STRING:
+ r(printf(" (string \"%s\")\n", t->value.string));
+ r->type = RESULT_STRING;
+ /* erk, this shoul;dn't need to strdup this ... */
+ r->value.string = g_strdup(t->value.string);
+ break;
+ case SEARCH_INT:
+ r(printf(" (int %d)\n", t->value.number));
+ r->type = RESULT_INT;
+ r->value.number = t->value.number;
+ break;
+ case SEARCH_FUNC:
+ g_free(r); /* <---- FIXME: ICK !! */
+ r(printf("function '%s'\n", t->value.func.sym->name));
+ return t->value.func.sym->func(ctx, t);
+ default:
+ printf("Warning: Unknown type encountered in parse tree: %d\n", t->type);
+ r->type = RESULT_UNDEFINED;
+ }
+
+ return r;
+}
+
+
+static void
+parse_dump_term(struct _searchterm *t, int depth)
+{
+ int dumpvals = FALSE;
+ int i;
+
+ if (t==NULL) {
+ printf("null term??\n");
+ return;
+ }
+
+ for (i=0;i<depth;i++)
+ printf(" ");
+
+ switch (t->type) {
+ case SEARCH_AND:
+ printf("(and \n");
+ dumpvals = 1;
+ break;
+ case SEARCH_OR:
+ printf("(or \n");
+ dumpvals = 1;
+ break;
+ case SEARCH_LT:
+ printf("(lt \n");
+ dumpvals = 1;
+ break;
+ case SEARCH_GT:
+ printf("(gt \n");
+ dumpvals = 1;
+ break;
+ case SEARCH_STRING:
+ printf(" \"%s\"", t->value.string);
+ break;
+ case SEARCH_INT:
+ printf(" %d", t->value.number);
+ break;
+ case SEARCH_FUNC:
+ printf(" (function %s", t->value.func.sym->name);
+ dumpvals = 1;
+ break;
+ default:
+ printf("unknown type: %d\n", t->type);
+ }
+
+ if (dumpvals) {
+ /*printf(" [%d] ", t->value.func.termcount);*/
+ for (i=0;i<t->value.func.termcount;i++) {
+ parse_dump_term(t->value.func.terms[i], depth+1);
+ }
+ for (i=0;i<depth;i++)
+ printf(" ");
+ printf(")\n");
+ }
+ printf("\n");
+}
+
+/*
+ PARSER
+*/
+
+static struct _searchterm *
+parse_new_term(int type)
+{
+ struct _searchterm *s = g_malloc0(sizeof(*s));
+ s->type = type;
+ return s;
+}
+
+static void
+parse_term_free(struct _searchterm *t)
+{
+ int i;
+
+ if (t==NULL) {
+ return;
+ }
+
+ switch (t->type) {
+ case SEARCH_AND:
+ case SEARCH_OR:
+ case SEARCH_LT:
+ case SEARCH_GT:
+ case SEARCH_FUNC:
+ for (i=0;i<t->value.func.termcount;i++) {
+ parse_term_free(t->value.func.terms[i]);
+ }
+ g_free(t->value.func.terms);
+ break;
+ case SEARCH_STRING:
+ g_free(t->value.string);
+ break;
+ case SEARCH_INT:
+ break;
+ default:
+ printf("parse_term_free: unknown type: %d\n", t->type);
+ }
+ g_free(t);
+}
+
+static struct _searchterm **
+parse_lists(GScanner *gs, int *len)
+{
+ int token;
+ struct _searchterm **terms;
+ int i=0;
+
+ p(printf("parsing lists\n"));
+
+ terms = g_malloc0(20*sizeof(*terms));
+
+ while ( (token = g_scanner_peek_next_token(gs)) != G_TOKEN_EOF
+ && token != ')') {
+ terms[i]=parse_list(gs, FALSE);
+ i++;
+ }
+
+ if (len)
+ *len = i;
+
+ p(printf("found %d subterms\n", i));
+
+ p(printf("done parsing lists, token= %d %c\n", token, token));
+ return terms;
+}
+
+static struct _searchterm **
+parse_values(GScanner *gs, int *len)
+{
+ int token;
+ struct _searchterm **terms;
+ int i=0;
+
+ p(printf("parsing values\n"));
+
+ terms = g_malloc0(20*sizeof(*terms));
+
+ while ( (token = g_scanner_peek_next_token(gs)) != G_TOKEN_EOF
+ && token != ')') {
+ terms[i]=parse_value(gs);
+ i++;
+ }
+
+ p(printf("found %d subterms\n", i));
+ *len = i;
+
+ p(printf("dont parsing values\n"));
+ return terms;
+}
+
+static struct _searchterm *
+parse_value(GScanner *gs)
+{
+ int token;
+ struct _searchterm *t = NULL;
+
+ p(printf("parsing value\n"));
+
+ token = g_scanner_get_next_token(gs);
+ switch(token) {
+ case G_TOKEN_LEFT_PAREN:
+ p(printf("got brace, its a list!\n"));
+ return parse_list(gs, TRUE);
+ case G_TOKEN_STRING:
+ p(printf("got string\n"));
+ t = parse_new_term(SEARCH_STRING);
+ t->value.string = g_strdup(g_scanner_cur_value(gs).v_string);
+ break;
+ case G_TOKEN_INT:
+ t = parse_new_term(SEARCH_INT);
+ t->value.number = g_scanner_cur_value(gs).v_int;
+ p(printf("got int\n"));
+ break;
+ default:
+ printf("Innvalid token trying to parse a list of values\n");
+ }
+ p(printf("done parsing value\n"));
+ return t;
+}
+
+/* FIXME: this needs some robustification */
+static struct _searchterm *
+parse_list(GScanner *gs, int gotbrace)
+{
+ int token;
+ struct _searchterm *t = NULL;
+
+ p(printf("parsing list\n"));
+ if (gotbrace)
+ token = '(';
+ else
+ token = g_scanner_get_next_token(gs);
+ if (token =='(') {
+ token = g_scanner_get_next_token(gs);
+ if (token == G_TOKEN_SYMBOL) {
+ struct _searchterm_symbol *s;
+
+ s = g_scanner_cur_value(gs).v_symbol;
+ p(printf("got funciton: %s\n", s->name));
+ t = parse_new_term(s->type);
+ t->value.func.sym = s;
+ p(printf("created new list %p\n", t));
+ switch(s->argtype) {
+ case 0: /* it MUST be a list of lists */
+ t->value.func.terms = parse_lists(gs, &t->value.func.termcount);
+ break;
+ case 1:
+ t->value.func.terms = parse_values(gs, &t->value.func.termcount);
+ break;
+ default:
+ printf("Error, internal error parsing symbols\n");
+ }
+ } else {
+ printf("unknown sequence encountered, type = %d\n", token);
+ }
+ token = g_scanner_get_next_token(gs);
+ if (token != ')') {
+ printf("Error, expected ')' not found\n");
+ }
+ } else {
+ printf("Error, list term without opening (\n");
+ }
+
+ p(printf("returning list %p\n", t));
+ return t;
+}
+
+GList *
+camel_mbox_folder_search_by_expression(CamelFolder *folder, char *expression, CamelException *ex)
+{
+ GScanner *gs;
+ int i;
+ struct _searchterm *t;
+ struct _searchcontext *ctx;
+ struct _searchresult *r;
+ GList *matches = NULL;
+
+ gs = g_scanner_new(&scanner_config);
+ for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++)
+ g_scanner_scope_add_symbol(gs, 0, symbols[i].name, &symbols[i]);
+
+ g_scanner_input_text(gs, expression, strlen(expression));
+ t = parse_list(gs, 0);
+
+ if (t) {
+ ctx = g_malloc0(sizeof(*ctx));
+ ctx->folder = folder;
+ ctx->summary = camel_folder_get_summary(folder, ex);
+ ctx->message_info = camel_folder_summary_get_message_info_list(ctx->summary);
+#ifdef HAVE_IBEX
+ ctx->index = ibex_open(CAMEL_MBOX_FOLDER(folder)->index_file_path, FALSE);
+ if (!ctx->index) {
+ perror("Cannot open index file, body searches will be ignored\n");
+ }
+#endif
+ r = term_eval(ctx, t);
+
+ /* now create a folder summary to return?? */
+ if (r
+ && r->type == RESULT_ARRAY_PTR) {
+ d(printf("got result ...\n"));
+ for (i=0;i<r->value.ptrarray->len;i++) {
+ d(printf("adding match: %s\n", (char *)g_ptr_array_index(r->value.ptrarray, i)));
+ matches = g_list_prepend(matches, g_strdup(g_ptr_array_index(r->value.ptrarray, i)));
+ }
+ result_free(r);
+ }
+
+ if (ctx->index)
+ ibex_close(ctx->index);
+
+ gtk_object_unref((GtkObject *)ctx->summary);
+ g_free(ctx);
+ parse_term_free(t);
+ } else {
+ printf("Warning, Could not parse expression!\n %s\n", expression);
+ }
+
+ g_scanner_destroy(gs);
+
+ return matches;
+}
diff --git a/camel/providers/mbox/camel-mbox-search.h b/camel/providers/mbox/camel-mbox-search.h
new file mode 100644
index 0000000000..16efd4a023
--- /dev/null
+++ b/camel/providers/mbox/camel-mbox-search.h
@@ -0,0 +1,10 @@
+
+#ifndef _CAMEL_MBOX_SEARCH_H
+#define _CAMEL_MBOX_SEARCH_H
+
+#include "camel-mbox-folder.h"
+
+GList *camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expression);
+
+#endif /* ! _CAMEL_MBOX_SEARCH_H */
+