src/lib-sql/sql-drivers-register.c
src/lib-storage/register/mail-storage-register.c
src/lib-storage/register/mailbox-list-register.c
+src/lmtp/lmtp
src/master/dovecot
src/master/ssl-build-param
src/plugins/convert/convert-tool
src/auth/Makefile
src/config/Makefile
src/lda/Makefile
+src/lmtp/Makefile
src/dict/Makefile
src/imap/Makefile
src/imap-login/Makefile
pop3-login \
pop3 \
lda \
+ lmtp \
config \
tests \
util \
--- /dev/null
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = lmtp
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-settings \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-imap \
+ -I$(top_srcdir)/src/lib-index \
+ -I$(top_srcdir)/src/lib-master \
+ -I$(top_srcdir)/src/lib-lda \
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index \
+ -I$(top_srcdir)/src/lib-storage/index/raw \
+ -DPKG_RUNDIR=\""$(rundir)"\"
+
+lmtp_LDFLAGS = -export-dynamic
+
+libs = \
+ ../lib-lda/liblda.a \
+ $(LIBDOVECOT_STORAGE) \
+ $(LIBDOVECOT)
+
+lmtp_LDADD = $(libs) $(MODULE_LIBS)
+
+lmtp_DEPENDENCIES = $(libs)
+
+lmtp_SOURCES = \
+ main.c \
+ client.c \
+ commands.c
+
+noinst_HEADERS = \
+ client.h \
+ commands.h
--- /dev/null
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "str.h"
+#include "llist.h"
+#include "istream.h"
+#include "ostream.h"
+#include "hostpid.h"
+#include "master-service-settings.h"
+#include "mail-namespace.h"
+#include "mail-storage.h"
+#include "main.h"
+#include "commands.h"
+#include "client.h"
+
+#include <unistd.h>
+
+#define CLIENT_IDLE_TIMEOUT_MSECS (1000*60)
+#define CLIENT_MAX_INPUT_SIZE 4096
+
+static struct client *clients = NULL;
+unsigned int clients_count = 0;
+
+static void client_idle_timeout(struct client *client)
+{
+ client_destroy(client,
+ t_strdup_printf("421 4.4.2 %s", client->my_domain),
+ "Disconnected for inactivity");
+}
+
+static int client_input_line(struct client *client, const char *line)
+{
+ const char *cmd, *args;
+
+ args = strchr(line, ' ');
+ if (args == NULL) {
+ cmd = line;
+ args = "";
+ } else {
+ cmd = t_strdup_until(line, args);
+ args++;
+ }
+ cmd = t_str_ucase(cmd);
+
+ if (strcmp(cmd, "LHLO") == 0)
+ return cmd_lhlo(client, args);
+ if (strcmp(cmd, "MAIL") == 0)
+ return cmd_mail(client, args);
+ if (strcmp(cmd, "RCPT") == 0)
+ return cmd_rcpt(client, args);
+ if (strcmp(cmd, "DATA") == 0)
+ return cmd_data(client, args);
+ if (strcmp(cmd, "QUIT") == 0)
+ return cmd_quit(client, args);
+ if (strcmp(cmd, "VRFY") == 0)
+ return cmd_vrfy(client, args);
+ if (strcmp(cmd, "RSET") == 0)
+ return cmd_rset(client, args);
+ if (strcmp(cmd, "NOOP") == 0)
+ return cmd_noop(client, args);
+
+ client_send_line(client, "502 5.5.2 Unknown command");
+ return 0;
+}
+
+int client_input_read(struct client *client)
+{
+ client->last_input = ioloop_time;
+ timeout_reset(client->to_idle);
+
+ switch (i_stream_read(client->input)) {
+ case -2:
+ /* buffer full */
+ client_destroy(client, "502 5.5.2",
+ "Disconnected: Input buffer full");
+ return -1;
+ case -1:
+ /* disconnected */
+ client_destroy(client, NULL, NULL);
+ return -1;
+ case 0:
+ /* nothing new read */
+ return 0;
+ default:
+ /* something was read */
+ return 0;
+ }
+}
+
+void client_input_handle(struct client *client)
+{
+ struct ostream *output;
+ const char *line;
+ int ret;
+
+ output = client->output;
+ o_stream_ref(output);
+ o_stream_cork(output);
+ while ((line = i_stream_next_line(client->input)) != NULL) {
+ T_BEGIN {
+ ret = client_input_line(client, line);
+ } T_END;
+ if (ret < 0)
+ break;
+ }
+ o_stream_uncork(output);
+ o_stream_unref(&output);
+}
+
+void client_input(struct client *client)
+{
+ if (client_input_read(client) < 0)
+ return;
+ client_input_handle(client);
+}
+
+static void client_raw_user_create(struct client *client)
+{
+ struct mail_namespace *raw_ns;
+ struct mail_namespace_settings raw_ns_set;
+ const char *error;
+ void **sets;
+
+ sets = master_service_settings_get_others(service);
+
+ client->raw_mail_user = mail_user_alloc("raw user", sets[0]);
+ mail_user_set_home(client->raw_mail_user, "/");
+ if (mail_user_init(client->raw_mail_user, &error) < 0)
+ i_fatal("Raw user initialization failed: %s", error);
+
+ memset(&raw_ns_set, 0, sizeof(raw_ns_set));
+ raw_ns_set.location = "/tmp";
+
+ raw_ns = mail_namespaces_init_empty(client->raw_mail_user);
+ raw_ns->flags |= NAMESPACE_FLAG_INTERNAL;
+ raw_ns->set = &raw_ns_set;
+ if (mail_storage_create(raw_ns, "raw", 0, &error) < 0)
+ i_fatal("Couldn't create internal raw storage: %s", error);
+}
+
+struct client *client_create(int fd_in, int fd_out)
+{
+ struct client *client;
+
+ /* always use nonblocking I/O */
+ net_set_nonblock(fd_in, TRUE);
+ net_set_nonblock(fd_out, TRUE);
+
+ client = i_new(struct client, 1);
+ client->fd_in = fd_in;
+ client->fd_out = fd_out;
+ client->input = i_stream_create_fd(fd_in, CLIENT_MAX_INPUT_SIZE, FALSE);
+ client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
+
+ client->io = io_add(fd_in, IO_READ, client_input, client);
+ client->last_input = ioloop_time;
+ client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
+ client_idle_timeout, client);
+ client->my_domain = my_hostname;
+ client->state_pool = pool_alloconly_create("client state", 4096);
+ client->state.mail_data_fd = -1;
+
+ DLLIST_PREPEND(&clients, client);
+ clients_count++;
+
+ client_send_line(client, "220 %s Dovecot LMTP ready",
+ client->my_domain);
+ client_raw_user_create(client);
+ return client;
+}
+
+void client_destroy(struct client *client, const char *prefix,
+ const char *reason)
+{
+ client_disconnect(client, prefix, reason);
+
+ clients_count--;
+ DLLIST_REMOVE(&clients, client);
+
+ mail_user_unref(&client->raw_mail_user);
+ if (client->io != NULL)
+ io_remove(&client->io);
+ timeout_remove(&client->to_idle);
+ i_stream_destroy(&client->input);
+ o_stream_destroy(&client->output);
+
+ if (close(client->fd_in) < 0)
+ i_error("close(client in) failed: %m");
+ if (client->fd_in != client->fd_out) {
+ if (close(client->fd_out) < 0)
+ i_error("close(client out) failed: %m");
+ }
+ client_state_reset(client);
+ pool_unref(&client->state_pool);
+ i_free(client);
+
+ listener_client_destroyed();
+}
+
+static const char *client_get_disconnect_reason(struct client *client)
+{
+ errno = client->input->stream_errno != 0 ?
+ client->input->stream_errno :
+ client->output->stream_errno;
+ return errno == 0 || errno == EPIPE ? "Connection closed" :
+ t_strdup_printf("Connection closed: %m");
+}
+
+void client_disconnect(struct client *client, const char *prefix,
+ const char *reason)
+{
+ if (client->disconnected)
+ return;
+
+ if (reason != NULL)
+ client_send_line(client, "%s %s", prefix, reason);
+ else
+ reason = client_get_disconnect_reason(client);
+ i_info("%s", reason);
+
+ client->disconnected = TRUE;
+}
+
+void client_state_reset(struct client *client)
+{
+ if (client->state.raw_mail != NULL)
+ mail_free(&client->state.raw_mail);
+ if (client->state.raw_trans != NULL)
+ mailbox_transaction_rollback(&client->state.raw_trans);
+ if (client->state.raw_box != NULL)
+ mailbox_close(&client->state.raw_box);
+
+ if (client->state.mail_data != NULL)
+ buffer_free(&client->state.mail_data);
+ if (client->state.mail_data_output != NULL)
+ o_stream_unref(&client->state.mail_data_output);
+ if (client->state.mail_data_fd != -1) {
+ if (close(client->state.mail_data_fd) < 0)
+ i_error("close(mail data fd) failed: %m");
+ }
+
+ memset(&client->state, 0, sizeof(client->state));
+ p_clear(client->state_pool);
+ client->state.mail_data_fd = -1;
+}
+
+void client_send_line(struct client *client, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ T_BEGIN {
+ string_t *str;
+
+ str = t_str_new(256);
+ str_vprintfa(str, fmt, args);
+ str_append(str, "\r\n");
+ o_stream_send(client->output, str_data(str), str_len(str));
+ } T_END;
+ va_end(args);
+}
+
+void clients_destroy(void)
+{
+ while (clients != NULL) {
+ client_destroy(clients,
+ t_strdup_printf("421 4.3.2 %s", clients->my_domain),
+ "Shutting down");
+ }
+}
--- /dev/null
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "network.h"
+
+#define CLIENT_MAIL_DATA_MAX_INMEMORY_SIZE (1024*128)
+
+struct mail_recipient {
+ const char *name;
+ struct mail_storage_service_multi_user *multi_user;
+};
+
+struct client_state {
+ const char *mail_from;
+ ARRAY_DEFINE(rcpt_to, struct mail_recipient);
+ unsigned int rcpt_idx;
+
+ unsigned int data_end_idx;
+
+ /* Initially we start writing to mail_data. If it grows too large,
+ start using mail_data_fd. */
+ buffer_t *mail_data;
+ int mail_data_fd;
+ struct ostream *mail_data_output;
+
+ struct mailbox *raw_box;
+ struct mailbox_transaction_context *raw_trans;
+ struct mail *raw_mail;
+
+ struct mail_user *dest_user;
+ struct mail *first_saved_mail;
+};
+
+struct client {
+ struct client *prev, *next;
+
+ int fd_in, fd_out;
+ struct io *io;
+ struct istream *input;
+ struct ostream *output;
+
+ struct timeout *to_idle;
+ time_t last_input;
+
+ struct ip_addr remote_ip, local_ip;
+ unsigned int remote_port, local_port;
+
+ struct mail_user *raw_mail_user;
+ const char *my_domain;
+
+ pool_t state_pool;
+ struct client_state state;
+
+ unsigned int disconnected:1;
+};
+
+extern unsigned int clients_count;
+
+struct client *client_create(int fd_in, int fd_out);
+void client_destroy(struct client *client, const char *prefix,
+ const char *reason);
+void client_disconnect(struct client *client, const char *prefix,
+ const char *reason);
+void client_state_reset(struct client *client);
+
+void client_input(struct client *client);
+void client_input_handle(struct client *client);
+int client_input_read(struct client *client);
+
+void client_send_line(struct client *client, const char *fmt, ...)
+ ATTR_FORMAT(2, 3);
+
+void clients_destroy(void);
+
+#endif
--- /dev/null
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "array.h"
+#include "istream.h"
+#include "mail-storage-service.h"
+#include "index/raw/raw-storage.h"
+#include "lda-settings.h"
+#include "mail-deliver.h"
+#include "main.h"
+#include "client.h"
+#include "commands.h"
+
+#define ERRSTR_MAILBOX_TEMP_FAIL "451 4.2.0 <%s> Temporary internal error"
+
+int cmd_lhlo(struct client *client, const char *args ATTR_UNUSED)
+{
+ client_state_reset(client);
+ client_send_line(client, "250-%s", client->my_domain);
+ client_send_line(client, "250-8BITMIME");
+ client_send_line(client, "250-ENHANCEDSTATUSCODES");
+ client_send_line(client, "250 PIPELINING");
+ return 0;
+}
+
+int cmd_mail(struct client *client, const char *args)
+{
+ const char *addr;
+ unsigned int len;
+
+ if (client->state.mail_from != NULL) {
+ client_send_line(client, "503 5.5.1 MAIL already given");
+ return 0;
+ }
+
+ addr = args;
+ args = strchr(args, ' ');
+ if (args == NULL)
+ args = "";
+ else {
+ addr = t_strdup_until(addr, args);
+ args++;
+ }
+ len = strlen(addr);
+ if (strncasecmp(addr, "FROM:<", 6) != 0 || addr[len-1] != '>') {
+ client_send_line(client, "501 5.5.4 Invalid parameters");
+ return 0;
+ }
+
+ if (*args != '\0') {
+ client_send_line(client, "501 5.5.4 Unsupported options");
+ return 0;
+ }
+
+ client->state.mail_from =
+ p_strndup(client->state_pool, addr + 6, len - 7);
+ p_array_init(&client->state.rcpt_to, client->state_pool, 64);
+ client_send_line(client, "250 2.1.0 OK");
+ return 0;
+}
+
+static bool rcpt_is_duplicate(struct client *client, const char *name)
+{
+ const struct mail_recipient *rcpts;
+ unsigned int i, count;
+
+ rcpts = array_get(&client->state.rcpt_to, &count);
+ for (i = 0; i < count; i++) {
+ if (strcmp(rcpts[i].name, name) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int cmd_rcpt(struct client *client, const char *args)
+{
+ struct mail_recipient rcpt;
+ const char *name, *error;
+ unsigned int len;
+ int ret;
+
+ if (client->state.mail_from == NULL) {
+ client_send_line(client, "503 5.5.1 MAIL needed first");
+ return 0;
+ }
+
+ len = strlen(args);
+ if (strncasecmp(args, "TO:<", 4) != 0 || args[len-1] != '>') {
+ client_send_line(client, "501 5.5.4 Invalid parameters");
+ return 0;
+ }
+
+ memset(&rcpt, 0, sizeof(rcpt));
+ name = t_strndup(args + 4, len - 5);
+
+ if (rcpt_is_duplicate(client, name)) {
+ client_send_line(client, "250 2.1.5 OK, ignoring duplicate");
+ return 0;
+ }
+
+ ret = mail_storage_service_multi_lookup(multi_service, name,
+ client->state_pool,
+ &rcpt.multi_user, &error);
+ if (ret < 0) {
+ i_error("User lookup failed: %s", error);
+ client_send_line(client,
+ "451 4.3.0 Temporary user lookup failure");
+ return 0;
+ }
+ if (ret == 0) {
+ client_send_line(client,
+ "550 5.1.1 <%s> User doesn't exist", name);
+ return 0;
+ }
+
+ rcpt.name = p_strdup(client->state_pool, name);
+ array_append(&client->state.rcpt_to, &rcpt, 1);
+
+ client_send_line(client, "250 2.1.5 OK");
+ return 0;
+}
+
+int cmd_quit(struct client *client, const char *args ATTR_UNUSED)
+{
+ client_destroy(client, "221 2.0.0", "Logged out");
+ return -1;
+}
+
+int cmd_vrfy(struct client *client, const char *args ATTR_UNUSED)
+{
+ client_send_line(client, "252 2.3.3 Try RCPT instead");
+ return 0;
+}
+
+int cmd_rset(struct client *client, const char *args ATTR_UNUSED)
+{
+ client_state_reset(client);
+ client_send_line(client, "250 2.0.0 OK");
+ return 0;
+}
+
+int cmd_noop(struct client *client, const char *args ATTR_UNUSED)
+{
+ client_send_line(client, "250 2.0.0 OK");
+ return 0;
+}
+
+static int
+client_deliver(struct client *client, const struct mail_recipient *rcpt,
+ struct mail *src_mail)
+{
+ struct mail_deliver_context dctx;
+ struct mail_storage *storage;
+ void **sets;
+ const char *error;
+ enum mail_error mail_error;
+ int ret;
+
+ i_set_failure_prefix(t_strdup_printf("lmtp(%s): ", rcpt->name));
+ if (mail_storage_service_multi_next(multi_service, rcpt->multi_user,
+ &client->state.dest_user,
+ &error) < 0) {
+ i_error("%s", error);
+ client_send_line(client, ERRSTR_MAILBOX_TEMP_FAIL, rcpt->name);
+ return -1;
+ }
+ sets = mail_storage_service_multi_user_get_set(rcpt->multi_user);
+
+ memset(&dctx, 0, sizeof(dctx));
+ dctx.pool = pool_alloconly_create("mail delivery", 1024);
+ dctx.set = sets[1];
+ dctx.src_mail = src_mail;
+ dctx.src_envelope_sender = client->state.mail_from;
+ dctx.dest_user = client->state.dest_user;
+ dctx.dest_addr = rcpt->name;
+ dctx.dest_mailbox_name = "INBOX";
+ dctx.save_dest_mail = array_count(&client->state.rcpt_to) > 1 &&
+ client->state.first_saved_mail == NULL;
+
+ if (mail_deliver(&dctx, &storage) == 0) {
+ if (dctx.dest_mail != NULL) {
+ i_assert(client->state.first_saved_mail == NULL);
+ client->state.first_saved_mail = dctx.dest_mail;
+ }
+ client_send_line(client, "250 2.0.0 <%s> Saved", rcpt->name);
+ ret = 0;
+ } else if (storage == NULL) {
+ /* This shouldn't happen */
+ i_error("BUG: Saving failed to unknown storage");
+ client_send_line(client, ERRSTR_MAILBOX_TEMP_FAIL,
+ rcpt->name);
+ ret = -1;
+ } else {
+ error = mail_storage_get_last_error(storage, &mail_error);
+ if (mail_error == MAIL_ERROR_NOSPACE) {
+ client_send_line(client, "%s <%s> %s",
+ dctx.set->quota_full_tempfail ?
+ "452 4.2.2" : "552 5.2.2",
+ rcpt->name, error);
+ } else {
+ client_send_line(client, "451 4.2.0 <%s> %s",
+ rcpt->name, error);
+ }
+ ret = -1;
+ }
+ pool_unref(&dctx.pool);
+ return ret;
+}
+
+static bool client_deliver_next(struct client *client, struct mail *src_mail)
+{
+ const struct mail_recipient *rcpts;
+ unsigned int count;
+ int ret;
+
+ rcpts = array_get(&client->state.rcpt_to, &count);
+ while (client->state.rcpt_idx < count) {
+ ret = client_deliver(client, &rcpts[client->state.rcpt_idx],
+ src_mail);
+ i_set_failure_prefix("lmtp: ");
+
+ client->state.rcpt_idx++;
+ if (ret == 0)
+ return TRUE;
+ /* failed. try the next one. */
+ if (client->state.dest_user != NULL)
+ mail_user_unref(&client->state.dest_user);
+ }
+ return FALSE;
+}
+
+static void client_rcpt_fail_all(struct client *client)
+{
+ const struct mail_recipient *rcpts;
+ unsigned int i, count;
+
+ rcpts = array_get(&client->state.rcpt_to, &count);
+ for (i = 0; i < count; i++) {
+ client_send_line(client, ERRSTR_MAILBOX_TEMP_FAIL,
+ rcpts[i].name);
+ }
+}
+
+static int client_open_raw_mail(struct client *client)
+{
+ static const char *wanted_headers[] = {
+ "From", "To", "Message-ID", "Subject", "Return-Path",
+ NULL
+ };
+ struct mail_storage *raw_storage =
+ client->raw_mail_user->namespaces->storage;
+ struct mailbox *box;
+ struct raw_mailbox *raw_box;
+ struct mailbox_header_lookup_ctx *headers_ctx;
+ struct istream *input;
+ enum mail_error error;
+
+ input = i_stream_create_from_data(client->state.mail_data->data,
+ client->state.mail_data->used);
+ client->state.raw_box = box =
+ mailbox_open(&raw_storage, "Dovecot Delivery Mail", input,
+ MAILBOX_OPEN_NO_INDEX_FILES);
+ i_stream_unref(&input);
+ if (box == NULL) {
+ i_error("Can't open delivery mail as raw: %s",
+ mail_storage_get_last_error(raw_storage, &error));
+ client_rcpt_fail_all(client);
+ return -1;
+ }
+ if (mailbox_sync(box, 0, 0, NULL) < 0) {
+ i_error("Can't sync delivery mail: %s",
+ mail_storage_get_last_error(raw_storage, &error));
+ client_rcpt_fail_all(client);
+ return -1;
+ }
+ raw_box = (struct raw_mailbox *)box;
+ raw_box->envelope_sender = client->state.mail_from;
+
+ client->state.raw_trans = mailbox_transaction_begin(box, 0);
+
+ headers_ctx = mailbox_header_lookup_init(box, wanted_headers);
+ client->state.raw_mail = mail_alloc(client->state.raw_trans,
+ 0, headers_ctx);
+ mailbox_header_lookup_unref(&headers_ctx);
+ mail_set_seq(client->state.raw_mail, 1);
+ return 0;
+}
+
+static void client_input_data_finish(struct client *client)
+{
+ struct mail *src_mail;
+
+ io_remove(&client->io);
+ client->io = io_add(client->fd_in, IO_READ, client_input, client);
+
+ if (client_open_raw_mail(client) < 0)
+ return;
+
+ /* save the message to the first recipient's mailbox */
+ src_mail = client->state.raw_mail;
+ if (!client_deliver_next(client, src_mail))
+ return;
+
+ if (client->state.first_saved_mail == NULL)
+ mail_user_unref(&client->state.dest_user);
+ else
+ src_mail = client->state.first_saved_mail;
+
+ /* use the first saved message to save it elsewhere too.
+ this might allow hard linking the files. */
+ while (client_deliver_next(client, src_mail))
+ mail_user_unref(&client->state.dest_user);
+
+ if (client->state.first_saved_mail != NULL) {
+ struct mail *mail = client->state.first_saved_mail;
+ struct mailbox_transaction_context *trans = mail->transaction;
+ struct mailbox *box = trans->box;
+ struct mail_user *user = box->storage->ns->user;
+
+ mail_free(&mail);
+ mailbox_transaction_rollback(&trans);
+ mailbox_close(&box);
+ mail_user_unref(&user);
+ }
+}
+
+static void
+client_input_add(struct client *client, const unsigned char *data, size_t size)
+{
+ buffer_append(client->state.mail_data, data, size);
+}
+
+static void client_input_data_handle(struct client *client)
+{
+#define DATA_DOT_NEXT_POS 3
+#define DATA_END_SIZE 5
+ static const char *data_end = "\r\n.\r\n";
+ const unsigned char *data;
+ size_t i, size, start, skip;
+ unsigned int rewind;
+
+ data = i_stream_get_data(client->input, &size);
+ skip = 0;
+ for (i = start = 0; i < size; i++) {
+ if (data[i] == data_end[client->state.data_end_idx]) {
+ if (++client->state.data_end_idx == DATA_END_SIZE) {
+ /* found the ending. drop the "." line out. */
+ skip = i + 1;
+ i -= DATA_END_SIZE - DATA_DOT_NEXT_POS;
+ client->state.data_end_idx = 0;
+ break;
+ }
+ } else if (client->state.data_end_idx == DATA_DOT_NEXT_POS) {
+ /* saw a dot at the beginning of line. drop it. */
+ client_input_add(client, data, i-1);
+ start = i;
+ client->state.data_end_idx = 0;
+ } else {
+ client->state.data_end_idx = 0;
+ }
+ }
+ if (client->state.data_end_idx >= DATA_DOT_NEXT_POS) {
+ /* we might not want to write the dot, so keep it in buffer
+ until we're sure what to do about it. */
+ rewind = client->state.data_end_idx - DATA_DOT_NEXT_POS + 1;
+ i -= rewind; size -= rewind;
+ }
+ client_input_add(client, data + start, i-start);
+ i_stream_skip(client->input, skip == 0 ? i : skip);
+
+ if (i < size) {
+ client_input_data_finish(client);
+ client_state_reset(client);
+ if (i_stream_have_bytes_left(client->input))
+ client_input_handle(client);
+ }
+}
+
+static void client_input_data(struct client *client)
+{
+ if (client_input_read(client) < 0)
+ return;
+
+ client_input_data_handle(client);
+}
+
+int cmd_data(struct client *client, const char *args ATTR_UNUSED)
+{
+ if (client->state.mail_from == NULL) {
+ client_send_line(client, "503 5.5.1 MAIL needed first");
+ return 0;
+ }
+ if (array_count(&client->state.rcpt_to) == 0) {
+ client_send_line(client, "554 5.5.1 No valid recipients");
+ return 0;
+ }
+
+ i_assert(client->state.mail_data == NULL);
+ client->state.mail_data = buffer_create_dynamic(default_pool, 1024*64);
+
+ io_remove(&client->io);
+ client->io = io_add(client->fd_in, IO_READ, client_input_data, client);
+ client_send_line(client, "354 OK");
+
+ client_input_data_handle(client);
+ return -1;
+}
--- /dev/null
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+struct client;
+
+int cmd_lhlo(struct client *client, const char *args);
+int cmd_mail(struct client *client, const char *args);
+int cmd_rcpt(struct client *client, const char *args);
+int cmd_quit(struct client *client, const char *args);
+int cmd_vrfy(struct client *client, const char *args);
+int cmd_rset(struct client *client, const char *args);
+int cmd_noop(struct client *client, const char *args);
+int cmd_data(struct client *client, const char *args);
+
+#endif
--- /dev/null
+/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "restrict-access.h"
+#include "fd-close-on-exec.h"
+#include "process-title.h"
+#include "master-service.h"
+#include "mail-storage-service.h"
+#include "lda-settings.h"
+#include "client.h"
+#include "main.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#define LMTP_MASTER_FIRST_LISTEN_FD 3
+
+#define IS_STANDALONE() \
+ (getenv("MASTER_SERVICE") == NULL)
+
+struct lmtp_listener {
+ int fd;
+ struct io *io;
+};
+
+struct master_service *service;
+struct mail_storage_service_multi_ctx *multi_service;
+
+static struct io *log_io = NULL;
+static ARRAY_DEFINE(listeners, struct lmtp_listener *);
+
+static void log_error_callback(void *context ATTR_UNUSED)
+{
+ /* the log fd is closed, don't die when trying to log later */
+ i_set_failure_ignore_errors(TRUE);
+
+ master_service_stop(service);
+}
+
+static void listen_connected(struct lmtp_listener *l)
+{
+ struct client *client;
+ struct ip_addr remote_ip;
+ unsigned int remote_port;
+ int fd;
+
+ fd = net_accept(l->fd, &remote_ip, &remote_port);
+ if (fd < 0) {
+ if (fd < -1)
+ i_error("accept() failed: %m");
+ return;
+ }
+ client = client_create(fd, fd);
+ client->remote_ip = remote_ip;
+ client->remote_port = remote_port;
+
+ (void)net_getsockname(fd, &client->local_ip, &client->local_port);
+}
+
+static void listen_start(void)
+{
+ struct lmtp_listener *const *l;
+ unsigned int i, count;
+
+ l = array_get(&listeners, &count);
+ for (i = 0; i < count; i++) {
+ i_assert(l[i]->io == NULL);
+ l[i]->io = io_add(l[i]->fd, IO_READ, listen_connected, l[i]);
+ }
+}
+
+static void listen_stop(void)
+{
+ struct lmtp_listener *const *l;
+ unsigned int i, count;
+
+ l = array_get(&listeners, &count);
+ for (i = 0; i < count; i++) {
+ i_assert(l[i]->io != NULL);
+ io_remove(&l[i]->io);
+ }
+}
+
+static void listen_free(void)
+{
+ struct lmtp_listener **l;
+ unsigned int i, count;
+
+ l = array_get_modifiable(&listeners, &count);
+ for (i = 0; i < count; i++) {
+ if (l[i]->io != NULL)
+ io_remove(&l[i]->io);
+ i_free(l[i]);
+ }
+ array_free(&listeners);
+}
+
+void listener_client_destroyed(void)
+{
+ if (array_count(&listeners) == 0)
+ master_service_stop(service);
+}
+
+static void main_init(void)
+{
+ struct lmtp_listener *l;
+ const char *value;
+ unsigned int i, count;
+
+ /* If master dies, the log fd gets closed and we'll quit */
+ log_io = io_add(STDERR_FILENO, IO_ERROR, log_error_callback, NULL);
+
+ value = getenv("LISTEN_FDS");
+ count = value == NULL ? 0 : atoi(value);
+ i_array_init(&listeners, count + 1);
+ for (i = 0; i < count; i++) {
+ l = i_new(struct lmtp_listener, 1);
+ l->fd = LMTP_MASTER_FIRST_LISTEN_FD + i;
+ array_append(&listeners, &l, 1);
+ }
+
+ if (count == 0)
+ (void)client_create(STDIN_FILENO, STDOUT_FILENO);
+ else
+ listen_start();
+}
+
+static void main_deinit(void)
+{
+ if (log_io != NULL)
+ io_remove(&log_io);
+ clients_destroy();
+ listen_free();
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+ const struct setting_parser_info *set_roots[] = {
+ &lda_setting_parser_info,
+ NULL
+ };
+ enum master_service_flags service_flags = 0;
+ enum mail_storage_service_flags storage_service_flags =
+ MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT |
+ MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
+ int c;
+
+#ifdef DEBUG
+ if (!IS_STANDALONE() && getenv("GDB") == NULL) {
+ const char *env;
+
+ env = getenv("LISTEN_FDS");
+ fd_debug_verify_leaks(LMTP_MASTER_FIRST_LISTEN_FD +
+ (env == NULL ? 0 : atoi(env)), 1024);
+ }
+#endif
+
+ if (IS_STANDALONE())
+ service_flags |= MASTER_SERVICE_FLAG_STANDALONE;
+
+ service = master_service_init("lmtp", service_flags, argc, argv);
+ while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
+ if (!master_service_parse_option(service, c, optarg))
+ i_fatal("Unknown argument: %c", c);
+ }
+
+ multi_service = mail_storage_service_multi_init(service, set_roots,
+ storage_service_flags);
+ restrict_access_allow_coredumps(TRUE);
+
+ process_title_init(argv, envp);
+
+ main_init();
+ master_service_run(service);
+
+ main_deinit();
+ mail_storage_service_multi_deinit(&multi_service);
+ master_service_deinit(&service);
+ return 0;
+}
--- /dev/null
+#ifndef MAIN_H
+#define MAIN_H
+
+extern struct master_service *service;
+extern struct mail_storage_service_multi_ctx *multi_service;
+
+void listener_client_destroyed(void);
+
+#endif