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