From 7714d03aa4df0fd1fcc8ec4481b637b2006ac701 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Mon, 1 Jun 2009 18:57:20 +0200 Subject: first version of trainstore (untested) --- trainstore.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 trainstore.c diff --git a/trainstore.c b/trainstore.c new file mode 100644 index 0000000..5cb7cf5 --- /dev/null +++ b/trainstore.c @@ -0,0 +1,325 @@ +/* + * mailing backend for dovecot antispam plugin + * + * Copyright (C) 2009 Alexander Sulfrian + * + * 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 + */ + +#include +#include +#include +#include +#include +#include + +#include "lib.h" +#include "dict.h" +#include "mail-storage-private.h" +#include "ostream.h" +#include "istream.h" + +#include "antispam-plugin.h" + +static const char *spamdir = NULL; +static const char *hamdir = NULL; +static const char *tmpdir = NULL; + +struct antispam_transaction_context { + int count; + char *tmpdir; + int tmplen; +}; + +struct antispam_transaction_context * +backend_start(struct mailbox *box __attr_unused__) +{ + struct antispam_transaction_context *ast; + char *tmp; + + ast = i_new(struct antispam_transaction_context, 1); + ast->count = 0; + + tmp = i_strconcat(tmp, "/antispam-mail-XXXXXX", NULL); + + ast->tmpdir = mkdtemp(tmp); + if (!ast->tmpdir) + i_free(tmp); + else + ast->tmplen = strlen(ast->tmpdir); + + return ast; +} + +static void clear_tmpdir(struct antispam_transaction_context *ast) +{ + char *buf; + + t_push(); + + buf = t_malloc(20 + ast->tmplen); + + while (ast->count > 0) { + ast->count--; + i_snprintf(buf, 20 + ast->tmplen - 1, "%s/%d", + ast->tmpdir, ast->count); + unlink(buf); + } + rmdir(ast->tmpdir); + + t_pop(); +} + +void backend_rollback(struct antispam_transaction_context *ast) +{ + if (ast->tmpdir) { + /* clear it! */ + clear_tmpdir(ast); + i_free(ast->tmpdir); + } + + i_free(ast); +} + +int backend_commit(struct mailbox_transaction_context *ctx __attr_unused__, + struct antispam_transaction_context *ast) +{ + if (!ast->tmpdir) { + i_free(ast); + return 0; + } + + i_free(ast->tmpdir); + i_free(ast); + + return 0; +} + +static char *create_unique_mailname(const char *date, const char *mail_id, char *sha1) +{ + char *unique_string; + unsigned char *sha_bytes; + + t_push(); + + unique_string = t_malloc(strlen(date) + strlen(mail_id) + 1); + i_snprintf(unique_string, strlen(date) + strlen(mail_id), "%s%s", date, mail_id); + + sha_bytes = t_malloc(20); + sha_bytes = SHA1((unsigned char*)unique_string, strlen(date) + strlen(mail_id), sha_bytes); + + int i; + for (i = 0; i < 20; i++) { + i_snprintf(sha1+(i*2), 2, "%hhx", sha_bytes[i]); + } + sha1[40] = 0; + + t_pop(); + + return sha1; +} + +int backend_handle_mail(struct mailbox_transaction_context *t, + struct antispam_transaction_context *ast, + struct mail *mail, enum classification wanted) +{ + struct istream *mailstream; + struct ostream *outstream; + int ret; + char *file; + const unsigned char *beginning; + size_t size; + int fd; + char *name = NULL, *check_file = NULL; + const char *date = NULL, *mail_id = NULL, *dest = NULL, *other = NULL; + struct stat sb; + + if (!hamdir || !spamdir) { + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "antispam plugin not configured"); + return -1; + } + + mailstream = get_mail_stream(mail); + if (!mailstream) { + mail_storage_set_error(t->box->storage, + ME(EXPUNGED) + "Failed to get mail contents"); + return -1; + } + + t_push(); + + /* Generate unique id for mail */ + + /* get date header */ + if (mail_get_first_header(mail, "Date", &date) == 0) { + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Invalid mail with no date header"); + ret = -1; + goto out; + } + + /* get message id */ + if (mail_get_first_header(mail, "Message-Id", &mail_id) == 0) { + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Invalid mail with no message id header"); + ret = -1; + goto out; + } + + /* hash name and message id */ + name = t_malloc(41 * sizeof(char)); + name = create_unique_mailname(date, mail_id, name); + + /* switch weather this should be ham or spam */ + switch (wanted) { + case CLASS_SPAM: + dest = spamdir; + other = hamdir; + break; + case CLASS_NOTSPAM: + dest = hamdir; + other = spamdir; + break; + } + + if (dest == NULL) { + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Failed to determine whether mail is ham or spam"); + ret = -1; + goto out; + } + + /* check if this mail is already marked as the opposit */ + check_file = t_malloc(2 + strlen(other) + strlen(name)); + i_snprintf(check_file, 1 + strlen(other) + strlen(name), "%s/%s", other, name); + + if (stat(check_file, &sb) == 0) { + debug("mail is marked opposit, remove it there"); + unlink(check_file); + } + + /* create file name */ + file = t_malloc(2 + strlen(dest) + strlen(name)); + i_snprintf(file, 1 + strlen(dest) + strlen(name), "%s/%s", dest, name); + + if (stat(file, &sb) == 0) { + debug("mail is already there, nothing to do"); + ret = 0; + goto out; + } + + fd = creat(file, 0600); + if (fd < 0) { + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Failed to create temporary file"); + ret = -1; + goto out; + } + + ast->count++; + + outstream = o_stream_create_from_fd(fd, t->box->pool); + if (!outstream) { + ret = -1; + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Failed to stream temporary file"); + goto out_close; + } + + if (o_stream_send(outstream, &wanted, sizeof(wanted)) + != sizeof(wanted)) { + ret = -1; + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Failed to write marker to temp file"); + goto failed_to_copy; + } + + if (i_stream_read_data(mailstream, &beginning, &size, 5) < 0 || + size < 5) { + ret = -1; + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Failed to read mail beginning"); + goto failed_to_copy; + } + + /* "From "? skip line */ + if (memcmp("From ", beginning, 5) == 0) { + i_stream_read_next_line(mailstream); + } + else { + if (o_stream_send_str(outstream, "From ") < 0) { + ret = -1; + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Failed to write line to temp"); + goto failed_to_copy; + } + } + + if (o_stream_send_istream(outstream, mailstream) < 0) { + ret = -1; + mail_storage_set_error(t->box->storage, + ME(NOTPOSSIBLE) + "Failed to copy to temporary file"); + goto failed_to_copy; + } + + ret = 0; + + failed_to_copy: + o_stream_destroy(&outstream); + out_close: + close(fd); + out: + t_pop(); + + return ret; +} + +void backend_init(pool_t pool __attr_unused__) +{ + const char *tmp; + + tmp = get_setting("DIR_SPAM"); + if (tmp) { + spamdir = tmp; + debug("mail backend spam directory %s\n", tmp); + } + + tmp = get_setting("DIR_NOTSPAM"); + if (tmp) { + hamdir = tmp; + debug("mail backend not-spam directory %s\n", tmp); + } + + tmp = get_setting("DIR_TMP"); + if (tmp) { + tmpdir = tmp; + debug("mail backend temporary directory %s\n", tmp); + } +} + +void backend_exit(void) +{ +} -- cgit v1.2.3