1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
4 * Michael Zucchi <NotZed@Ximian.com>
6 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU Lesser General Public
10 * License as published by the Free Software Foundation.
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 GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
28 /* POSIX requires <sys/types.h> be included before <regex.h> */
29 #include <sys/types.h>
40 #include <glib/gi18n-lib.h>
46 #include "camel-debug.h"
47 #include "camel-filter-search.h"
48 #include "camel-iconv.h"
49 #include "camel-mime-message.h"
50 #include "camel-multipart.h"
51 #include "camel-provider.h"
52 #include "camel-search-private.h"
53 #include "camel-session.h"
54 #include "camel-stream-fs.h"
55 #include "camel-stream-mem.h"
56 #include "camel-string-utils.h"
57 #include "camel-url.h"
62 CamelSession *session;
63 CamelFilterSearchGetMessageFunc get_message;
64 gpointer get_message_data;
65 CamelMimeMessage *message;
66 CamelMessageInfo *info;
69 } FilterMessageSearch;
71 /* CamelSExp callbacks */
72 static CamelSExpResult *header_contains (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
73 static CamelSExpResult *header_has_words (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
74 static CamelSExpResult *header_matches (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
75 static CamelSExpResult *header_starts_with (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
76 static CamelSExpResult *header_ends_with (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
77 static CamelSExpResult *header_exists (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
78 static CamelSExpResult *header_soundex (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
79 static CamelSExpResult *header_regex (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
80 static CamelSExpResult *header_full_regex (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
81 static CamelSExpResult *match_all (struct _CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, FilterMessageSearch *fms);
82 static CamelSExpResult *body_contains (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
83 static CamelSExpResult *body_regex (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
84 static CamelSExpResult *user_flag (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
85 static CamelSExpResult *user_tag (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
86 static CamelSExpResult *system_flag (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
87 static CamelSExpResult *get_sent_date (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
88 static CamelSExpResult *get_received_date (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
89 static CamelSExpResult *get_current_date (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
90 static CamelSExpResult *get_relative_months (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
91 static CamelSExpResult *header_source (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
92 static CamelSExpResult *get_size (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
93 static CamelSExpResult *pipe_message (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
94 static CamelSExpResult *junk_test (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, FilterMessageSearch *fms);
96 /* builtin functions */
100 gint type; /* set to 1 if a function can perform shortcut evaluation, or
101 doesn't execute everything, 0 otherwise */
103 { "match-all", (CamelSExpFunc) match_all, 1 },
104 { "body-contains", (CamelSExpFunc) body_contains, 0 },
105 { "body-regex", (CamelSExpFunc) body_regex, 0 },
106 { "header-contains", (CamelSExpFunc) header_contains, 0 },
107 { "header-has-words", (CamelSExpFunc) header_has_words, 0 },
108 { "header-matches", (CamelSExpFunc) header_matches, 0 },
109 { "header-starts-with", (CamelSExpFunc) header_starts_with, 0 },
110 { "header-ends-with", (CamelSExpFunc) header_ends_with, 0 },
111 { "header-exists", (CamelSExpFunc) header_exists, 0 },
112 { "header-soundex", (CamelSExpFunc) header_soundex, 0 },
113 { "header-regex", (CamelSExpFunc) header_regex, 0 },
114 { "header-full-regex", (CamelSExpFunc) header_full_regex, 0 },
115 { "user-tag", (CamelSExpFunc) user_tag, 0 },
116 { "user-flag", (CamelSExpFunc) user_flag, 0 },
117 { "system-flag", (CamelSExpFunc) system_flag, 0 },
118 { "get-sent-date", (CamelSExpFunc) get_sent_date, 0 },
119 { "get-received-date", (CamelSExpFunc) get_received_date, 0 },
120 { "get-current-date", (CamelSExpFunc) get_current_date, 0 },
121 { "get-relative-months",(CamelSExpFunc) get_relative_months,0 },
122 { "header-source", (CamelSExpFunc) header_source, 0 },
123 { "get-size", (CamelSExpFunc) get_size, 0 },
124 { "pipe-message", (CamelSExpFunc) pipe_message, 0 },
125 { "junk-test", (CamelSExpFunc) junk_test, 0 },
128 static CamelMimeMessage *
129 camel_filter_search_get_message (FilterMessageSearch *fms,
130 struct _CamelSExp *sexp)
135 fms->message = fms->get_message (fms->get_message_data, fms->error);
137 if (fms->message == NULL)
138 camel_sexp_fatal_error (sexp, _("Failed to retrieve message"));
144 check_header_in_message_info (CamelMessageInfo *info,
146 struct _CamelSExpResult **argv,
147 camel_search_match_t how,
150 struct _KnownHeaders {
151 const gchar *header_name;
153 } known_headers[] = {
154 { "Subject", CAMEL_MESSAGE_INFO_SUBJECT },
155 { "From", CAMEL_MESSAGE_INFO_FROM },
156 { "To", CAMEL_MESSAGE_INFO_TO },
157 { "Cc", CAMEL_MESSAGE_INFO_CC }
159 camel_search_t type = CAMEL_SEARCH_TYPE_ENCODED;
160 const gchar *name, *value;
161 gboolean found = FALSE;
164 g_return_val_if_fail (argc > 1, FALSE);
165 g_return_val_if_fail (argv != NULL, FALSE);
166 g_return_val_if_fail (matched != NULL, FALSE);
171 name = argv[0]->value.string;
172 g_return_val_if_fail (name != NULL, FALSE);
176 for (ii = 0; ii < G_N_ELEMENTS (known_headers); ii++) {
177 found = g_ascii_strcasecmp (name, known_headers[ii].header_name) == 0;
179 value = camel_message_info_ptr (info, known_headers[ii].info_key);
180 if (known_headers[ii].info_key == CAMEL_MESSAGE_INFO_FROM ||
181 known_headers[ii].info_key == CAMEL_MESSAGE_INFO_TO ||
182 known_headers[ii].info_key == CAMEL_MESSAGE_INFO_CC)
183 type = CAMEL_SEARCH_TYPE_ADDRESS_ENCODED;
188 if (!found || !value)
191 for (ii = 1; ii < argc && !*matched; ii++) {
192 if (argv[ii]->type == CAMEL_SEXP_RES_STRING)
193 *matched = camel_search_header_match (value, argv[ii]->value.string, how, type, NULL);
199 static CamelSExpResult *
200 check_header (struct _CamelSExp *f,
202 struct _CamelSExpResult **argv,
203 FilterMessageSearch *fms,
204 camel_search_match_t how)
206 gboolean matched = FALSE;
210 if (argc > 1 && argv[0]->type == CAMEL_SEXP_RES_STRING) {
211 gchar *name = argv[0]->value.string;
213 /* shortcut: a match for "" against any header always matches */
214 for (i = 1; i < argc && !matched; i++)
215 matched = argv[i]->type == CAMEL_SEXP_RES_STRING && argv[i]->value.string[0] == 0;
217 if (g_ascii_strcasecmp (name, "x-camel-mlist") == 0) {
218 const gchar *list = camel_message_info_mlist (fms->info);
221 for (i = 1; i < argc && !matched; i++) {
222 if (argv[i]->type == CAMEL_SEXP_RES_STRING)
223 matched = camel_search_header_match (list, argv[i]->value.string, how, CAMEL_SEARCH_TYPE_MLIST, NULL);
226 } else if (fms->message || !check_header_in_message_info (fms->info, argc, argv, how, &matched)) {
227 CamelMimeMessage *message;
228 CamelMimePart *mime_part;
229 struct _camel_header_raw *header;
230 const gchar *charset = NULL;
231 camel_search_t type = CAMEL_SEARCH_TYPE_ENCODED;
232 CamelContentType *ct;
234 message = camel_filter_search_get_message (fms, f);
235 mime_part = CAMEL_MIME_PART (message);
237 /* FIXME: what about Resent-To, Resent-Cc and Resent-From? */
238 if (g_ascii_strcasecmp ("to", name) == 0 || g_ascii_strcasecmp ("cc", name) == 0 || g_ascii_strcasecmp ("from", name) == 0)
239 type = CAMEL_SEARCH_TYPE_ADDRESS_ENCODED;
241 ct = camel_mime_part_get_content_type (mime_part);
243 charset = camel_content_type_param (ct, "charset");
244 charset = camel_iconv_charset_name (charset);
248 for (header = mime_part->headers; header && !matched; header = header->next) {
249 if (!g_ascii_strcasecmp (header->name, name)) {
250 for (i = 1; i < argc && !matched; i++) {
251 if (argv[i]->type == CAMEL_SEXP_RES_STRING)
252 matched = camel_search_header_match (header->value, argv[i]->value.string, how, type, charset);
259 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
260 r->value.boolean = matched;
265 static CamelSExpResult *
266 header_contains (struct _CamelSExp *f,
268 struct _CamelSExpResult **argv,
269 FilterMessageSearch *fms)
271 return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_CONTAINS);
274 static CamelSExpResult *
275 header_has_words (struct _CamelSExp *f,
277 struct _CamelSExpResult **argv,
278 FilterMessageSearch *fms)
280 return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_WORD);
283 static CamelSExpResult *
284 header_matches (struct _CamelSExp *f,
286 struct _CamelSExpResult **argv,
287 FilterMessageSearch *fms)
289 return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_EXACT);
292 static CamelSExpResult *
293 header_starts_with (struct _CamelSExp *f,
295 struct _CamelSExpResult **argv,
296 FilterMessageSearch *fms)
298 return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_STARTS);
301 static CamelSExpResult *
302 header_ends_with (struct _CamelSExp *f,
304 struct _CamelSExpResult **argv,
305 FilterMessageSearch *fms)
307 return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_ENDS);
310 static CamelSExpResult *
311 header_soundex (struct _CamelSExp *f,
313 struct _CamelSExpResult **argv,
314 FilterMessageSearch *fms)
316 return check_header (f, argc, argv, fms, CAMEL_SEARCH_MATCH_SOUNDEX);
319 static CamelSExpResult *
320 header_exists (struct _CamelSExp *f,
322 struct _CamelSExpResult **argv,
323 FilterMessageSearch *fms)
325 CamelMimeMessage *message;
326 gboolean matched = FALSE;
330 message = camel_filter_search_get_message (fms, f);
332 for (i = 0; i < argc && !matched; i++) {
333 if (argv[i]->type == CAMEL_SEXP_RES_STRING)
334 matched = camel_medium_get_header (CAMEL_MEDIUM (message), argv[i]->value.string) != NULL;
337 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
338 r->value.boolean = matched;
343 static CamelSExpResult *
344 header_regex (struct _CamelSExp *f,
346 struct _CamelSExpResult **argv,
347 FilterMessageSearch *fms)
349 CamelSExpResult *r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
350 CamelMimeMessage *message;
352 const gchar *contents;
354 message = camel_filter_search_get_message (fms, f);
356 if (argc > 1 && argv[0]->type == CAMEL_SEXP_RES_STRING
357 && (contents = camel_medium_get_header (CAMEL_MEDIUM (message), argv[0]->value.string))
358 && camel_search_build_match_regex (&pattern, CAMEL_SEARCH_MATCH_REGEX | CAMEL_SEARCH_MATCH_ICASE, argc - 1, argv + 1, fms->error) == 0) {
359 r->value.boolean = regexec (&pattern, contents, 0, NULL, 0) == 0;
362 r->value.boolean = FALSE;
368 get_full_header (CamelMimeMessage *message)
370 CamelMimePart *mime_part;
371 GString *str = g_string_new ("");
373 struct _camel_header_raw *h;
375 mime_part = CAMEL_MIME_PART (message);
377 for (h = mime_part->headers; h; h = h->next) {
378 if (h->value != NULL) {
379 g_string_append (str, h->name);
380 if (isspace (h->value[0]))
381 g_string_append (str, ":");
383 g_string_append (str, ": ");
384 g_string_append (str, h->value);
385 g_string_append_c (str, '\n');
390 g_string_free (str, FALSE);
395 static CamelSExpResult *
396 header_full_regex (struct _CamelSExp *f,
398 struct _CamelSExpResult **argv,
399 FilterMessageSearch *fms)
401 CamelSExpResult *r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
402 CamelMimeMessage *message;
406 if (camel_search_build_match_regex (&pattern, CAMEL_SEARCH_MATCH_REGEX | CAMEL_SEARCH_MATCH_ICASE | CAMEL_SEARCH_MATCH_NEWLINE,
407 argc, argv, fms->error) == 0) {
408 message = camel_filter_search_get_message (fms, f);
409 contents = get_full_header (message);
410 r->value.boolean = regexec (&pattern, contents, 0, NULL, 0) == 0;
414 r->value.boolean = FALSE;
419 static CamelSExpResult *
420 match_all (struct _CamelSExp *f,
422 struct _CamelSExpTerm **argv,
423 FilterMessageSearch *fms)
425 /* match-all: when dealing with single messages is a no-op */
429 return camel_sexp_term_eval (f, argv[0]);
431 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
432 r->value.boolean = TRUE;
437 static CamelSExpResult *
438 body_contains (struct _CamelSExp *f,
440 struct _CamelSExpResult **argv,
441 FilterMessageSearch *fms)
443 CamelSExpResult *r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
444 CamelMimeMessage *message;
447 if (camel_search_build_match_regex (&pattern, CAMEL_SEARCH_MATCH_ICASE, argc, argv, fms->error) == 0) {
448 message = camel_filter_search_get_message (fms, f);
449 r->value.boolean = camel_search_message_body_contains ((CamelDataWrapper *) message, &pattern);
452 r->value.boolean = FALSE;
457 static CamelSExpResult *
458 body_regex (struct _CamelSExp *f,
460 struct _CamelSExpResult **argv,
461 FilterMessageSearch *fms)
463 CamelSExpResult *r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
464 CamelMimeMessage *message;
467 if (camel_search_build_match_regex (&pattern, CAMEL_SEARCH_MATCH_ICASE | CAMEL_SEARCH_MATCH_REGEX | CAMEL_SEARCH_MATCH_NEWLINE,
468 argc, argv, fms->error) == 0) {
469 message = camel_filter_search_get_message (fms, f);
470 r->value.boolean = camel_search_message_body_contains ((CamelDataWrapper *) message, &pattern);
473 r->value.boolean = FALSE;
478 static CamelSExpResult *
479 user_flag (struct _CamelSExp *f,
481 struct _CamelSExpResult **argv,
482 FilterMessageSearch *fms)
485 gboolean truth = FALSE;
488 /* performs an OR of all words */
489 for (i = 0; i < argc && !truth; i++) {
490 if (argv[i]->type == CAMEL_SEXP_RES_STRING
491 && camel_message_info_user_flag (fms->info, argv[i]->value.string)) {
497 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
498 r->value.boolean = truth;
503 static CamelSExpResult *
504 system_flag (struct _CamelSExp *f,
506 struct _CamelSExpResult **argv,
507 FilterMessageSearch *fms)
511 if (argc != 1 || argv[0]->type != CAMEL_SEXP_RES_STRING)
512 camel_sexp_fatal_error (f, _("Invalid arguments to (system-flag)"));
514 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
515 r->value.boolean = camel_system_flag_get (camel_message_info_flags (fms->info), argv[0]->value.string);
520 static CamelSExpResult *
521 user_tag (struct _CamelSExp *f,
523 struct _CamelSExpResult **argv,
524 FilterMessageSearch *fms)
529 if (argc != 1 || argv[0]->type != CAMEL_SEXP_RES_STRING)
530 camel_sexp_fatal_error (f, _("Invalid arguments to (user-tag)"));
532 tag = camel_message_info_user_tag (fms->info, argv[0]->value.string);
534 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING);
535 r->value.string = g_strdup (tag ? tag : "");
540 static CamelSExpResult *
541 get_sent_date (struct _CamelSExp *f,
543 struct _CamelSExpResult **argv,
544 FilterMessageSearch *fms)
546 CamelMimeMessage *message;
549 message = camel_filter_search_get_message (fms, f);
550 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT);
551 r->value.number = camel_mime_message_get_date (message, NULL);
556 static CamelSExpResult *
557 get_received_date (struct _CamelSExp *f,
559 struct _CamelSExpResult **argv,
560 FilterMessageSearch *fms)
562 CamelMimeMessage *message;
565 message = camel_filter_search_get_message (fms, f);
566 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT);
567 r->value.number = camel_mime_message_get_date_received (message, NULL);
572 static CamelSExpResult *
573 get_current_date (struct _CamelSExp *f,
575 struct _CamelSExpResult **argv,
576 FilterMessageSearch *fms)
580 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT);
581 r->value.number = time (NULL);
586 static CamelSExpResult *
587 get_relative_months (struct _CamelSExp *f,
589 struct _CamelSExpResult **argv,
590 FilterMessageSearch *fms)
594 if (argc != 1 || argv[0]->type != CAMEL_SEXP_RES_INT) {
595 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
596 r->value.boolean = FALSE;
598 g_debug ("%s: Expecting 1 argument, an integer, but got %d arguments", G_STRFUNC, argc);
600 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT);
601 r->value.number = camel_folder_search_util_add_months (time (NULL), argv[0]->value.number);
607 static CamelService *
608 ref_service_for_source (CamelSession *session,
611 CamelService *service = NULL;
613 /* Source strings are now CamelService UIDs. */
615 service = camel_session_ref_service (session, src);
617 /* For backward-compability, also handle CamelService URLs. */
618 if (service == NULL && src != NULL) {
621 url = camel_url_new (src, NULL);
623 if (service == NULL && url != NULL)
624 service = camel_session_ref_service_by_url (
625 session, url, CAMEL_PROVIDER_STORE);
627 if (service == NULL && url != NULL)
628 service = camel_session_ref_service_by_url (
629 session, url, CAMEL_PROVIDER_TRANSPORT);
632 camel_url_free (url);
638 static CamelSExpResult *
639 header_source (struct _CamelSExp *f,
641 struct _CamelSExpResult **argv,
642 FilterMessageSearch *fms)
644 CamelMimeMessage *message;
647 CamelService *msg_source = NULL;
648 gboolean truth = FALSE;
653 message = camel_filter_search_get_message (fms, f);
654 src = camel_mime_message_get_source (message);
658 msg_source = ref_service_for_source (fms->session, src);
660 if (msg_source != NULL) {
663 for (ii = 0; ii < argc && !truth; ii++) {
664 if (argv[ii]->type == CAMEL_SEXP_RES_STRING) {
665 CamelService *candidate;
667 candidate = ref_service_for_source (
669 argv[ii]->value.string);
670 if (candidate != NULL) {
671 truth = (msg_source == candidate);
672 g_object_unref (candidate);
677 g_object_unref (msg_source);
680 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
681 r->value.boolean = truth;
686 /* remember, the size comparisons are done at Kbytes */
687 static CamelSExpResult *
688 get_size (struct _CamelSExp *f,
690 struct _CamelSExpResult **argv,
691 FilterMessageSearch *fms)
695 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT);
696 r->value.number = camel_message_info_size (fms->info) / 1024;
703 child_setup_func (gpointer user_data)
708 #define child_setup_func NULL
714 } child_watch_data_t;
717 child_watch (GPid pid,
721 child_watch_data_t *child_watch_data = data;
723 g_spawn_close_pid (pid);
725 child_watch_data->child_status = status;
726 g_main_loop_quit (child_watch_data->loop);
730 run_command (struct _CamelSExp *f,
732 struct _CamelSExpResult **argv,
733 FilterMessageSearch *fms)
735 CamelMimeMessage *message;
740 GError *error = NULL;
742 child_watch_data_t child_watch_data;
744 GMainContext *context;
746 if (argc < 1 || argv[0]->value.string[0] == '\0')
749 args = g_ptr_array_new ();
750 for (i = 0; i < argc; i++)
751 g_ptr_array_add (args, argv[i]->value.string);
752 g_ptr_array_add (args, NULL);
754 if (!g_spawn_async_with_pipes (NULL,
755 (gchar **) args->pdata,
757 G_SPAWN_DO_NOT_REAP_CHILD |
758 G_SPAWN_SEARCH_PATH |
759 G_SPAWN_STDOUT_TO_DEV_NULL |
760 G_SPAWN_STDERR_TO_DEV_NULL,
768 g_ptr_array_free (args, TRUE);
771 fms->error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
772 _("Failed to create child process '%s': %s"),
773 argv[0]->value.string, error->message);
774 g_error_free (error);
778 g_ptr_array_free (args, TRUE);
780 message = camel_filter_search_get_message (fms, f);
782 stream = camel_stream_fs_new_with_fd (pipe_to_child);
783 camel_data_wrapper_write_to_stream_sync (
784 CAMEL_DATA_WRAPPER (message), stream, NULL, NULL);
785 camel_stream_flush (stream, NULL, NULL);
786 g_object_unref (stream);
788 context = g_main_context_new ();
789 child_watch_data.loop = g_main_loop_new (context, FALSE);
790 g_main_context_unref (context);
792 source = g_child_watch_source_new (child_pid);
793 g_source_set_callback (source, (GSourceFunc) child_watch, &child_watch_data, NULL);
794 g_source_attach (source, g_main_loop_get_context (child_watch_data.loop));
795 g_source_unref (source);
797 g_main_loop_run (child_watch_data.loop);
798 g_main_loop_unref (child_watch_data.loop);
801 if (WIFEXITED (child_watch_data.child_status))
802 return WEXITSTATUS (child_watch_data.child_status);
806 return child_watch_data.child_status;
810 static CamelSExpResult *
811 pipe_message (struct _CamelSExp *f,
813 struct _CamelSExpResult **argv,
814 FilterMessageSearch *fms)
819 /* make sure all args are strings */
820 for (i = 0; i < argc; i++) {
821 if (argv[i]->type != CAMEL_SEXP_RES_STRING) {
827 retval = run_command (f, argc, argv, fms);
830 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT);
831 r->value.number = retval;
836 static CamelSExpResult *
837 junk_test (struct _CamelSExp *f,
839 struct _CamelSExpResult **argv,
840 FilterMessageSearch *fms)
843 CamelMessageInfo *info = fms->info;
844 CamelJunkFilter *junk_filter;
845 CamelMessageFlags flags;
846 CamelMimeMessage *message;
847 CamelJunkStatus status;
848 const GHashTable *ht;
849 const struct _camel_header_param *node;
850 gboolean sender_is_known;
851 gboolean message_is_junk = FALSE;
852 GError *error = NULL;
854 junk_filter = camel_session_get_junk_filter (fms->session);
855 if (junk_filter == NULL)
858 /* Check if the message is already classified. */
860 flags = camel_message_info_flags (info);
862 if (flags & CAMEL_MESSAGE_JUNK) {
863 if (camel_debug ("junk"))
865 "Message has a Junk flag set already, "
866 "skipping junk test...\n");
870 if (flags & CAMEL_MESSAGE_NOTJUNK) {
871 if (camel_debug ("junk"))
873 "Message has a NotJunk flag set already, "
874 "skipping junk test...\n");
878 /* Check the headers for a junk designation. */
880 ht = camel_session_get_junk_headers (fms->session);
881 node = camel_message_info_headers (info);
883 while (node != NULL) {
884 const gchar *value = NULL;
886 if (node->name != NULL)
887 value = g_hash_table_lookup (
888 (GHashTable *) ht, node->name);
892 (camel_strstrcase (node->value, value) != NULL);
894 if (message_is_junk) {
895 if (camel_debug ("junk"))
897 "Message contains \"%s: %s\"",
905 /* If the sender is known, the message is not junk. */
907 sender_is_known = camel_session_lookup_addressbook (
908 fms->session, camel_message_info_from (info));
909 if (camel_debug ("junk"))
911 "Sender '%s' in book? %d\n",
912 camel_message_info_from (info),
917 /* Consult 3rd party junk filtering software. */
919 message = camel_filter_search_get_message (fms, f);
920 status = camel_junk_filter_classify (
921 junk_filter, message, NULL, &error);
924 const gchar *status_desc;
927 case CAMEL_JUNK_STATUS_INCONCLUSIVE:
928 status_desc = "inconclusive";
929 message_is_junk = FALSE;
931 case CAMEL_JUNK_STATUS_MESSAGE_IS_JUNK:
932 status_desc = "junk";
933 message_is_junk = TRUE;
935 case CAMEL_JUNK_STATUS_MESSAGE_IS_NOT_JUNK:
936 status_desc = "not junk";
937 message_is_junk = FALSE;
940 g_warn_if_reached ();
941 status_desc = "invalid";
942 message_is_junk = FALSE;
946 if (camel_debug ("junk"))
948 "Junk filter classification: %s\n",
951 g_warn_if_fail (status == CAMEL_JUNK_STATUS_ERROR);
952 g_warning ("%s: %s", G_STRFUNC, error->message);
953 g_error_free (error);
954 message_is_junk = FALSE;
958 if (camel_debug ("junk"))
960 "Message is determined to be %s\n",
961 message_is_junk ? "*JUNK*" : "clean");
964 r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL);
965 r->value.number = message_is_junk;
971 * camel_filter_search_match:
973 * @get_message: function to retrieve the message if necessary
974 * @data: data for above
978 * @error: return location for a #GError, or %NULL
980 * Returns: one of CAMEL_SEARCH_MATCHED, CAMEL_SEARCH_NOMATCH, or
981 * CAMEL_SEARCH_ERROR.
984 camel_filter_search_match (CamelSession *session,
985 CamelFilterSearchGetMessageFunc get_message,
987 CamelMessageInfo *info,
989 const gchar *expression,
992 FilterMessageSearch fms;
994 CamelSExpResult *result;
998 fms.session = session;
999 fms.get_message = get_message;
1000 fms.get_message_data = data;
1003 fms.source = source;
1006 sexp = camel_sexp_new ();
1008 for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
1009 if (symbols[i].type == 1)
1010 camel_sexp_add_ifunction (sexp, 0, symbols[i].name, (CamelSExpIFunc) symbols[i].func, &fms);
1012 camel_sexp_add_function (sexp, 0, symbols[i].name, symbols[i].func, &fms);
1015 camel_sexp_input_text (sexp, expression, strlen (expression));
1016 if (camel_sexp_parse (sexp) == -1) {
1017 /* A filter search is a search through your filters,
1018 * ie. your filters is the corpus being searched thru. */
1020 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1021 _("Error executing filter search: %s: %s"),
1022 camel_sexp_error (sexp), expression);
1026 result = camel_sexp_eval (sexp);
1027 if (result == NULL) {
1029 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1030 _("Error executing filter search: %s: %s"),
1031 camel_sexp_error (sexp), expression);
1035 if (result->type == CAMEL_SEXP_RES_BOOL)
1036 retval = result->value.boolean ? CAMEL_SEARCH_MATCHED : CAMEL_SEARCH_NOMATCH;
1038 retval = CAMEL_SEARCH_NOMATCH;
1040 camel_sexp_result_free (sexp, result);
1041 g_object_unref (sexp);
1044 g_object_unref (fms.message);
1050 g_object_unref (fms.message);
1052 g_object_unref (sexp);
1054 return CAMEL_SEARCH_ERROR;