[dovecot-cvs] dovecot/src/deliver .cvsignore, NONE, 1.1 Makefile.am, NONE, 1.1 deliver.c, NONE, 1.1

cras at dovecot.org cras at dovecot.org
Sun Mar 13 01:52:02 EET 2005


Update of /var/lib/cvs/dovecot/src/deliver
In directory talvi:/tmp/cvs-serv29785/src/deliver

Added Files:
	.cvsignore Makefile.am deliver.c 
Log Message:
Added initial version of Dovecot LDA.



--- NEW FILE: .cvsignore ---
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations
deliver

--- NEW FILE: Makefile.am ---
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

--- NEW FILE: deliver.c ---
/* 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;
}



More information about the dovecot-cvs mailing list