dovecot-2.2: Implemented dot output stream. This outputs data in...

dovecot at dovecot.org dovecot at dovecot.org
Sat Nov 15 01:27:56 UTC 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/2866a692ec47
changeset: 18097:2866a692ec47
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Sat Nov 15 02:37:45 2014 +0200
description:
Implemented dot output stream. This outputs data in the dot-escaped format needed for the SMTP data command.
The sequence CRLF.CRLF is sent upon closing the stream.

diffstat:

 src/lib-mail/Makefile.am        |    7 +
 src/lib-mail/ostream-dot.c      |  180 ++++++++++++++++++++++++++++++++++++++++
 src/lib-mail/ostream-dot.h      |    9 ++
 src/lib-mail/test-ostream-dot.c |   77 +++++++++++++++++
 4 files changed, 273 insertions(+), 0 deletions(-)

diffs (truncated from 316 to 300 lines):

diff -r 8eaaff8e2c63 -r 2866a692ec47 src/lib-mail/Makefile.am
--- a/src/lib-mail/Makefile.am	Sat Nov 15 02:37:03 2014 +0200
+++ b/src/lib-mail/Makefile.am	Sat Nov 15 02:37:45 2014 +0200
@@ -28,6 +28,7 @@
 	message-part-serialize.c \
 	message-search.c \
 	message-size.c \
+	ostream-dot.c \
 	quoted-printable.c \
 	rfc2231-parser.c \
 	rfc822-parser.c
@@ -56,6 +57,7 @@
 	message-part-serialize.h \
 	message-search.h \
 	message-size.h \
+	ostream-dot.h \
 	quoted-printable.h \
 	rfc2231-parser.h \
 	rfc822-parser.h
@@ -79,6 +81,7 @@
 	test-message-id \
 	test-message-parser \
 	test-message-part \
+	test-ostream-dot \
 	test-quoted-printable \
 	test-rfc2231-parser
 
@@ -157,6 +160,10 @@
 test_message_part_LDADD = message-part.lo message-parser.lo message-header-parser.lo message-size.lo rfc822-parser.lo rfc2231-parser.lo $(test_libs)
 test_message_part_DEPENDENCIES = $(test_deps)
 
+test_ostream_dot_SOURCES = test-ostream-dot.c
+test_ostream_dot_LDADD = ostream-dot.lo $(test_libs)
+test_ostream_dot_DEPENDENCIES = $(test_deps)
+
 test_quoted_printable_SOURCES = test-quoted-printable.c
 test_quoted_printable_LDADD = quoted-printable.lo $(test_libs)
 test_quoted_printable_DEPENDENCIES = $(test_deps)
