changeset 3199:938f948651f1 HEAD

Added initial version of Dovecot LDA.
author Timo Sirainen <tss@iki.fi>
date Sun, 13 Mar 2005 01:51:58 +0200
parents cb285bd5d8c9
children a9a5a399a027
files configure.in src/Makefile.am src/deliver/.cvsignore src/deliver/Makefile.am src/deliver/deliver.c
diffstat 5 files changed, 485 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Sun Mar 13 01:10:33 2005 +0200
+++ b/configure.in	Sun Mar 13 01:51:58 2005 +0200
@@ -1,4 +1,4 @@
-AC_INIT(dovecot, 1.0-test62, [dovecot@dovecot.org])
+AC_INIT(dovecot, 1.0-test65, [dovecot@dovecot.org])
 AC_CONFIG_SRCDIR([src])
 
 AC_CONFIG_HEADERS([config.h])
@@ -1370,6 +1370,7 @@
 src/master/Makefile
 src/pop3/Makefile
 src/pop3-login/Makefile
+src/deliver/Makefile
 src/util/Makefile
 stamp.h
 dovecot.spec])
--- a/src/Makefile.am	Sun Mar 13 01:10:33 2005 +0200
+++ b/src/Makefile.am	Sun Mar 13 01:51:58 2005 +0200
@@ -2,4 +2,22 @@
 POP3D = pop3-login pop3
 endif
 
