From: Timo Sirainen Date: Sun, 15 Feb 2015 08:03:10 +0000 (+0200) Subject: lib-mail: ostream-dot API changes X-Git-Tag: 2.2.16.rc1~53 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=83773f7eb5e12e68f5efee7278bdab35d2ee84c0;p=thirdparty%2Fdovecot%2Fcore.git 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. --- diff --git a/src/lib-mail/ostream-dot.c b/src/lib-mail/ostream-dot.c index 7052e2cf07..9d18611211 100644 --- a/src/lib-mail/ostream-dot.c +++ b/src/lib-mail/ostream-dot.c @@ -10,25 +10,52 @@ enum dot_ostream_state { 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 (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_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 (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 @@ o_stream_dot_sendv(struct ostream_private *stream, 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 @@ o_stream_dot_sendv(struct ostream_private *stream, /* 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 @@ o_stream_dot_sendv(struct ostream_private *stream, break; } break; + case STREAM_STATE_DONE: + i_unreached(); } if (add != 0) { @@ -168,13 +201,15 @@ o_stream_dot_sendv(struct ostream_private *stream, } 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 --git a/src/lib-mail/ostream-dot.h b/src/lib-mail/ostream-dot.h index ed90852037..4e22089fd8 100644 --- a/src/lib-mail/ostream-dot.h +++ b/src/lib-mail/ostream-dot.h @@ -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 --git a/src/lib-mail/test-ostream-dot.c b/src/lib-mail/test-ostream-dot.c index 4775fec22e..5465a15ccd 100644 --- a/src/lib-mail/test-ostream-dot.c +++ b/src/lib-mail/test-ostream-dot.c @@ -26,7 +26,7 @@ static void test_ostream_dot_one(const struct dot_test *test) 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 @@ static void test_ostream_dot_one(const struct dot_test *test) test_assert(test_input->eof); + test_assert(o_stream_flush(output) > 0); o_stream_unref(&output); o_stream_unref(&test_output);