istream-header-filter.c (dovecot-2.3.16) | : | istream-header-filter.c (dovecot-2.3.17) | ||
---|---|---|---|---|
/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ | /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */ | |||
#include "lib.h" | #include "lib.h" | |||
#include "array.h" | #include "array.h" | |||
#include "memarea.h" | ||||
#include "sort.h" | #include "sort.h" | |||
#include "message-parser.h" | #include "message-parser.h" | |||
#include "istream-private.h" | #include "istream-private.h" | |||
#include "istream-header-filter.h" | #include "istream-header-filter.h" | |||
struct header_filter_istream_snapshot { | ||||
struct istream_snapshot snapshot; | ||||
struct header_filter_istream *mstream; | ||||
buffer_t *hdr_buf; | ||||
}; | ||||
struct header_filter_istream { | struct header_filter_istream { | |||
struct istream_private istream; | struct istream_private istream; | |||
pool_t pool; | pool_t pool; | |||
struct message_header_parser_ctx *hdr_ctx; | struct message_header_parser_ctx *hdr_ctx; | |||
const char **headers; | const char **headers; | |||
unsigned int headers_count; | unsigned int headers_count; | |||
header_filter_callback *callback; | header_filter_callback *callback; | |||
skipping to change at line 46 | skipping to change at line 53 | |||
bool crlf_preserve:1; | bool crlf_preserve:1; | |||
bool hide_body:1; | bool hide_body:1; | |||
bool add_missing_eoh:1; | bool add_missing_eoh:1; | |||
bool end_body_with_lf:1; | bool end_body_with_lf:1; | |||
bool last_lf_added:1; | bool last_lf_added:1; | |||
bool last_orig_crlf:1; | bool last_orig_crlf:1; | |||
bool last_added_newline:1; | bool last_added_newline:1; | |||
bool eoh_not_matched:1; | bool eoh_not_matched:1; | |||
bool callbacks_called:1; | bool callbacks_called:1; | |||
bool prev_matched:1; | bool prev_matched:1; | |||
bool snapshot_pending:1; | ||||
}; | }; | |||
header_filter_callback *null_header_filter_callback = NULL; | header_filter_callback *null_header_filter_callback = NULL; | |||
static ssize_t i_stream_header_filter_read(struct istream_private *stream); | static ssize_t i_stream_header_filter_read(struct istream_private *stream); | |||
static void i_stream_header_filter_destroy(struct iostream_private *stream) | static void i_stream_header_filter_destroy(struct iostream_private *stream) | |||
{ | { | |||
struct header_filter_istream *mstream = | struct header_filter_istream *mstream = | |||
(struct header_filter_istream *)stream; | (struct header_filter_istream *)stream; | |||
if (mstream->hdr_ctx != NULL) | if (mstream->hdr_ctx != NULL) | |||
message_parse_header_deinit(&mstream->hdr_ctx); | message_parse_header_deinit(&mstream->hdr_ctx); | |||
if (array_is_created(&mstream->match_change_lines)) | if (array_is_created(&mstream->match_change_lines)) | |||
array_free(&mstream->match_change_lines); | array_free(&mstream->match_change_lines); | |||
if (!mstream->snapshot_pending) | ||||
buffer_free(&mstream->hdr_buf); | ||||
else { | ||||
/* Clear hdr_buf to make sure | ||||
i_stream_header_filter_snapshot_free() frees it. */ | ||||
mstream->hdr_buf = NULL; | ||||
} | ||||
pool_unref(&mstream->pool); | pool_unref(&mstream->pool); | |||
} | } | |||
static ssize_t | static ssize_t | |||
read_mixed(struct header_filter_istream *mstream, size_t body_highwater_size) | read_mixed(struct header_filter_istream *mstream, size_t body_highwater_size) | |||
{ | { | |||
const unsigned char *data; | const unsigned char *data; | |||
size_t pos; | size_t pos; | |||
ssize_t ret; | ssize_t ret; | |||
skipping to change at line 154 | skipping to change at line 169 | |||
ssize_t ret; | ssize_t ret; | |||
size_t pos; | size_t pos; | |||
mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos); | mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos); | |||
ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip); | ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip); | |||
i_assert(ret >= 0); | i_assert(ret >= 0); | |||
mstream->istream.pos = pos; | mstream->istream.pos = pos; | |||
return ret; | return ret; | |||
} | } | |||
static void hdr_buf_realloc_if_needed(struct header_filter_istream *mstream) | ||||
{ | ||||
if (!mstream->snapshot_pending) | ||||
return; | ||||
/* hdr_buf exists in a snapshot. Leave it be and create a copy of it | ||||
that we modify. */ | ||||
buffer_t *old_buf = mstream->hdr_buf; | ||||
mstream->hdr_buf = buffer_create_dynamic(default_pool, | ||||
I_MAX(1024, old_buf->used)); | ||||
buffer_append(mstream->hdr_buf, old_buf->data, old_buf->used); | ||||
mstream->snapshot_pending = FALSE; | ||||
mstream->istream.buffer = mstream->hdr_buf->data; | ||||
} | ||||
static ssize_t read_header(struct header_filter_istream *mstream) | static ssize_t read_header(struct header_filter_istream *mstream) | |||
{ | { | |||
struct message_header_line *hdr; | struct message_header_line *hdr; | |||
uoff_t highwater_offset; | uoff_t highwater_offset; | |||
size_t max_buffer_size; | size_t max_buffer_size; | |||
ssize_t ret, ret2; | ssize_t ret, ret2; | |||
int hdr_ret; | int hdr_ret; | |||
if (mstream->hdr_ctx == NULL) { | if (mstream->hdr_ctx == NULL) { | |||
mstream->hdr_ctx = | mstream->hdr_ctx = | |||
message_parse_header_init(mstream->istream.parent, | message_parse_header_init(mstream->istream.parent, | |||
NULL, 0); | NULL, 0); | |||
} | } | |||
/* remove skipped data from hdr_buf */ | /* remove skipped data from hdr_buf */ | |||
hdr_buf_realloc_if_needed(mstream); | ||||
buffer_copy(mstream->hdr_buf, 0, | buffer_copy(mstream->hdr_buf, 0, | |||
mstream->hdr_buf, mstream->istream.skip, SIZE_MAX); | mstream->hdr_buf, mstream->istream.skip, SIZE_MAX); | |||
mstream->istream.pos -= mstream->istream.skip; | mstream->istream.pos -= mstream->istream.skip; | |||
mstream->istream.skip = 0; | mstream->istream.skip = 0; | |||
buffer_set_used_size(mstream->hdr_buf, mstream->istream.pos); | buffer_set_used_size(mstream->hdr_buf, mstream->istream.pos); | |||
if (mstream->header_read) { | if (mstream->header_read) { | |||
i_assert(mstream->istream.skip == 0); | i_assert(mstream->istream.skip == 0); | |||
highwater_offset = mstream->istream.istream.v_offset + | highwater_offset = mstream->istream.istream.v_offset + | |||
skipping to change at line 417 | skipping to change at line 449 | |||
else if (size > 0) | else if (size > 0) | |||
last_lf = data[size-1] == '\n'; | last_lf = data[size-1] == '\n'; | |||
else | else | |||
last_lf = FALSE; | last_lf = FALSE; | |||
if (ret == -1 && stream->parent->eof && !last_lf) { | if (ret == -1 && stream->parent->eof && !last_lf) { | |||
/* missing LF, need to add it */ | /* missing LF, need to add it */ | |||
i_assert(!mstream->last_lf_added); | i_assert(!mstream->last_lf_added); | |||
i_assert(size == 0 || data[size-1] != '\n'); | i_assert(size == 0 || data[size-1] != '\n'); | |||
hdr_buf_realloc_if_needed(mstream); | ||||
buffer_set_used_size(mstream->hdr_buf, 0); | buffer_set_used_size(mstream->hdr_buf, 0); | |||
buffer_append(mstream->hdr_buf, data, size); | buffer_append(mstream->hdr_buf, data, size); | |||
if (mstream->crlf) | if (mstream->crlf) | |||
buffer_append_c(mstream->hdr_buf, '\r'); | buffer_append_c(mstream->hdr_buf, '\r'); | |||
buffer_append_c(mstream->hdr_buf, '\n'); | buffer_append_c(mstream->hdr_buf, '\n'); | |||
mstream->last_lf_offset = last_offset; | mstream->last_lf_offset = last_offset; | |||
mstream->last_lf_added = TRUE; | mstream->last_lf_added = TRUE; | |||
stream->skip = 0; | stream->skip = 0; | |||
stream->pos = mstream->hdr_buf->used; | stream->pos = mstream->hdr_buf->used; | |||
skipping to change at line 511 | skipping to change at line 544 | |||
i_stream_read_memarea(&mstream->istream.istream) != -1) { | i_stream_read_memarea(&mstream->istream.istream) != -1) { | |||
pos = i_stream_get_data_size(&mstream->istream.istream); | pos = i_stream_get_data_size(&mstream->istream.istream); | |||
i_stream_skip(&mstream->istream.istream, pos); | i_stream_skip(&mstream->istream.istream, pos); | |||
} | } | |||
return mstream->istream.istream.stream_errno != 0 ? -1 : 0; | return mstream->istream.istream.stream_errno != 0 ? -1 : 0; | |||
} | } | |||
static void | static void | |||
stream_reset_to(struct header_filter_istream *mstream, uoff_t v_offset) | stream_reset_to(struct header_filter_istream *mstream, uoff_t v_offset) | |||
{ | { | |||
hdr_buf_realloc_if_needed(mstream); | ||||
mstream->istream.istream.v_offset = v_offset; | mstream->istream.istream.v_offset = v_offset; | |||
mstream->istream.skip = mstream->istream.pos = 0; | mstream->istream.skip = mstream->istream.pos = 0; | |||
mstream->istream.buffer = NULL; | mstream->istream.buffer = NULL; | |||
buffer_set_used_size(mstream->hdr_buf, 0); | buffer_set_used_size(mstream->hdr_buf, 0); | |||
} | } | |||
static void i_stream_header_filter_seek(struct istream_private *stream, | static void i_stream_header_filter_seek(struct istream_private *stream, | |||
uoff_t v_offset, bool mark ATTR_UNUSED) | uoff_t v_offset, bool mark ATTR_UNUSED) | |||
{ | { | |||
struct header_filter_istream *mstream = | struct header_filter_istream *mstream = | |||
skipping to change at line 554 | skipping to change at line 588 | |||
if (skip_header(mstream) < 0) | if (skip_header(mstream) < 0) | |||
return; | return; | |||
stream_reset_to(mstream, v_offset); | stream_reset_to(mstream, v_offset); | |||
if (v_offset < mstream->header_size.virtual_size) { | if (v_offset < mstream->header_size.virtual_size) { | |||
/* seek into headers. we'll have to re-parse them, use | /* seek into headers. we'll have to re-parse them, use | |||
skip_count to set the wanted position */ | skip_count to set the wanted position */ | |||
i_stream_header_filter_seek_to_header(mstream, v_offset); | i_stream_header_filter_seek_to_header(mstream, v_offset); | |||
} else { | } else { | |||
/* body */ | /* body */ | |||
v_offset += mstream->header_size.physical_size - | v_offset -= mstream->header_size.virtual_size; | |||
mstream->header_size.virtual_size; | v_offset += mstream->header_size.physical_size; | |||
i_stream_seek(stream->parent, | i_stream_seek(stream->parent, | |||
stream->parent_start_offset + v_offset); | stream->parent_start_offset + v_offset); | |||
} | } | |||
} | } | |||
static void ATTR_NORETURN | static void ATTR_NORETURN | |||
i_stream_header_filter_sync(struct istream_private *stream ATTR_UNUSED) | i_stream_header_filter_sync(struct istream_private *stream ATTR_UNUSED) | |||
{ | { | |||
i_panic("istream-header-filter sync() not implemented"); | i_panic("istream-header-filter sync() not implemented"); | |||
} | } | |||
skipping to change at line 620 | skipping to change at line 654 | |||
stream->statbuf.st_size += ret; | stream->statbuf.st_size += ret; | |||
} | } | |||
stream->statbuf.st_size -= | stream->statbuf.st_size -= | |||
(off_t)mstream->header_size.physical_size - | (off_t)mstream->header_size.physical_size - | |||
(off_t)mstream->header_size.virtual_size; | (off_t)mstream->header_size.virtual_size; | |||
i_stream_seek(&stream->istream, old_offset); | i_stream_seek(&stream->istream, old_offset); | |||
return 0; | return 0; | |||
} | } | |||
static void | ||||
i_stream_header_filter_snapshot_free(struct istream_snapshot *_snapshot) | ||||
{ | ||||
struct header_filter_istream_snapshot *snapshot = | ||||
container_of(_snapshot, struct header_filter_istream_snapshot, sn | ||||
apshot); | ||||
if (snapshot->mstream->hdr_buf != snapshot->hdr_buf) | ||||
buffer_free(&snapshot->hdr_buf); | ||||
else { | ||||
i_assert(snapshot->mstream->snapshot_pending); | ||||
snapshot->mstream->snapshot_pending = FALSE; | ||||
} | ||||
i_free(snapshot); | ||||
} | ||||
static struct istream_snapshot * | ||||
i_stream_header_filter_snapshot(struct istream_private *stream, | ||||
struct istream_snapshot *prev_snapshot) | ||||
{ | ||||
struct header_filter_istream *mstream = | ||||
(struct header_filter_istream *)stream; | ||||
struct header_filter_istream_snapshot *snapshot; | ||||
if (stream->buffer != mstream->hdr_buf->data) { | ||||
/* reading body */ | ||||
return i_stream_default_snapshot(stream, prev_snapshot); | ||||
} | ||||
/* snapshot the header buffer */ | ||||
snapshot = i_new(struct header_filter_istream_snapshot, 1); | ||||
snapshot->mstream = mstream; | ||||
snapshot->hdr_buf = mstream->hdr_buf; | ||||
snapshot->snapshot.free = i_stream_header_filter_snapshot_free; | ||||
snapshot->snapshot.prev_snapshot = prev_snapshot; | ||||
mstream->snapshot_pending = TRUE; | ||||
return &snapshot->snapshot; | ||||
} | ||||
#undef i_stream_create_header_filter | #undef i_stream_create_header_filter | |||
struct istream * | struct istream * | |||
i_stream_create_header_filter(struct istream *input, | i_stream_create_header_filter(struct istream *input, | |||
enum header_filter_flags flags, | enum header_filter_flags flags, | |||
const char *const *headers, | const char *const *headers, | |||
unsigned int headers_count, | unsigned int headers_count, | |||
header_filter_callback *callback, void *context) | header_filter_callback *callback, void *context) | |||
{ | { | |||
struct header_filter_istream *mstream; | struct header_filter_istream *mstream; | |||
unsigned int i, j; | unsigned int i, j; | |||
int ret; | int ret; | |||
i_assert((flags & (HEADER_FILTER_INCLUDE|HEADER_FILTER_EXCLUDE)) != 0); | i_assert((flags & (HEADER_FILTER_INCLUDE|HEADER_FILTER_EXCLUDE)) != 0); | |||
mstream = i_new(struct header_filter_istream, 1); | mstream = i_new(struct header_filter_istream, 1); | |||
mstream->pool = pool_alloconly_create(MEMPOOL_GROWING | mstream->pool = pool_alloconly_create(MEMPOOL_GROWING | |||
"header filter stream", 4096); | "header filter stream", 256); | |||
mstream->istream.max_buffer_size = input->real_stream->max_buffer_size; | mstream->istream.max_buffer_size = input->real_stream->max_buffer_size; | |||
mstream->headers = headers_count == 0 ? NULL : | mstream->headers = headers_count == 0 ? NULL : | |||
p_new(mstream->pool, const char *, headers_count); | p_new(mstream->pool, const char *, headers_count); | |||
for (i = j = 0; i < headers_count; i++) { | for (i = j = 0; i < headers_count; i++) { | |||
ret = j == 0 ? -1 : | ret = j == 0 ? -1 : | |||
strcasecmp(mstream->headers[j-1], headers[i]); | strcasecmp(mstream->headers[j-1], headers[i]); | |||
if (ret == 0) { | if (ret == 0) { | |||
/* drop duplicate */ | /* drop duplicate */ | |||
continue; | continue; | |||
} | } | |||
i_assert(ret < 0); | i_assert(ret < 0); | |||
mstream->headers[j++] = p_strdup(mstream->pool, headers[i]); | mstream->headers[j++] = p_strdup(mstream->pool, headers[i]); | |||
} | } | |||
mstream->headers_count = j; | mstream->headers_count = j; | |||
mstream->hdr_buf = buffer_create_dynamic(mstream->pool, 1024); | mstream->hdr_buf = buffer_create_dynamic(default_pool, 1024); | |||
mstream->callback = callback; | mstream->callback = callback; | |||
mstream->context = context; | mstream->context = context; | |||
mstream->exclude = (flags & HEADER_FILTER_EXCLUDE) != 0; | mstream->exclude = (flags & HEADER_FILTER_EXCLUDE) != 0; | |||
if ((flags & HEADER_FILTER_CRLF_PRESERVE) != 0) | if ((flags & HEADER_FILTER_CRLF_PRESERVE) != 0) | |||
mstream->crlf_preserve = TRUE; | mstream->crlf_preserve = TRUE; | |||
else if ((flags & HEADER_FILTER_NO_CR) != 0) | else if ((flags & HEADER_FILTER_NO_CR) != 0) | |||
mstream->crlf = FALSE; | mstream->crlf = FALSE; | |||
else | else | |||
mstream->crlf = TRUE; | mstream->crlf = TRUE; | |||
skipping to change at line 675 | skipping to change at line 747 | |||
mstream->end_body_with_lf = | mstream->end_body_with_lf = | |||
(flags & HEADER_FILTER_END_BODY_WITH_LF) != 0; | (flags & HEADER_FILTER_END_BODY_WITH_LF) != 0; | |||
mstream->last_lf_offset = UOFF_T_MAX; | mstream->last_lf_offset = UOFF_T_MAX; | |||
mstream->last_added_newline = TRUE; | mstream->last_added_newline = TRUE; | |||
mstream->istream.iostream.destroy = i_stream_header_filter_destroy; | mstream->istream.iostream.destroy = i_stream_header_filter_destroy; | |||
mstream->istream.read = i_stream_header_filter_read; | mstream->istream.read = i_stream_header_filter_read; | |||
mstream->istream.seek = i_stream_header_filter_seek; | mstream->istream.seek = i_stream_header_filter_seek; | |||
mstream->istream.sync = i_stream_header_filter_sync; | mstream->istream.sync = i_stream_header_filter_sync; | |||
mstream->istream.stat = i_stream_header_filter_stat; | mstream->istream.stat = i_stream_header_filter_stat; | |||
mstream->istream.snapshot = i_stream_header_filter_snapshot; | ||||
mstream->istream.istream.readable_fd = FALSE; | mstream->istream.istream.readable_fd = FALSE; | |||
mstream->istream.istream.blocking = input->blocking; | mstream->istream.istream.blocking = input->blocking; | |||
mstream->istream.istream.seekable = input->seekable; | mstream->istream.istream.seekable = input->seekable; | |||
return i_stream_create(&mstream->istream, input, -1, 0); | return i_stream_create(&mstream->istream, input, -1, 0); | |||
} | } | |||
void i_stream_header_filter_add(struct header_filter_istream *input, | void i_stream_header_filter_add(struct header_filter_istream *input, | |||
const void *data, size_t size) | const void *data, size_t size) | |||
{ | { | |||
hdr_buf_realloc_if_needed(input); | ||||
buffer_append(input->hdr_buf, data, size); | buffer_append(input->hdr_buf, data, size); | |||
input->headers_edited = TRUE; | input->headers_edited = TRUE; | |||
} | } | |||
End of changes. 14 change blocks. | ||||
4 lines changed or deleted | 79 lines changed or added |