]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-mail: ostream-dot API changes
authorTimo Sirainen <tss@iki.fi>
Sun, 15 Feb 2015 08:03:10 +0000 (10:03 +0200)
committerTimo Sirainen <tss@iki.fi>
Sun, 15 Feb 2015 08:03:10 +0000 (10:03 +0200)
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.

src/lib-mail/ostream-dot.c
src/lib-mail/ostream-dot.h
src/lib-mail/test-ostream-dot.c

index 7052e2cf0771c98f1e4c2c582ac29b4db0a1a6f1..9d1861121181a8a0651b30ca3754c4925b5ce2e9 100644 (file)
@@ -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));
 }
index ed908520371087f98e7dfb85e1f878580b953df9..4e22089fd873aff958ea985fdd12ffac5d79d02a 100644 (file)
@@ -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
index 4775fec22e56112ede7f8ea7f871d3b87ef2c78c..5465a15ccdf8e3f2e5014a0103a630d3611d34bc 100644 (file)
@@ -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);