diff -r 8eaaff8e2c63 -r 2866a692ec47 src/lib-mail/ostream-dot.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/ostream-dot.c	Sat Nov 15 02:37:45 2014 +0200
@@ -0,0 +1,180 @@
+/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "ostream-private.h"
+#include "ostream-dot.h"
+
+enum dot_ostream_state {
+	STREAM_STATE_INIT = 0,
+	STREAM_STATE_NONE,
+	STREAM_STATE_CR,
+	STREAM_STATE_CRLF,
+};
+
+struct dot_ostream {
+	struct ostream_private ostream;
+
+	enum dot_ostream_state state;
+};
+
+static void
+o_stream_dot_close(struct iostream_private *stream,
+				    bool close_parent)
+{
+	struct dot_ostream *dstream = (struct dot_ostream *)stream;
+
+	if (dstream->state == STREAM_STATE_CRLF)
+		(void)o_stream_send(dstream->ostream.parent, ".\r\n", 3);
+	else
+		(void)o_stream_send(dstream->ostream.parent, "\r\n.\r\n", 5);
+	(void)o_stream_flush(&dstream->ostream.ostream);
+
+	if (close_parent)
+		o_stream_close(dstream->ostream.parent);
+}
+
+static ssize_t
+o_stream_dot_sendv(struct ostream_private *stream,
+		    const struct const_iovec *iov, unsigned int iov_count)
+{
+	struct dot_ostream *dstream = (struct dot_ostream *)stream;
+	ARRAY(struct const_iovec) iov_arr;
+	const struct const_iovec *iov_new;
+	size_t max_bytes, sent, added;
+	unsigned int count, i;
+	ssize_t ret;
+
+	if ((ret=o_stream_flush(stream->parent)) <= 0) {
+		/* error / we still couldn't flush existing data to
+		   parent stream. */
+		o_stream_copy_error_from_parent(stream);
+		return ret;
+	}
+
+	/* check for dots */
+	t_array_init(&iov_arr, iov_count + 32);
+	max_bytes = o_stream_get_buffer_avail_size(stream->parent); // FIXME: what if max_buffer_size is 0?
+	sent = added = 0;
+	for (i = 0; i < iov_count && max_bytes > 0; i++) {
+		size_t size = iov[i].iov_len, chunk;
+		const char *data = iov[i].iov_base, *p, *pend;
+		struct const_iovec iovn;
+
+		p = data;
+		pend = CONST_PTR_OFFSET(data, size);
+		for (; p < pend && (size_t)(p-data) < (max_bytes-2); p++) {
+			char add = 0;
+
+			switch (dstream->state) {
+			/* none */
+			case STREAM_STATE_NONE:
+				switch (*p) {
+				case '\n':
+					dstream->state = STREAM_STATE_CRLF;
+					/* add missing CR */
+					add = '\r';
+					break;
+				case '\r':
+					dstream->state = STREAM_STATE_CR;
+					break;
+				}
+				break;
+			/* got CR */
+			case STREAM_STATE_CR:
+				switch (*p) {
+				case '\r':
+					break;
+				case '\n':
+					dstream->state = STREAM_STATE_CRLF;
+					break;
+				default:
+					dstream->state = STREAM_STATE_NONE;
+					break;
+				}
+				break;
+			/* got CRLF, or the first line */
+			case STREAM_STATE_INIT:
+			case STREAM_STATE_CRLF:
+				switch (*p) {
+				case '\r':
+					dstream->state = STREAM_STATE_CR;
+					break;
+				case '\n':
+					dstream->state = STREAM_STATE_CRLF;
+					/* add missing CR */
+					add = '\r';
+					break;
+				case '.':
+					/* add dot */
+					add = '.';
+				default:
+					dstream->state = STREAM_STATE_NONE;
+					break;
+				}
+				break;
+			}
+
+			if (add != 0) {
+				chunk = (size_t)(p - data);
+				if (chunk > 0) {
+					/* forward chunk to new iovec */
+					iovn.iov_base = data;
+					iovn.iov_len = chunk;
+					array_append(&iov_arr, &iovn, 1);
+					data = p;
+					max_bytes -= chunk;
+					sent += chunk;
+				}
+				/* insert byte (substitute one with pair) */
+				data++;
+				iovn.iov_base = (add == '\r' ? "\r\n" : "..");
+				iovn.iov_len = 2;
+				array_append(&iov_arr, &iovn, 1);
+				max_bytes -= 2;
+				added++;
+				sent++;
+			}
+		}
+
+		if (max_bytes == 0)
+			break;
+		chunk = ((size_t)(p-data) >= (max_bytes-2) ?
+				max_bytes - 2 : (size_t)(p - data));	
+		if (chunk > 0) {
+			iovn.iov_base = data;
+			iovn.iov_len = chunk;
+			array_append(&iov_arr, &iovn, 1);
+			max_bytes -= chunk;
+			sent += chunk;
+		}
+	}
+
+	/* send */
+	iov_new = array_get(&iov_arr, &count);
+	if (count == 0) {
+		ret = 0;
+	} else if ((ret=o_stream_sendv(stream->parent, iov_new, count)) <= 0) {
+		i_assert(ret < 0);
+		o_stream_copy_error_from_parent(stream);
+		return -1;
+	}
+
+	/* all must be sent */
+	i_assert((size_t)ret == sent + added);
+
+	stream->ostream.offset += sent;
+	return sent;
+}
+
+struct ostream *
+o_stream_create_dot(struct ostream *output)
+{
+	struct dot_ostream *dstream;
+
+	dstream = i_new(struct dot_ostream, 1);
+	dstream->ostream.sendv = o_stream_dot_sendv;
+	dstream->ostream.iostream.close = o_stream_dot_close;
+	dstream->ostream.max_buffer_size = output->real_stream->max_buffer_size;
+	return o_stream_create(&dstream->ostream, output, o_stream_get_fd(output));
+}
diff -r 8eaaff8e2c63 -r 2866a692ec47 src/lib-mail/ostream-dot.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/ostream-dot.h	Sat Nov 15 02:37:45 2014 +0200
@@ -0,0 +1,9 @@
+#ifndef OSTREAM_DOT_H
+#define OSTREAM_DOT_H
+
+/* Create output stream for writing SMTP DATA style message: Add additional "."
+   to lines beginning with it. Write line that contains only "." upon close().
+ */
+struct ostream *o_stream_create_dot(struct ostream *output);
+
+#endif
diff -r 8eaaff8e2c63 -r 2866a692ec47 src/lib-mail/test-ostream-dot.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-mail/test-ostream-dot.c	Sat Nov 15 02:37:45 2014 +0200
@@ -0,0 +1,77 @@
+/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "str.h"
+#include "istream.h"
+#include "ostream.h"
+#include "ostream-dot.h"
+#include "test-common.h"
+
+struct dot_test {
+	const char *input;
+	const char *output;
+};
+
+static void test_ostream_dot_one(const struct dot_test *test)
+{
+	struct istream *test_input;
+	struct ostream *output, *test_output;
+	buffer_t *output_data;
+	const unsigned char *data;
+	size_t size;
+	ssize_t ret;
+
+	test_input = test_istream_create(test->input);
+	output_data = buffer_create_dynamic(pool_datastack_create(), 1024);
+	test_output = o_stream_create_buffer(output_data);
+
+	output = o_stream_create_dot(test_output);
+
+	while ((ret = i_stream_read(test_input)) > 0 || ret == -2) {
+		data = i_stream_get_data(test_input, &size);
+		ret = o_stream_send(output, data, size);
+		test_assert(ret >= 0);
+		if (ret <= 0)
+			break;
+		i_stream_skip(test_input, ret);
+	}
+
+	test_assert(test_input->eof);
+
+	o_stream_unref(&output);
+	o_stream_unref(&test_output);
+
+	test_assert(strcmp(str_c(output_data), test->output) == 0);
+
+	i_stream_unref(&test_input);
+}
+
+static void test_ostream_dot(void)
+{
+	static struct dot_test tests[] = {
+		{ "foo\r\n.\r\n", "foo\r\n..\r\n.\r\n" },
+		{ "foo\n.\n", "foo\r\n..\r\n.\r\n" },
+		{ ".foo\r\n.\r\nfoo\r\n", "..foo\r\n..\r\nfoo\r\n.\r\n" },
+		{ ".foo\n.\nfoo\n", "..foo\r\n..\r\nfoo\r\n.\r\n" },
+		{ "\r\n", "\r\n.\r\n" },
+		{ "\n", "\r\n.\r\n" },
+		{ "", "\r\n.\r\n" },
+	};
+	unsigned int i;


More information about the dovecot-cvs mailing list