diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2007-09-30 13:34:59 +0200 |
---|---|---|
committer | Johannes Berg <johannes@sipsolutions.net> | 2007-09-30 13:34:59 +0200 |
commit | d30cef3c11f07889c2f8e86340b013174d28fc5c (patch) | |
tree | b4c935edff52f9829e1e8fd927a681596e26b4a8 /plugin.c | |
parent | 96730567e4a1fc835ed8a2c93988c2cacd1f914c (diff) | |
download | dovecot-antispam-d30cef3c11f07889c2f8e86340b013174d28fc5c.tar.gz dovecot-antispam-d30cef3c11f07889c2f8e86340b013174d28fc5c.tar.xz dovecot-antispam-d30cef3c11f07889c2f8e86340b013174d28fc5c.zip |
start of rewrite
Diffstat (limited to '')
-rw-r--r-- | plugin.c | 421 |
1 files changed, 111 insertions, 310 deletions
@@ -1,218 +1,97 @@ /* - dspam plugin for dovecot - - Copyright (C) 2004-2006 Johannes Berg <johannes@sipsolutions.net> - 2006 Frank Cusack - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License Version 2 as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - - based on the original framework http://www.dovecot.org/patches/1.0/copy_plugin.c - - Please see http://johannes.sipsolutions.net/wiki/Projects/dovecot-dspam-integration - for more information on this code. - - To compile: - make "plugins" directory right beside "src" in the dovecot source tree, - copy this into there and run - - cc -fPIC -shared -Wall \ - -I../src/ \ - -I../src/lib \ - -I.. \ - -I../src/lib-storage \ - -I../src/lib-mail \ - -I../src/lib-imap \ - -I../src/imap/ \ - -DHAVE_CONFIG_H \ - -DDSPAM=\"/path/to/dspam\" \ - dspam.c -o lib_dspam.so - - (if you leave out -DDSPAM=... then /usr/bin/dspam is taken as default) - - Install the plugin in the usual dovecot module location. -*/ - -/* - * If you need to ignore a trash folder, define a trash folder - * name as follows, or alternatively give -DIGNORE_TRASH_NAME=\"Trash\" on - * the cc command line. + * antispam plugin for dovecot + * + * Copyright (C) 2004-2007 Johannes Berg <johannes@sipsolutions.net> + * 2006 Frank Cusack + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License Version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + * based on the original framework http://www.dovecot.org/patches/1.0/copy_plugin.c + * + * Please see http://johannes.sipsolutions.net/wiki/Projects/dovecot-dspam-integration + * for more information on this code. + * + * Install the plugin in the usual dovecot module location. */ -/*#define IGNORE_TRASH_NAME "Trash"*/ -#include "common.h" -#include "str.h" -#include "strfuncs.h" -#include "commands.h" -#include "imap-search.h" -#include "lib-storage/mail-storage.h" -#include "lib/mempool.h" -#include "mail-storage.h" #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <fcntl.h> +#include <time.h> #ifdef DEBUG #include <syslog.h> #endif -#define SIGHEADERLINE "X-DSPAM-Signature" -#define MAXSIGLEN 100 +/* dovecot headers we need */ +#include "lib.h" +#include "mempool.h" +#include "str.h" +#include "strfuncs.h" +#include "commands.h" +#include "imap-search.h" +#include "lib-storage/mail-storage.h" +#include "mail-storage.h" +#include "client.h" +#include "ostream.h" -#ifndef DSPAM -#define DSPAM "/usr/bin/dspam" -#endif /* DSPAM */ +/* internal stuff we need */ +#include "plugin.h" -static int call_dspam(const char *signature, int is_spam) +static struct signature *list_add(pool_t pool, struct signature *list) { - pid_t pid; - int s; - char class_arg[16 + 2]; - char sign_arg[MAXSIGLEN + 2]; - int pipes[2]; - - s = snprintf(sign_arg, 101, "--signature=%s", signature); - if (s > MAXSIGLEN || s <= 0) - return -1; - - snprintf(class_arg, 17, "--class=%s", is_spam ? "spam" : "innocent"); - - pipe(pipes); /* for dspam stderr */ - - pid = fork(); - if (pid < 0) - return -1; - - if (pid) { - int status; - /* well. dspam doesn't report an error if it has an error, - but instead only prints stuff to stderr. Usually, it - won't print anything, so we treat it having output as - an error condition */ - - char buf[1024]; - int readsize; - close(pipes[1]); - - do { - readsize = read(pipes[0], buf, 1024); - if (readsize < 0) { - readsize = -1; - if (errno == EINTR) - readsize = -2; - } - } while (readsize == -2); - - if (readsize != 0) { - close(pipes[0]); - return -1; - } - - waitpid(pid, &status, 0); - if (!WIFEXITED(status)) { - close(pipes[0]); - return -1; - } + struct signature *n; - readsize = read(pipes[0], buf, 1024); - if (readsize != 0) { - close(pipes[0]); - return -1; - } - - close(pipes[0]); - return WEXITSTATUS(status); - } else { - int fd = open("/dev/null", O_RDONLY); - close(0); - close(1); - close(2); - /* see above */ - close(pipes[0]); - - if (dup2(pipes[1], 2) != 2) { - exit(1); - } - if (dup2(pipes[1], 1) != 1) { - exit(1); - } - close(pipes[1]); - - if (dup2(fd, 0) != 0) { - exit(1); - } - close(fd); + n = p_malloc(pool, sizeof(struct signature)); + n->next = list; + n->sig = NULL; -#ifdef DEBUG - syslog(LOG_INFO, DSPAM " --source=error --stdout %s %s", - class_arg, sign_arg); -#endif - execl(DSPAM, DSPAM, "--source=error", "--stdout", class_arg, - sign_arg, NULL); - exit(127); /* fall through if dspam can't be found */ - return -1; /* never executed */ - } + return n; } -struct dspam_signature_list { - struct dspam_signature_list *next; - char *sig; -}; -typedef struct dspam_signature_list *siglist_t; -static siglist_t list_append(pool_t pool, siglist_t * list) +static void client_send_sendalive_if_needed(struct client *client) { - siglist_t l = *list; - siglist_t p = NULL; - siglist_t n; + time_t now, last_io; - while (l != NULL) { - p = l; - l = l->next; - } - n = p_malloc(pool, sizeof(struct dspam_signature_list)); - n->next = NULL; - n->sig = NULL; - if (p == NULL) { - *list = n; - } else { - p->next = n; + if (o_stream_get_buffer_used_size(client->output) != 0) + return; + + now = time(NULL); + last_io = I_MAX(client->last_input, client->last_output); + if (now - last_io > MAIL_STORAGE_STAYALIVE_SECS) { + o_stream_send_str(client->output, "* OK Hang in there..\r\n"); + o_stream_flush(client->output); + client->last_output = now; } - return n; } -static int -fetch_and_copy_reclassified(struct mailbox_transaction_context *t, - struct mailbox *srcbox, - struct mail_search_arg *search_args, - int is_spam, int *enh_error) +static int fetch_and_copy(struct client *client, + struct mailbox_transaction_context *t, + struct mail_search_arg *search_args) { struct mail_search_context *search_ctx; - struct mailbox_transaction_context *src_trans; + struct mailbox_transaction_context *src_trans; struct mail_keywords *keywords; const char *const *keywords_list; struct mail *mail; + unsigned int copy_count = 0; int ret; - const char *signature; - struct dspam_signature_list *siglist = NULL; - pool_t listpool = pool_alloconly_create("dspam-siglist-pool", 1024); - - *enh_error = 0; - - src_trans = mailbox_transaction_begin(srcbox, 0); + src_trans = mailbox_transaction_begin(client->mailbox, 0); search_ctx = mailbox_search_init(src_trans, NULL, search_args, NULL); mail = mail_alloc(src_trans, MAIL_FETCH_STREAM_HEADER | @@ -224,21 +103,15 @@ fetch_and_copy_reclassified(struct mailbox_transaction_context *t, break; } - signature = mail_get_first_header(mail, SIGHEADERLINE); - if (is_empty_str(signature)) { - ret = -1; - *enh_error = -2; - break; - } - list_append(listpool, &siglist)->sig = - p_strdup(listpool, signature); + if ((++copy_count % COPY_CHECK_INTERVAL) == 0) + client_send_sendalive_if_needed(client); keywords_list = mail_get_keywords(mail); keywords = strarray_length(keywords_list) == 0 ? NULL : - mailbox_keywords_create(t, keywords_list); + mailbox_keywords_create(t, keywords_list); if (mailbox_copy(t, mail, mail_get_flags(mail), keywords, NULL) < 0) - ret = -1; + ret = mail->expunged ? 0 : -1; mailbox_keywords_free(t, &keywords); } mail_free(&mail); @@ -246,75 +119,23 @@ fetch_and_copy_reclassified(struct mailbox_transaction_context *t, if (mailbox_search_deinit(&search_ctx) < 0) ret = -1; - /* got all signatures now, walk them passing to dspam */ - while (siglist) { - if ((*enh_error = call_dspam(siglist->sig, is_spam))) { - ret = -1; - break; - } - siglist = siglist->next; - } - - pool_unref(listpool); - - if (*enh_error) { - mailbox_transaction_rollback(&src_trans); - } else { - if (mailbox_transaction_commit(&src_trans, 0) < 0) - ret = -1; - } + if (mailbox_transaction_commit(&src_trans, 0) < 0) + ret = -1; return ret; } -static bool cmd_append_spam_plugin(struct client_command_context *cmd) -{ - const char *mailbox; - struct mail_storage *storage; - struct mailbox *box; - - /* <mailbox> */ - if (!client_read_string_args(cmd, 1, &mailbox)) - return FALSE; - - storage = client_find_storage(cmd, &mailbox); - if (storage == NULL) - return FALSE; - /* TODO: is this really the best way to handle this? maybe more logic could be provided */ - box = - mailbox_open(storage, mailbox, NULL, - MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT); - if (box != NULL) { - - if (mailbox_equals(box, storage, "SPAM")) { - mailbox_close(&box); - return cmd_sync(cmd, 0, 0, - "NO Cannot APPEND to SPAM box, sorry."); - } - - mailbox_close(&box); - } - - return cmd_append(cmd); -} -static bool cmd_copy_spam_plugin(struct client_command_context *cmd) +static bool cmd_copy_antispam(struct client_command_context *cmd) { struct client *client = cmd->client; struct mail_storage *storage; struct mailbox *destbox; struct mailbox_transaction_context *t; - struct mail_search_arg *search_arg; + struct mail_search_arg *search_arg; const char *messageset, *mailbox; - enum mailbox_sync_flags sync_flags = 0; + enum mailbox_sync_flags sync_flags = 0; int ret; - int spam_folder = 0; - int enh_error = 0, is_spam; -#ifdef IGNORE_TRASH_NAME - int is_trash; - int trash_folder = 0; -#endif - struct mailbox *box; /* <message set> <mailbox> */ if (!client_read_string_args(cmd, 2, &messageset, &mailbox)) @@ -323,42 +144,6 @@ static bool cmd_copy_spam_plugin(struct client_command_context *cmd) if (!client_verify_open_mailbox(cmd)) return TRUE; - storage = client_find_storage(cmd, &mailbox); - if (storage == NULL) - return FALSE; - box = - mailbox_open(storage, mailbox, NULL, - MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT); - if (!box) { - client_send_storage_error(cmd, storage); - return TRUE; - } - - is_spam = mailbox_equals(box, storage, "SPAM"); - spam_folder = is_spam - || mailbox_equals(cmd->client->mailbox, storage, "SPAM"); -#ifdef IGNORE_TRASH_NAME - is_trash = mailbox_equals(box, storage, IGNORE_TRASH_NAME); - trash_folder = is_trash - || mailbox_equals(cmd->client->mailbox, storage, IGNORE_TRASH_NAME); -#endif - - mailbox_close(&box); - - /* only act on spam */ - if (!spam_folder) - return cmd_copy(cmd); -#ifdef IGNORE_TRASH_NAME - /* ignore any mail going into or out of trash - * This means users can circumvent re-classification - * by moving into trash and then out again... - * All in all, it may be a better idea to not use - * a Trash folder at all :) */ - if (trash_folder) - return cmd_copy(cmd); -#endif - - /* otherwise, do (almost) everything the copy would have done */ /* open the destination mailbox */ if (!client_verify_mailbox_name(cmd, mailbox, TRUE, FALSE)) return TRUE; @@ -375,6 +160,7 @@ static bool cmd_copy_spam_plugin(struct client_command_context *cmd) destbox = client->mailbox; else { destbox = mailbox_open(storage, mailbox, NULL, + MAILBOX_OPEN_SAVEONLY | MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT); if (destbox == NULL) { @@ -385,9 +171,7 @@ static bool cmd_copy_spam_plugin(struct client_command_context *cmd) t = mailbox_transaction_begin(destbox, MAILBOX_TRANSACTION_FLAG_EXTERNAL); - ret = - fetch_and_copy_reclassified(t, client->mailbox, search_arg, is_spam, - &enh_error); + ret = fetch_and_copy(client, t, search_arg); if (ret <= 0) mailbox_transaction_rollback(&t); @@ -406,41 +190,58 @@ static bool cmd_copy_spam_plugin(struct client_command_context *cmd) else if (ret == 0) { /* some messages were expunged, sync them */ return cmd_sync(cmd, 0, 0, - "NO Some of the requested messages no longer exist."); + "NO Some of the requested messages no longer exist."); } else { - switch (enh_error) { - case -2: + client_send_storage_error(cmd, storage); + return TRUE; + } +} + + +static bool cmd_append_antispam(struct client_command_context *cmd) +{ + const char *mailbox; + struct mail_storage *storage; + struct mailbox *box; + + /* <mailbox> */ + if (!client_read_string_args(cmd, 1, &mailbox)) + return FALSE; + + storage = client_find_storage(cmd, &mailbox); + if (storage == NULL) + return FALSE; + box = + mailbox_open(storage, mailbox, NULL, + MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT); + if (box != NULL) { + + if (mailbox_equals(box, storage, "SPAM")) { + mailbox_close(&box); return cmd_sync(cmd, 0, 0, - "NO Some messages did not have " - SIGHEADERLINE " header line"); - break; - case -3: - return cmd_sync(cmd, 0, 0, "NO Failed to call dspam"); - break; - case 0: - client_send_storage_error(cmd, storage); - return TRUE; - break; - default: - return cmd_sync(cmd, 0, 0, "NO dspam failed"); - break; + "NO Cannot APPEND to SPAM box, sorry."); } + + mailbox_close(&box); } - return TRUE; + return cmd_append(cmd); } + void dspam_init(void) { command_unregister("COPY"); command_unregister("APPEND"); command_unregister("UID COPY"); - /* i_strdup() here is a kludge to avoid crashing in commands_deinit() + /* + * i_strdup() here is a kludge to avoid crashing in commands_deinit() * since modules are unloaded before it's called, this "COPY" string - * would otherwise point to nonexisting memory. */ - command_register(i_strdup("COPY"), cmd_copy_spam_plugin); - command_register(i_strdup("UID COPY"), cmd_copy_spam_plugin); - command_register(i_strdup("APPEND"), cmd_append_spam_plugin); + * would otherwise point to nonexisting memory. + */ + command_register(i_strdup("COPY"), cmd_copy_antispam); + command_register(i_strdup("UID COPY"), cmd_copy_antispam); + command_register(i_strdup("APPEND"), cmd_append_antispam); } void dspam_deinit(void) |