dovecot-2.0: istream-header-filter: Added HEADER_FILTER_END_BODY...
dovecot at dovecot.org
dovecot at dovecot.org
Fri Jul 30 20:02:22 EEST 2010
details: http://hg.dovecot.org/dovecot-2.0/rev/22e20ccc14bc
changeset: 11908:22e20ccc14bc
user: Timo Sirainen <tss at iki.fi>
date: Fri Jul 30 18:01:34 2010 +0100
description:
istream-header-filter: Added HEADER_FILTER_END_BODY_WITH_LF flag.
If body doesn't end with LF character, it adds it automatically.
diffstat:
src/lib-mail/istream-header-filter.c | 81 +++++++++++++++++++++++++-
src/lib-mail/istream-header-filter.h | 4 +-
src/lib-mail/test-istream-header-filter.c | 61 ++++++++++++++++++++-
3 files changed, 140 insertions(+), 6 deletions(-)
diffs (252 lines):
diff -r b43c8d765d44 -r 22e20ccc14bc src/lib-mail/istream-header-filter.c
--- a/src/lib-mail/istream-header-filter.c Fri Jul 30 16:55:58 2010 +0100
+++ b/src/lib-mail/istream-header-filter.c Fri Jul 30 18:01:34 2010 +0100
@@ -23,6 +23,7 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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;
diff -r b43c8d765d44 -r 22e20ccc14bc src/lib-mail/istream-header-filter.h
--- a/src/lib-mail/istream-header-filter.h Fri Jul 30 16:55:58 2010 +0100
+++ b/src/lib-mail/istream-header-filter.h Fri Jul 30 18:01:34 2010 +0100
@@ -12,7 +12,9 @@
/* 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;
diff -r b43c8d765d44 -r 22e20ccc14bc src/lib-mail/test-istream-header-filter.c
--- a/src/lib-mail/test-istream-header-filter.c Fri Jul 30 16:55:58 2010 +0100
+++ b/src/lib-mail/test-istream-header-filter.c Fri Jul 30 18:01:34 2010 +0100
@@ -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 @@
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 @@
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);
More information about the dovecot-cvs
mailing list