Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-filter-search.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *           Michael Zucchi <NotZed@Ximian.com>
5  *
6  *  Copyright 2000 Ximian, Inc. (www.ximian.com)
7  *  Copyright 2001 Ximian Inc. (www.ximian.com)
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU Lesser General Public
11  * License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this program; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 /* POSIX requires <sys/types.h> be included before <regex.h> */
30 #include <sys/types.h>
31
32 #include <ctype.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <regex.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #ifndef G_OS_WIN32
42 #include <sys/wait.h>
43 #endif
44
45 #include <glib.h>
46 #include <glib/gi18n-lib.h>
47
48 #include <libedataserver/e-iconv.h>
49 #include <libedataserver/e-sexp.h>
50
51 #include "camel-debug.h"
52 #include "camel-exception.h"
53 #include "camel-filter-search.h"
54 #include "camel-mime-message.h"
55 #include "camel-multipart.h"
56 #include "camel-provider.h"
57 #include "camel-search-private.h"
58 #include "camel-session.h"
59 #include "camel-stream-fs.h"
60 #include "camel-stream-mem.h"
61 #include "camel-url.h"
62
63 #define d(x)
64
65 typedef struct {
66         CamelSession *session;
67         CamelFilterSearchGetMessageFunc get_message;
68         void *get_message_data;
69         CamelMimeMessage *message;
70         CamelMessageInfo *info;
71         const char *source;
72         CamelException *ex;
73 } FilterMessageSearch;
74
75 /* ESExp callbacks */
76 static ESExpResult *header_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
77 static ESExpResult *header_matches (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
78 static ESExpResult *header_starts_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
79 static ESExpResult *header_ends_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
80 static ESExpResult *header_exists (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
81 static ESExpResult *header_soundex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
82 static ESExpResult *header_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
83 static ESExpResult *header_full_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
84 static ESExpResult *match_all (struct _ESExp *f, int argc, struct _ESExpTerm **argv, FilterMessageSearch *fms);
85 static ESExpResult *body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
86 static ESExpResult *body_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
87 static ESExpResult *user_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
88 static ESExpResult *user_tag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
89 static ESExpResult *system_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
90 static ESExpResult *get_sent_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
91 static ESExpResult *get_received_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
92 static ESExpResult *get_current_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
93 static ESExpResult *header_source (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
94 static ESExpResult *get_size (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
95 static ESExpResult *pipe_message (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
96 static ESExpResult *junk_test (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms);
97
98 /* builtin functions */
99 static struct {
100         char *name;
101         ESExpFunc *func;
102         int type;               /* set to 1 if a function can perform shortcut evaluation, or
103                                    doesn't execute everything, 0 otherwise */
104 } symbols[] = {
105         { "match-all",          (ESExpFunc *) match_all,          1 },
106         { "body-contains",      (ESExpFunc *) body_contains,      0 },
107         { "body-regex",         (ESExpFunc *) body_regex,         0 },
108         { "header-contains",    (ESExpFunc *) header_contains,    0 },
109         { "header-matches",     (ESExpFunc *) header_matches,     0 },
110         { "header-starts-with", (ESExpFunc *) header_starts_with, 0 },
111         { "header-ends-with",   (ESExpFunc *) header_ends_with,   0 },
112         { "header-exists",      (ESExpFunc *) header_exists,      0 },
113         { "header-soundex",     (ESExpFunc *) header_soundex,     0 },
114         { "header-regex",       (ESExpFunc *) header_regex,       0 },
115         { "header-full-regex",  (ESExpFunc *) header_full_regex,  0 },
116         { "user-tag",           (ESExpFunc *) user_tag,           0 },
117         { "user-flag",          (ESExpFunc *) user_flag,          0 },
118         { "system-flag",        (ESExpFunc *) system_flag,        0 },
119         { "get-sent-date",      (ESExpFunc *) get_sent_date,      0 },
120         { "get-received-date",  (ESExpFunc *) get_received_date,  0 },
121         { "get-current-date",   (ESExpFunc *) get_current_date,   0 },
122         { "header-source",      (ESExpFunc *) header_source,      0 },
123         { "get-size",           (ESExpFunc *) get_size,           0 },
124         { "pipe-message",       (ESExpFunc *) pipe_message,       0 },
125         { "junk-test",          (ESExpFunc *) junk_test,          0 },
126 };
127
128
129 static CamelMimeMessage *
130 camel_filter_search_get_message (FilterMessageSearch *fms, struct _ESExp *sexp)
131 {
132         if (fms->message)
133                 return fms->message;
134         
135         fms->message = fms->get_message (fms->get_message_data, fms->ex);
136         
137         if (fms->message == NULL)
138                 e_sexp_fatal_error (sexp, _("Failed to retrieve message"));
139         
140         return fms->message;
141 }
142
143 static ESExpResult *
144 check_header (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms, camel_search_match_t how)
145 {
146         gboolean matched = FALSE;
147         ESExpResult *r;
148         int i;
149         
150         if (argc > 1 && argv[0]->type == ESEXP_RES_STRING) {
151                 char *name = argv[0]->value.string;
152
153                 /* shortcut: a match for "" against any header always matches */
154                 for (i=1; i<argc && !matched; i++)
155                         matched = argv[i]->type == ESEXP_RES_STRING && argv[i]->value.string[0] == 0;
156
157                 if (g_ascii_strcasecmp(name, "x-camel-mlist") == 0) {
158                         const char *list = camel_message_info_mlist(fms->info);
159
160                         if (list) {
161                                 for (i=1; i<argc && !matched; i++) {
162                                         if (argv[i]->type == ESEXP_RES_STRING)
163                                                 matched = camel_search_header_match(list, argv[i]->value.string, how, CAMEL_SEARCH_TYPE_MLIST, NULL);
164                                 }
165                         }
166                 } else {
167                         CamelMimeMessage *message = camel_filter_search_get_message (fms, f);
168                         struct _camel_header_raw *header;
169                         const char *charset = NULL;
170                         camel_search_t type = CAMEL_SEARCH_TYPE_ENCODED;
171                         CamelContentType *ct;
172
173                         /* FIXME: what about Resent-To, Resent-Cc and Resent-From? */
174                         if (g_ascii_strcasecmp("to", name) == 0 || g_ascii_strcasecmp("cc", name) == 0 || g_ascii_strcasecmp("from", name) == 0)
175                                 type = CAMEL_SEARCH_TYPE_ADDRESS_ENCODED;
176                         else if (message) {
177                                 ct = camel_mime_part_get_content_type (CAMEL_MIME_PART (message));
178                                 if (ct) {
179                                         charset = camel_content_type_param (ct, "charset");
180                                         charset = e_iconv_charset_name (charset);
181                                 }
182                         }
183
184                         for (header = ((CamelMimePart *)message)->headers; header && !matched; header = header->next) {
185                                 if (!g_ascii_strcasecmp(header->name, name)) {
186                                         for (i=1; i<argc && !matched; i++) {
187                                                 if (argv[i]->type == ESEXP_RES_STRING)
188                                                         matched = camel_search_header_match(header->value, argv[i]->value.string, how, type, charset);
189                                         }
190                                 }
191                         }
192                 }
193         }
194         
195         r = e_sexp_result_new (f, ESEXP_RES_BOOL);
196         r->value.bool = matched;
197         
198         return r;
199 }
200
201 static ESExpResult *
202 header_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
203 {
204         return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_CONTAINS);
205 }
206
207
208 static ESExpResult *
209 header_matches (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
210 {
211         return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_EXACT);
212 }
213
214 static ESExpResult *
215 header_starts_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
216 {
217         return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_STARTS);
218 }
219
220 static ESExpResult *
221 header_ends_with (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
222 {
223         return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_ENDS);
224 }
225
226 static ESExpResult *
227 header_soundex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
228 {
229         return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_SOUNDEX);
230 }
231
232 static ESExpResult *
233 header_exists (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
234 {
235         CamelMimeMessage *message;
236         gboolean matched = FALSE;
237         ESExpResult *r;
238         int i;
239         
240         message = camel_filter_search_get_message (fms, f);
241         
242         for (i = 0; i < argc && !matched; i++) {
243                 if (argv[i]->type == ESEXP_RES_STRING)
244                         matched = camel_medium_get_header (CAMEL_MEDIUM (message), argv[i]->value.string) != NULL;
245         }
246         
247         r = e_sexp_result_new (f, ESEXP_RES_BOOL);
248         r->value.bool = matched;
249         
250         return r;
251 }
252
253 static ESExpResult *
254 header_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
255 {
256         ESExpResult *r = e_sexp_result_new (f, ESEXP_RES_BOOL);
257         CamelMimeMessage *message;
258         regex_t pattern;
259         const char *contents;
260         
261         message = camel_filter_search_get_message (fms, f);
262         
263         if (argc > 1 && argv[0]->type == ESEXP_RES_STRING
264             && (contents = camel_medium_get_header (CAMEL_MEDIUM (message), argv[0]->value.string))
265             && camel_search_build_match_regex(&pattern, CAMEL_SEARCH_MATCH_REGEX|CAMEL_SEARCH_MATCH_ICASE, argc-1, argv+1, fms->ex) == 0) {
266                 r->value.bool = regexec (&pattern, contents, 0, NULL, 0) == 0;
267                 regfree (&pattern);
268         } else
269                 r->value.bool = FALSE;
270         
271         return r;
272 }
273
274 static gchar *
275 get_full_header (CamelMimeMessage *message)
276 {
277         CamelMimePart *mp = CAMEL_MIME_PART (message);
278         GString *str = g_string_new ("");
279         char   *ret;
280         struct _camel_header_raw *h;
281         
282         for (h = mp->headers; h; h = h->next) {
283                 if (h->value != NULL) {
284                         g_string_append (str, h->name);
285                         if (isspace (h->value[0]))
286                                 g_string_append (str, ":");
287                         else
288                                 g_string_append (str, ": ");
289                         g_string_append (str, h->value);
290                         g_string_append_c(str, '\n');
291                 }
292         }
293         
294         ret = str->str;
295         g_string_free (str, FALSE);
296         
297         return ret;
298 }
299
300 static ESExpResult *
301 header_full_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
302 {
303         ESExpResult *r = e_sexp_result_new (f, ESEXP_RES_BOOL);
304         CamelMimeMessage *message;
305         regex_t pattern;
306         char *contents;
307         
308         if (camel_search_build_match_regex(&pattern, CAMEL_SEARCH_MATCH_REGEX|CAMEL_SEARCH_MATCH_ICASE|CAMEL_SEARCH_MATCH_NEWLINE,
309                                            argc, argv, fms->ex) == 0) {
310                 message = camel_filter_search_get_message (fms, f);
311                 contents = get_full_header (message);
312                 r->value.bool = regexec (&pattern, contents, 0, NULL, 0) == 0;
313                 g_free (contents);
314                 regfree (&pattern);
315         } else
316                 r->value.bool = FALSE;
317         
318         return r;
319 }
320
321 static ESExpResult *
322 match_all (struct _ESExp *f, int argc, struct _ESExpTerm **argv, FilterMessageSearch *fms)
323 {
324         /* match-all: when dealing with single messages is a no-op */
325         ESExpResult *r;
326         
327         if (argc > 0)
328                 return e_sexp_term_eval (f, argv[0]);
329         
330         r = e_sexp_result_new (f, ESEXP_RES_BOOL);
331         r->value.bool = TRUE;
332         
333         return r;
334 }
335
336 static ESExpResult *
337 body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
338 {
339         ESExpResult *r = e_sexp_result_new (f, ESEXP_RES_BOOL);
340         CamelMimeMessage *message;
341         regex_t pattern;
342         
343         if (camel_search_build_match_regex (&pattern, CAMEL_SEARCH_MATCH_ICASE, argc, argv, fms->ex) == 0) {
344                 message = camel_filter_search_get_message (fms, f);
345                 r->value.bool = camel_search_message_body_contains ((CamelDataWrapper *) message, &pattern);
346                 regfree (&pattern);
347         } else
348                 r->value.bool = FALSE;
349         
350         return r;
351 }
352
353 static ESExpResult *
354 body_regex (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
355 {
356         ESExpResult *r = e_sexp_result_new(f, ESEXP_RES_BOOL);
357         CamelMimeMessage *message;
358         regex_t pattern;
359         
360         if (camel_search_build_match_regex(&pattern, CAMEL_SEARCH_MATCH_ICASE|CAMEL_SEARCH_MATCH_REGEX|CAMEL_SEARCH_MATCH_NEWLINE,
361                                            argc, argv, fms->ex) == 0) {
362                 message = camel_filter_search_get_message (fms, f);
363                 r->value.bool = camel_search_message_body_contains ((CamelDataWrapper *) message, &pattern);
364                 regfree (&pattern);
365         } else
366                 r->value.bool = FALSE;
367         
368         return r;
369 }
370
371 static ESExpResult *
372 user_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
373 {
374         ESExpResult *r;
375         gboolean truth = FALSE;
376         int i;
377         
378         /* performs an OR of all words */
379         for (i = 0; i < argc && !truth; i++) {
380                 if (argv[i]->type == ESEXP_RES_STRING
381                     && camel_message_info_user_flag(fms->info, argv[i]->value.string)) {
382                         truth = TRUE;
383                         break;
384                 }
385         }
386         
387         r = e_sexp_result_new (f, ESEXP_RES_BOOL);
388         r->value.bool = truth;
389         
390         return r;
391 }
392
393 static ESExpResult *
394 system_flag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
395 {
396         ESExpResult *r;
397         
398         if (argc != 1 || argv[0]->type != ESEXP_RES_STRING)
399                 e_sexp_fatal_error(f, _("Invalid arguments to (system-flag)"));
400         
401         r = e_sexp_result_new (f, ESEXP_RES_BOOL);
402         r->value.bool = camel_system_flag_get (camel_message_info_flags(fms->info), argv[0]->value.string);
403         
404         return r;
405 }
406
407 static ESExpResult *
408 user_tag (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
409 {
410         ESExpResult *r;
411         const char *tag;
412
413         if (argc != 1 || argv[0]->type != ESEXP_RES_STRING)
414                 e_sexp_fatal_error(f, _("Invalid arguments to (user-tag)"));
415         
416         tag = camel_message_info_user_tag(fms->info, argv[0]->value.string);
417         
418         r = e_sexp_result_new (f, ESEXP_RES_STRING);
419         r->value.string = g_strdup (tag ? tag : "");
420         
421         return r;
422 }
423
424 static ESExpResult *
425 get_sent_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
426 {
427         CamelMimeMessage *message;
428         ESExpResult *r;
429         
430         message = camel_filter_search_get_message (fms, f);
431         r = e_sexp_result_new (f, ESEXP_RES_INT);
432         r->value.number = camel_mime_message_get_date (message, NULL);
433         
434         return r;
435 }
436
437 static ESExpResult *
438 get_received_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
439 {
440         CamelMimeMessage *message;
441         ESExpResult *r;
442         
443         message = camel_filter_search_get_message (fms, f);
444         r = e_sexp_result_new (f, ESEXP_RES_INT);
445         r->value.number = camel_mime_message_get_date_received (message, NULL);
446         
447         return r;
448 }
449
450 static ESExpResult *
451 get_current_date (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
452 {
453         ESExpResult *r;
454         
455         r = e_sexp_result_new (f, ESEXP_RES_INT);
456         r->value.number = time (NULL);
457         
458         return r;
459 }
460
461 static ESExpResult *
462 header_source (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
463 {
464         CamelMimeMessage *message;
465         ESExpResult *r;
466         const char *src;
467         int truth = FALSE, i;
468         CamelProvider *provider;
469         CamelURL *uria, *urib;
470
471         if (fms->source) {
472                 src = fms->source;
473         } else {
474                 message = camel_filter_search_get_message(fms, f);
475                 src = camel_mime_message_get_source(message);
476         }
477
478         if (src
479             && (provider = camel_provider_get(src, NULL))
480             && provider->url_equal) {
481                 uria = camel_url_new(src, NULL);
482                 if (uria) {
483                         for (i=0;i<argc && !truth;i++) {
484                                 if (argv[i]->type == ESEXP_RES_STRING
485                                     && (urib = camel_url_new(argv[i]->value.string, NULL))) {
486                                         truth = provider->url_equal(uria, urib);
487                                         camel_url_free(urib);
488                                 }
489                         }
490                         camel_url_free(uria);
491                 }
492         }
493
494         r = e_sexp_result_new(f, ESEXP_RES_BOOL);
495         r->value.bool = truth;
496         
497         return r;
498 }
499
500 /* remember, the size comparisons are done at Kbytes */
501 static ESExpResult *
502 get_size (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
503 {
504         ESExpResult *r;
505         
506         r = e_sexp_result_new(f, ESEXP_RES_INT);
507         r->value.number = camel_message_info_size(fms->info) / 1024;
508
509         return r;
510 }
511
512 #ifndef G_OS_WIN32
513 static void
514 child_setup_func (gpointer user_data)
515 {
516         setsid ();
517 }
518 #else
519 #define child_setup_func NULL
520 #endif
521
522 typedef struct {
523         gint child_status;
524         GMainLoop *loop;
525 } child_watch_data_t;
526
527 static void
528 child_watch (GPid     pid,
529              gint     status,
530              gpointer data)
531 {
532         child_watch_data_t *child_watch_data = data;
533
534         g_spawn_close_pid (pid);
535
536         child_watch_data->child_status = status;
537         g_main_loop_quit (child_watch_data->loop);
538 }
539
540 static int
541 run_command (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
542 {
543         CamelMimeMessage *message;
544         CamelStream *stream;
545         int i;
546         int pipe_to_child;
547         GPid child_pid;
548         GError *error = NULL;
549         GPtrArray *args;
550         child_watch_data_t child_watch_data;
551         GSource *source;
552         GMainContext *context;
553         
554         if (argc < 1 || argv[0]->value.string[0] == '\0')
555                 return 0;
556         
557         args = g_ptr_array_new ();
558         for (i = 0; i < argc; i++)
559                 g_ptr_array_add (args, argv[i]->value.string);
560         g_ptr_array_add (args, NULL);
561         
562         if (!g_spawn_async_with_pipes (NULL,
563                                        (gchar **) args->pdata,
564                                        NULL,
565                                        G_SPAWN_DO_NOT_REAP_CHILD |
566                                        G_SPAWN_SEARCH_PATH |
567                                        G_SPAWN_STDOUT_TO_DEV_NULL |
568                                        G_SPAWN_STDERR_TO_DEV_NULL,
569                                        child_setup_func,
570                                        NULL,
571                                        &child_pid,
572                                        &pipe_to_child,
573                                        NULL,
574                                        NULL,
575                                        &error)) {
576                 g_ptr_array_free (args, TRUE);
577
578                 camel_exception_setv (fms->ex, CAMEL_EXCEPTION_SYSTEM,
579                                       _("Failed to create create child process '%s': %s"),
580                                       argv[0]->value.string, error->message);
581                 g_error_free (error);
582                 return -1;
583         }
584         
585         g_ptr_array_free (args, TRUE);
586         
587         message = camel_filter_search_get_message (fms, f);
588         
589         stream = camel_stream_fs_new_with_fd (pipe_to_child);
590         camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream);
591         camel_stream_flush (stream);
592         camel_object_unref (stream);
593         
594         context = g_main_context_new ();
595         child_watch_data.loop = g_main_loop_new (context, FALSE);
596         g_main_context_unref (context);
597
598         source = g_child_watch_source_new (child_pid);
599         g_source_set_callback (source, (GSourceFunc) child_watch, &child_watch_data, NULL);
600         g_source_attach (source, g_main_loop_get_context (child_watch_data.loop));
601         g_source_unref (source);
602
603         g_main_loop_run (child_watch_data.loop);
604         g_main_loop_unref (child_watch_data.loop);
605
606 #ifndef G_OS_WIN32
607         if (WIFEXITED (child_watch_data.child_status))
608                 return WEXITSTATUS (child_watch_data.child_status);
609         else
610                 return -1;
611 #else
612         return child_watch_data.child_status;
613 #endif
614 }
615
616 static ESExpResult *
617 pipe_message (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
618 {
619         ESExpResult *r;
620         int retval, i;
621         
622         /* make sure all args are strings */
623         for (i = 0; i < argc; i++) {
624                 if (argv[i]->type != ESEXP_RES_STRING) {
625                         retval = -1;
626                         goto done;
627                 }
628         }
629         
630         retval = run_command (f, argc, argv, fms);
631         
632  done:
633         r = e_sexp_result_new (f, ESEXP_RES_INT);
634         r->value.number = retval;
635         
636         return r;
637 }
638
639 static ESExpResult *
640 junk_test (struct _ESExp *f, int argc, struct _ESExpResult **argv, FilterMessageSearch *fms)
641 {
642         ESExpResult *r;
643         gboolean retval = FALSE;
644
645         if (fms->session->junk_plugin != NULL) {
646                 retval = camel_junk_plugin_check_junk (fms->session->junk_plugin, camel_filter_search_get_message (fms, f));
647                 
648                 if (camel_debug ("junk"))
649                         printf("junk filter => %s\n", retval ? "*JUNK*" : "clean");
650         }
651
652         r = e_sexp_result_new (f, ESEXP_RES_BOOL);
653         r->value.number = retval;
654
655         return r;
656 }
657
658 /**
659  * camel_filter_search_match:
660  * @session:
661  * @get_message: function to retrieve the message if necessary
662  * @data: data for above
663  * @info:
664  * @source:
665  * @expression:
666  * @ex:
667  *
668  * Returns one of CAMEL_SEARCH_MATCHED, CAMEL_SEARCH_NOMATCH, or CAMEL_SEARCH_ERROR.
669  **/
670 int
671 camel_filter_search_match (CamelSession *session,
672                            CamelFilterSearchGetMessageFunc get_message, void *data,
673                            CamelMessageInfo *info, const char *source,
674                            const char *expression, CamelException *ex)
675 {
676         FilterMessageSearch fms;
677         ESExp *sexp;
678         ESExpResult *result;
679         gboolean retval;
680         int i;
681
682         fms.session = session;
683         fms.get_message = get_message;
684         fms.get_message_data = data;
685         fms.message = NULL;
686         fms.info = info;
687         fms.source = source;
688         fms.ex = ex;
689         
690         sexp = e_sexp_new ();
691         
692         for (i = 0; i < sizeof (symbols) / sizeof (symbols[0]); i++) {
693                 if (symbols[i].type == 1)
694                         e_sexp_add_ifunction (sexp, 0, symbols[i].name, (ESExpIFunc *)symbols[i].func, &fms);
695                 else
696                         e_sexp_add_function (sexp, 0, symbols[i].name, symbols[i].func, &fms);
697         }
698         
699         e_sexp_input_text (sexp, expression, strlen (expression));
700         if (e_sexp_parse (sexp) == -1) {
701                 if (!camel_exception_is_set (ex))
702                         /* A filter search is a search through your filters, ie. your filters is the corpus being searched thru. */
703                         camel_exception_setv (ex, 1, _("Error executing filter search: %s: %s"),
704                                               e_sexp_error (sexp), expression);
705                 goto error;
706         }
707         
708         result = e_sexp_eval (sexp);
709         if (result == NULL) {
710                 if (!camel_exception_is_set (ex))
711                         camel_exception_setv (ex, 1, _("Error executing filter search: %s: %s"),
712                                               e_sexp_error (sexp), expression);
713                 goto error;
714         }
715         
716         if (result->type == ESEXP_RES_BOOL)
717                 retval = result->value.bool ? CAMEL_SEARCH_MATCHED : CAMEL_SEARCH_NOMATCH;
718         else
719                 retval = CAMEL_SEARCH_NOMATCH;
720         
721         e_sexp_result_free (sexp, result);
722         e_sexp_unref (sexp);
723         
724         if (fms.message)
725                 camel_object_unref (fms.message);
726         
727         return retval;
728         
729  error:
730         if (fms.message)
731                 camel_object_unref (fms.message);
732         
733         e_sexp_unref (sexp);
734         
735         return CAMEL_SEARCH_ERROR;
736 }