2 * Copyright 2000 HelixCode (http://www.helixcode.com).
5 * Michael Zucchi <notzed@helixcode.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 #include <camel/gmime-utils.h>
30 #include <camel/camel-log.h>
31 #include "camel/camel-mime-message.h"
32 #include "camel/camel-mime-part.h"
33 #include "camel/camel-stream.h"
34 #include "camel/camel-stream-fs.h"
35 #include "camel/camel.h"
36 #include "camel-mbox-folder.h"
37 #include "camel-mbox-summary.h"
39 #include "camel-mbox-search.h"
49 #define p(x) /* parse debug */
50 #define r(x) /* run debug */
51 #define d(x) /* general debug */
58 list = (body-contains string+)
59 bool = (body-contains string+)
60 Returns a list of all messages containing any of the strings in the message.
61 If within a match-all, then returns true for the current message.
63 list = (match-all bool-expr)
64 Returns a list of all messages for which the bool expression is true.
65 The bool-expr is evaluated for each message in turn.
66 It is more efficient not to perform body-content comparisons inside a
70 Returns a time_t of the date-sent of the message.
72 bool = (header-contains string string+)
73 Returns true if the current message (inside a match-all operator)
74 has a header 'string1', which contains any of the following strings.
78 struct _searchcontext {
79 int id; /* id of this search */
80 int cancelled; /* search cancelled? */
85 ibex *index; /* index of content for this folder */
88 CamelFolderSummary *summary;
89 const GArray *message_info;
91 CamelMessageInfo *message_current; /* when performing a (match operation */
94 struct _glib_sux_donkeys {
98 /* or, store all unique values */
100 g_lib_sux_htor(char *key, int value, struct _glib_sux_donkeys *fuckup)
102 g_ptr_array_add(fuckup->uids, key);
106 func_body_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
110 struct _searchcontext *ctx = data;
112 if (ctx->message_current) {
115 r = e_sexp_result_new(ESEXP_RES_BOOL);
117 for (i=0;i<argc && !truth;i++) {
118 if (argv[i]->type == ESEXP_RES_STRING) {
119 truth = ibex_find_name(ctx->index, ctx->message_current->uid, argv[i]->value.string);
121 g_warning("Invalid type passed to body-contains match function");
125 g_warning("Cannot perform indexed query with no index");
127 r->value.bool = truth;
129 r = e_sexp_result_new(ESEXP_RES_ARRAY_PTR);
134 r->value.ptrarray = ibex_find(ctx->index, argv[0]->value.string);
136 GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal);
138 struct _glib_sux_donkeys lambdafoo;
140 /* this sux, perform an or operation on the result(s) of each word */
141 for (i=0;i<argc;i++) {
142 if (argv[i]->type == ESEXP_RES_STRING) {
143 pa = ibex_find(ctx->index, argv[i]->value.string);
144 for (j=0;j<pa->len;j++) {
145 g_hash_table_insert(ht, g_ptr_array_index(pa, j), (void *)1);
147 g_ptr_array_free(pa, FALSE);
150 lambdafoo.uids = g_ptr_array_new();
151 g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htor, &lambdafoo);
152 r->value.ptrarray = lambdafoo.uids;
155 r->value.ptrarray = g_ptr_array_new();
163 func_date_sent(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
166 struct _searchcontext *ctx = data;
168 r = e_sexp_result_new(ESEXP_RES_INT);
170 if (ctx->message_current) {
171 g_warning("FIXME: implement date parsing ...");
172 /* r->value.number = get_date(ctx->message_current); */
174 r->value.number = time(0);
181 func_match_all(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data)
185 struct _searchcontext *ctx = data;
188 g_warning("match-all only takes a single argument, other arguments ignored");
190 r = e_sexp_result_new(ESEXP_RES_ARRAY_PTR);
191 r->value.ptrarray = g_ptr_array_new();
193 for (i=0;i<ctx->message_info->len;i++) {
195 ctx->message_current = &g_array_index(ctx->message_info, CamelMessageInfo, i);
196 r1 = e_sexp_term_eval(f, argv[0]);
197 if (r1->type == ESEXP_RES_BOOL) {
199 g_ptr_array_add(r->value.ptrarray, ctx->message_current->uid);
201 g_warning("invalid syntax, matches require a single bool result");
203 e_sexp_result_free(r1);
205 g_ptr_array_add(r->value.ptrarray, ctx->message_current->uid);
208 ctx->message_current = NULL;
214 func_header_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data)
217 struct _searchcontext *ctx = data;
220 r(printf("executing header-contains\n"));
222 /* are we inside a match-all? */
223 if (ctx->message_current && argc>1
224 && argv[0]->type == ESEXP_RES_STRING) {
225 char *headername, *header;
228 /* only a subset of headers are supported .. */
229 headername = argv[0]->value.string;
230 if (!strcasecmp(headername, "subject")) {
231 header = ctx->message_current->subject;
232 } else if (!strcasecmp(headername, "date")) {
233 header = ctx->message_current->sent_date;
234 } else if (!strcasecmp(headername, "from")) {
235 header = ctx->message_current->sender;
237 g_warning("Performing query on unknown header: %s", headername);
242 for (i=1;i<argc && !truth;i++) {
243 if (argv[i]->type == ESEXP_RES_STRING
244 && strstr(header, argv[i]->value.string)) {
245 printf("%s got a match with %s of %s\n", ctx->message_current->uid, header, argv[i]->value.string);
252 r = e_sexp_result_new(ESEXP_RES_BOOL);
253 r->value.bool = truth;
259 /* 'builtin' functions */
263 int type; /* set to 1 if a function can perform shortcut evaluation, or
264 doesn't execute everything, 0 otherwise */
266 { "body-contains", func_body_contains, 0 },
267 { "date-sent", func_date_sent, 0 },
268 { "match-all", (ESExpFunc *)func_match_all, 1 },
269 { "header-contains", func_header_contains, 0 },
272 int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expression,
273 CamelSearchFunc *func, void *data, CamelException *ex)
276 struct _searchcontext *ctx;
277 GList *matches = NULL;
281 /* setup our expression evaluator */
284 ctx = g_malloc0(sizeof(*ctx));
286 ctx->id = ((CamelMboxFolder *)folder)->search_id++;
288 /* setup out context */
289 ctx->folder = folder;
290 ctx->summary = camel_folder_get_summary(folder, ex);
292 if (ctx->summary == NULL || camel_exception_get_id (ex)) {
293 printf ("Cannot get summary\n"
294 "Full description : %s\n", camel_exception_get_description (ex));
296 gtk_object_unref((GtkObject *)f);
300 gtk_object_ref((GtkObject *)ctx->summary);
302 /* FIXME: the index should be global to the folder */
303 ctx->message_info = CAMEL_MBOX_SUMMARY(ctx->summary)->message_info;
304 ctx->message_current = NULL;
305 ctx->index = ibex_open(CAMEL_MBOX_FOLDER(folder)->index_file_path, FALSE);
307 perror("Cannot open index file (ignored)");
310 ((CamelMboxFolder *)folder)->searches = g_list_append(((CamelMboxFolder *)folder)->searches, ctx);
312 for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) {
313 if (symbols[i].type == 1) {
314 e_sexp_add_ifunction(f, 0, symbols[i].name, (ESExpIFunc *)symbols[i].func, ctx);
316 e_sexp_add_function(f, 0, symbols[i].name, symbols[i].func, ctx);
320 e_sexp_input_text(f, expression, strlen(expression));
324 /* now create a folder summary to return?? */
326 && r->type == ESEXP_RES_ARRAY_PTR) {
327 d(printf("got result ...\n"));
328 for (i=0;i<r->value.ptrarray->len;i++) {
329 d(printf("adding match: %s\n", (char *)g_ptr_array_index(r->value.ptrarray, i)));
330 matches = g_list_prepend(matches, g_strdup(g_ptr_array_index(r->value.ptrarray, i)));
332 if (!ctx->cancelled) {
333 func(folder, ctx->id, TRUE, matches, data);
335 g_list_free(matches);
336 e_sexp_result_free(r);
338 printf("no result!\n");
342 ibex_close(ctx->index);
344 gtk_object_unref((GtkObject *)ctx->summary);
345 gtk_object_unref((GtkObject *)f);
348 ((CamelMboxFolder *)folder)->searches = g_list_remove(((CamelMboxFolder *)folder)->searches, ctx);
355 static struct _searchcontext *
356 find_context(CamelMboxFolder *f, int id)
358 struct _searchcontext *ctx;
373 gboolean camel_mbox_folder_search_complete(CamelFolder *folder, int searchid, int wait, CamelException *ex)
375 struct _searchcontext *ctx;
377 ctx = find_context((CamelMboxFolder *)folder, searchid);
380 return ctx->cancelled;
382 /* if its been removed, its complete ... */
386 void camel_mbox_folder_search_cancel(CamelFolder *folder, int searchid, CamelException *ex)
388 struct _searchcontext *ctx;
390 ctx = find_context((CamelMboxFolder *)folder, searchid);
392 ctx->cancelled = TRUE;
396 /* FIXME: set exception, return */
399 #else /* HAVE_FILTER */
401 int camel_mbox_folder_search_by_expression(CamelFolder *folder, const char *expression,
402 CamelSearchFunc *func, void *data, CamelException *ex)
407 gboolean camel_mbox_folder_search_complete(CamelFolder *folder, int searchid, int wait, CamelException *ex)
412 void camel_mbox_folder_search_cancel(CamelFolder *folder, int searchid, CamelException *ex)
417 #endif /*! HAVE_FILTER */