From 18e4f74e5001b1a3cfac7f45ccd9891a676a91d6 Mon Sep 17 00:00:00 2001 From: Colin Leroy Date: Wed, 30 Aug 2006 18:29:32 +0000 Subject: [PATCH] 2006-08-30 [cleroy] 2.4.0cvs114 * src/procmsg.c * src/procmsg.h Provide a way to filter a list of messages in an optimised manner, and add a list-filtering hook * src/folder.c * src/inc.c * src/mbox.c Use the list filtering * src/plugins/bogofilter/bogofilter.c Use the list-filtering hook Use -b (bulk) mode for more speed when learning and filtering * src/etpan/imap-thread.c Make log output shorter in UID SEARCH --- ChangeLog | 19 ++ PATCHSETS | 1 + configure.ac | 2 +- src/etpan/imap-thread.c | 41 +++++ src/folder.c | 62 ++++--- src/inc.c | 33 ++-- src/mbox.c | 23 ++- src/plugins/bogofilter/bogofilter.c | 273 ++++++++++++++++++++-------- src/procmsg.c | 81 +++++++++ src/procmsg.h | 14 +- 10 files changed, 410 insertions(+), 139 deletions(-) diff --git a/ChangeLog b/ChangeLog index 895e73a19..1c522f28c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2006-08-30 [colin] 2.4.0cvs114 + + * src/procmsg.c + * src/procmsg.h + Provide a way to filter a list of + messages in an optimised manner, and + add a list-filtering hook + * src/folder.c + * src/inc.c + * src/mbox.c + Use the list filtering + * src/plugins/bogofilter/bogofilter.c + Use the list-filtering hook + Use -b (bulk) mode for more speed + when learning and filtering + * src/etpan/imap-thread.c + Make log output shorter in UID SEARCH + + 2006-08-30 [colin] 2.4.0cvs113 * src/plugins/bogofilter/bogofilter.c diff --git a/PATCHSETS b/PATCHSETS index 0320445d9..19c59dab0 100644 --- a/PATCHSETS +++ b/PATCHSETS @@ -1805,3 +1805,4 @@ ( cvs diff -u -r 1.207.2.113 -r 1.207.2.114 src/folderview.c; cvs diff -u -r 1.83.2.82 -r 1.83.2.83 src/mimeview.c; cvs diff -u -r 1.20.2.14 -r 1.20.2.15 src/mimeview.h; cvs diff -u -r 1.395.2.236 -r 1.395.2.237 src/summaryview.c; cvs diff -u -r 1.96.2.141 -r 1.96.2.142 src/textview.c; cvs diff -u -r 1.5.2.39 -r 1.5.2.40 src/gtk/gtkutils.c; cvs diff -u -r 1.4.2.24 -r 1.4.2.25 src/gtk/gtkutils.h; ) > 2.4.0cvs111.patchset ( cvs diff -u -r 1.16.2.38 -r 1.16.2.39 src/msgcache.c; ) > 2.4.0cvs112.patchset ( cvs diff -u -r 1.1.2.4 -r 1.1.2.5 src/plugins/bogofilter/bogofilter.c; ) > 2.4.0cvs113.patchset +( cvs diff -u -r 1.213.2.111 -r 1.213.2.112 src/folder.c; cvs diff -u -r 1.149.2.55 -r 1.149.2.56 src/inc.c; cvs diff -u -r 1.28.2.24 -r 1.28.2.25 src/mbox.c; cvs diff -u -r 1.150.2.76 -r 1.150.2.77 src/procmsg.c; cvs diff -u -r 1.60.2.34 -r 1.60.2.35 src/procmsg.h; cvs diff -u -r 1.1.4.47 -r 1.1.4.48 src/etpan/imap-thread.c; cvs diff -u -r 1.1.2.5 -r 1.1.2.6 src/plugins/bogofilter/bogofilter.c; ) > 2.4.0cvs114.patchset diff --git a/configure.ac b/configure.ac index 3d0d5aee6..faf5c88e4 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,7 @@ MINOR_VERSION=4 MICRO_VERSION=0 INTERFACE_AGE=0 BINARY_AGE=0 -EXTRA_VERSION=113 +EXTRA_VERSION=114 EXTRA_RELEASE= EXTRA_GTK2_VERSION= diff --git a/src/etpan/imap-thread.c b/src/etpan/imap-thread.c index 263e0dcbe..815473641 100644 --- a/src/etpan/imap-thread.c +++ b/src/etpan/imap-thread.c @@ -106,6 +106,43 @@ void imap_logger_fetch(int direction, const char * str, size_t size) free(buf); } +void imap_logger_uid(int direction, const char * str, size_t size) +{ + gchar *buf; + gchar **lines; + int i = 0; + + buf = malloc(size+1); + memset(buf, 0, size+1); + strncpy(buf, str, size); + buf[size] = '\0'; + if (!strncmp(buf, "<<<<<<<", 7) + || !strncmp(buf, ">>>>>>>", 7)) { + free(buf); + return; + } + while (strstr(buf, "\r")) + *strstr(buf, "\r") = ' '; + while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + + lines = g_strsplit(buf, "\n", -1); + + if (direction == 0 && size > 64) { + gchar tmp[32]; + strncpy2(tmp, lines[0], 31); + log_print("IMAP4%c %s[... - %zd bytes more]\n", direction?'>':'<', tmp, + size-32); + } else { + while (lines[i] && *lines[i]) { + log_print("IMAP4%c %s\n", direction?'>':'<', lines[i]); + i++; + } + } + g_strfreev(lines); + free(buf); +} + void imap_logger_append(int direction, const char * str, size_t size) { gchar *buf; @@ -1226,8 +1263,12 @@ static void search_run(struct etpan_thread_op * op) result->error = -1; result->search_result = NULL; } else { + mailstream_logger = imap_logger_uid; + r = mailimap_uid_search(param->imap, NULL, key, &search_result); + mailstream_logger = imap_logger_cmd; + /* free the key (with the imapset) */ mailimap_search_key_free(key); diff --git a/src/folder.c b/src/folder.c index cc2e53d65..2ff4c7713 100644 --- a/src/folder.c +++ b/src/folder.c @@ -1934,47 +1934,57 @@ gint folder_item_scan_full(FolderItem *item, gboolean filtering) folder_item_update_freeze(); if (newmsg_list != NULL) { GSList *elem, *to_filter = NULL; - int total = g_slist_length(newmsg_list), cur = 0; - - if ((filtering == TRUE) && - (item->stype == F_INBOX) && - (item->folder->account != NULL) && - (item->folder->account->filter_on_recv)) - statusbar_print_all(_("Filtering messages...\n")); + gboolean do_filter = (filtering == TRUE) && + (item->stype == F_INBOX) && + (item->folder->account != NULL) && + (item->folder->account->filter_on_recv); for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) { MsgInfo *msginfo = (MsgInfo *) elem->data; - statusbar_progress_all(cur++,total, 10); - msgcache_add_msg(item->cache, msginfo); - if ((filtering == TRUE) && - (item->stype == F_INBOX) && - (item->folder->account != NULL) && - (item->folder->account->filter_on_recv) && - procmsg_msginfo_filter(msginfo, item->folder->account)) - to_filter = g_slist_prepend(to_filter, msginfo); - else { + if (!do_filter) { exists_list = g_slist_prepend(exists_list, msginfo); - + if(prefs_common.thread_by_subject && MSG_IS_IGNORE_THREAD(msginfo->flags) && !subject_table_lookup(subject_table, msginfo->subject)) { subject_table_insert(subject_table, msginfo->subject, msginfo); - } + } } } - filtering_move_and_copy_msgs(to_filter); - for (elem = to_filter; elem; elem = g_slist_next(elem)) { - MsgInfo *msginfo = (MsgInfo *)elem->data; - procmsg_msginfo_free(msginfo); - } - g_slist_free(to_filter); + if (do_filter) { + GSList *unfiltered; + procmsg_msglist_filter(newmsg_list, item->folder->account, + &to_filter, &unfiltered, + TRUE); + + if (to_filter != NULL) { + filtering_move_and_copy_msgs(to_filter); + for (elem = to_filter; elem; elem = g_slist_next(elem)) { + MsgInfo *msginfo = (MsgInfo *)elem->data; + procmsg_msginfo_free(msginfo); + } + g_slist_free(to_filter); + } + if (unfiltered != NULL) { + for (elem = unfiltered; elem; elem = g_slist_next(elem)) { + MsgInfo *msginfo = (MsgInfo *)elem->data; + exists_list = g_slist_prepend(exists_list, msginfo); + + if(prefs_common.thread_by_subject && + MSG_IS_IGNORE_THREAD(msginfo->flags) && + !subject_table_lookup(subject_table, msginfo->subject)) { + subject_table_insert(subject_table, msginfo->subject, msginfo); + } + } + g_slist_free(unfiltered); + } + } + g_slist_free(newmsg_list); - statusbar_progress_all(0,0,0); - statusbar_pop_all(); update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT; } diff --git a/src/inc.c b/src/inc.c index 7d8162f5e..180959208 100644 --- a/src/inc.c +++ b/src/inc.c @@ -488,7 +488,6 @@ static gint inc_start(IncProgressDialog *inc_dialog) gchar *msg; gchar *fin_msg; FolderItem *processing, *inbox; - MsgInfo *msginfo; GSList *msglist, *msglist_element; gboolean cancelled = FALSE; @@ -542,9 +541,9 @@ static gint inc_start(IncProgressDialog *inc_dialog) } for (; inc_dialog->queue_list != NULL && !cancelled; inc_dialog->cur_row++) { - int cur = 1, total = 0; session = inc_dialog->queue_list->data; pop3_session = POP3_SESSION(session->session); + GSList *filtered, *unfiltered; if (pop3_session->pass == NULL) { SET_PIXMAP_AND_TEXT(okpix, _("Cancelled")); @@ -630,25 +629,16 @@ static gint inc_start(IncProgressDialog *inc_dialog) /* process messages */ folder_item_update_freeze(); - if (pop3_session->ac_prefs->filter_on_recv) - statusbar_print_all(_("Filtering messages...\n")); - total = g_slist_length(msglist); - - for(msglist_element = msglist; msglist_element != NULL; - msglist_element = msglist_element->next) { - gchar *filename; - msginfo = (MsgInfo *) msglist_element->data; - filename = folder_item_fetch_msg(processing, msginfo->msgnum); - g_free(filename); + + procmsg_msglist_filter(msglist, pop3_session->ac_prefs, + &filtered, &unfiltered, + pop3_session->ac_prefs->filter_on_recv); - if (pop3_session->ac_prefs->filter_on_recv) - statusbar_progress_all(cur++,total, prefs_common.statusbar_update_step); + if (filtered != NULL) + filtering_move_and_copy_msgs(filtered); + if (unfiltered != NULL) + folder_item_move_msgs(inbox, unfiltered); - if (!pop3_session->ac_prefs->filter_on_recv || - !procmsg_msginfo_filter(msginfo, pop3_session->ac_prefs)) - folder_item_move_msg(inbox, msginfo); - } - filtering_move_and_copy_msgs(msglist); for(msglist_element = msglist; msglist_element != NULL; msglist_element = msglist_element->next) { MsgInfo *msginfo = (MsgInfo *)msglist_element->data; @@ -656,10 +646,9 @@ static gint inc_start(IncProgressDialog *inc_dialog) } folder_item_update_thaw(); - statusbar_progress_all(0,0,0); - statusbar_pop_all(); - g_slist_free(msglist); + g_slist_free(filtered); + g_slist_free(unfiltered); statusbar_pop_all(); diff --git a/src/mbox.c b/src/mbox.c index 53de34675..a704db11c 100644 --- a/src/mbox.c +++ b/src/mbox.c @@ -70,7 +70,7 @@ gint proc_mbox(FolderItem *dest, const gchar *mbox, gboolean apply_filter) gint lines; MsgInfo *msginfo; gboolean more; - GSList *to_filter = NULL, *to_drop = NULL, *cur, *to_add = NULL; + GSList *to_filter = NULL, *filtered = NULL, *unfiltered = NULL, *cur, *to_add = NULL; gboolean printed = FALSE; FolderItem *dropfolder; @@ -226,10 +226,7 @@ gint proc_mbox(FolderItem *dest, const gchar *mbox, gboolean apply_filter) return -1; } msginfo = folder_item_get_msginfo(dropfolder, msgnum); - if (!procmsg_msginfo_filter(msginfo, NULL)) - to_drop = g_slist_prepend(to_drop, msginfo); - else - to_filter = g_slist_prepend(to_filter, msginfo); + to_filter = g_slist_prepend(to_filter, msginfo); } else { MsgFileInfo *finfo = g_new0(MsgFileInfo, 1); finfo->file = tmp_file; @@ -251,21 +248,23 @@ gint proc_mbox(FolderItem *dest, const gchar *mbox, gboolean apply_filter) statusbar_pop_all(); if (apply_filter) { - to_drop = g_slist_reverse(to_drop); - folder_item_move_msgs(dest, to_drop); - for (cur = to_drop; cur; cur = g_slist_next(cur)) { + procmsg_msglist_filter(to_filter, NULL, &filtered, &unfiltered, TRUE); + unfiltered = g_slist_reverse(unfiltered); + folder_item_move_msgs(dest, unfiltered); + for (cur = unfiltered; cur; cur = g_slist_next(cur)) { MsgInfo *info = (MsgInfo *)cur->data; procmsg_msginfo_free(info); } - to_filter = g_slist_reverse(to_filter); - filtering_move_and_copy_msgs(to_filter); - for (cur = to_filter; cur; cur = g_slist_next(cur)) { + filtered = g_slist_reverse(filtered); + filtering_move_and_copy_msgs(filtered); + for (cur = filtered; cur; cur = g_slist_next(cur)) { MsgInfo *info = (MsgInfo *)cur->data; procmsg_msginfo_free(info); } - g_slist_free(to_drop); + g_slist_free(unfiltered); + g_slist_free(filtered); g_slist_free(to_filter); } else if (to_add) { folder_item_add_msgs(dropfolder, to_add, TRUE); diff --git a/src/plugins/bogofilter/bogofilter.c b/src/plugins/bogofilter/bogofilter.c index 0c6a38853..e38ef19c9 100644 --- a/src/plugins/bogofilter/bogofilter.c +++ b/src/plugins/bogofilter/bogofilter.c @@ -71,10 +71,7 @@ #include #endif -enum { - CHILD_RUNNING = 1 << 0, - TIMEOUT_RUNNING = 1 << 1, -}; +#define MAILS_PER_BATCH 20 static guint hook_id = -1; static MessageCallback message_callback; @@ -96,72 +93,160 @@ static PrefParam param[] = { {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL} }; +/* + * Helper function for spawn_with_input() - write an entire + * string to a fd. + */ +static gboolean +write_all (int fd, + const char *buf, + gsize to_write) +{ + while (to_write > 0) + { + gssize count = write (fd, buf, to_write); + if (count < 0) + { + if (errno != EINTR) + return FALSE; + } + else + { + to_write -= count; + buf += count; + } + } + + return TRUE; +} + static gboolean mail_filtering_hook(gpointer source, gpointer data) { MailFilteringData *mail_filtering_data = (MailFilteringData *) source; MsgInfo *msginfo = mail_filtering_data->msginfo; - gboolean is_spam = FALSE; + GSList *msglist = mail_filtering_data->msglist; + GSList *cur = NULL; static gboolean warned_error = FALSE; gchar *file = NULL, *cmd = NULL; - int status = 3; + int status = 0; gchar *bogo_exec = (config.bogopath && *config.bogopath) ? config.bogopath:"bogofilter"; + int total = 0, curnum = 0; + GSList *spams = NULL; + gchar buf[BUFSIZ]; + + gchar *bogo_args[4]; + GPid bogo_pid; + gint bogo_stdin, bogo_stdout; + GError *error = NULL; + gboolean bogo_forked; if (!config.process_emails) { return FALSE; } - debug_print("Filtering message %d\n", msginfo->msgnum); + + if (msglist == NULL && msginfo != NULL) { + g_warning("wrong call to bogofilter mail_filtering_hook"); + return FALSE; + } + + total = g_slist_length(msglist); if (message_callback != NULL) - message_callback(_("Bogofilter: filtering message..."), 0, 0); + message_callback(_("Bogofilter: filtering messages..."), total, 0); - file = procmsg_get_message_file(msginfo); + cmd = g_strdup_printf("%s -T -b", bogo_exec); - if (file) - cmd = g_strdup_printf("%s -I %s", bogo_exec, file); - - if (cmd) - status = system(cmd); - - if (status == -1) - status = 3; - else - status = WEXITSTATUS(status); - - g_free(cmd); - g_free(file); - debug_print("bogofilter status %d\n", status); - is_spam = (status == 0); - - if (is_spam) { - debug_print("message is spam\n"); - procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0); - if (config.receive_spam) { - FolderItem *save_folder; - - if ((!config.save_folder) || - (config.save_folder[0] == '\0') || - ((save_folder = folder_find_item_from_identifier(config.save_folder)) == NULL)) - save_folder = folder_get_default_trash(); - - procmsg_msginfo_unset_flags(msginfo, ~0, 0); - procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0); - folder_item_move_msg(save_folder, msginfo); - } else { - folder_item_remove_msg(msginfo->folder, msginfo->msgnum); - } + bogo_args[0] = bogo_exec; + bogo_args[1] = "-T"; + bogo_args[2] = "-b"; + bogo_args[3] = NULL; - return TRUE; + bogo_forked = g_spawn_async_with_pipes( + NULL, bogo_args,NULL, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &bogo_pid, &bogo_stdin, + &bogo_stdout, NULL, &error); + + if (bogo_forked == FALSE) { + g_warning("%s\n", error ? error->message:"ERROR???"); + g_error_free(error); + error = NULL; + status = -1; } else { - debug_print("message is ham\n"); - procmsg_msginfo_unset_flags(msginfo, MSG_SPAM, 0); + for (cur = msglist; cur; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + debug_print("Filtering message %d (%d/%d)\n", msginfo->msgnum, curnum, total); + + if (message_callback != NULL) + message_callback(NULL, total, curnum++); + + file = procmsg_get_message_file(msginfo); + + if (file) { + gchar *tmp = g_strdup_printf("%s\n",file); + write_all(bogo_stdin, tmp, strlen(tmp)); + g_free(tmp); + memset(buf, 0, sizeof(buf)); + if (read(bogo_stdout, buf, sizeof(buf)-1) < 0) { + printf("ERROR 2\n"); + } else { + gchar **parts = NULL; + if (strchr(buf, '/')) { + tmp = strrchr(buf, '/')+1; + } else { + tmp = buf; + } + parts = g_strsplit(tmp, " ", 0); + debug_print("read %s\n", buf); + if (parts && parts[0] && parts[1] && *parts[1] == 'S') { + debug_print("message %d is spam\n", msginfo->msgnum); + procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0); + if (config.receive_spam) { + procmsg_msginfo_unset_flags(msginfo, ~0, 0); + procmsg_msginfo_set_flags(msginfo, MSG_SPAM, 0); + spams = g_slist_prepend(spams, msginfo); + } else { + folder_item_remove_msg(msginfo->folder, msginfo->msgnum); + } + mail_filtering_data->filtered = g_slist_prepend( + mail_filtering_data->filtered, msginfo); + } else { + debug_print("message %d is ham\n", msginfo->msgnum); + procmsg_msginfo_unset_flags(msginfo, MSG_SPAM, 0); + mail_filtering_data->unfiltered = g_slist_prepend( + mail_filtering_data->unfiltered, msginfo); + } + g_strfreev(parts); + } + g_free(file); + } else { + mail_filtering_data->unfiltered = g_slist_prepend( + mail_filtering_data->unfiltered, msginfo); + } + } } - if (status == 3) { /* I/O or other errors */ - gchar *msg = _("The Bogofilter plugin couldn't filter " + if (status != -1) { + close(bogo_stdout); + close(bogo_stdin); + waitpid(bogo_pid, &status, 0); + if (!WIFEXITED(status)) + status = -1; + else + status = WEXITSTATUS(status); + } + + if (status < 0 || status > 2) { /* I/O or other errors */ + gchar *msg = NULL; + + if (status == 3) + msg = g_strdup_printf(_("The Bogofilter plugin couldn't filter " "a message. The probable cause of the " "error is that it didn't learn from any mail.\n" "Use \"/Mark/Mark as spam\" and \"/Mark/Mark as " "ham\" to train Bogofilter with a few hundred " - "spam and ham messages."); + "spam and ham messages.")); + else + msg = g_strdup_printf(_("The Bogofilter plugin couldn't filter " + "a message. the command `%s` couldn't be run."), cmd); if (!prefs_common.no_recv_err_panel) { if (!warned_error) { alertpanel_error(msg); @@ -172,7 +257,31 @@ static gboolean mail_filtering_hook(gpointer source, gpointer data) log_error(tmp); g_free(tmp); } + g_free(msg); } + if (status < 0 || status > 2) { + g_slist_free(mail_filtering_data->filtered); + g_slist_free(mail_filtering_data->unfiltered); + g_slist_free(spams); + mail_filtering_data->filtered = NULL; + mail_filtering_data->unfiltered = NULL; + } else if (config.receive_spam && spams) { + FolderItem *save_folder; + + if ((!config.save_folder) || + (config.save_folder[0] == '\0') || + ((save_folder = folder_find_item_from_identifier(config.save_folder)) == NULL)) + save_folder = folder_get_default_trash(); + + folder_item_move_msgs(save_folder, spams); + } + + if (message_callback != NULL) + message_callback(NULL, 0, 0); + mail_filtering_data->filtered = g_slist_reverse( + mail_filtering_data->filtered); + mail_filtering_data->unfiltered = g_slist_reverse( + mail_filtering_data->unfiltered); return FALSE; } @@ -267,46 +376,58 @@ int bogofilter_learn(MsgInfo *msginfo, GSList *msglist, gboolean spam) message_callback(NULL, total, done); } } else if (some_correction || some_no_correction) { - int count = 0; - gchar *file_list = NULL; cur = msglist; - while (cur && status == 0) { + gchar *bogo_args[4]; + GPid bogo_pid; + gint bogo_stdin; + GError *error = NULL; + gboolean bogo_forked; + + bogo_args[0] = (gchar *)bogo_exec; + if (some_correction && !some_no_correction) + bogo_args[1] = "-Sn"; + else if (some_no_correction && !some_correction) + bogo_args[1] = spam ? "-s":"-n"; + bogo_args[2] = "-b"; + bogo_args[3] = NULL; + + bogo_forked = g_spawn_async_with_pipes( + NULL, bogo_args,NULL, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &bogo_pid, &bogo_stdin, + NULL, NULL, &error); + + while (bogo_forked && cur) { gchar *tmp = NULL; info = (MsgInfo *)cur->data; file = procmsg_get_message_file(info); if (file) { - tmp = g_strdup_printf("%s%s'%s'", - file_list?file_list:"", - file_list?" ":"", + tmp = g_strdup_printf("%s\n", file); - g_free(file_list); - file_list = tmp; + write_all(bogo_stdin, tmp, strlen(tmp)); + g_free(tmp); } g_free(file); - count ++; done++; - if (count > 10 || cur->next == NULL) { - /* flush */ - if (some_correction && !some_no_correction) - cmd = g_strdup_printf("%s -Sn -B %s", bogo_exec, file_list); - else if (some_no_correction && !some_correction) - cmd = g_strdup_printf("%s -%c -B %s", bogo_exec, spam?'s':'n', file_list); - else - g_warning("duuh bogofilter plugin shouldn't be there!\n"); - if ((status = execute_command_line(cmd, FALSE)) != 0) - alertpanel_error(_("Learning failed; `%s` returned with status %d."), - cmd, status); - count = 0; - g_free(cmd); - g_free(file_list); - file_list = NULL; - } if (message_callback != NULL) message_callback(NULL, total, done); cur = cur->next; } - g_free(file_list); + if (bogo_forked) { + close(bogo_stdin); + waitpid(bogo_pid, &status, 0); + if (!WIFEXITED(status)) + status = -1; + else + status = WEXITSTATUS(status); + } + if (!bogo_forked || status != 0) { + alertpanel_error(_("Learning failed; `%s` returned with error:\n%s"), + cmd, error ? error->message:_("Unknown error")); + if (error) + g_error_free(error); + } + } if (message_callback != NULL) @@ -440,7 +561,7 @@ struct PluginFeature *plugin_provides(void) void bogofilter_register_hook(void) { - hook_id = hooks_register_hook(MAIL_FILTERING_HOOKLIST, mail_filtering_hook, NULL); + hook_id = hooks_register_hook(MAIL_LISTFILTERING_HOOKLIST, mail_filtering_hook, NULL); if (hook_id == -1) { g_warning("Failed to register mail filtering hook"); config.process_emails = FALSE; @@ -450,6 +571,6 @@ void bogofilter_register_hook(void) void bogofilter_unregister_hook(void) { if (hook_id != -1) { - hooks_unregister_hook(MAIL_FILTERING_HOOKLIST, hook_id); + hooks_unregister_hook(MAIL_LISTFILTERING_HOOKLIST, hook_id); } } diff --git a/src/procmsg.c b/src/procmsg.c index 4879c1b94..f91a206bb 100644 --- a/src/procmsg.c +++ b/src/procmsg.c @@ -2146,6 +2146,9 @@ gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs) MailFilteringData mail_filtering_data; mail_filtering_data.msginfo = msginfo; + mail_filtering_data.msglist = NULL; + mail_filtering_data.filtered = NULL; + mail_filtering_data.unfiltered = NULL; if (hooks_invoke(MAIL_FILTERING_HOOKLIST, &mail_filtering_data)) { return TRUE; } @@ -2159,6 +2162,84 @@ gboolean procmsg_msginfo_filter(MsgInfo *msginfo, PrefsAccount* ac_prefs) return FALSE; } +void procmsg_msglist_filter(GSList *list, PrefsAccount *ac, + GSList **filtered, GSList **unfiltered, + gboolean do_filter) +{ + GSList *cur, *to_do = NULL; + gint total = 0, curnum = 0; + MailFilteringData mail_filtering_data; + + g_return_if_fail(filtered != NULL); + g_return_if_fail(unfiltered != NULL); + + *filtered = NULL; + *unfiltered = NULL; + + if (list == NULL) + return; + + total = g_slist_length(list); + + if (!do_filter) { + *filtered = NULL; + *unfiltered = g_slist_copy(list); + return; + } + + statusbar_print_all(_("Filtering messages...\n")); + + mail_filtering_data.msginfo = NULL; + mail_filtering_data.msglist = list; + mail_filtering_data.filtered = NULL; + mail_filtering_data.unfiltered = NULL; + + hooks_invoke(MAIL_LISTFILTERING_HOOKLIST, &mail_filtering_data); + + if (mail_filtering_data.filtered == NULL && + mail_filtering_data.unfiltered == NULL) { + /* nothing happened */ + debug_print(MAIL_LISTFILTERING_HOOKLIST " did nothing. filtering whole list normally.\n"); + to_do = list; + } + if (mail_filtering_data.filtered != NULL) { + /* keep track of what's been filtered by the hooks */ + debug_print(MAIL_LISTFILTERING_HOOKLIST " filtered some stuff. total %d filtered %d unfilt %d.\n", + g_slist_length(list), + g_slist_length(mail_filtering_data.filtered), + g_slist_length(mail_filtering_data.unfiltered)); + + *filtered = g_slist_copy(mail_filtering_data.filtered); + } + if (mail_filtering_data.unfiltered != NULL) { + /* what the hooks didn't handle will go in filtered or + * unfiltered in the next loop */ + debug_print(MAIL_LISTFILTERING_HOOKLIST " left unfiltered stuff. total %d filtered %d unfilt %d.\n", + g_slist_length(list), + g_slist_length(mail_filtering_data.filtered), + g_slist_length(mail_filtering_data.unfiltered)); + to_do = mail_filtering_data.unfiltered; + } + + for (cur = to_do; cur; cur = cur->next) { + MsgInfo *info = (MsgInfo *)cur->data; + if (procmsg_msginfo_filter(info, ac)) + *filtered = g_slist_prepend(*filtered, info); + else + *unfiltered = g_slist_prepend(*unfiltered, info); + statusbar_progress_all(curnum++, total, prefs_common.statusbar_update_step); + } + + g_slist_free(mail_filtering_data.filtered); + g_slist_free(mail_filtering_data.unfiltered); + + *filtered = g_slist_reverse(*filtered); + *unfiltered = g_slist_reverse(*unfiltered); + + statusbar_progress_all(0,0,0); + statusbar_pop_all(); +} + MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo) { MsgInfo *tmp_msginfo = NULL; diff --git a/src/procmsg.h b/src/procmsg.h index ed364125e..c1edc2ed2 100644 --- a/src/procmsg.h +++ b/src/procmsg.h @@ -152,6 +152,7 @@ typedef guint32 MsgTmpFlags; #define MSGINFO_UPDATE_HOOKLIST "msginfo_update" #define MAIL_FILTERING_HOOKLIST "mail_filtering_hooklist" +#define MAIL_LISTFILTERING_HOOKLIST "mail_listfiltering_hooklist" #define MAIL_POSTFILTERING_HOOKLIST "mail_postfiltering_hooklist" typedef enum { @@ -260,6 +261,9 @@ struct _MsgInfoUpdate { struct _MailFilteringData { MsgInfo *msginfo; + GSList *msglist; + GSList *filtered; + GSList *unfiltered; }; GHashTable *procmsg_msg_hash_table_create (GSList *mlist); @@ -349,8 +353,14 @@ void procmsg_update_unread_children (MsgInfo *info, gboolean newly_marked); void procmsg_msginfo_set_to_folder (MsgInfo *msginfo, FolderItem *to_folder); -gboolean procmsg_msginfo_filter (MsgInfo *msginfo, - PrefsAccount *ac_prefs); +gboolean procmsg_msginfo_filter (MsgInfo *msginfo, + PrefsAccount *ac_prefs); +void procmsg_msglist_filter (GSList *list, + PrefsAccount *ac, + GSList **filtered, + GSList **unfiltered, + gboolean do_filter); + MsgInfo *procmsg_msginfo_new_from_mimeinfo (MsgInfo *src_msginfo, MimeInfo *mimeinfo); -- 2.25.1