dovecot-2.2: lib-mail: ostream-dot API changes

dovecot at dovecot.org dovecot at dovecot.org
Sun Feb 15 08:11:30 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/ca24e6d34345
changeset: 18253:ca24e6d34345
user:      Timo Sirainen <tss at iki.fi>
date:      Sun Feb 15 10:03:10 2015 +0200
description:
lib-mail: ostream-dot API changes
The dot-line sending is now done when o_stream_flush() is called. This
should be used instead of relying on close() doing it, because it still
needs to write a few bytes and there are no guarantees that the parent
stream allows sending them.

Also added force_extra_crlf parameter, which specifies whether CRLF should
always be written before the dot-line, even when it already contained CRLF.
This is useful if the input is read with
i_stream_create_dot(send_last_lf=FALSE), since it allows sending a stream
that doesn't have to end with LF.

diffstat:

 src/lib-mail/ostream-dot.c      |  55 +++++++++++++++++++++++++++++++++-------
 src/lib-mail/ostream-dot.h      |  12 ++++++--
 src/lib-mail/test-ostream-dot.c |   3 +-
 3 files changed, 56 insertions(+), 14 deletions(-)

diffs (151 lines):

diff -r 42b3ac799f2f -r ca24e6d34345 src/lib-mail/ostream-dot.c
--- a/src/lib-mail/ostream-dot.c	Sun Feb 15 09:55:56 2015 +0200
+++ b/src/lib-mail/ostream-dot.c	Sun Feb 15 10:03:10 2015 +0200
@@ -10,25 +10,52 @@
 	STREAM_STATE_NONE,
 	STREAM_STATE_CR,
 	STREAM_STATE_CRLF,
+	STREAM_STATE_DONE
 };
 
 struct dot_ostream {
 	struct ostream_private ostream;
 
 	enum dot_ostream_state state;
+	bool force_extra_crlf;
 };
 
-static void
-o_stream_dot_close(struct iostream_private *stream,
-				    bool close_parent)
+static int
+o_stream_dot_flush(struct ostream_private *stream)
 {
 	struct dot_ostream *dstream = (struct dot_ostream *)stream;
+	int ret;
 
-	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 (o_stream_get_buffer_avail_size(stream->parent) < 5) {
+		/* make space for the dot line */
+		if ((ret = o_stream_flush(stream->parent)) <= 0) {
+			if (ret < 0)
+				o_stream_copy_error_from_parent(stream);
+			return ret;
+		}
+	}
+
+	if (dstream->state == STREAM_STATE_DONE)
+		;
+	else if (dstream->state == STREAM_STATE_CRLF &&
+		 !dstream->force_extra_crlf) {
+		ret = o_stream_send(stream->parent, ".\r\n", 3);
+		i_assert(ret == 3);
+	} else {
+		ret = o_stream_send(stream->parent, "\r\n.\r\n", 5);
+		i_assert(ret == 5);
+	}
+	dstream->state = STREAM_STATE_DONE;
+
+	if ((ret = o_stream_flush(stream->parent)) < 0)
+		o_stream_copy_error_from_parent(stream);
+	return ret;
+}
+
+static void
+o_stream_dot_close(struct iostream_private *stream, bool close_parent)
+{
+	struct dot_ostream *dstream = (struct dot_ostream *)stream;
 
 	if (close_parent)
 		o_stream_close(dstream->ostream.parent);
@@ -45,6 +72,8 @@
 	unsigned int count, i;
 	ssize_t ret;
 
+	i_assert(dstream->state != STREAM_STATE_DONE);
+
 	if ((ret=o_stream_flush(stream->parent)) <= 0) {
 		/* error / we still couldn't flush existing data to
 		   parent stream. */
@@ -54,7 +83,9 @@
 
 	/* 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?
+	max_bytes = o_stream_get_buffer_avail_size(stream->parent);
+	i_assert(max_bytes > 0); /* FIXME: not supported currently */
+
 	sent = added = 0;
 	for (i = 0; i < iov_count && max_bytes > 0; i++) {
 		size_t size = iov[i].iov_len, chunk;
@@ -113,6 +144,8 @@
 					break;
 				}
 				break;
+			case STREAM_STATE_DONE:
+				i_unreached();
 			}
 
 			if (add != 0) {
@@ -168,13 +201,15 @@
 }
 
 struct ostream *
-o_stream_create_dot(struct ostream *output)
+o_stream_create_dot(struct ostream *output, bool force_extra_crlf)
 {
 	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.flush = o_stream_dot_flush;
 	dstream->ostream.max_buffer_size = output->real_stream->max_buffer_size;
+	dstream->force_extra_crlf = force_extra_crlf;
 	return o_stream_create(&dstream->ostream, output, o_stream_get_fd(output));
 }
diff -r 42b3ac799f2f -r ca24e6d34345 src/lib-mail/ostream-dot.h
--- a/src/lib-mail/ostream-dot.h	Sun Feb 15 09:55:56 2015 +0200
+++ b/src/lib-mail/ostream-dot.h	Sun Feb 15 10:03:10 2015 +0200
@@ -2,8 +2,14 @@
 #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);
+   to lines that start with ".". Write a line that contains only "." upon
+   o_stream_flush(). (This is also called at close(), but it shouldn't be
+   relied on since it could fail due to output buffer being full.)
+
+   If output ends with CRLF, force_extra_crlf controls whether additional CRLF
+   is written before the "." line. This parameter should match
+   i_stream_create_dot()'s send_last_lf parameter (reversed). */
+struct ostream *o_stream_create_dot(struct ostream *output,
+				    bool force_extra_crlf);
 
 #endif
diff -r 42b3ac799f2f -r ca24e6d34345 src/lib-mail/test-ostream-dot.c
--- a/src/lib-mail/test-ostream-dot.c	Sun Feb 15 09:55:56 2015 +0200
+++ b/src/lib-mail/test-ostream-dot.c	Sun Feb 15 10:03:10 2015 +0200
@@ -26,7 +26,7 @@
 	output_data = buffer_create_dynamic(pool_datastack_create(), 1024);
 	test_output = o_stream_create_buffer(output_data);
 
-	output = o_stream_create_dot(test_output);
+	output = o_stream_create_dot(test_output, FALSE);
 
 	while ((ret = i_stream_read(test_input)) > 0 || ret == -2) {
 		data = i_stream_get_data(test_input, &size);
@@ -39,6 +39,7 @@
 
 	test_assert(test_input->eof);
 
+	test_assert(o_stream_flush(output) > 0);
 	o_stream_unref(&output);
 	o_stream_unref(&test_output);
 


More information about the dovecot-cvs mailing list