]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
istream-header-filter: Added HEADER_FILTER_END_BODY_WITH_LF flag.
authorTimo Sirainen <tss@iki.fi>
Fri, 30 Jul 2010 17:01:34 +0000 (18:01 +0100)
committerTimo Sirainen <tss@iki.fi>
Fri, 30 Jul 2010 17:01:34 +0000 (18:01 +0100)
If body doesn't end with LF character, it adds it automatically.

src/lib-mail/istream-header-filter.c
src/lib-mail/istream-header-filter.h
src/lib-mail/test-istream-header-filter.c

index 477302cdbdc95b86c5d731bf787e4974de11a450..797e4bf837985c8778b6922ae55195d29b8d6fd8 100644 (file)
@@ -23,6 +23,7 @@ struct header_filter_istream {
        buffer_t *hdr_buf;
        struct message_size header_size;
        uoff_t skip_count;
+       uoff_t last_lf_offset;
 
        unsigned int cur_line, parsed_lines;
        ARRAY_DEFINE(match_change_lines, unsigned int);
@@ -34,6 +35,8 @@ struct header_filter_istream {
        unsigned int crlf:1;
        unsigned int hide_body:1;
        unsigned int add_missing_eoh:1;
+       unsigned int end_body_with_lf:1;
+       unsigned int last_lf_added:1;
 };
 
 header_filter_callback *null_header_filter_callback = NULL;
@@ -64,18 +67,37 @@ read_mixed(struct header_filter_istream *mstream, size_t body_highwater_size)
        }
 
        data = i_stream_get_data(mstream->istream.parent, &pos);
-       if (pos == body_highwater_size) {
+       if (pos <= body_highwater_size) {
+               i_assert(pos == body_highwater_size ||
+                        (mstream->end_body_with_lf &&
+                         pos+1 == body_highwater_size));
+
                ret = i_stream_read(mstream->istream.parent);
                mstream->istream.istream.stream_errno =
                        mstream->istream.parent->stream_errno;
                mstream->istream.istream.eof = mstream->istream.parent->eof;
 
-               if (ret <= 0)
+               if (ret <= 0) {
+                       i_assert(pos > 0);
+
+                       data = mstream->hdr_buf->data;
+                       pos = mstream->hdr_buf->used;
+                       if (mstream->end_body_with_lf && data[pos-1] != '\n' &&
+                           ret == -1 && mstream->istream.istream.eof) {
+                               /* add missing trailing LF to body */
+                               if (mstream->crlf)
+                                       buffer_append_c(mstream->hdr_buf, '\r');
+                               buffer_append_c(mstream->hdr_buf, '\n');
+                               mstream->istream.buffer =
+                                       buffer_get_data(mstream->hdr_buf,
+                                                       &mstream->istream.pos);
+                               return mstream->hdr_buf->used - pos;
+                       }
                        return ret;
+               }
 
                data = i_stream_get_data(mstream->istream.parent, &pos);
        }
-       i_assert(pos > body_highwater_size);
        buffer_append(mstream->hdr_buf, data + body_highwater_size,
                      pos - body_highwater_size);
 
@@ -273,6 +295,45 @@ static ssize_t read_header(struct header_filter_istream *mstream)
        return ret;
 }
 
