dovecot-2.0: lib-lda: Added LMTP client code.

dovecot at dovecot.org dovecot at dovecot.org
Mon Aug 31 18:38:53 EEST 2009


details:   http://hg.dovecot.org/dovecot-2.0/rev/c3da5347b1c5
changeset: 9831:c3da5347b1c5
user:      Timo Sirainen <tss at iki.fi>
date:      Mon Aug 31 11:37:55 2009 -0400
description:
lib-lda: Added LMTP client code.

diffstat:

3 files changed, 465 insertions(+), 2 deletions(-)
src/lib-lda/Makefile.am   |    6 
src/lib-lda/lmtp-client.c |  426 +++++++++++++++++++++++++++++++++++++++++++++
src/lib-lda/lmtp-client.h |   35 +++

diffs (truncated from 492 to 300 lines):

diff -r 0919ab922086 -r c3da5347b1c5 src/lib-lda/Makefile.am
--- a/src/lib-lda/Makefile.am	Mon Aug 31 11:37:25 2009 -0400
+++ b/src/lib-lda/Makefile.am	Mon Aug 31 11:37:55 2009 -0400
@@ -8,15 +8,17 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib-storage
 
 liblda_a_SOURCES = \
+	duplicate.c \
 	lda-settings.c \
-	duplicate.c \
+	lmtp-client.c \
 	mail-deliver.c \
 	mail-send.c \
 	smtp-client.c
 
 headers = \
+	duplicate.h \
 	lda-settings.h \
-	duplicate.h \
+	lmtp-client.h \
 	mail-deliver.h \
 	mail-send.h \
 	smtp-client.h
