bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainenheader_filter_callback *null_header_filter_callback = NULL;
11b7d7a86d6e6d945ade461c5b1280b8a0825f61Timo Sirainenstatic ssize_t i_stream_header_filter_read(struct istream_private *stream);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void i_stream_header_filter_destroy(struct iostream_private *stream)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen message_parse_header_deinit(&mstream->hdr_ctx);
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen if (array_is_created(&mstream->match_change_lines))
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainenread_mixed(struct header_filter_istream *mstream, size_t body_highwater_size)
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen data = i_stream_get_data(mstream->istream.parent, &pos);
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen ret = i_stream_read_memarea(mstream->istream.parent);
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen mstream->istream.istream.eof = mstream->istream.parent->eof;
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen if (mstream->end_body_with_lf && data[pos-1] != '\n' &&
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen /* add missing trailing LF to body */
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen mstream->istream.buffer = mstream->hdr_buf->data;
b88c43d09a288e99d439c78de4cc613212ea924cTimo Sirainen mstream->istream.pos = mstream->hdr_buf->used;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen data = i_stream_get_data(mstream->istream.parent, &pos);
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen buffer_append(mstream->hdr_buf, data + body_highwater_size,
c2afdbf963d0564a542d71ca784deb0c2f7776d0Timo Sirainen mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos);
c2afdbf963d0564a542d71ca784deb0c2f7776d0Timo Sirainen ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip);
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainenstatic int cmp_uint(const unsigned int *i1, const unsigned int *i2)
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainenstatic bool match_line_changed(struct header_filter_istream *mstream)
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen if (!array_is_created(&mstream->match_change_lines))
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainen return array_bsearch(&mstream->match_change_lines, &mstream->cur_line,
3818c2a96699d09f65b880df6e7962570a387eacTimo Sirainenstatic void add_eol(struct header_filter_istream *mstream, bool orig_crlf)
3818c2a96699d09f65b880df6e7962570a387eacTimo Sirainen if (mstream->crlf || (orig_crlf && mstream->crlf_preserve))
3c07b1481e4e1c2de64a3ae3c07bc7ac6d9def65Timo Sirainenstatic ssize_t hdr_stream_update_pos(struct header_filter_istream *mstream)
3c07b1481e4e1c2de64a3ae3c07bc7ac6d9def65Timo Sirainen mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos);
3c07b1481e4e1c2de64a3ae3c07bc7ac6d9def65Timo Sirainen ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip);
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainenstatic ssize_t read_header(struct header_filter_istream *mstream)
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen message_parse_header_init(mstream->istream.parent,
c2afdbf963d0564a542d71ca784deb0c2f7776d0Timo Sirainen /* remove skipped data from hdr_buf */
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen mstream->hdr_buf, mstream->istream.skip, (size_t)-1);
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen mstream->istream.pos -= mstream->istream.skip;
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen buffer_set_used_size(mstream->hdr_buf, mstream->istream.pos);
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen highwater_offset = mstream->istream.istream.v_offset +
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen if (highwater_offset >= mstream->header_size.virtual_size) {
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen /* we want to return mixed headers and body */
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen size_t body_highwater_size = highwater_offset -
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen return read_mixed(mstream, body_highwater_size);
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen max_buffer_size = i_stream_get_max_buffer_size(&mstream->istream.istream);
fdf15b2c4fd47078108f556a58c6b9f365449f9eTimo Sirainen if (mstream->hdr_buf->used >= max_buffer_size) {
5a2917dd7248d61c540dd310627a781934ba5334Timo Sirainen while ((hdr_ret = message_parse_header_next(mstream->hdr_ctx,
bf5682ed5399fce13b2053e20098e2de19751c59Timo Sirainen if (mstream->header_parsed && !mstream->headers_edited) {
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen /* Header line continued - use only the first line's
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen matched-result. Otherwise multiline headers might
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen end up being only partially picked, which wouldn't
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen be very good. However, allow callbacks to modify
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen the headers in any way they want. */
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen /* no include/exclude headers - default matching */
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen matched = i_bsearch(hdr->name, mstream->headers,
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen /* nothing gets excluded */
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen } else if (!mstream->header_parsed || mstream->headers_edited) {
82705acc82d625a60146b72b9296146dd8312261Timo Sirainen /* first time in this line or we have actually modified
82705acc82d625a60146b72b9296146dd8312261Timo Sirainen the header so we always want to call the callbacks */
270c7cd669b1ad4b69edb366dc5f22262948bfedTimo Sirainen !hdr->continued && !mstream->headers_edited) {
0989e8ba44ec35dc9322e424b5213b96596319e7Timo Sirainen if (!array_is_created(&mstream->match_change_lines))
0989e8ba44ec35dc9322e424b5213b96596319e7Timo Sirainen i_array_init(&mstream->match_change_lines, 8);
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen /* second time in this line. was it excluded by the
e59cc428c2e2233adedba549a556b83a01ce809eTimo Sirainen callback the first time? */
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen if (mstream->skip_count >= mstream->hdr_buf->used) {
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen /* we need more */
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen mstream->skip_count -= mstream->hdr_buf->used;
0928812e725cd3a4debab2a93d0c9b0436a4de9fTimo Sirainen if (mstream->hdr_buf->used >= max_buffer_size)
10d2dbb8343d9f7a294519224e5e08e1c13e7453Timo Sirainen const unsigned char *data = mstream->hdr_buf->data;
3b5028747f195bca08751015e5b5a28cd5f4f8d4Timo Sirainen if (mstream->istream.parent->stream_errno != 0) {
3b5028747f195bca08751015e5b5a28cd5f4f8d4Timo Sirainen if (!mstream->seen_eoh && mstream->add_missing_eoh) {
201ce62e1dc315e5dd5e59c24660ed0946343bbbTimo Sirainen if (mstream->header_parsed && !mstream->headers_edited) {
69eb1a17f443454e0b3025f86f428671ace06e89Josef 'Jeff' Sipek struct message_header_line fake_eoh_hdr = {
69eb1a17f443454e0b3025f86f428671ace06e89Josef 'Jeff' Sipek mstream->callback(mstream, &fake_eoh_hdr,
69eb1a17f443454e0b3025f86f428671ace06e89Josef 'Jeff' Sipek add_eol(mstream, mstream->last_orig_crlf);
395458c963dbf061a8cc167646e01d564bb92050Timo Sirainen /* don't copy eof here because we're only returning headers here.
395458c963dbf061a8cc167646e01d564bb92050Timo Sirainen the body will be returned in separate read() call. */
acbb7e67acebcda7ed12eb3e0e02e1d799f24e3aTimo Sirainen /* need more data to finish parsing headers. we may have some
acbb7e67acebcda7ed12eb3e0e02e1d799f24e3aTimo Sirainen data already available though. */
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen /* finished */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen message_parse_header_deinit(&mstream->hdr_ctx);
71ecc97593a25c2444316b74e5882e6ce0a82c0cTimo Sirainen if ((!mstream->header_parsed || mstream->headers_edited ||
3c07b1481e4e1c2de64a3ae3c07bc7ac6d9def65Timo Sirainen /* check if the callback added more headers.
71ecc97593a25c2444316b74e5882e6ce0a82c0cTimo Sirainen this is allowed only if EOH wasn't added yet. */
b335d3864f1134a100be84105d4c56be161da4d0Timo Sirainen /* we're at the end of headers. */
11b7d7a86d6e6d945ade461c5b1280b8a0825f61Timo Sirainen return i_stream_header_filter_read(&mstream->istream);
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainenhandle_end_body_with_lf(struct header_filter_istream *mstream, ssize_t ret)
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen struct istream_private *stream = &mstream->istream;
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen data = i_stream_get_data(stream->parent, &size);
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen last_offset = stream->parent->v_offset + size-1;
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen else if (size > 0)
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen if (ret == -1 && stream->parent->eof && !last_lf) {
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen /* missing LF, need to add it */
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen mstream->last_lf_offset = last_lf ? last_offset : (uoff_t)-1;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic ssize_t i_stream_header_filter_read(struct istream_private *stream)
fdf15b2c4fd47078108f556a58c6b9f365449f9eTimo Sirainen stream->istream.v_offset < mstream->header_size.virtual_size)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen v_offset = stream->parent_start_offset + stream->istream.v_offset -
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen ret = i_stream_read_copy_from_parent(&stream->istream);
4334b9b032298defd4d3906f5357698ff016ead0Timo Siraineni_stream_header_filter_seek_to_header(struct header_filter_istream *mstream,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen mstream->istream.parent->real_stream->access_counter;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen message_parse_header_deinit(&mstream->hdr_ctx);
2739e568eb2f33f6219d97261c94e67b68da2794Timo Sirainenstatic int skip_header(struct header_filter_istream *mstream)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen mstream->istream.parent->real_stream->access_counter) {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* need to re-parse headers */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen i_stream_header_filter_seek_to_header(mstream, 0);
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen i_stream_read_memarea(&mstream->istream.istream) != -1) {
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen pos = i_stream_get_data_size(&mstream->istream.istream);
fff56e138fc28fd6fa141122ac768162d6f7e4d1Timo Sirainen i_stream_skip(&mstream->istream.istream, pos);
2739e568eb2f33f6219d97261c94e67b68da2794Timo Sirainen return mstream->istream.istream.stream_errno != 0 ? -1 : 0;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenstream_reset_to(struct header_filter_istream *mstream, uoff_t v_offset)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen mstream->istream.skip = mstream->istream.pos = 0;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void i_stream_header_filter_seek(struct istream_private *stream,
9a1a2583687a5bd9d47bdfd435c78b46ca5d5af6Timo Sirainen /* just reset the input buffer */
33a8963217d05d0a36739c59dfc71bd0c51d6f67Timo Sirainen /* if last_lf_added=TRUE, we're currently at EOF. So reset it only if
33a8963217d05d0a36739c59dfc71bd0c51d6f67Timo Sirainen we're seeking backwards, otherwise we would just add a duplicate */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* seeking to beginning of headers. */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen i_stream_header_filter_seek_to_header(mstream, 0);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* if we haven't parsed the whole header yet, we don't know if we
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen want to seek inside header or body. so make sure we've parsed the
b82e145e384466f60dda7e349505e1092938345fTimo Sirainen if (v_offset < mstream->header_size.virtual_size) {
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen /* seek into headers. we'll have to re-parse them, use
a72355fef49c512c13b7d8c3e07f4f76ebd57a10Timo Sirainen skip_count to set the wanted position */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen i_stream_header_filter_seek_to_header(mstream, v_offset);
b82e145e384466f60dda7e349505e1092938345fTimo Sirainen v_offset += mstream->header_size.physical_size -
a94936bafd127680184da114c6a177b37ff656e5Timo Siraineni_stream_header_filter_sync(struct istream_private *stream ATTR_UNUSED)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen i_panic("istream-header-filter sync() not implemented");
a94936bafd127680184da114c6a177b37ff656e5Timo Siraineni_stream_header_filter_stat(struct istream_private *stream, bool exact)
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen if (i_stream_stat(stream->parent, exact, &st) < 0) {
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen /* fix the filtered header size */
32e3da4a505b3b258d44abdf4366fbfc95680027Timo Sirainen /* no body */
32e3da4a505b3b258d44abdf4366fbfc95680027Timo Sirainen stream->statbuf.st_size = mstream->header_size.physical_size;
c8e34785671d3b13f4500144a2ec7af968ce3307Timo Sirainen /* no last-LF */
c8e34785671d3b13f4500144a2ec7af968ce3307Timo Sirainen /* yes, we have added LF */
c8e34785671d3b13f4500144a2ec7af968ce3307Timo Sirainen stream->statbuf.st_size += mstream->crlf ? 2 : 1;
c8e34785671d3b13f4500144a2ec7af968ce3307Timo Sirainen } else if (mstream->last_lf_offset != (uoff_t)-1) {
c8e34785671d3b13f4500144a2ec7af968ce3307Timo Sirainen /* no, we didn't need to add LF */
c8e34785671d3b13f4500144a2ec7af968ce3307Timo Sirainen /* check if we need to add LF */
c8e34785671d3b13f4500144a2ec7af968ce3307Timo Sirainen i_stream_seek(stream->parent, st->st_size - 1);
c8e34785671d3b13f4500144a2ec7af968ce3307Timo Sirainen ssize_t ret = handle_end_body_with_lf(mstream, -1);
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Siraineni_stream_create_header_filter(struct istream *input,
9d3ccd79130199ffdb19a688027d49bf20a4aaaaTimo Sirainen const char *const *headers,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen header_filter_callback *callback, void *context)
b32c01699fa9cb0f3ceb73e3618d762b36f2428fTimo Sirainen unsigned int i, j;
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Sirainen i_assert((flags & (HEADER_FILTER_INCLUDE|HEADER_FILTER_EXCLUDE)) != 0);
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen mstream = i_new(struct header_filter_istream, 1);
221351ed85c839e0b03d82c47654c3d17202e3dbTimo Sirainen mstream->pool = pool_alloconly_create(MEMPOOL_GROWING
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen mstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
7122ddeac635cdc896ecc8093dad2e942115a98aTimo Sirainen mstream->headers = headers_count == 0 ? NULL :
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen p_new(mstream->pool, const char *, headers_count);
b32c01699fa9cb0f3ceb73e3618d762b36f2428fTimo Sirainen for (i = j = 0; i < headers_count; i++) {
b2b2b63e233f66aa7c115217a876cf3310baa107Timo Sirainen strcasecmp(mstream->headers[j-1], headers[i]);
b32c01699fa9cb0f3ceb73e3618d762b36f2428fTimo Sirainen /* drop duplicate */
b32c01699fa9cb0f3ceb73e3618d762b36f2428fTimo Sirainen mstream->headers[j++] = p_strdup(mstream->pool, headers[i]);
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen mstream->hdr_buf = buffer_create_dynamic(mstream->pool, 1024);
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Sirainen mstream->exclude = (flags & HEADER_FILTER_EXCLUDE) != 0;
3818c2a96699d09f65b880df6e7962570a387eacTimo Sirainen if ((flags & HEADER_FILTER_CRLF_PRESERVE) != 0)
185ed0142fbbfb86e7a98519e7c6f11ec00723cdTimo Sirainen mstream->hide_body = (flags & HEADER_FILTER_HIDE_BODY) != 0;
aeed83649fbeef2cba51fd909635c4463ee642e7Timo Sirainen mstream->add_missing_eoh = (flags & HEADER_FILTER_ADD_MISSING_EOH) != 0;
ef3ebb56989cb9b1b80edd133a091338e4206836Timo Sirainen (flags & HEADER_FILTER_END_BODY_WITH_LF) != 0;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen mstream->istream.iostream.destroy = i_stream_header_filter_destroy;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen mstream->istream.read = i_stream_header_filter_read;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen mstream->istream.seek = i_stream_header_filter_seek;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen mstream->istream.sync = i_stream_header_filter_sync;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen mstream->istream.stat = i_stream_header_filter_stat;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen mstream->istream.istream.blocking = input->blocking;
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen mstream->istream.istream.seekable = input->seekable;
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen return i_stream_create(&mstream->istream, input, -1, 0);