[dovecot-cvs] dovecot/src/deliver Makefile.am, 1.1,
1.1.2.1 auth-client.c, NONE, 1.1.2.1 auth-client.h, NONE,
1.1.2.1 deliver.c, 1.20, 1.20.2.1 deliver.h, NONE,
1.1.2.1 duplicate.c, NONE, 1.1.2.1 duplicate.h, NONE,
1.1.2.1 mail-send.c, NONE, 1.1.2.1 mail-send.h, NONE,
1.1.2.1 smtp-client.c, NONE, 1.1.2.1 smtp-client.h, NONE, 1.1.2.1
cras at dovecot.org
cras at dovecot.org
Sun Jun 11 14:10:58 EEST 2006
Update of /var/lib/cvs/dovecot/src/deliver
In directory talvi:/tmp/cvs-serv13521/src/deliver
Modified Files:
Tag: branch_1_0
Makefile.am deliver.c
Added Files:
Tag: branch_1_0
auth-client.c auth-client.h deliver.h duplicate.c duplicate.h
mail-send.c mail-send.h smtp-client.c smtp-client.h
Log Message:
Moved all the non-Sieve code from dovecot-lda in here and rewrote parts of
it to be cleaner. The Sieve code can now be run as a plugin from
dovecot-sieve CVS module.
Index: Makefile.am
===================================================================
RCS file: /var/lib/cvs/dovecot/src/deliver/Makefile.am,v
retrieving revision 1.1
retrieving revision 1.1.2.1
diff -u -d -r1.1 -r1.1.2.1
--- Makefile.am 12 Mar 2005 23:52:00 -0000 1.1
+++ Makefile.am 11 Jun 2006 11:10:55 -0000 1.1.2.1
@@ -4,10 +4,17 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-dict \
-I$(top_srcdir)/src/lib-mail \
-I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index/mbox \
-DSYSCONFDIR=\""$(sysconfdir)"\"
+# get some functions included which only plugins use. liblib should probably
+# be a shared library so this wouldn't be needed..
+unused_objects = \
+ ../lib/str-sanitize.o
+
libs = \
../lib-storage/register/libstorage-register.a \
$(STORAGE_LIBS) \
@@ -15,8 +22,10 @@
../lib-storage/subscription-file/libstorage_subscription_file.a \
../lib-imap/libimap.a \
../lib-mail/libmail.a \
+ ../lib-dict/libdict.a \
../lib-charset/libcharset.a \
- ../lib/liblib.a
+ ../lib/liblib.a \
+ $(unused_objects)
deliver_LDADD = \
$(libs) \
@@ -27,4 +36,15 @@
deliver_DEPENDENCIES = $(libs)
deliver_SOURCES = \
- deliver.c
+ auth-client.c \
+ deliver.c \
+ duplicate.c \
+ mail-send.c \
+ smtp-client.c
+
+noinst_HEADERS = \
+ auth-client.h \
+ deliver.h \
+ duplicate.h \
+ mail-send.h \
+ smtp-client.h
--- NEW FILE: auth-client.c ---
/* Copyright (C) 2005-2006 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "network.h"
#include "istream.h"
#include "ostream.h"
#include "env-util.h"
#include "restrict-access.h"
#include "auth-client.h"
#include <unistd.h>
#include <sysexits.h>
#define MAX_INBUF_SIZE 8192
#define MAX_OUTBUF_SIZE 512
static int return_value;
struct auth_connection {
int fd;
struct io *io;
struct istream *input;
struct ostream *output;
struct ioloop *ioloop;
unsigned int handshaked:1;
};
static void auth_connection_destroy(struct auth_connection *conn)
{
io_loop_stop(conn->ioloop);
io_remove(&conn->io);
i_stream_unref(&conn->input);
o_stream_unref(&conn->output);
if (close(conn->fd) < 0)
i_error("close() failed: %m");
i_free(conn);
}
static void auth_parse_input(const char *args)
{
const char *const *tmp, *key, *value;
int home_found = FALSE;
for (tmp = t_strsplit(args, "\t"); *tmp != NULL; tmp++) {
if (strncmp(*tmp, "uid=", 4) == 0) {
env_put(t_strconcat("RESTRICT_SETUID=",
*tmp + 4, NULL));
} else if (strncmp(*tmp, "gid=", 4) == 0) {
env_put(t_strconcat("RESTRICT_SETGID=",
*tmp + 4, NULL));
} else if (strncmp(*tmp, "chroot=", 7) == 0) {
env_put(t_strconcat("RESTRICT_CHROOT=",
*tmp + 7, NULL));
} else if (strncmp(*tmp, "home=", 5) == 0) {
home_found = TRUE;
env_put(t_strconcat("HOME=", *tmp + 5, NULL));
} else {
key = t_str_ucase(t_strcut(*tmp, '='));
value = strchr(*tmp, '=');
if (value != NULL)
env_put(t_strconcat(key, "=", value+1, NULL));
}
}
if (!home_found) {
/* we must have a home directory */
i_error("userdb didn't return a home directory");
return_value = EX_TEMPFAIL;
return;
}
restrict_access_by_env(TRUE);
return_value = EX_OK;
}
static void auth_input(void *context)
{
struct auth_connection *conn = context;
const char *line;
switch (i_stream_read(conn->input)) {
case 0:
return;
case -1:
/* disconnected */
auth_connection_destroy(conn);
return;
case -2:
/* buffer full */
i_error("BUG: Auth master sent us more than %d bytes",
MAX_INBUF_SIZE);
auth_connection_destroy(conn);
return;
}
if (!conn->handshaked) {
while ((line = i_stream_next_line(conn->input)) != NULL) {
if (strncmp(line, "VERSION\t", 8) == 0) {
if (strncmp(line + 8, "1\t", 2) != 0) {
i_error("Auth master version mismatch");
auth_connection_destroy(conn);
return;
}
} else if (strncmp(line, "SPID\t", 5) == 0) {
conn->handshaked = TRUE;
break;
}
}
}
line = i_stream_next_line(conn->input);
if (line != NULL) {
if (strncmp(line, "USER\t1\t", 7) == 0) {
auth_parse_input(line + 7);
} else if (strcmp(line, "NOTFOUND\t1") == 0)
return_value = EX_NOUSER;
else if (strncmp(line, "FAIL\t1\t", 7) == 0)
return_value = EX_TEMPFAIL;
else {
i_error("BUG: Unexpected input from auth master: %s",
line);
}
auth_connection_destroy(conn);
}
}
static struct auth_connection *auth_connection_new(const char *auth_socket)
{
struct auth_connection *conn;
int fd;
fd = net_connect_unix(auth_socket);
if (fd < 0) {
i_error("net_connect(%s) failed: %m", auth_socket);
return NULL;
}
conn = i_new(struct auth_connection, 1);
conn->fd = fd;
conn->input =
i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, FALSE);
conn->output =
o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE, FALSE);
conn->io = io_add(fd, IO_READ, auth_input, conn);
return conn;
}
int auth_client_put_user_env(struct ioloop *ioloop, const char *auth_socket,
const char *user)
{
struct auth_connection *conn;
conn = auth_connection_new(auth_socket);
if (conn == NULL)
return EX_TEMPFAIL;
conn->ioloop = ioloop;
o_stream_send_str(conn->output,
t_strconcat("VERSION\t1\t0\n"
"USER\t1\t", user, "\t"
"service=deliver\n", NULL));
return_value = EX_TEMPFAIL;
io_loop_run(ioloop);
return return_value;
}
--- NEW FILE: auth-client.h ---
#ifndef __AUTH_CLIENT_H
#define __AUTH_CLIENT_H
int auth_client_put_user_env(struct ioloop *ioloop, const char *auth_socket,
const char *user);
#endif
Index: deliver.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/deliver/deliver.c,v
retrieving revision 1.20
retrieving revision 1.20.2.1
diff -u -d -r1.20 -r1.20.2.1
--- deliver.c 3 May 2006 22:58:53 -0000 1.20
+++ deliver.c 11 Jun 2006 11:10:55 -0000 1.20.2.1
@@ -1,43 +1,47 @@
-/* Copyright (C) 2005 Timo Sirainen */
-
-/* FIXME: pretty ugly thing. */
-#error This program is somewhat out of date, use dovecot-lda instead
+/* Copyright (C) 2005-2006 Timo Sirainen */
#include "lib.h"
#include "lib-signals.h"
#include "ioloop.h"
+#include "hostpid.h"
+#include "home-expand.h"
#include "env-util.h"
-#include "network.h"
-#include "restrict-access.h"
+#include "fd-set-nonblock.h"
#include "istream.h"
-#include "ostream.h"
+#include "istream-seekable.h"
+#include "module-dir.h"
#include "str.h"
#include "var-expand.h"
-#include "mail-storage.h"
+#include "message-address.h"
+#include "dict-client.h"
+#include "mbox-from.h"
+#include "auth-client.h"
+#include "mail-send.h"
+#include "duplicate.h"
+#include "deliver.h"
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
-#include <sysexits.h>
+#include <syslog.h>
-#define DEFAULT_CONFIG_FILE SYSCONFDIR"/dovecot-deliver.conf"
-#define DEFAULT_AUTH_SOCKET_PATH "/var/run/dovecot-auth-master"
+#define DEFAULT_CONFIG_FILE SYSCONFDIR"/dovecot.conf"
+#define DEFAULT_AUTH_SOCKET_PATH "/var/run/dovecot/auth-master"
+#define DEFAULT_SENDMAIL_PATH "/usr/lib/sendmail"
-#define MAX_INBUF_SIZE 8192
-#define MAX_OUTBUF_SIZE 512
+/* After buffer grows larger than this, create a temporary file to /tmp
+ where to read the mail. */
+#define MAIL_MAX_MEMORY_BUFFER (1024*128)
-struct auth_connection {
- int fd;
- struct io *io;
- struct istream *input;
- struct ostream *output;
+struct deliver_settings *deliver_set;
+deliver_mail_func_t *deliver_mail = NULL;
- unsigned int handshaked:1;
-};
+void (*hook_mail_storage_created)(struct mail_storage *storage) = NULL;
+static struct module *modules;
static struct ioloop *ioloop;
-static int return_value = EX_SOFTWARE;
static void sig_die(int signo, void *context __attr_unused__)
{
@@ -57,202 +61,104 @@
ctx = mailbox_sync_init(box, 0);
while (mailbox_sync_next(ctx, &sync_rec) > 0)
;
- return mailbox_sync_deinit(ctx, &status);
+ return mailbox_sync_deinit(&ctx, &status);
}
-struct save_mail_context {
- struct mail_save_context *save_ctx;
- struct istream *input;
- int ret;
-};
-
-static void save_mail_input(void *context)
+static struct mailbox *
+mailbox_open_or_create_synced(struct mail_storage *storage, const char *name)
{
- struct save_mail_context *ctx = context;
+ struct mailbox *box;
+ bool syntax, temp;
- if (ctx->input->closed ||
- mailbox_save_continue(ctx->save_ctx) < 0)
- io_loop_stop(ioloop);
- else if (ctx->input->eof) {
- ctx->ret = 0;
- io_loop_stop(ioloop);
+ box = mailbox_open(storage, name, NULL, MAILBOX_OPEN_FAST |
+ MAILBOX_OPEN_KEEP_RECENT);
+ if (box != NULL)
+ return box;
+
+ (void)mail_storage_get_last_error(storage, &syntax, &temp);
+ if (syntax || temp)
+ return NULL;
+
+ /* probably the mailbox just doesn't exist. try creating it. */
+ if (mail_storage_mailbox_create(storage, name, FALSE) < 0)
+ return NULL;
+
+ /* and try opening again */
+ box = mailbox_open(storage, name, NULL, MAILBOX_OPEN_FAST |
+ MAILBOX_OPEN_KEEP_RECENT);
+ if (box == NULL)
+ return NULL;
+
+ if (sync_quick(box) < 0) {
+ mailbox_close(&box);
+ return NULL;
}
+ return box;
}
-static int save_mail(struct mail_storage *storage, const char *mailbox,
- struct istream *input)
+int deliver_save(struct mail_storage *storage, const char *mailbox,
+ struct mail *mail, enum mail_flags flags,
+ const char *const *keywords)
{
struct mailbox *box;
struct mailbox_transaction_context *t;
- struct save_mail_context ctx;
- struct io *io;
+ struct mail_keywords *kw;
int ret = 0;
- box = mailbox_open(storage, mailbox, NULL, MAILBOX_OPEN_FAST |
- MAILBOX_OPEN_KEEP_RECENT);
+ box = mailbox_open_or_create_synced(storage, mailbox);
if (box == NULL)
- return FALSE;
-
- if (sync_quick(box) < 0) {
- mailbox_close(box);
- return FALSE;
- }
+ return -1;
t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL);
- memset(&ctx, 0, sizeof(ctx));
- ctx.ret = -1;
- ctx.input = input;
- ctx.save_ctx = mailbox_save_init(t, 0, NULL, (time_t)-1, 0, NULL,
- input, FALSE);
-
- io = io_add(i_stream_get_fd(input), IO_READ, save_mail_input, &ctx);
- io_loop_run(ioloop);
- io_remove(io);
-
- ret = ctx.ret;
- if (ret < 0)
- mailbox_save_cancel(ctx.save_ctx);
- else
- ret = mailbox_save_finish(ctx.save_ctx, NULL);
+ kw = strarray_length(keywords) == 0 ? NULL :
+ mailbox_keywords_create(t, keywords);
+ if (mailbox_copy(t, mail, flags, kw, NULL) < 0)
+ ret = -1;
+ mailbox_keywords_free(t, &kw);
if (ret < 0)
- mailbox_transaction_rollback(t);
+ mailbox_transaction_rollback(&t);
else
- ret = mailbox_transaction_commit(t, 0);
+ ret = mailbox_transaction_commit(&t, 0);
- mailbox_close(box);
+ mailbox_close(&box);
return ret;
}
-static void auth_connection_destroy(struct auth_connection *conn)
-{
- io_loop_stop(ioloop);
-
- io_remove(conn->io);
- i_stream_destroy(conn->input);
- o_stream_destroy(conn->output);
- if (close(conn->fd) < 0)
- i_error("close() failed: %m");
- i_free(conn);
-}
-
-static void auth_parse_input(const char *args)
-{
- const char *const *tmp;
-
- for (tmp = t_strsplit(args, "\t"); *tmp != NULL; tmp++) {
- if (strncmp(*tmp, "uid=", 4) == 0) {
- env_put(t_strconcat("RESTRICT_SETUID=",
- *tmp + 4, NULL));
- } else if (strncmp(*tmp, "gid=", 4) == 0) {
- env_put(t_strconcat("RESTRICT_SETGID=",
- *tmp + 4, NULL));
- } else if (strncmp(*tmp, "chroot=", 7) == 0) {
- env_put(t_strconcat("RESTRICT_CHROOT=",
- *tmp + 7, NULL));
- } else if (strncmp(*tmp, "home=", 5) == 0)
- env_put(t_strconcat("HOME=", *tmp + 5, NULL));
- }
-
- restrict_access_by_env(TRUE);
- return_value = EX_OK;
-}
-
-static void auth_input(void *context)
+const char *deliver_get_return_address(struct mail *mail)
{
- struct auth_connection *conn = context;
- const char *line;
-
- switch (i_stream_read(conn->input)) {
- case 0:
- return;
- case -1:
- /* disconnected */
- auth_connection_destroy(conn);
- return;
- case -2:
- /* buffer full */
- i_error("BUG: Auth master sent us more than %d bytes",
- MAX_INBUF_SIZE);
- auth_connection_destroy(conn);
- return;
- }
-
- if (!conn->handshaked) {
- while ((line = i_stream_next_line(conn->input)) != NULL) {
- if (strncmp(line, "VERSION\t", 8) == 0) {
- if (strncmp(line + 8, "1\t", 2) != 0) {
- i_error("Auth master version mismatch");
- auth_connection_destroy(conn);
- return;
- }
- } else if (strncmp(line, "SPID\t", 5) == 0) {
- conn->handshaked = TRUE;
- break;
- }
- }
- }
+ struct message_address *addr;
+ const char *str;
- line = i_stream_next_line(conn->input);
- if (line != NULL) {
- if (strncmp(line, "USER\t1\t", 7) == 0) {
- auth_parse_input(line + 7);
- } else if (strcmp(line, "NOTFOUND\t1") == 0)
- return_value = EX_NOUSER;
- else if (strncmp(line, "FAIL\t1\t", 7) == 0)
- return_value = EX_TEMPFAIL;
- else {
- i_error("BUG: Unexpected input from auth master: %s",
- line);
- }
- auth_connection_destroy(conn);
- }
+ str = mail_get_first_header(mail, "Return-Path");
+ addr = str == NULL ? NULL :
+ message_address_parse(pool_datastack_create(),
+ (const unsigned char *)str,
+ strlen(str), 1);
+ return addr == NULL || addr->mailbox == NULL || addr->domain == NULL ?
+ NULL : t_strconcat(addr->mailbox, "@", addr->domain, NULL);
}
-static struct auth_connection *auth_connection_new(const char *auth_socket)
+const char *deliver_get_new_message_id(void)
{
- struct auth_connection *conn;
- int fd;
-
- fd = net_connect_unix(auth_socket);
- if (fd < 0) {
- i_error("net_connect(%s) failed: %m", auth_socket);
- return NULL;
- }
+ static int count = 0;
- conn = i_new(struct auth_connection, 1);
- conn->fd = fd;
- conn->input =
- i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, FALSE);
- conn->output =
- o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE, FALSE);
- conn->io = io_add(fd, IO_READ, auth_input, conn);
- return conn;
+ return t_strdup_printf("<dovecot-%s-%s-%d@%s>",
+ dec2str(ioloop_timeval.tv_sec),
+ dec2str(ioloop_timeval.tv_usec),
+ count++, deliver_set->hostname);
}
-static int user_init(const char *auth_socket, const char *destination)
-{
- struct auth_connection *conn;
-
- conn = auth_connection_new(auth_socket);
- if (conn == NULL)
- return EX_TEMPFAIL;
-
- o_stream_send_str(conn->output,
- t_strconcat("VERSION\t1\t0\n"
- "USER\t1\t", destination, "\t"
- "service=deliver\n", NULL));
-
- io_loop_run(ioloop);
- return return_value;
-}
+#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
static void config_file_init(const char *path)
{
struct istream *input;
- const char *line, *p, *key, *value;
- int fd;
+ const char *key, *value;
+ char *line, *p, quote;
+ int fd, sections = 0, lda_section = FALSE;
+ size_t len;
fd = open(path, O_RDONLY);
if (fd < 0)
@@ -261,12 +167,54 @@
t_push();
input = i_stream_create_file(fd, default_pool, 1024, TRUE);
while ((line = i_stream_read_next_line(input)) != NULL) {
- while (*line == ' ') line++;
- if (*line == '#')
+ /* @UNSAFE: line is modified */
+
+ /* skip whitespace */
+ while (IS_WHITE(*line))
+ line++;
+
+ /* ignore comments or empty lines */
+ if (*line == '#' || *line == '\0')
continue;
+ /* strip away comments. pretty kludgy way really.. */
+ for (p = line; *p != '\0'; p++) {
+ if (*p == '\'' || *p == '"') {
+ quote = *p;
+ for (p++; *p != quote && *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0')
+ p++;
+ }
+ if (*p == '\0')
+ break;
+ } else if (*p == '#') {
+ *p = '\0';
+ break;
+ }
+ }
+
+ /* remove whitespace from end of line */
+ len = strlen(line);
+ while (IS_WHITE(line[len-1]))
+ len--;
+ line[len] = '\0';
+
value = p = strchr(line, '=');
- if (value == NULL)
+ if (value == NULL) {
+ if (strchr(line, '{') != NULL) {
+ if (strcmp(line, "protocol lda {") == 0 ||
+ strcmp(line, "plugin {") == 0)
+ lda_section = TRUE;
+ sections++;
+ }
+ if (*line == '}') {
+ sections--;
+ lda_section = FALSE;
+ }
+ continue;
+ }
+
+ if (sections > 0 && !lda_section)
continue;
while (p > line && p[-1] == ' ') p--;
@@ -278,7 +226,7 @@
env_put(t_strconcat(t_str_ucase(key), "=", value, NULL));
}
- i_stream_unref(input);
+ i_stream_unref(&input);
t_pop();
}
@@ -309,7 +257,7 @@
tab[4].value = home;
tab[5].value = NULL;
tab[6].value = NULL;
- tab[7].value = dec2str(getpid());
+ tab[7].value = my_pid;
return tab;
}
@@ -343,18 +291,70 @@
return str_c(str);
}
+static struct istream *create_mbox_stream(int fd)
+{
+ const char *mbox_hdr;
+ struct istream *input_list[4], *input;
+
+ fd_set_nonblock(fd, FALSE);
+
+ mbox_hdr = mbox_from_create("dovecot.deliver", ioloop_time);
+
+ input_list[0] = i_stream_create_from_data(default_pool, mbox_hdr,
+ strlen(mbox_hdr));
+ input_list[1] = i_stream_create_file(fd, default_pool, 4096, FALSE);
+ input_list[2] = i_stream_create_from_data(default_pool, "\n", 1);
+ input_list[3] = NULL;
+
+ input = i_stream_create_seekable(input_list, default_pool,
+ MAIL_MAX_MEMORY_BUFFER,
+ "/tmp/dovecot.deliver.");
+ i_stream_unref(&input_list[0]);
+ i_stream_unref(&input_list[1]);
+ i_stream_unref(&input_list[2]);
+ return input;
+}
+
+static void open_logfile(const char *username)
+{
+ const char *prefix;
+
+ prefix = t_strdup_printf("deliver(%s)", username);
+ if (getenv("LOG_PATH") == NULL) {
+ const char *env = getenv("SYSLOG_FACILITY");
+ i_set_failure_syslog(prefix, LOG_NDELAY,
+ env == NULL ? LOG_MAIL : atoi(env));
+ } else {
+ /* log to file or stderr */
+ i_set_failure_file(getenv("LOG_PATH"), prefix);
+ }
+
+ if (getenv("INFO_LOG_PATH") != NULL)
+ i_set_info_file(getenv("INFO_LOG_PATH"));
+
+ i_set_failure_timestamp_format(getenv("LOG_TIMESTAMP"));
+}
+
+static void print_help(void)
+{
+ printf("Usage: deliver [-c <config file>] [-d <destination user>] [-m <mailbox>]\n");
+}
+
int main(int argc, char *argv[])
{
- const char *auth_socket = DEFAULT_AUTH_SOCKET_PATH;
+ const char *config_path = DEFAULT_CONFIG_FILE;
const char *mailbox = "INBOX";
- const char *destination, *mail;
+ const char *auth_socket, *env_tz;
+ const char *home, *destination, *user, *mail_env, *str;
const struct var_expand_table *table;
enum mail_storage_flags flags;
enum mail_storage_lock_method lock_method;
- struct mail_storage *storage;
+ struct mail_storage *storage, *mbox_storage;
+ struct mailbox *box;
struct istream *input;
+ struct mailbox_transaction_context *t;
+ struct mail *mail;
int i, ret;
- const char *str;
lib_init();
ioloop = io_loop_create(default_pool);
@@ -364,6 +364,18 @@
lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
lib_signals_ignore(SIGPIPE);
lib_signals_set_handler(SIGALRM, FALSE, NULL, NULL);
+#ifdef SIGXFSZ
+ lib_signals_set_handler(SIGXFSZ, FALSE, NULL, NULL);
+#endif
+
+ /* Clean up environment. */
+ env_tz = getenv("TZ");
+ home = getenv("HOME");
+ env_clean();
+ if (env_tz != NULL)
+ env_put(t_strconcat("TZ=", env_tz, NULL));
+ if (home != NULL)
+ env_put(t_strconcat("HOME=", home, NULL));
destination = NULL;
for (i = 1; i < argc; i++) {
@@ -375,14 +387,14 @@
"Missing destination argument");
}
destination = argv[i];
- } else if (strcmp(argv[i], "-a") == 0) {
- /* auth master socket path */
+ } else if (strcmp(argv[i], "-c") == 0) {
+ /* config file path */
i++;
if (i == argc) {
i_fatal_status(EX_USAGE,
- "Missing auth socket path argument");
+ "Missing config file path argument");
}
- auth_socket = argv[i];
+ config_path = argv[i];
} else if (strcmp(argv[i], "-m") == 0) {
/* destination mailbox */
i++;
@@ -392,67 +404,163 @@
}
mailbox = argv[i];
} else {
+ print_help();
i_fatal_status(EX_USAGE,
"Unknown argument: %s", argv[1]);
}
}
-
- config_file_init(DEFAULT_CONFIG_FILE);
-
- if (destination != NULL) {
- ret = user_init(auth_socket, destination);
- if (ret != 0)
- return ret;
- } else if (geteuid() != 0) {
+ if (destination != NULL)
+ user = destination;
+ else if (geteuid() != 0) {
/* we're non-root. get our username. */
struct passwd *pw;
pw = getpwuid(geteuid());
if (pw != NULL)
- destination = t_strdup(pw->pw_name);
- }
-
- if (destination == NULL) {
+ user = t_strdup(pw->pw_name);
+ else {
+ i_fatal("Couldn't lookup our username (uid=%s)",
+ dec2str(geteuid()));
+ }
+ } else {
i_fatal_status(EX_USAGE,
"destination user parameter (-d user) not given");
}
+ config_file_init(config_path);
+ open_logfile(user);
+
+ if (destination != NULL) {
+ auth_socket = getenv("AUTH_SOCKET_PATH");
+ if (auth_socket == NULL)
+ auth_socket = DEFAULT_AUTH_SOCKET_PATH;
+
+ ret = auth_client_put_user_env(ioloop, auth_socket,
+ destination);
+ if (ret != 0)
+ return ret;
+
+ home = getenv("HOME");
+ if (home != NULL) {
+ /* If possible chdir to home directory so core file
+ could be written. If it fails, don't worry. */
+ (void)chdir(home);
+ }
+ } else {
+ destination = user;
+ }
+
+ deliver_set = i_new(struct deliver_settings, 1);
+ deliver_set->hostname = getenv("HOSTNAME");
+ if (deliver_set->hostname == NULL)
+ deliver_set->hostname = my_hostname;
+ deliver_set->postmaster_address = getenv("POSTMASTER_ADDRESS");
+ if (deliver_set->postmaster_address == NULL) {
+ i_fatal_status(EX_CONFIG,
+ "postmaster_address setting not given");
+ }
+ deliver_set->sendmail_path = getenv("SENDMAIL_PATH");
+ if (deliver_set->sendmail_path == NULL)
+ deliver_set->sendmail_path = DEFAULT_SENDMAIL_PATH;
+
+ dict_client_register();
+ duplicate_init();
mail_storage_init();
mail_storage_register_all();
- mail = getenv("MAIL");
- if (mail == NULL)
- i_fatal_status(EX_CONFIG, "mail setting not given");
-
- table = get_var_expand_table(destination, getenv("HOME"));
- mail = expand_mail_env(mail, table);
+ /* MAIL comes from userdb, DEFAULT_MAIL_ENV from dovecot.conf */
+ mail_env = getenv("MAIL");
+ if (mail_env == NULL)
+ mail_env = getenv("DEFAULT_MAIL_ENV");
+ if (mail_env != NULL) {
+ table = get_var_expand_table(destination, getenv("HOME"));
+ mail_env = expand_mail_env(mail_env, table);
+ }
str = getenv("POP3_UIDL_FORMAT");
if (str != NULL && (str = strchr(str, '%')) != NULL &&
str != NULL && var_get_key(str + 1) == 'm')
flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;
+ if (getenv("MAIL_PLUGIN_DIR") == NULL)
+ modules = NULL;
+ else {
+ if (getenv("MAIL_PLUGIN_DIR") == NULL) {
+ i_fatal("MAIL_PLUGINS given but "
+ "MAIL_PLUGIN_DIR was not");
+ }
+ modules = module_dir_load(getenv("MAIL_PLUGIN_DIR"),
+ getenv("MAIL_PLUGINS"), TRUE);
+ }
+
/* FIXME: how should we handle namespaces? */
mail_storage_parse_env(&flags, &lock_method);
- storage = mail_storage_create_with_data(mail, destination,
+ storage = mail_storage_create_with_data(mail_env, destination,
flags, lock_method);
if (storage == NULL) {
i_fatal_status(EX_CONFIG,
"Failed to create storage for '%s' with mail '%s'",
- destination, mail == NULL ? "(null)" : mail);
+ destination, mail_env == NULL ? "(null)" : mail_env);
}
- net_set_nonblock(0, TRUE);
- input = i_stream_create_file(0, default_pool, 8192, FALSE);
- if (save_mail(storage, mailbox, input) < 0)
- return EX_TEMPFAIL;
- i_stream_unref(input);
+ if (hook_mail_storage_created != NULL)
+ hook_mail_storage_created(storage);
- mail_storage_destroy(storage);
- mail_storage_deinit();
+ mbox_storage = mail_storage_create("mbox", "/tmp", destination, 0,
+ MAIL_STORAGE_LOCK_FCNTL);
+ input = create_mbox_stream(0);
+ box = mailbox_open(mbox_storage, "Dovecot Delivery Mail", input,
+ MAILBOX_OPEN_NO_INDEX_FILES);
+ if (box == NULL)
+ i_fatal("Can't open delivery mail as mbox");
+ if (sync_quick(box) < 0)
+ i_fatal("Can't sync delivery mail");
+
+ t = mailbox_transaction_begin(box, 0);
+ mail = mail_alloc(t, 0, NULL);
+ if (mail_set_seq(mail, 1) < 0)
+ i_fatal("mail_set_seq() failed");
+
+ ret = deliver_mail == NULL ? 0 :
+ deliver_mail(storage, mail, destination, mailbox);
+
+ if (ret <= 0) {
+ /* plugins didn't handle this. save into INBOX. */
+ i_stream_seek(input, 0);
+ if (deliver_save(storage, mailbox, mail, 0, NULL) < 0) {
+ const char *error;
+ bool syntax, temporary_error;
+ int ret;
+
+ error = mail_storage_get_last_error(storage, &syntax,
+ &temporary_error);
+ if (temporary_error)
+ return EX_TEMPFAIL;
+
+ /* we'll have to reply with permanent failure */
+ ret = mail_send_rejection(mail, destination, error);
+ if (ret != 0)
+ return ret < 0 ? EX_TEMPFAIL : ret;
+ /* ok, rejection sent */
+ }
+ }
+ i_stream_unref(&input);
+
+ mail_free(&mail);
+ mailbox_transaction_rollback(&t);
+ mailbox_close(&box);
+
+ mail_storage_destroy(&mbox_storage);
+ mail_storage_destroy(&storage);
+
+ module_dir_unload(&modules);
+ mail_storage_deinit();
+
+ duplicate_deinit();
+ dict_client_unregister();
lib_signals_deinit();
- io_loop_destroy(ioloop);
+ io_loop_destroy(&ioloop);
lib_deinit();
return EX_OK;
--- NEW FILE: deliver.h ---
#ifndef __DELIVER_H
#define __DELIVER_H
#include <sysexits.h>
#include "lib.h"
#include "mail-storage.h"
struct deliver_settings {
const char *hostname;
const char *postmaster_address;
const char *sendmail_path;
};
extern struct deliver_settings *deliver_set;
typedef int deliver_mail_func_t(struct mail_storage *storage, struct mail *mail,
const char *username, const char *mailbox);
extern deliver_mail_func_t *deliver_mail;
/* Save a mail into given mailbox with given flags and keywords. */
int deliver_save(struct mail_storage *storage, const char *mailbox,
struct mail *mail, enum mail_flags flags,
const char *const *keywords);
/* Extracts user at domain from Return-Path header. Returns NULL if not found. */
const char *deliver_get_return_address(struct mail *mail);
/* Returns a new unique Message-ID */
const char *deliver_get_new_message_id(void);
#endif
--- NEW FILE: duplicate.c ---
/* Copyright (C) 2005-2006 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "home-expand.h"
#include "file-dotlock.h"
#include "hash.h"
#include "duplicate.h"
#include <fcntl.h>
#include <unistd.h>
#define DUPLICATE_PATH "~/.dovecot.lda-dupes"
#define COMPRESS_PERCENTAGE 10
struct duplicate {
const void *id;
unsigned int id_size;
const char *user;
time_t time;
};
struct duplicate_file {
pool_t pool;
struct hash_table *hash;
const char *path;
int new_fd;
struct dotlock *dotlock;
unsigned int changed:1;
};
static struct dotlock_settings duplicate_dotlock_set = {
MEMBER(temp_prefix) NULL,
MEMBER(lock_suffix) NULL,
MEMBER(timeout) 10,
MEMBER(stale_timeout) 60,
MEMBER(callback) NULL,
MEMBER(context) NULL,
MEMBER(use_excl_lock) FALSE
};
static struct duplicate_file *duplicate_file = NULL;
static int duplicate_cmp(const void *p1, const void *p2)
{
const struct duplicate *d1 = p1, *d2 = p2;
return (d1->id_size == d2->id_size &&
memcmp(d1->id, d2->id, d1->id_size) == 0 &&
strcasecmp(d1->user, d2->user) == 0) ? 0 : 1;
}
static unsigned int duplicate_hash(const void *p)
{
/* a char* hash function from ASU -- from glib */
const struct duplicate *d = p;
const unsigned char *s = d->id, *end = s + d->id_size;
unsigned int g, h = 0;
while (s != end) {
h = (h << 4) + *s;
if ((g = h & 0xf0000000UL)) {
h = h ^ (g >> 24);
h = h ^ g;
}
s++;
}
return h ^ strcase_hash(d->user);
}
static int duplicate_read(struct duplicate_file *file)
{
int fd;
struct istream *input;
const unsigned char *data;
size_t size;
time_t stamp;
unsigned int offset, id_size, user_size, change_count;
fd = open(file->path, O_RDONLY);
if (fd == -1) {
if (errno == ENOENT)
return 0;
i_error("open(%s) failed: %m", file->path);
return -1;
}
/* <timestamp> <id_size> <user_size> <id> <user> */
input = i_stream_create_file(fd, default_pool, 4096, FALSE);
change_count = 0;
while (i_stream_read_data(input, &data, &size, sizeof(stamp) +
sizeof(id_size) + sizeof(user_size)) > 0) {
offset = 0;
memcpy(&stamp, data, sizeof(stamp));
offset += sizeof(stamp);
memcpy(&id_size, data + offset, sizeof(id_size));
offset += sizeof(id_size);
memcpy(&user_size, data + offset, sizeof(user_size));
offset += sizeof(user_size);
i_stream_skip(input, offset);
if (i_stream_read_data(input, &data, &size,
id_size + user_size - 1) <= 0) {
i_error("unexpected end of file in %s", file->path);
break;
}
if (stamp >= ioloop_time) {
/* still valid, save it */
struct duplicate *d;
void *new_id;
new_id = p_malloc(file->pool, id_size);
memcpy(new_id, data, id_size);
d = p_new(file->pool, struct duplicate, 1);
d->id = new_id;
d->id_size = id_size;
d->user = p_strndup(file->pool,
data + id_size, user_size);
hash_insert(file->hash, d, d);
} else {
change_count++;
}
i_stream_skip(input, id_size + user_size);
}
if (hash_size(file->hash) * COMPRESS_PERCENTAGE / 100 > change_count)
file->changed = TRUE;
i_stream_unref(&input);
if (close(fd) < 0)
i_error("close(%s) failed: %m", file->path);
return 0;
}
static struct duplicate_file *duplicate_new(const char *path)
{
struct duplicate_file *file;
pool_t pool;
pool = pool_alloconly_create("duplicates", 10240);
file = p_new(pool, struct duplicate_file, 1);
file->pool = pool;
file->path = p_strdup(pool, path);
file->new_fd = file_dotlock_open(&duplicate_dotlock_set, path, 0,
&file->dotlock);
file->hash = hash_create(default_pool, pool, 0,
duplicate_hash, duplicate_cmp);
(void)duplicate_read(file);
return file;
}
static void duplicate_free(struct duplicate_file *file)
{
if (file->dotlock != NULL)
file_dotlock_delete(&file->dotlock);
hash_destroy(file->hash);
pool_unref(file->pool);
}
int duplicate_check(const void *id, size_t id_size, const char *user)
{
struct duplicate d;
if (duplicate_file == NULL)
duplicate_file = duplicate_new(home_expand(DUPLICATE_PATH));
d.id = id;
d.id_size = id_size;
d.user = user;
return hash_lookup(duplicate_file->hash, &d) != NULL;
}
void duplicate_mark(const void *id, size_t id_size,
const char *user, time_t time)
{
struct duplicate *d;
void *new_id;
if (duplicate_file == NULL)
duplicate_file = duplicate_new(home_expand(DUPLICATE_PATH));
new_id = p_malloc(duplicate_file->pool, id_size);
memcpy(new_id, id, id_size);
d = p_new(duplicate_file->pool, struct duplicate, 1);
d->id = new_id;
d->id_size = id_size;
d->user = p_strdup(duplicate_file->pool, user);
d->time = time;
duplicate_file->changed = TRUE;
hash_insert(duplicate_file->hash, d, d);
}
void duplicate_flush(void)
{
struct duplicate_file *file = duplicate_file;
struct ostream *output;
struct hash_iterate_context *iter;
void *key, *value;
if (duplicate_file == NULL || !file->changed || file->new_fd == -1)
return;
output = o_stream_create_file(file->new_fd, default_pool, 4096, FALSE);
iter = hash_iterate_init(file->hash);
while (hash_iterate(iter, &key, &value)) {
struct duplicate *d = value;
unsigned int user_size = strlen(d->user);
o_stream_send(output, &d->time, sizeof(d->time));
o_stream_send(output, &d->id_size, sizeof(d->id_size));
o_stream_send(output, &user_size, sizeof(user_size));
o_stream_send(output, d->id, d->id_size);
o_stream_send(output, d->user, user_size);
}
hash_iterate_deinit(iter);
o_stream_unref(&output);
file->changed = FALSE;
if (file_dotlock_replace(&file->dotlock, 0) < 0)
i_error("file_dotlock_replace(%s) failed: %m", file->path);
file->new_fd = -1;
}
void duplicate_init(void)
{
}
void duplicate_deinit(void)
{
if (duplicate_file != NULL) {
duplicate_flush();
duplicate_free(duplicate_file);
}
}
--- NEW FILE: duplicate.h ---
#ifndef __DUPLICATE_H
#define __DUPLICATE_H
#define DUPLICATE_DEFAULT_KEEP (3600 * 24)
int duplicate_check(const void *id, size_t id_size, const char *user);
void duplicate_mark(const void *id, size_t id_size,
const char *user, time_t time);
void duplicate_flush(void);
void duplicate_init(void);
void duplicate_deinit(void);
#endif
--- NEW FILE: mail-send.c ---
/* Copyright (C) 2005-2006 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "hostpid.h"
#include "istream.h"
#include "message-date.h"
#include "message-size.h"
#include "duplicate.h"
#include "istream-header-filter.h"
#include "smtp-client.h"
#include "deliver.h"
#include "mail-send.h"
#include <sys/wait.h>
#define MAIL_REJECTION_HUMAN_REASON \
"Your message was automatically rejected by Dovecot Mail Delivery Agent.\r\n" \
"\r\n" \
"The following reason was given:\r\n" \
"%s\r\n"
int global_outgoing_count = 0;
int mail_send_rejection(struct mail *mail, const char *recipient,
const char *reason)
{
struct istream *input;
struct smtp_client *smtp_client;
FILE *f;
struct message_size hdr_size;
const char *return_addr, *str;
const unsigned char *data;
const char *msgid, *boundary;
size_t size;
int ret;
return_addr = deliver_get_return_address(mail);
if (return_addr == NULL) {
i_info("Return-Path missing, rejection reason: %s", reason);
return -1;
}
smtp_client = smtp_client_open(return_addr, NULL, &f);
msgid = deliver_get_new_message_id();
duplicate_mark(msgid, strlen(msgid), recipient,
ioloop_time + DUPLICATE_DEFAULT_KEEP);
boundary = t_strdup_printf("%s/%s", my_pid, deliver_set->hostname);
fprintf(f, "Message-ID: %s\r\n", msgid);
fprintf(f, "Date: %s\r\n", message_date_create(ioloop_time));
fprintf(f, "From: Mail Delivery Subsystem <%s>\r\n",
deliver_set->postmaster_address);
fprintf(f, "To: <%s>\r\n", return_addr);
fprintf(f, "MIME-Version: 1.0\r\n");
fprintf(f, "Content-Type: "
"multipart/report; report-type=disposition-notification;\r\n"
"\tboundary=\"%s\"\r\n", boundary);
fprintf(f, "Subject: Automatically rejected mail\r\n");
fprintf(f, "Auto-Submitted: auto-replied (rejected)\r\n");
fprintf(f, "Precedence: bulk\r\n");
fprintf(f, "\r\nThis is a MIME-encapsulated message\r\n\r\n");
/* human readable status report */
fprintf(f, "--%s\r\n", boundary);
fprintf(f, "Content-Type: text/plain; charset=utf-8\r\n");
fprintf(f, "Content-Disposition: inline\r\n");
fprintf(f, "Content-Transfer-Encoding: 8bit\r\n\r\n");
fprintf(f, MAIL_REJECTION_HUMAN_REASON"\r\n", reason);
/* MDN status report */
fprintf(f, "--%s\r\n"
"Content-Type: message/disposition-notification\r\n\r\n",
boundary);
fprintf(f, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n",
deliver_set->hostname);
str = mail_get_first_header(mail, "Original-Recipient");
if (str != NULL)
fprintf(f, "Original-Recipient: rfc822; %s\r\n", str);
fprintf(f, "Final-Recipient: rfc822; %s\r\n", recipient);
str = mail_get_first_header(mail, "Message-ID");
if (str != NULL)
fprintf(f, "Original-Message-ID: %s\r\n", str);
fprintf(f, "Disposition: "
"automatic-action/MDN-sent-automatically; deleted\r\n");
fprintf(f, "\r\n");
/* original message's headers */
fprintf(f, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary);
input = mail_get_stream(mail, &hdr_size, NULL);
if (input != NULL) {
input = i_stream_create_limit(default_pool, input,
0, hdr_size.physical_size);
while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
fwrite(data, size, 1, f);
i_stream_skip(input, size);
}
i_stream_unref(&input);
i_assert(ret != 0);
}
fprintf(f, "\r\n\r\n--%s--\r\n", boundary);
return smtp_client_close(smtp_client);
}
int mail_send_forward(struct mail *mail, const char *forwardto)
{
static const char *hide_headers[] = {
"Return-Path"
};
struct istream *input;
struct smtp_client *smtp_client;
FILE *f;
const unsigned char *data;
size_t size;
int ret;
input = mail_get_stream(mail, NULL, NULL);
if (input == NULL)
return -1;
smtp_client = smtp_client_open(forwardto,
mail_get_first_header(mail, "Return-Path"),
&f);
input = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE |
HEADER_FILTER_NO_CR, hide_headers,
sizeof(hide_headers) /
sizeof(hide_headers[0]), NULL, NULL);
while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
fwrite(data, size, 1, f);
i_stream_skip(input, size);
}
return smtp_client_close(smtp_client);
}
--- NEW FILE: mail-send.h ---
#ifndef __MAIL_SEND_H
#define __MAIL_SEND_H
struct mail;
int mail_send_rejection(struct mail *mail, const char *recipient,
const char *reason);
int mail_send_forward(struct mail *mail, const char *forwardto);
#endif
--- NEW FILE: smtp-client.c ---
/* Copyright (C) 2006 Timo Sirainen */
#include "lib.h"
#include "deliver.h"
#include "smtp-client.h"
#include <unistd.h>
#include <sys/wait.h>
struct smtp_client {
FILE *f;
pid_t pid;
};
static struct smtp_client *smtp_client_devnull(FILE **file_r)
{
struct smtp_client *client;
client = i_new(struct smtp_client, 1);
client->f = *file_r = fopen("/dev/null", "w");
if (client->f == NULL)
i_fatal("fopen() failed: %m");
client->pid = (pid_t)-1;
return client;
}
static void smtp_client_run_sendmail(const char *destination,
const char *return_path, int fd)
{
const char *argv[7];
argv[0] = deliver_set->sendmail_path;
argv[1] = "-i"; /* ignore dots */
argv[2] = "-f";
argv[3] = return_path != NULL && *return_path != '\0' ?
return_path : "<>";
argv[4] = "--";
argv[5] = destination;
argv[6] = NULL;
if (dup2(fd, STDIN_FILENO) < 0)
i_fatal("dup2() failed: %m");
(void)execv(deliver_set->sendmail_path, (char **)argv);
i_fatal("execv(%s) failed: %m", deliver_set->sendmail_path);
}
struct smtp_client *smtp_client_open(const char *destination,
const char *return_path, FILE **file_r)
{
struct smtp_client *client;
int fd[2];
pid_t pid;
if (pipe(fd) < 0) {
i_error("pipe() failed: %m");
return smtp_client_devnull(file_r);
}
if ((pid = fork()) == (pid_t)-1) {
i_error("fork() failed: %m");
(void)close(fd[0]); (void)close(fd[1]);
return smtp_client_devnull(file_r);
}
if (pid == 0) {
/* child */
(void)close(fd[1]);
smtp_client_run_sendmail(destination, return_path, fd[0]);
}
(void)close(fd[0]);
client = i_new(struct smtp_client, 1);
client->f = *file_r = fdopen(fd[1], "w");
if (client->f == NULL)
i_fatal("fdopen() failed: %m");
return client;
}
int smtp_client_close(struct smtp_client *client)
{
int ret = EX_TEMPFAIL, status;
fclose(client->f);
if (client->pid == (pid_t)-1) {
/* smtp_client_open() failed already */
} else if (waitpid(client->pid, &status, 0) < 0)
i_error("waitpid() failed: %m");
else if (WIFEXITED(status)) {
ret = WEXITSTATUS(status);
if (ret != 0) {
i_error("Sendmail process terminated abnormally, "
"exit status %d", ret);
}
} else if (WIFSIGNALED(status)) {
i_error("Sendmail process terminated abnormally, "
"signal %d", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
i_error("Sendmail process stopped, signal %d",
WSTOPSIG(status));
} else {
i_error("Sendmail process terminated abnormally, "
"return status %d", status);
}
i_free(client);
return ret;
}
--- NEW FILE: smtp-client.h ---
#ifndef __SMTP_CLIENT_H
#define __SMTP_CLIENT_H
#include <stdio.h>
struct smtp_client *smtp_client_open(const char *destination,
const char *return_path, FILE **file_r);
/* Returns sysexits-compatible return value */
int smtp_client_close(struct smtp_client *client);
#endif
More information about the dovecot-cvs
mailing list