diff -r 0919ab922086 -r c3da5347b1c5 src/lib-lda/lmtp-client.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-lda/lmtp-client.c	Mon Aug 31 11:37:55 2009 -0400
@@ -0,0 +1,426 @@
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "network.h"
+#include "istream.h"
+#include "ostream.h"
+#include "lmtp-client.h"
+
+#include <ctype.h>
+
+#define LMTP_MAX_LINE_LEN 1024
+
+enum lmtp_input_state {
+	LMTP_INPUT_STATE_GREET,
+	LMTP_INPUT_STATE_LHLO,
+	LMTP_INPUT_STATE_MAIL_FROM,
+	LMTP_INPUT_STATE_RCPT_TO,
+	LMTP_INPUT_STATE_DATA_CONTINUE,
+	LMTP_INPUT_STATE_DATA
+};
+
+struct lmtp_rcpt {
+	const char *address;
+	lmtp_callback_t *rcpt_to_callback;
+	lmtp_callback_t *data_callback;
+	void *context;
+
+	unsigned int data_called:1;
+	unsigned int failed:1;
+};
+
+struct lmtp_client {
+	pool_t pool;
+	const char *mail_from;
+
+	const char *my_hostname;
+	const char *host;
+	struct ip_addr ip;
+	unsigned int port;
+	enum lmtp_client_protocol protocol;
+	enum lmtp_input_state input_state;
+
+	struct istream *input;
+	struct ostream *output;
+	struct io *io;
+	int fd;
+
+	ARRAY_DEFINE(recipients, struct lmtp_rcpt);
+	unsigned int rcpt_next_receive_idx;
+	unsigned int rcpt_next_data_idx;
+	unsigned int rcpt_next_send_idx;
+	struct istream *data_input;
+	unsigned char output_last;
+};
+
+static void lmtp_client_send_rcpts(struct lmtp_client *client);
+
+struct lmtp_client *
+lmtp_client_init(const char *mail_from, const char *my_hostname)
+{
+	struct lmtp_client *client;
+	pool_t pool;
+
+	i_assert(*mail_from == '<');
+
+	pool = pool_alloconly_create("lmtp client", 512);
+	client = p_new(pool, struct lmtp_client, 1);
+	client->pool = pool;
+	client->mail_from = p_strdup(pool, mail_from);
+	client->my_hostname = p_strdup(pool, my_hostname);
+	client->fd = -1;
+	p_array_init(&client->recipients, pool, 16);
+	return client;
+}
+
+static void lmtp_client_close(struct lmtp_client *client)
+{
+	if (client->io != NULL)
+		io_remove(&client->io);
+	if (client->input != NULL)
+		i_stream_unref(&client->input);
+	if (client->output != NULL)
+		o_stream_unref(&client->output);
+	if (client->fd != -1) {
+		net_disconnect(client->fd);
+		client->fd = -1;
+	}
+	if (client->data_input != NULL)
+		i_stream_unref(&client->data_input);
+}
+
+void lmtp_client_deinit(struct lmtp_client **_client)
+{
+	struct lmtp_client *client = *_client;
+
+	*_client = NULL;
+
+	lmtp_client_close(client);
+	pool_unref(&client->pool);
+}
+
+static void lmtp_client_fail(struct lmtp_client *client, const char *line)
+{
+	struct lmtp_rcpt *recipients;
+	unsigned int i, count;
+
+	recipients = array_get_modifiable(&client->recipients, &count);
+	for (i = client->rcpt_next_receive_idx; i < count; i++) {
+		recipients[i].rcpt_to_callback(FALSE, line,
+					       recipients[i].context);
+		recipients[i].failed = TRUE;
+	}
+	for (i = client->rcpt_next_data_idx; i < count; i++) {
+		if (!recipients[i].failed) {
+			recipients[i].data_callback(FALSE, line,
+						    recipients[i].context);
+		}
+	}
+	lmtp_client_close(client);
+}
+
+static bool
+lmtp_client_rcpt_next(struct lmtp_client *client, const char *line)
+{
+	struct lmtp_rcpt *recipients;
+	unsigned int i, count;
+	bool success, all_sent;
+
+	success = line[0] == '2';
+
+	recipients = array_get_modifiable(&client->recipients, &count);
+	for (i = client->rcpt_next_receive_idx; i < count; i++) {
+		recipients[i].failed = !success;
+		recipients[i].rcpt_to_callback(success, line,
+					       recipients[i].context);
+	}
+	all_sent = i == client->rcpt_next_receive_idx;
+	client->rcpt_next_receive_idx = i;
+	return all_sent && client->data_input != NULL;
+}
+
+static int
+lmtp_client_data_next(struct lmtp_client *client, const char *line)
+{
+	struct lmtp_rcpt *rcpt;
+	bool last;
+
+	rcpt = array_idx_modifiable(&client->recipients,
+				    client->rcpt_next_data_idx);
+	rcpt->failed = line[0] != '2';
+	last = ++client->rcpt_next_data_idx == array_count(&client->recipients);
+
+	rcpt->data_callback(!rcpt->failed, line, rcpt->context);
+	return last ? -1 : 0;
+}
+
+static void lmtp_client_send_data(struct lmtp_client *client)
+{
+	const unsigned char *data;
+	unsigned char add;
+	size_t i, size;
+	int ret;
+
+	while ((ret = i_stream_read_data(client->data_input,
+					 &data, &size, 0)) > 0) {
+		add = '\0';
+		for (i = 0; i < size; i++) {
+			if (data[i] == '\n') {
+				if ((i == 0 && client->output_last != '\r') ||
+				    (i > 0 && data[i-1] != '\r')) {
+					/* missing CR */
+					add = '\r';
+					break;
+				}
+			} else if (data[i] == '.' &&
+				   ((i == 0 && client->output_last == '\n') ||
+				    (i > 0 && data[i-1] == '\n'))) {
+				/* escape the dot */
+				add = '.';
+				break;
+			}
+		}
+
+		if (i > 0) {
+			if (o_stream_send(client->output, data, i) < 0)
+				break;
+			client->output_last = data[i-1];
+			i_stream_skip(client->data_input, i);
+		}
+
+		if (o_stream_get_buffer_used_size(client->output) >= 4096) {
+			if ((ret = o_stream_flush(client->output)) < 0)
+				break;
+			if (ret == 0) {
+				/* continue later */
+				return;
+			}
+		}
+
+		if (add != '\0') {
+			if (o_stream_send(client->output, &add, 1) < 0)
+				break;
+
+			client->output_last = add;
+		}
+	}
+	if (ret == 0 || ret == -2) {
+		/* -2 can happen with tee istreams */
+		return;
+	}
+
+	if (client->output_last != '\n') {
+		/* didn't end with CRLF */
+		(void)o_stream_send(client->output, "\r\n", 2);
+	}
+	(void)o_stream_send(client->output, ".\r\n", 3);
+}
+
+static void lmtp_client_send_handshake(struct lmtp_client *client)
+{
+	o_stream_cork(client->output);
+	switch (client->protocol) {
+	case LMTP_CLIENT_PROTOCOL_LMTP:
+		o_stream_send_str(client->output,
+			t_strdup_printf("LHLO %s\r\n", client->my_hostname));
+		break;
+	case LMTP_CLIENT_PROTOCOL_SMTP:
+		o_stream_send_str(client->output,
+			t_strdup_printf("EHLO %s\r\n", client->my_hostname));
+		break;
+	}
+	o_stream_send_str(client->output,
+		t_strdup_printf("MAIL FROM:%s\r\n", client->mail_from));
+	o_stream_uncork(client->output);
+}
+
+static int lmtp_input_get_reply_code(const char *line, int *reply_code_r)
+{
+	if (!i_isdigit(line[0]) || !i_isdigit(line[1]) || !i_isdigit(line[2]))
+		return -1;
+
+	*reply_code_r = (line[0]-'0') * 100 +
+		(line[1]-'0') * 10 +
+		(line[2]-'0');
+
+	if (line[3] == ' ') {
+		/* final reply */
+		return 1;
+	} else if (line[3] == '-') {
+		/* multiline reply. just ignore it. */
+		return 0;
+	} else {
+		/* invalid input */
+		return -1;
+	}
+}
+
+static int lmtp_client_input_line(struct lmtp_client *client, const char *line)
+{
+	int ret, reply_code = 0;
+
+	if ((ret = lmtp_input_get_reply_code(line, &reply_code)) <= 0) {
+		if (ret == 0)
+			return 0;
+		lmtp_client_fail(client, line);
+		return -1;
+	}
+
+	switch (client->input_state) {
+	case LMTP_INPUT_STATE_GREET:
+		if (reply_code != 220) {


More information about the dovecot-cvs mailing list