+static ssize_t
+handle_end_body_with_lf(struct header_filter_istream *mstream, ssize_t ret)
+{
+       struct istream_private *stream = &mstream->istream;
+       const unsigned char *data;
+       size_t size, last_offset;
+       bool last_lf;
+
+       data = i_stream_get_data(stream->parent, &size);
+       last_offset = stream->parent->v_offset + size-1;
+
+       if (mstream->last_lf_offset == last_offset)
+               last_lf = TRUE;
+       else if (size > 0)
+               last_lf = data[size-1] == '\n';
+       else
+               last_lf = FALSE;
+
+       if (ret == -1 && stream->parent->eof && !last_lf) {
+               /* missing LF, need to add it */
+               i_assert(size == 0 || data[size-1] != '\n');
+               buffer_reset(mstream->hdr_buf);
+               buffer_append(mstream->hdr_buf, data, size);
+               if (mstream->crlf)
+                       buffer_append_c(mstream->hdr_buf, '\r');
+               buffer_append_c(mstream->hdr_buf, '\n');
+               mstream->last_lf_offset = last_offset;
+               mstream->last_lf_added = TRUE;
+
+               stream->skip = 0;
+               stream->pos = size + 1;
+               stream->buffer = mstream->hdr_buf->data;
+               return mstream->crlf ? 2 : 1;
+       } else {
+               mstream->last_lf_offset = last_lf ? last_offset : (uoff_t)-1;
+       }
+       return ret;
+}
+
 static ssize_t i_stream_header_filter_read(struct istream_private *stream)
 {
        struct header_filter_istream *mstream =
@@ -280,6 +341,11 @@ static ssize_t i_stream_header_filter_read(struct istream_private *stream)
        uoff_t v_offset;
        ssize_t ret;
 
+       if (mstream->last_lf_added) {
+               stream->istream.eof = TRUE;
+               return -1;
+       }
+
        if (!mstream->header_read ||
            stream->istream.v_offset < mstream->header_size.virtual_size) {
                ret = read_header(mstream);
@@ -296,7 +362,10 @@ static ssize_t i_stream_header_filter_read(struct istream_private *stream)
                mstream->header_size.virtual_size +
                mstream->header_size.physical_size;
        i_stream_seek(stream->parent, v_offset);
-       return i_stream_read_copy_from_parent(&stream->istream);
+       ret = i_stream_read_copy_from_parent(&stream->istream);
+       if (mstream->end_body_with_lf)
+               ret = handle_end_body_with_lf(mstream, ret);
+       return ret;
 }
 
 static void
@@ -353,6 +422,8 @@ static void i_stream_header_filter_seek(struct istream_private *stream,
        struct header_filter_istream *mstream =
                (struct header_filter_istream *)stream;
 
+       mstream->last_lf_added = FALSE;
+
        if (stream->istream.v_offset == v_offset) {
                /* just reset the input buffer */
                stream_reset_to(mstream, v_offset);
@@ -447,6 +518,8 @@ i_stream_create_header_filter(struct istream *input,
        mstream->crlf = (flags & HEADER_FILTER_NO_CR) == 0;
        mstream->hide_body = (flags & HEADER_FILTER_HIDE_BODY) != 0;
        mstream->add_missing_eoh = (flags & HEADER_FILTER_ADD_MISSING_EOH) != 0;
+       mstream->end_body_with_lf =
+               (flags & HEADER_FILTER_END_BODY_WITH_LF) != 0;
 
        mstream->istream.iostream.destroy = i_stream_header_filter_destroy;
        mstream->istream.read = i_stream_header_filter_read;
index 4a4490a9ac29f162d796aad96315d0d35eb36e76..e667bd4a54c6a3e76db3a84a2fef7b6c8616e16c 100644 (file)
@@ -12,7 +12,9 @@ enum header_filter_flags {
        /* Return EOF at the beginning of message body. */
        HEADER_FILTER_HIDE_BODY         = 0x08,
        /* If the empty "end of headers" line doesn't exist, add it. */
-       HEADER_FILTER_ADD_MISSING_EOH   = 0x10
+       HEADER_FILTER_ADD_MISSING_EOH   = 0x10,
+       /* If body doesn't end with [CR]LF, add it/them. */
+       HEADER_FILTER_END_BODY_WITH_LF  = 0x20
 };
 
 struct message_header_line;
index 17f263243077571ca61764e2a3445e93c6c98d0d..f292fec3a5d3c23a5fb94b547c01060f3f39dd17 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "str.h"
 #include "istream.h"
 #include "message-header-parser.h"
 #include "istream-header-filter.h"
@@ -26,7 +27,7 @@ static void test_istream_filter(void)
        const unsigned char *data;
        size_t size;
 
-       test_begin("i_stream_create_header_filter()");
+       test_begin("i_stream_create_header_filter(exclude)");
        istream = test_istream_create(input);
        filter = i_stream_create_header_filter(istream,
                                               HEADER_FILTER_EXCLUDE |
@@ -64,10 +65,68 @@ static void test_istream_filter(void)
        test_end();
 }
 
+static void test_istream_end_body_with_lf(void)
+{
+       const char *input = "From: foo\n\nhello world";
+       const char *output = "From: foo\n\nhello world\n";
+       struct istream *istream, *filter;
+       unsigned int i, input_len = strlen(input);
+       unsigned int output_len = strlen(output);
+       const unsigned char *data;
+       string_t *str = t_str_new(64);
+       size_t size;
+
+       test_begin("i_stream_create_header_filter(end_body_with_lf)");
+       istream = test_istream_create(input);
+       filter = i_stream_create_header_filter(istream,
+                                              HEADER_FILTER_EXCLUDE |
+                                              HEADER_FILTER_NO_CR |
+                                              HEADER_FILTER_END_BODY_WITH_LF,
+                                              NULL, 0,
+                                              null_header_filter_callback, NULL);
+
+       for (i = 1; i < input_len; i++) {
+               test_istream_set_size(istream, i);
+               test_assert(i_stream_read(filter) >= 0);
+       }
+       test_istream_set_size(istream, input_len);
+       test_assert(i_stream_read(filter) > 0);
+       test_assert(i_stream_read(filter) > 0);
+       test_assert(i_stream_read(filter) == -1);
+
+       data = i_stream_get_data(filter, &size);
+       test_assert(size == output_len && memcmp(data, output, size) == 0);
+
+       i_stream_skip(filter, size);
+       i_stream_seek(filter, 0);
+       for (i = 1; i < input_len; i++) {
+               test_istream_set_size(istream, i);
+               test_assert(i_stream_read(filter) >= 0);
+
+               data = i_stream_get_data(filter, &size);
+               str_append_n(str, data, size);
+               i_stream_skip(filter, size);
+       }
+       test_istream_set_size(istream, input_len);
+       test_assert(i_stream_read(filter) == 1);
+       test_assert(i_stream_read(filter) == 1);
+       test_assert(i_stream_read(filter) == -1);
+
+       data = i_stream_get_data(filter, &size);
+       str_append_n(str, data, size);
+       test_assert(strcmp(str_c(str), output) == 0);
+
+       i_stream_unref(&filter);
+       i_stream_unref(&istream);
+
+       test_end();
+}
+
 int main(void)
 {
        static void (*test_functions[])(void) = {
                test_istream_filter,
+               test_istream_end_body_with_lf,
                NULL
        };
        return test_run(test_functions);