-SUBDIRS = lib lib-sql lib-ntlm lib-settings lib-charset lib-mail lib-imap lib-index lib-storage lib-auth auth master login-common imap-login imap $(POP3D) util
+SUBDIRS = \
+	lib \
+	lib-sql \
+	lib-ntlm \
+	lib-settings \
+	lib-charset \
+	lib-mail \
+	lib-imap \
+	lib-index \
+	lib-storage \
+	lib-auth \
+	auth \
+	master \
+	login-common \
+	imap-login \
+	imap \
+	$(POP3D) \
+	deliver \
+	util
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/deliver/.cvsignore	Sun Mar 13 01:51:58 2005 +0200
@@ -0,0 +1,9 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+deliver
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/deliver/Makefile.am	Sun Mar 13 01:51:58 2005 +0200
@@ -0,0 +1,30 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = deliver
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-mail \
+	-I$(top_srcdir)/src/lib-storage \
+	-DSYSCONFDIR=\""$(sysconfdir)"\"
+
+libs = \
+	../lib-storage/register/libstorage-register.a \
+	$(STORAGE_LIBS) \
+	../lib-storage/libstorage.a \
+	../lib-storage/subscription-file/libstorage_subscription_file.a \
+	../lib-imap/libimap.a \
+	../lib-mail/libmail.a \
+	../lib-charset/libcharset.a \
+	../lib/liblib.a
+
+deliver_LDADD = \
+	$(libs) \
+	$(LIBICONV) \
+	$(RAND_LIBS) \
+	$(MODULE_LIBS)
+
+deliver_DEPENDENCIES = $(libs)
+
+deliver_SOURCES = \
+	deliver.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/deliver/deliver.c	Sun Mar 13 01:51:58 2005 +0200
@@ -0,0 +1,425 @@
+/* Copyright (C) 2005 Timo Sirainen */
+
+/* FIXME: pretty ugly thing. */
+
+#include "lib.h"
+#include "lib-signals.h"
+#include "ioloop.h"
+#include "env-util.h"
+#include "network.h"
+#include "restrict-access.h"
+#include "istream.h"
+#include "ostream.h"
+#include "str.h"
+#include "var-expand.h"
+#include "mail-storage.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <sysexits.h>
+
+#define DEFAULT_CONFIG_FILE SYSCONFDIR"/dovecot-deliver.conf"
+#define DEFAULT_AUTH_SOCKET_PATH "/var/run/dovecot/auth-master"
+
+#define MAX_INBUF_SIZE 8192
+#define MAX_OUTBUF_SIZE 512
+
+struct auth_connection {
+	int fd;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	unsigned int handshaked:1;
+};
+
+static struct ioloop *ioloop;
+static int return_value = EX_SOFTWARE;
+
+static void sig_quit(int signo __attr_unused__)
+{
+	io_loop_stop(ioloop);
+}
+
+static int sync_quick(struct mailbox *box)
+{
+	struct mailbox_sync_context *ctx;
+        struct mailbox_sync_rec sync_rec;
+	struct mailbox_status status;
+
+	ctx = mailbox_sync_init(box, 0);
+	while (mailbox_sync_next(ctx, &sync_rec) > 0)
+		;
+	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)
+{
+	struct save_mail_context *ctx = context;
+
+	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);
+	}
+}
+
+static int save_mail(struct mail_storage *storage, const char *mailbox,
+		     struct istream *input)
+{
+	struct mailbox *box;
+	struct mailbox_transaction_context *t;
+        struct save_mail_context ctx;
+	struct io *io;
+	int ret = 0;
+
+	box = mailbox_open(storage, mailbox, MAILBOX_OPEN_FAST |
+			   MAILBOX_OPEN_KEEP_RECENT);
+	if (box == NULL)
+		return FALSE;
+
+	if (sync_quick(box) < 0) {
+		mailbox_close(box);
+		return FALSE;
+	}
+
+	t = mailbox_transaction_begin(box, FALSE);
+
+	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);
+
+	if (ret < 0)
+		mailbox_transaction_rollback(t);
+	else
+		ret = mailbox_transaction_commit(t, 0);
+
+	mailbox_close(box);
+	return ret;
+}
+
+static void auth_connection_destroy(struct auth_connection *conn)
+{
+	io_loop_stop(ioloop);
+
+	io_remove(conn->io);
+	i_stream_unref(conn->input);
+	o_stream_unref(conn->output);
+	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)
+{
+	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;
+}
+
+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\nUSER\t1\t",
+				      destination, "\n", NULL));
+
+	io_loop_run(ioloop);
+	return return_value;
+}
+
+static void config_file_init(const char *path)
+{
+	struct istream *input;
+	const char *line, *p, *key, *value;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		i_fatal_status(EX_CONFIG, "open(%s) failed: %m", path);
+
+	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 == '#')
+			continue;
+
+		value = p = strchr(line, '=');
+		if (value == NULL)
+			continue;
+
+		while (p > line && p[-1] == ' ') p--;
+		key = t_strdup_until(line, p);
+
+		do {
+			value++;
+		} while (*value == ' ');
+
+		env_put(t_strconcat(t_str_ucase(key), "=", value, NULL));
+	}
+	i_stream_unref(input);
+	t_pop();
+}
+
+static const struct var_expand_table *
+get_var_expand_table(const char *user, const char *home)
+{
+	static struct var_expand_table static_tab[] = {
+		{ 'u', NULL },
+		{ 'n', NULL },
+		{ 'd', NULL },
+		{ 's', NULL },
+		{ 'h', NULL },
+		{ 'l', NULL },
+		{ 'r', NULL },
+		{ 'p', NULL },
+		{ '\0', NULL }
+	};
+	struct var_expand_table *tab;
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+
+	tab[0].value = user;
+	tab[1].value = t_strcut(user, '@');
+	tab[2].value = strchr(user, '@');
+	if (tab[2].value != NULL) tab[2].value++;
+	tab[3].value = "DELIVER";
+	tab[4].value = home;
+	tab[5].value = NULL;
+	tab[6].value = NULL;
+	tab[7].value = dec2str(getpid());
+
+	return tab;
+}
+
+static const char *
+expand_mail_env(const char *env, const struct var_expand_table *table)
+{
+	string_t *str;
+	const char *p;
+
+	str = t_str_new(256);
+
+	/* it's either type:data or just data */
+	p = strchr(env, ':');
+	if (p != NULL) {
+		while (env != p) {
+			str_append_c(str, *env);
+			env++;
+		}
+
+		str_append_c(str, *env++);
+	}
+
+	if (env[0] == '~' && env[1] == '/') {
+		/* expand home */
+		env = t_strconcat("%h", env+1, NULL);
+	}
+
+	/* expand %vars */
+	var_expand(str, env, table);
+	return str_c(str);
+}
+
+int main(int argc, char *argv[])
+{
+	const char *auth_socket = DEFAULT_AUTH_SOCKET_PATH;
+	const char *destination, *mail;
+        const struct var_expand_table *table;
+	struct mail_storage *storage;
+	struct istream *input;
+	int i, ret;
+
+	lib_init();
+	lib_init_signals(sig_quit);
+	ioloop = io_loop_create(default_pool);
+
+	destination = NULL;
+	for (i = 1; i < argc; i++) {
+		if (strcmp(argv[i], "-d") == 0) {
+			/* destination user */
+			i++;
+			if (i == argc) {
+				i_fatal_status(EX_USAGE,
+					       "Missing destination argument");
+			}
+			destination = argv[i];
+		} else if (strcmp(argv[i], "-a") == 0) {
+			/* auth master socket path */
+			i++;
+			if (i == argc) {
+				i_fatal_status(EX_USAGE,
+					"Missing auth socket path argument");
+			}
+			auth_socket = argv[i];
+		} else {
+			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) {
+		/* 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) {
+		i_fatal_status(EX_USAGE,
+			"destination user parameter (-d user) not given");
+	}
+
+        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);
+
+	/* FIXME: how should we handle namespaces? */
+	storage = mail_storage_create_with_data(mail, destination, 0);
+	if (storage == NULL) {
+		i_fatal_status(EX_CONFIG,
+			"Failed to create storage for '%s' with mail '%s'",
+			destination, mail == NULL ? "(null)" : mail);
+	}
+
+	net_set_nonblock(0, TRUE);
+	input = i_stream_create_file(0, default_pool, 8192, FALSE);
+	if (!save_mail(storage, "INBOX", input))
+		return EX_TEMPFAIL;
+	i_stream_unref(input);
+
+        mail_storage_destroy(storage);
+        mail_storage_deinit();
+	io_loop_destroy(ioloop);
+	lib_deinit();
+
+        return EX_OK;
+}