aboutsummaryrefslogtreecommitdiffstats
path: root/plugin.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2007-09-30 13:34:59 +0200
committerJohannes Berg <johannes@sipsolutions.net>2007-09-30 13:34:59 +0200
commitd30cef3c11f07889c2f8e86340b013174d28fc5c (patch)
treeb4c935edff52f9829e1e8fd927a681596e26b4a8 /plugin.c
parent96730567e4a1fc835ed8a2c93988c2cacd1f914c (diff)
downloaddovecot-antispam-d30cef3c11f07889c2f8e86340b013174d28fc5c.tar.gz
dovecot-antispam-d30cef3c11f07889c2f8e86340b013174d28fc5c.tar.xz
dovecot-antispam-d30cef3c11f07889c2f8e86340b013174d28fc5c.zip
start of rewrite
Diffstat (limited to '')
-rw-r--r--plugin.c421
1 files changed, 111 insertions, 310 deletions
diff --git a/plugin.c b/plugin.c
index bcd546e..3f7eb97 100644
--- a/plugin.c
+++ b/plugin.c
@@ -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)