1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 * Authors: Michael Zucchi <notzed@ximian.com>
6 * Jeffrey Stedfast <fejj@ximian.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.
31 #include <sys/types.h>
34 #include <glib/gstdio.h>
35 #include <glib/gi18n-lib.h>
41 #include "camel-debug.h"
42 #include "camel-file-utils.h"
43 #include "camel-filter-driver.h"
44 #include "camel-filter-search.h"
45 #include "camel-mime-message.h"
46 #include "camel-service.h"
47 #include "camel-session.h"
48 #include "camel-sexp.h"
49 #include "camel-store.h"
50 #include "camel-stream-fs.h"
51 #include "camel-stream-mem.h"
55 /* an invalid pointer */
56 #define FOLDER_INVALID ((gpointer)~0)
58 #define CAMEL_FILTER_DRIVER_GET_PRIVATE(obj) \
59 (G_TYPE_INSTANCE_GET_PRIVATE \
60 ((obj), CAMEL_TYPE_FILTER_DRIVER, CamelFilterDriverPrivate))
62 /* type of status for a log report */
65 FILTER_LOG_START, /* start of new log entry */
66 FILTER_LOG_ACTION, /* an action performed */
67 FILTER_LOG_END /* end of log */
70 /* list of rule nodes */
77 struct _CamelFilterDriverPrivate {
78 GHashTable *globals; /* global variables */
80 CamelSession *session;
82 CamelFolder *defaultfolder; /* defualt folder */
84 CamelFilterStatusFunc *statusfunc; /* status callback */
85 gpointer statusdata; /* status callback data */
87 CamelFilterShellFunc *shellfunc; /* execute shell command callback */
88 gpointer shelldata; /* execute shell command callback data */
90 CamelFilterPlaySoundFunc *playfunc; /* play-sound command callback */
91 gpointer playdata; /* play-sound command callback data */
93 CamelFilterSystemBeepFunc *beep; /* system beep callback */
94 gpointer beepdata; /* system beep callback data */
97 CamelFilterGetFolderFunc get_folder;
101 GHashTable *folders; /* folders that message has been copied to */
102 gint closed; /* close count */
103 GHashTable *only_once; /* actions to run only-once */
105 gboolean terminated; /* message processing was terminated */
106 gboolean deleted; /* message was marked for deletion */
107 gboolean copied; /* message was copied to some folder or another */
108 gboolean moved; /* message was moved to some folder or another */
110 CamelMimeMessage *message; /* input message */
111 CamelMessageInfo *info; /* message summary info */
112 const gchar *uid; /* message uid */
113 CamelFolder *source; /* message source folder */
114 gboolean modified; /* has the input message been modified? */
116 FILE *logfile; /* log file */
118 GQueue rules; /* queue of _filter_rule structs */
126 static void camel_filter_driver_log (CamelFilterDriver *driver, enum filter_log_t status, const gchar *desc, ...);
128 static CamelFolder *open_folder (CamelFilterDriver *d, const gchar *folder_url);
129 static gint close_folders (CamelFilterDriver *d);
131 static CamelSExpResult *do_delete (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
132 static CamelSExpResult *do_forward_to (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
133 static CamelSExpResult *do_copy (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
134 static CamelSExpResult *do_move (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
135 static CamelSExpResult *do_stop (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
136 static CamelSExpResult *do_label (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
137 static CamelSExpResult *do_color (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
138 static CamelSExpResult *do_score (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
139 static CamelSExpResult *do_adjust_score (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
140 static CamelSExpResult *set_flag (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
141 static CamelSExpResult *unset_flag (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
142 static CamelSExpResult *do_shell (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
143 static CamelSExpResult *do_beep (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
144 static CamelSExpResult *play_sound (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
145 static CamelSExpResult *do_only_once (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
146 static CamelSExpResult *pipe_message (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, CamelFilterDriver *);
148 /* these are our filter actions - each must have a callback */
152 gint type; /* set to 1 if a function can perform shortcut evaluation, or
153 doesn't execute everything, 0 otherwise */
155 { "delete", (CamelSExpFunc) do_delete, 0 },
156 { "forward-to", (CamelSExpFunc) do_forward_to, 0 },
157 { "copy-to", (CamelSExpFunc) do_copy, 0 },
158 { "move-to", (CamelSExpFunc) do_move, 0 },
159 { "stop", (CamelSExpFunc) do_stop, 0 },
160 { "set-label", (CamelSExpFunc) do_label, 0 },
161 { "set-color", (CamelSExpFunc) do_color, 0 },
162 { "set-score", (CamelSExpFunc) do_score, 0 },
163 { "adjust-score", (CamelSExpFunc) do_adjust_score, 0 },
164 { "set-system-flag", (CamelSExpFunc) set_flag, 0 },
165 { "unset-system-flag", (CamelSExpFunc) unset_flag, 0 },
166 { "pipe-message", (CamelSExpFunc) pipe_message, 0 },
167 { "shell", (CamelSExpFunc) do_shell, 0 },
168 { "beep", (CamelSExpFunc) do_beep, 0 },
169 { "play-sound", (CamelSExpFunc) play_sound, 0 },
170 { "only-once", (CamelSExpFunc) do_only_once, 0 }
173 G_DEFINE_TYPE (CamelFilterDriver, camel_filter_driver, CAMEL_TYPE_OBJECT)
176 free_hash_strings (gpointer key,
185 filter_rule_compare_by_name (struct _filter_rule *rule,
188 return g_strcmp0 (rule->name, name);
192 filter_driver_dispose (GObject *object)
194 CamelFilterDriverPrivate *priv;
196 priv = CAMEL_FILTER_DRIVER_GET_PRIVATE (object);
198 if (priv->defaultfolder != NULL) {
199 camel_folder_thaw (priv->defaultfolder);
200 g_object_unref (priv->defaultfolder);
201 priv->defaultfolder = NULL;
204 if (priv->session != NULL) {
205 g_object_unref (priv->session);
206 priv->session = NULL;
209 /* Chain up to parent's dispose() method. */
210 G_OBJECT_CLASS (camel_filter_driver_parent_class)->dispose (object);
214 filter_driver_finalize (GObject *object)
216 CamelFilterDriverPrivate *priv;
217 struct _filter_rule *node;
219 priv = CAMEL_FILTER_DRIVER_GET_PRIVATE (object);
221 /* close all folders that were opened for appending */
222 close_folders (CAMEL_FILTER_DRIVER (object));
223 g_hash_table_destroy (priv->folders);
225 g_hash_table_foreach (priv->globals, free_hash_strings, object);
226 g_hash_table_destroy (priv->globals);
228 g_hash_table_foreach (priv->only_once, free_hash_strings, object);
229 g_hash_table_destroy (priv->only_once);
231 g_object_unref (priv->eval);
233 while ((node = g_queue_pop_head (&priv->rules)) != NULL) {
234 g_free (node->match);
235 g_free (node->action);
240 /* Chain up to parent's finalize() method. */
241 G_OBJECT_CLASS (camel_filter_driver_parent_class)->finalize (object);
245 camel_filter_driver_class_init (CamelFilterDriverClass *class)
247 GObjectClass *object_class;
249 g_type_class_add_private (class, sizeof (CamelFilterDriverPrivate));
251 object_class = G_OBJECT_CLASS (class);
252 object_class->dispose = filter_driver_dispose;
253 object_class->finalize = filter_driver_finalize;
257 camel_filter_driver_init (CamelFilterDriver *filter_driver)
261 filter_driver->priv = CAMEL_FILTER_DRIVER_GET_PRIVATE (filter_driver);
263 g_queue_init (&filter_driver->priv->rules);
265 filter_driver->priv->eval = camel_sexp_new ();
267 /* Load in builtin symbols */
268 for (ii = 0; ii < G_N_ELEMENTS (symbols); ii++) {
269 if (symbols[ii].type == 1) {
270 camel_sexp_add_ifunction (
271 filter_driver->priv->eval, 0,
272 symbols[ii].name, (CamelSExpIFunc)
273 symbols[ii].func, filter_driver);
275 camel_sexp_add_function (
276 filter_driver->priv->eval, 0,
277 symbols[ii].name, symbols[ii].func,
282 filter_driver->priv->globals =
283 g_hash_table_new (g_str_hash, g_str_equal);
285 filter_driver->priv->folders =
286 g_hash_table_new (g_str_hash, g_str_equal);
288 filter_driver->priv->only_once =
289 g_hash_table_new (g_str_hash, g_str_equal);
293 * camel_filter_driver_new:
295 * Returns: A new CamelFilterDriver object
298 camel_filter_driver_new (CamelSession *session)
300 CamelFilterDriver *d;
302 d = g_object_new (CAMEL_TYPE_FILTER_DRIVER, NULL);
303 d->priv->session = g_object_ref (session);
309 camel_filter_driver_set_folder_func (CamelFilterDriver *d,
310 CamelFilterGetFolderFunc get_folder,
313 d->priv->get_folder = get_folder;
314 d->priv->data = data;
318 camel_filter_driver_set_logfile (CamelFilterDriver *d,
321 d->priv->logfile = logfile;
325 camel_filter_driver_set_status_func (CamelFilterDriver *d,
326 CamelFilterStatusFunc *func,
329 d->priv->statusfunc = func;
330 d->priv->statusdata = data;
334 camel_filter_driver_set_shell_func (CamelFilterDriver *d,
335 CamelFilterShellFunc *func,
338 d->priv->shellfunc = func;
339 d->priv->shelldata = data;
343 camel_filter_driver_set_play_sound_func (CamelFilterDriver *d,
344 CamelFilterPlaySoundFunc *func,
347 d->priv->playfunc = func;
348 d->priv->playdata = data;
352 camel_filter_driver_set_system_beep_func (CamelFilterDriver *d,
353 CamelFilterSystemBeepFunc *func,
356 d->priv->beep = func;
357 d->priv->beepdata = data;
361 camel_filter_driver_set_default_folder (CamelFilterDriver *d,
364 if (d->priv->defaultfolder) {
365 camel_folder_thaw (d->priv->defaultfolder);
366 g_object_unref (d->priv->defaultfolder);
369 d->priv->defaultfolder = def;
371 if (d->priv->defaultfolder) {
372 camel_folder_freeze (d->priv->defaultfolder);
373 g_object_ref (d->priv->defaultfolder);
378 camel_filter_driver_add_rule (CamelFilterDriver *d,
383 struct _filter_rule *node;
385 node = g_malloc (sizeof (*node));
386 node->match = g_strdup (match);
387 node->action = g_strdup (action);
388 node->name = g_strdup (name);
390 g_queue_push_tail (&d->priv->rules, node);
394 camel_filter_driver_remove_rule_by_name (CamelFilterDriver *d,
399 link = g_queue_find_custom (
400 &d->priv->rules, name,
401 (GCompareFunc) filter_rule_compare_by_name);
404 struct _filter_rule *rule = link->data;
406 g_queue_delete_link (&d->priv->rules, link);
408 g_free (rule->match);
409 g_free (rule->action);
420 report_status (CamelFilterDriver *driver,
421 enum camel_filter_status_t status,
426 /* call user-defined status report function */
430 if (driver->priv->statusfunc) {
432 str = g_strdup_vprintf (desc, ap);
434 driver->priv->statusfunc (driver, status, pc, str, driver->priv->statusdata);
441 camel_filter_driver_set_global (CamelFilterDriver *d,
445 gchar *oldkey, *oldvalue;
447 if (g_hash_table_lookup_extended (d->priv->globals, name, (gpointer) &oldkey, (gpointer) &oldvalue)) {
449 g_hash_table_insert (d->priv->globals, oldkey, g_strdup (value));
451 g_hash_table_insert (d->priv->globals, g_strdup (name), g_strdup (value));
456 static CamelSExpResult *
457 do_delete (struct _CamelSExp *f,
459 struct _CamelSExpResult **argv,
460 CamelFilterDriver *driver)
462 d (fprintf (stderr, "doing delete\n"));
463 driver->priv->deleted = TRUE;
464 camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Delete");
469 static CamelSExpResult *
470 do_forward_to (struct _CamelSExp *f,
472 struct _CamelSExpResult **argv,
473 CamelFilterDriver *driver)
475 d (fprintf (stderr, "marking message for forwarding\n"));
477 /* requires one parameter, string with a destination address */
478 if (argc < 1 || argv[0]->type != CAMEL_SEXP_RES_STRING)
481 /* make sure we have the message... */
482 if (driver->priv->message == NULL) {
483 /* FIXME Pass a GCancellable */
484 driver->priv->message = camel_folder_get_message_sync (
485 driver->priv->source,
486 driver->priv->uid, NULL,
487 &driver->priv->error);
488 if (driver->priv->message == NULL)
492 camel_filter_driver_log (
493 driver, FILTER_LOG_ACTION,
494 "Forward message to '%s'",
495 argv[0]->value.string);
497 /* XXX Not cancellable. */
498 camel_session_forward_to_sync (
499 driver->priv->session,
500 driver->priv->source,
501 driver->priv->message,
502 argv[0]->value.string,
504 &driver->priv->error);
509 static CamelSExpResult *
510 do_copy (struct _CamelSExp *f,
512 struct _CamelSExpResult **argv,
513 CamelFilterDriver *driver)
517 d (fprintf (stderr, "copying message...\n"));
519 for (i = 0; i < argc; i++) {
520 if (argv[i]->type == CAMEL_SEXP_RES_STRING) {
521 /* open folders we intent to copy to */
522 gchar *folder = argv[i]->value.string;
525 outbox = open_folder (driver, folder);
529 if (outbox == driver->priv->source)
532 if (!driver->priv->modified &&
533 driver->priv->uid != NULL &&
534 driver->priv->source != NULL &&
535 camel_folder_has_summary_capability (
536 driver->priv->source)) {
539 uids = g_ptr_array_new ();
541 uids, (gchar *) driver->priv->uid);
542 /* FIXME Pass a GCancellable */
543 camel_folder_transfer_messages_to_sync (
544 driver->priv->source,
545 uids, outbox, FALSE, NULL, NULL,
546 &driver->priv->error);
547 g_ptr_array_free (uids, TRUE);
549 if (driver->priv->message == NULL)
550 /* FIXME Pass a GCancellable */
551 driver->priv->message = camel_folder_get_message_sync (
552 driver->priv->source,
553 driver->priv->uid, NULL,
554 &driver->priv->error);
556 if (!driver->priv->message)
559 /* FIXME Pass a GCancellable */
560 camel_folder_append_message_sync (
561 outbox, driver->priv->message,
562 driver->priv->info, NULL, NULL,
563 &driver->priv->error);
566 if (driver->priv->error == NULL)
567 driver->priv->copied = TRUE;
569 camel_filter_driver_log (
570 driver, FILTER_LOG_ACTION,
571 "Copy to folder %s", folder);
578 static CamelSExpResult *
579 do_move (struct _CamelSExp *f,
581 struct _CamelSExpResult **argv,
582 CamelFilterDriver *driver)
586 d (fprintf (stderr, "moving message...\n"));
588 for (i = 0; i < argc; i++) {
589 if (argv[i]->type == CAMEL_SEXP_RES_STRING) {
590 /* open folders we intent to move to */
591 gchar *folder = argv[i]->value.string;
595 outbox = open_folder (driver, folder);
599 if (outbox == driver->priv->source)
602 /* only delete on last folder (only 1 can ever be supplied by ui currently) */
603 last = (i == argc - 1);
605 if (!driver->priv->modified && driver->priv->uid && driver->priv->source && camel_folder_has_summary_capability (driver->priv->source)) {
608 uids = g_ptr_array_new ();
609 g_ptr_array_add (uids, (gchar *) driver->priv->uid);
610 /* FIXME Pass a GCancellable */
611 camel_folder_transfer_messages_to_sync (
612 driver->priv->source, uids, outbox, last,
613 NULL, NULL, &driver->priv->error);
614 g_ptr_array_free (uids, TRUE);
616 if (driver->priv->message == NULL)
617 /* FIXME Pass a GCancellable */
618 driver->priv->message = camel_folder_get_message_sync (
619 driver->priv->source, driver->priv->uid, NULL, &driver->priv->error);
621 if (!driver->priv->message)
624 /* FIXME Pass a GCancellable */
625 camel_folder_append_message_sync (
626 outbox, driver->priv->message, driver->priv->info,
627 NULL, NULL, &driver->priv->error);
629 if (driver->priv->error == NULL && last) {
630 if (driver->priv->source && driver->priv->uid && camel_folder_has_summary_capability (driver->priv->source))
631 camel_folder_set_message_flags (
632 driver->priv->source, driver->priv->uid,
633 CAMEL_MESSAGE_DELETED |
634 CAMEL_MESSAGE_SEEN, ~0);
636 camel_message_info_set_flags (
638 CAMEL_MESSAGE_DELETED |
640 CAMEL_MESSAGE_FOLDER_FLAGGED,
645 if (driver->priv->error == NULL) {
646 driver->priv->moved = TRUE;
647 camel_filter_driver_log (
648 driver, FILTER_LOG_ACTION,
649 "Move to folder %s", folder);
654 /* implicit 'stop' with 'move' */
655 camel_filter_driver_log (
656 driver, FILTER_LOG_ACTION,
657 "Stopped processing");
658 driver->priv->terminated = TRUE;
663 static CamelSExpResult *
664 do_stop (struct _CamelSExp *f,
666 struct _CamelSExpResult **argv,
667 CamelFilterDriver *driver)
669 camel_filter_driver_log (
670 driver, FILTER_LOG_ACTION,
671 "Stopped processing");
672 d (fprintf (stderr, "terminating message processing\n"));
673 driver->priv->terminated = TRUE;
678 static CamelSExpResult *
679 do_label (struct _CamelSExp *f,
681 struct _CamelSExpResult **argv,
682 CamelFilterDriver *driver)
684 d (fprintf (stderr, "setting label tag\n"));
685 if (argc > 0 && argv[0]->type == CAMEL_SEXP_RES_STRING) {
686 /* This is a list of new labels, we should used these in case of passing in old names.
687 * This all is required only because backward compatibility. */
688 const gchar *new_labels[] = { "$Labelimportant", "$Labelwork", "$Labelpersonal", "$Labeltodo", "$Labellater", NULL};
692 label = argv[0]->value.string;
694 for (i = 0; new_labels[i]; i++) {
695 if (label && strcmp (new_labels[i] + 6, label) == 0) {
696 label = new_labels[i];
701 if (driver->priv->source && driver->priv->uid && camel_folder_has_summary_capability (driver->priv->source))
702 camel_folder_set_message_user_flag (driver->priv->source, driver->priv->uid, label, TRUE);
704 camel_message_info_set_user_flag (driver->priv->info, label, TRUE);
705 camel_filter_driver_log (
706 driver, FILTER_LOG_ACTION,
707 "Set label to %s", label);
713 static CamelSExpResult *
714 do_color (struct _CamelSExp *f,
716 struct _CamelSExpResult **argv,
717 CamelFilterDriver *driver)
719 d (fprintf (stderr, "setting color tag\n"));
720 if (argc > 0 && argv[0]->type == CAMEL_SEXP_RES_STRING) {
721 const gchar *color = argv[0]->value.string;
723 if (color && !*color)
726 if (driver->priv->source && driver->priv->uid && camel_folder_has_summary_capability (driver->priv->source))
727 camel_folder_set_message_user_tag (driver->priv->source, driver->priv->uid, "color", color);
729 camel_message_info_set_user_tag (driver->priv->info, "color", color);
730 camel_filter_driver_log (
731 driver, FILTER_LOG_ACTION,
732 "Set color to %s", color ? color : "None");
738 static CamelSExpResult *
739 do_score (struct _CamelSExp *f,
741 struct _CamelSExpResult **argv,
742 CamelFilterDriver *driver)
744 d (fprintf (stderr, "setting score tag\n"));
745 if (argc > 0 && argv[0]->type == CAMEL_SEXP_RES_INT) {
748 value = g_strdup_printf ("%d", argv[0]->value.number);
749 camel_message_info_set_user_tag (driver->priv->info, "score", value);
750 camel_filter_driver_log (
751 driver, FILTER_LOG_ACTION,
752 "Set score to %d", argv[0]->value.number);
759 static CamelSExpResult *
760 do_adjust_score (struct _CamelSExp *f,
762 struct _CamelSExpResult **argv,
763 CamelFilterDriver *driver)
765 d (fprintf (stderr, "adjusting score tag\n"));
766 if (argc > 0 && argv[0]->type == CAMEL_SEXP_RES_INT) {
770 value = (gchar *) camel_message_info_user_tag (driver->priv->info, "score");
771 old = value ? atoi (value) : 0;
772 value = g_strdup_printf ("%d", old + argv[0]->value.number);
773 camel_message_info_set_user_tag (driver->priv->info, "score", value);
774 camel_filter_driver_log (
775 driver, FILTER_LOG_ACTION,
776 "Adjust score (%d) to %s",
777 argv[0]->value.number, value);
784 static CamelSExpResult *
785 set_flag (struct _CamelSExp *f,
787 struct _CamelSExpResult **argv,
788 CamelFilterDriver *driver)
792 d (fprintf (stderr, "setting flag\n"));
793 if (argc == 1 && argv[0]->type == CAMEL_SEXP_RES_STRING) {
794 flags = camel_system_flag (argv[0]->value.string);
795 if (driver->priv->source && driver->priv->uid && camel_folder_has_summary_capability (driver->priv->source))
796 camel_folder_set_message_flags (
797 driver->priv->source, driver->priv->uid, flags, ~0);
799 camel_message_info_set_flags (
800 driver->priv->info, flags |
801 CAMEL_MESSAGE_FOLDER_FLAGGED, ~0);
802 camel_filter_driver_log (
803 driver, FILTER_LOG_ACTION,
804 "Set %s flag", argv[0]->value.string);
810 static CamelSExpResult *
811 unset_flag (struct _CamelSExp *f,
813 struct _CamelSExpResult **argv,
814 CamelFilterDriver *driver)
818 d (fprintf (stderr, "unsetting flag\n"));
819 if (argc == 1 && argv[0]->type == CAMEL_SEXP_RES_STRING) {
820 flags = camel_system_flag (argv[0]->value.string);
821 if (driver->priv->source && driver->priv->uid && camel_folder_has_summary_capability (driver->priv->source))
822 camel_folder_set_message_flags (
823 driver->priv->source, driver->priv->uid, flags, 0);
825 camel_message_info_set_flags (
826 driver->priv->info, flags |
827 CAMEL_MESSAGE_FOLDER_FLAGGED, 0);
828 camel_filter_driver_log (
829 driver, FILTER_LOG_ACTION,
830 "Unset %s flag", argv[0]->value.string);
838 child_setup_func (gpointer user_data)
843 #define child_setup_func NULL
849 } child_watch_data_t;
852 child_watch (GPid pid,
856 child_watch_data_t *child_watch_data = data;
858 g_spawn_close_pid (pid);
860 child_watch_data->child_status = status;
862 g_main_loop_quit (child_watch_data->loop);
866 pipe_to_system (struct _CamelSExp *f,
868 struct _CamelSExpResult **argv,
869 CamelFilterDriver *driver)
871 gint i, pipe_to_child, pipe_from_child;
872 CamelMimeMessage *message = NULL;
873 CamelMimeParser *parser;
874 CamelStream *stream, *mem;
876 GError *error = NULL;
878 child_watch_data_t child_watch_data;
880 GMainContext *context;
882 if (argc < 1 || argv[0]->value.string[0] == '\0')
885 /* make sure we have the message... */
886 if (driver->priv->message == NULL) {
887 /* FIXME Pass a GCancellable */
888 driver->priv->message = camel_folder_get_message_sync (
889 driver->priv->source, driver->priv->uid, NULL, &driver->priv->error);
890 if (driver->priv->message == NULL)
894 args = g_ptr_array_new ();
895 for (i = 0; i < argc; i++)
896 g_ptr_array_add (args, argv[i]->value.string);
897 g_ptr_array_add (args, NULL);
899 if (!g_spawn_async_with_pipes (NULL,
900 (gchar **) args->pdata,
902 G_SPAWN_DO_NOT_REAP_CHILD |
903 G_SPAWN_SEARCH_PATH |
904 G_SPAWN_STDERR_TO_DEV_NULL,
912 g_ptr_array_free (args, TRUE);
915 &driver->priv->error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
916 _("Failed to create child process '%s': %s"),
917 argv[0]->value.string, error->message);
918 g_error_free (error);
922 g_ptr_array_free (args, TRUE);
924 stream = camel_stream_fs_new_with_fd (pipe_to_child);
925 if (camel_data_wrapper_write_to_stream_sync (
926 CAMEL_DATA_WRAPPER (driver->priv->message), stream, NULL, NULL) == -1) {
927 g_object_unref (stream);
928 close (pipe_from_child);
932 if (camel_stream_flush (stream, NULL, NULL) == -1) {
933 g_object_unref (stream);
934 close (pipe_from_child);
938 g_object_unref (stream);
940 stream = camel_stream_fs_new_with_fd (pipe_from_child);
941 mem = camel_stream_mem_new ();
942 if (camel_stream_write_to_stream (stream, mem, NULL, NULL) == -1) {
943 g_object_unref (stream);
944 g_object_unref (mem);
948 g_object_unref (stream);
950 g_seekable_seek (G_SEEKABLE (mem), 0, G_SEEK_SET, NULL, NULL);
952 parser = camel_mime_parser_new ();
953 camel_mime_parser_init_with_stream (parser, mem, NULL);
954 camel_mime_parser_scan_from (parser, FALSE);
955 g_object_unref (mem);
957 message = camel_mime_message_new ();
958 if (!camel_mime_part_construct_from_parser_sync (
959 (CamelMimePart *) message, parser, NULL, NULL)) {
960 gint err = camel_mime_parser_errno (parser);
962 &driver->priv->error, G_IO_ERROR,
963 g_io_error_from_errno (err),
964 _("Invalid message stream received from %s: %s"),
965 argv[0]->value.string, g_strerror (err));
966 g_object_unref (message);
969 g_object_unref (driver->priv->message);
970 driver->priv->message = message;
971 driver->priv->modified = TRUE;
974 g_object_unref (parser);
977 context = g_main_context_new ();
978 child_watch_data.loop = g_main_loop_new (context, FALSE);
979 g_main_context_unref (context);
981 source = g_child_watch_source_new (child_pid);
982 g_source_set_callback (source, (GSourceFunc) child_watch, &child_watch_data, NULL);
983 g_source_attach (source, g_main_loop_get_context (child_watch_data.loop));
984 g_source_unref (source);
986 g_main_loop_run (child_watch_data.loop);
987 g_main_loop_unref (child_watch_data.loop);
990 if (message && WIFEXITED (child_watch_data.child_status))
991 return WEXITSTATUS (child_watch_data.child_status);
995 return child_watch_data.child_status;
999 static CamelSExpResult *
1000 pipe_message (struct _CamelSExp *f,
1002 struct _CamelSExpResult **argv,
1003 CamelFilterDriver *driver)
1007 /* make sure all args are strings */
1008 for (i = 0; i < argc; i++) {
1009 if (argv[i]->type != CAMEL_SEXP_RES_STRING)
1013 camel_filter_driver_log (
1014 driver, FILTER_LOG_ACTION,
1015 "Piping message to %s", argv[0]->value.string);
1016 pipe_to_system (f, argc, argv, driver);
1021 static CamelSExpResult *
1022 do_shell (struct _CamelSExp *f,
1024 struct _CamelSExpResult **argv,
1025 CamelFilterDriver *driver)
1031 d (fprintf (stderr, "executing shell command\n"));
1033 command = g_string_new ("");
1035 args = g_ptr_array_new ();
1037 /* make sure all args are strings */
1038 for (i = 0; i < argc; i++) {
1039 if (argv[i]->type != CAMEL_SEXP_RES_STRING)
1042 g_ptr_array_add (args, argv[i]->value.string);
1044 g_string_append (command, argv[i]->value.string);
1045 g_string_append_c (command, ' ');
1048 g_string_truncate (command, command->len - 1);
1050 if (driver->priv->shellfunc && argc >= 1) {
1051 driver->priv->shellfunc (driver, argc, (gchar **) args->pdata, driver->priv->shelldata);
1052 camel_filter_driver_log (
1053 driver, FILTER_LOG_ACTION,
1054 "Executing shell command: [%s]", command->str);
1059 g_ptr_array_free (args, TRUE);
1060 g_string_free (command, TRUE);
1065 static CamelSExpResult *
1066 do_beep (struct _CamelSExp *f,
1068 struct _CamelSExpResult **argv,
1069 CamelFilterDriver *driver)
1071 d (fprintf (stderr, "beep\n"));
1073 if (driver->priv->beep) {
1074 driver->priv->beep (driver, driver->priv->beepdata);
1075 camel_filter_driver_log (driver, FILTER_LOG_ACTION, "Beep");
1081 static CamelSExpResult *
1082 play_sound (struct _CamelSExp *f,
1084 struct _CamelSExpResult **argv,
1085 CamelFilterDriver *driver)
1087 d (fprintf (stderr, "play sound\n"));
1089 if (driver->priv->playfunc && argc == 1 && argv[0]->type == CAMEL_SEXP_RES_STRING) {
1090 driver->priv->playfunc (driver, argv[0]->value.string, driver->priv->playdata);
1091 camel_filter_driver_log (
1092 driver, FILTER_LOG_ACTION, "Play sound");
1098 static CamelSExpResult *
1099 do_only_once (struct _CamelSExp *f,
1101 struct _CamelSExpResult **argv,
1102 CamelFilterDriver *driver)
1104 d (fprintf (stderr, "only once\n"));
1106 if (argc == 2 && !g_hash_table_lookup (driver->priv->only_once, argv[0]->value.string))
1107 g_hash_table_insert (
1108 driver->priv->only_once,
1109 g_strdup (argv[0]->value.string),
1110 g_strdup (argv[1]->value.string));
1115 static CamelFolder *
1116 open_folder (CamelFilterDriver *driver,
1117 const gchar *folder_url)
1119 CamelFolder *camelfolder;
1121 /* we have a lookup table of currently open folders */
1122 camelfolder = g_hash_table_lookup (driver->priv->folders, folder_url);
1124 return camelfolder == FOLDER_INVALID ? NULL : camelfolder;
1126 /* if we have a default folder, ignore exceptions. This is so
1127 * a bad filter rule on pop or local delivery doesn't result
1128 * in duplicate mails, just mail going to inbox. Otherwise,
1129 * we want to know about exceptions and abort processing */
1130 if (driver->priv->defaultfolder) {
1131 camelfolder = driver->priv->get_folder (driver, folder_url, driver->priv->data, NULL);
1133 camelfolder = driver->priv->get_folder (driver, folder_url, driver->priv->data, &driver->priv->error);
1137 g_hash_table_insert (driver->priv->folders, g_strdup (folder_url), camelfolder);
1138 camel_folder_freeze (camelfolder);
1140 g_hash_table_insert (driver->priv->folders, g_strdup (folder_url), FOLDER_INVALID);
1147 close_folder (gpointer key,
1151 CamelFolder *folder = value;
1152 CamelFilterDriver *driver = data;
1154 driver->priv->closed++;
1157 if (folder != FOLDER_INVALID) {
1158 /* FIXME Pass a GCancellable */
1159 if (camel_folder_synchronize_sync (folder, FALSE, NULL,
1160 (driver->priv->error != NULL) ? NULL : &driver->priv->error))
1161 camel_folder_refresh_info_sync (folder, NULL,
1162 (driver->priv->error != NULL) ? NULL : &driver->priv->error);
1163 camel_folder_thaw (folder);
1164 g_object_unref (folder);
1168 driver, CAMEL_FILTER_STATUS_PROGRESS,
1169 g_hash_table_size (driver->priv->folders) * 100 /
1170 driver->priv->closed, _("Syncing folders"));
1173 /* flush/close all folders */
1175 close_folders (CamelFilterDriver *driver)
1178 driver, CAMEL_FILTER_STATUS_PROGRESS,
1179 0, _("Syncing folders"));
1181 driver->priv->closed = 0;
1182 g_hash_table_foreach (driver->priv->folders, close_folder, driver);
1183 g_hash_table_destroy (driver->priv->folders);
1184 driver->priv->folders = g_hash_table_new (g_str_hash, g_str_equal);
1186 /* FIXME: status from driver */
1192 free_key (gpointer key,
1201 camel_filter_driver_log (CamelFilterDriver *driver,
1202 enum filter_log_t status,
1206 if (driver->priv->logfile) {
1212 va_start (ap, desc);
1213 str = g_strdup_vprintf (desc, ap);
1218 case FILTER_LOG_START: {
1219 /* write log header */
1220 const gchar *subject = NULL;
1221 const gchar *from = NULL;
1225 /* FIXME: does this need locking? Probably */
1227 from = camel_message_info_from (driver->priv->info);
1228 subject = camel_message_info_subject (driver->priv->info);
1231 strftime (date, 49, "%a, %d %b %Y %H:%M:%S", localtime (&t));
1233 driver->priv->logfile,
1234 "Applied filter \"%s\" to "
1235 "message from %s - \"%s\" at %s\n",
1236 str, from ? from : "unknown",
1237 subject ? subject : "", date);
1241 case FILTER_LOG_ACTION:
1242 fprintf (driver->priv->logfile, "Action: %s\n", str);
1244 case FILTER_LOG_END:
1245 fprintf (driver->priv->logfile, "\n");
1248 /* nothing else is loggable */
1256 struct _run_only_once {
1257 CamelFilterDriver *driver;
1262 run_only_once (gpointer key,
1264 struct _run_only_once *data)
1266 CamelFilterDriver *driver = data->driver;
1269 d (printf ("evaluating: %s\n\n", action));
1271 camel_sexp_input_text (driver->priv->eval, action, strlen (action));
1272 if (camel_sexp_parse (driver->priv->eval) == -1) {
1273 if (data->error == NULL)
1276 CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1277 _("Error parsing filter: %s: %s"),
1278 camel_sexp_error (driver->priv->eval), action);
1282 r = camel_sexp_eval (driver->priv->eval);
1284 if (data->error == NULL)
1287 CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1288 _("Error executing filter: %s: %s"),
1289 camel_sexp_error (driver->priv->eval), action);
1293 camel_sexp_result_free (driver->priv->eval, r);
1304 * camel_filter_driver_flush:
1306 * @error: return location for a #GError, or %NULL
1308 * Flush all of the only-once filter actions.
1311 camel_filter_driver_flush (CamelFilterDriver *driver,
1314 struct _run_only_once data;
1316 if (!driver->priv->only_once)
1319 data.driver = driver;
1322 g_hash_table_foreach_remove (driver->priv->only_once, (GHRFunc) run_only_once, &data);
1324 if (data.error != NULL)
1325 g_propagate_error (error, data.error);
1329 decode_flags_from_xev (const gchar *xev,
1330 CamelMessageInfoBase *mi)
1332 guint32 uid, flags = 0;
1335 /* check for uid/flags */
1336 header = camel_header_token_decode (xev);
1337 if (!(header && strlen (header) == strlen ("00000000-0000")
1338 && sscanf (header, "%08x-%04x", &uid, &flags) == 2)) {
1349 * camel_filter_driver_filter_mbox:
1350 * @driver: CamelFilterDriver
1351 * @mbox: mbox filename to be filtered
1352 * @original_source_url:
1353 * @cancellable: optional #GCancellable object, or %NULL
1354 * @error: return location for a #GError, or %NULL
1356 * Filters an mbox file based on rules defined in the FilterDriver
1357 * object. Is more efficient as it doesn't need to open the folder
1358 * through Camel directly.
1360 * Returns: -1 if errors were encountered during filtering,
1361 * otherwise returns 0.
1365 camel_filter_driver_filter_mbox (CamelFilterDriver *driver,
1367 const gchar *original_source_url,
1368 GCancellable *cancellable,
1371 CamelMimeParser *mp = NULL;
1372 gchar *source_url = NULL;
1380 fd = g_open (mbox, O_RDONLY | O_BINARY, 0);
1383 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1384 _("Unable to open spool folder"));
1387 /* to get the filesize */
1388 if (fstat (fd, &st) != 0)
1391 mp = camel_mime_parser_new ();
1392 camel_mime_parser_scan_from (mp, TRUE);
1393 if (camel_mime_parser_init_with_fd (mp, fd) == -1) {
1395 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1396 _("Unable to process spool folder"));
1401 source_url = g_filename_to_uri (mbox, NULL, NULL);
1403 while (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM) {
1404 CamelMessageInfo *info;
1405 CamelMimeMessage *message;
1406 CamelMimePart *mime_part;
1409 GError *local_error = NULL;
1412 pc = (gint)(100.0 * ((double) camel_mime_parser_tell (mp) / (double) st.st_size));
1415 driver, CAMEL_FILTER_STATUS_START,
1416 pc, _("Getting message %d (%d%%)"), i, pc);
1418 message = camel_mime_message_new ();
1419 mime_part = CAMEL_MIME_PART (message);
1421 if (!camel_mime_part_construct_from_parser_sync (
1422 mime_part, mp, cancellable, error)) {
1424 driver, CAMEL_FILTER_STATUS_END,
1425 100, _("Failed on message %d"), i);
1426 g_object_unref (message);
1430 info = camel_message_info_new_from_header (NULL, mime_part->headers);
1431 /* Try and see if it has X-Evolution headers */
1432 xev = camel_header_raw_find (&mime_part->headers, "X-Evolution", NULL);
1434 decode_flags_from_xev (xev, (CamelMessageInfoBase *) info);
1436 ((CamelMessageInfoBase *) info)->size = camel_mime_parser_tell (mp) - last;
1438 last = camel_mime_parser_tell (mp);
1439 status = camel_filter_driver_filter_message (
1440 driver, message, info, NULL, NULL, source_url,
1441 original_source_url ? original_source_url :
1442 source_url, cancellable, &local_error);
1443 g_object_unref (message);
1444 if (local_error != NULL || status == -1) {
1446 driver, CAMEL_FILTER_STATUS_END,
1447 100, _("Failed on message %d"), i);
1448 camel_message_info_free (info);
1449 g_propagate_error (error, local_error);
1455 /* skip over the FROM_END state */
1456 camel_mime_parser_step (mp, NULL, NULL);
1458 camel_message_info_free (info);
1461 if (driver->priv->defaultfolder) {
1463 driver, CAMEL_FILTER_STATUS_PROGRESS,
1464 100, _("Syncing folder"));
1465 camel_folder_synchronize_sync (
1466 driver->priv->defaultfolder, FALSE, cancellable, NULL);
1469 report_status (driver, CAMEL_FILTER_STATUS_END, 100, _("Complete"));
1473 g_free (source_url);
1477 g_object_unref (mp);
1483 * camel_filter_driver_filter_folder:
1484 * @driver: CamelFilterDriver
1485 * @folder: CamelFolder to be filtered
1486 * @cache: UID cache (needed for POP folders)
1487 * @uids: message uids to be filtered or NULL (as a shortcut to filter
1489 * @remove: TRUE to mark filtered messages as deleted
1490 * @cancellable: optional #GCancellable object, or %NULL
1491 * @error: return location for a #GError, or %NULL
1493 * Filters a folder based on rules defined in the FilterDriver
1496 * Returns: -1 if errors were encountered during filtering,
1497 * otherwise returns 0.
1501 camel_filter_driver_filter_folder (CamelFilterDriver *driver,
1502 CamelFolder *folder,
1503 CamelUIDCache *cache,
1506 GCancellable *cancellable,
1509 gboolean freeuids = FALSE;
1510 CamelMessageInfo *info;
1511 CamelStore *parent_store;
1512 const gchar *store_uid;
1516 parent_store = camel_folder_get_parent_store (folder);
1517 store_uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
1520 uids = camel_folder_get_uids (folder);
1524 for (i = 0; i < uids->len; i++) {
1525 gint pc = (100 * i) / uids->len;
1526 GError *local_error = NULL;
1529 driver, CAMEL_FILTER_STATUS_START,
1530 pc, _("Getting message %d of %d"),
1533 if (camel_folder_has_summary_capability (folder))
1534 info = camel_folder_get_message_info (folder, uids->pdata[i]);
1538 status = camel_filter_driver_filter_message (
1539 driver, NULL, info, uids->pdata[i], folder,
1540 store_uid, store_uid, cancellable, &local_error);
1542 if (camel_folder_has_summary_capability (folder))
1543 camel_folder_free_message_info (folder, info);
1545 if (local_error != NULL || status == -1) {
1547 driver, CAMEL_FILTER_STATUS_END, 100,
1548 _("Failed at message %d of %d"),
1550 g_propagate_error (error, local_error);
1556 camel_folder_set_message_flags (
1557 folder, uids->pdata[i],
1558 CAMEL_MESSAGE_DELETED |
1559 CAMEL_MESSAGE_SEEN, ~0);
1562 camel_uid_cache_save_uid (cache, uids->pdata[i]);
1563 if (cache && (i % 10) == 0)
1564 camel_uid_cache_save (cache);
1567 /* Save the cache of any pending mails. */
1569 camel_uid_cache_save (cache);
1571 if (driver->priv->defaultfolder) {
1573 driver, CAMEL_FILTER_STATUS_PROGRESS,
1574 100, _("Syncing folder"));
1575 camel_folder_synchronize_sync (
1576 driver->priv->defaultfolder, FALSE, cancellable, NULL);
1581 driver, CAMEL_FILTER_STATUS_END,
1582 100, _("Complete"));
1585 camel_folder_free_uids (folder, uids);
1590 struct _get_message {
1591 struct _CamelFilterDriverPrivate *priv;
1592 const gchar *store_uid;
1595 static CamelMimeMessage *
1596 get_message_cb (gpointer data,
1599 struct _get_message *msgdata = data;
1600 CamelMimeMessage *message;
1602 if (msgdata->priv->message) {
1603 message = g_object_ref (msgdata->priv->message);
1607 if (msgdata->priv->uid != NULL)
1608 uid = msgdata->priv->uid;
1610 uid = camel_message_info_uid (msgdata->priv->info);
1612 /* FIXME Pass a GCancellable */
1613 message = camel_folder_get_message_sync (
1614 msgdata->priv->source, uid, NULL, error);
1617 if (message != NULL && camel_mime_message_get_source (message) == NULL)
1618 camel_mime_message_set_source (message, msgdata->store_uid);
1624 * camel_filter_driver_filter_message:
1625 * @driver: CamelFilterDriver
1626 * @message: message to filter or NULL
1627 * @info: message info or NULL
1628 * @uid: message uid or NULL
1629 * @source: source folder or NULL
1630 * @store_uid: UID of source store, or %NULL
1631 * @original_store_uid: UID of source store (pre-movemail), or %NULL
1632 * @cancellable: optional #GCancellable object, or %NULL
1633 * @error: return location for a #GError, or %NULL
1635 * Filters a message based on rules defined in the FilterDriver
1636 * object. If the source folder (@source) and the uid (@uid) are
1637 * provided, the filter will operate on the CamelFolder (which in
1638 * certain cases is more efficient than using the default
1639 * camel_folder_append_message() function).
1641 * Returns: -1 if errors were encountered during filtering,
1642 * otherwise returns 0.
1646 camel_filter_driver_filter_message (CamelFilterDriver *driver,
1647 CamelMimeMessage *message,
1648 CamelMessageInfo *info,
1650 CamelFolder *source,
1651 const gchar *store_uid,
1652 const gchar *original_store_uid,
1653 GCancellable *cancellable,
1656 CamelFilterDriverPrivate *p = driver->priv;
1657 gboolean freeinfo = FALSE;
1658 gboolean filtered = FALSE;
1663 /* FIXME: make me into a g_return_if_fail/g_assert or whatever... */
1664 if (message == NULL && (source == NULL || uid == NULL)) {
1665 g_warning ("there is no way to fetch the message using the information provided...");
1670 struct _camel_header_raw *h;
1673 g_object_ref (message);
1675 message = camel_folder_get_message_sync (
1676 source, uid, cancellable, error);
1681 h = CAMEL_MIME_PART (message)->headers;
1682 info = camel_message_info_new_from_header (NULL, h);
1685 if (camel_message_info_flags (info) & CAMEL_MESSAGE_DELETED)
1688 uid = camel_message_info_uid (info);
1691 g_object_ref (message);
1694 driver->priv->terminated = FALSE;
1695 driver->priv->deleted = FALSE;
1696 driver->priv->copied = FALSE;
1697 driver->priv->moved = FALSE;
1698 driver->priv->message = message;
1699 driver->priv->info = info;
1700 driver->priv->uid = uid;
1701 driver->priv->source = source;
1703 if (message != NULL && camel_mime_message_get_source (message) == NULL)
1704 camel_mime_message_set_source (message, original_store_uid);
1706 if (g_strcmp0 (store_uid, "local") == 0 ||
1707 g_strcmp0 (store_uid, "vfolder") == 0) {
1711 if (g_strcmp0 (original_store_uid, "local") == 0 ||
1712 g_strcmp0 (original_store_uid, "vfolder") == 0) {
1713 original_store_uid = NULL;
1716 list = g_queue_peek_head_link (&driver->priv->rules);
1717 result = CAMEL_SEARCH_NOMATCH;
1719 for (link = list; link != NULL; link = g_list_next (link)) {
1720 struct _filter_rule *rule = link->data;
1721 struct _get_message data;
1723 if (driver->priv->terminated)
1726 d (printf ("applying rule %s\naction %s\n", rule->match, rule->action));
1729 data.store_uid = original_store_uid;
1731 if (original_store_uid == NULL)
1732 original_store_uid = store_uid;
1734 result = camel_filter_search_match (
1735 driver->priv->session, get_message_cb, &data, driver->priv->info,
1736 original_store_uid, rule->match, &driver->priv->error);
1739 case CAMEL_SEARCH_ERROR:
1741 &driver->priv->error,
1742 _("Execution of filter '%s' failed: "),
1745 case CAMEL_SEARCH_MATCHED:
1747 camel_filter_driver_log (
1748 driver, FILTER_LOG_START,
1751 if (camel_debug (":filter"))
1753 "filtering '%s' applying rule %s\n",
1754 camel_message_info_subject (info) ?
1755 camel_message_info_subject (info) :
1756 "?no subject?", rule->name);
1758 /* perform necessary filtering actions */
1759 camel_sexp_input_text (
1761 rule->action, strlen (rule->action));
1762 if (camel_sexp_parse (driver->priv->eval) == -1) {
1765 CAMEL_ERROR_GENERIC,
1766 _("Error parsing filter '%s': %s: %s"),
1768 camel_sexp_error (driver->priv->eval),
1772 r = camel_sexp_eval (driver->priv->eval);
1773 if (driver->priv->error != NULL) {
1775 &driver->priv->error,
1776 _("Execution of filter '%s' failed: "),
1784 CAMEL_ERROR_GENERIC,
1785 _("Error executing filter '%s': %s: %s"),
1787 camel_sexp_error (driver->priv->eval),
1791 camel_sexp_result_free (driver->priv->eval, r);
1797 /* *Now* we can set the DELETED flag... */
1798 if (driver->priv->deleted) {
1799 if (driver->priv->source && driver->priv->uid && camel_folder_has_summary_capability (driver->priv->source))
1800 camel_folder_set_message_flags (
1801 driver->priv->source, driver->priv->uid,
1802 CAMEL_MESSAGE_DELETED |
1803 CAMEL_MESSAGE_SEEN, ~0);
1805 camel_message_info_set_flags (
1806 info, CAMEL_MESSAGE_DELETED |
1807 CAMEL_MESSAGE_SEEN |
1808 CAMEL_MESSAGE_FOLDER_FLAGGED, ~0);
1811 /* Logic: if !Moved and there exists a default folder... */
1812 if (!(driver->priv->copied && driver->priv->deleted) && !driver->priv->moved && driver->priv->defaultfolder) {
1813 /* copy it to the default inbox */
1815 camel_filter_driver_log (
1816 driver, FILTER_LOG_ACTION,
1817 "Copy to default folder");
1819 if (camel_debug (":filter"))
1821 "filtering '%s' copy %s to default folder\n",
1822 camel_message_info_subject (info) ? camel_message_info_subject (info):"?no subject?",
1823 driver->priv->modified?"modified message":"");
1825 if (!driver->priv->modified && driver->priv->uid && driver->priv->source && camel_folder_has_summary_capability (driver->priv->source)) {
1828 uids = g_ptr_array_new ();
1829 g_ptr_array_add (uids, (gchar *) driver->priv->uid);
1830 camel_folder_transfer_messages_to_sync (
1831 driver->priv->source, uids, driver->priv->defaultfolder,
1832 FALSE, NULL, cancellable, &driver->priv->error);
1833 g_ptr_array_free (uids, TRUE);
1835 if (driver->priv->message == NULL) {
1836 driver->priv->message = camel_folder_get_message_sync (
1837 source, uid, cancellable, error);
1838 if (!driver->priv->message)
1842 camel_folder_append_message_sync (
1843 driver->priv->defaultfolder,
1844 driver->priv->message,
1845 driver->priv->info, NULL,
1847 &driver->priv->error);
1851 if (driver->priv->message)
1852 g_object_unref (driver->priv->message);
1855 camel_message_info_free (info);
1861 camel_filter_driver_log (driver, FILTER_LOG_END, NULL);
1863 if (driver->priv->message)
1864 g_object_unref (driver->priv->message);
1867 camel_message_info_free (info);
1869 g_propagate_error (error, driver->priv->error);
1870 driver->priv->error = NULL;