istream-header-filter.c revision adb90447f6bf7b11b5fca7e87a3f256622fdef9f
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2003-2016 Dovecot authors, see the included COPYING file */
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen const char **headers;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenheader_filter_callback *null_header_filter_callback = NULL;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenstatic ssize_t i_stream_header_filter_read(struct istream_private *stream);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic void i_stream_header_filter_destroy(struct iostream_private *stream)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen message_parse_header_deinit(&mstream->hdr_ctx);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (array_is_created(&mstream->match_change_lines))
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainenread_mixed(struct header_filter_istream *mstream, size_t body_highwater_size)
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen const unsigned char *data;
9847ec56efa15fa063eea9988eee2d4ed9ec7d58Timo Sirainen data = i_stream_get_data(mstream->istream.parent, &pos);
291ce16fffca75e8598a8c9dceb08613413dcb07Timo Sirainen mstream->istream.istream.eof = mstream->istream.parent->eof;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen if (mstream->end_body_with_lf && data[pos-1] != '\n' &&
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* add missing trailing LF to body */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen data = i_stream_get_data(mstream->istream.parent, &pos);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen buffer_append(mstream->hdr_buf, data + body_highwater_size,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip);
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainenstatic int cmp_uint(const unsigned int *i1, const unsigned int *i2)
7900eb30bac4a46b259522c58362884661483d7cJosef 'Jeff' Sipekstatic bool match_line_changed(struct header_filter_istream *mstream)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (!array_is_created(&mstream->match_change_lines))
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainen return array_bsearch(&mstream->match_change_lines, &mstream->cur_line,
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainenstatic void add_eol(struct header_filter_istream *mstream, bool orig_crlf)
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen if (mstream->crlf || (orig_crlf && mstream->crlf_preserve))
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenstatic ssize_t hdr_stream_update_pos(struct header_filter_istream *mstream)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mstream->istream.buffer = buffer_get_data(mstream->hdr_buf, &pos);
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen ret = (ssize_t)(pos - mstream->istream.pos - mstream->istream.skip);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic ssize_t read_header(struct header_filter_istream *mstream)
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen message_parse_header_init(mstream->istream.parent,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen /* remove skipped data from hdr_buf */
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen mstream->hdr_buf, mstream->istream.skip, (size_t)-1);
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen mstream->istream.pos -= mstream->istream.skip;
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen buffer_set_used_size(mstream->hdr_buf, mstream->istream.pos);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen highwater_offset = mstream->istream.istream.v_offset +
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (highwater_offset >= mstream->header_size.virtual_size) {
cff1f182205e674285cf3ff446a0dcf7afea277dTimo Sirainen /* we want to return mixed headers and body */
cff1f182205e674285cf3ff446a0dcf7afea277dTimo Sirainen size_t body_highwater_size = highwater_offset -
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen return read_mixed(mstream, body_highwater_size);
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen while ((hdr_ret = message_parse_header_next(mstream->hdr_ctx,
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen matched = mstream->headers_count == 0 ? FALSE :
aa47c9bd1d1fc70cd699c49fd1ca92dbc7615953Timo Sirainen /* nothing gets excluded */
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen } else if (mstream->cur_line > mstream->parsed_lines ||
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen /* first time in this line or we have actually modified
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen the header so we always want to call the callbacks */
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen if (!array_is_created(&mstream->match_change_lines))
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_array_init(&mstream->match_change_lines, 8);
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen /* second time in this line. was it excluded by the
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen callback the first time? */
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen if (mstream->skip_count >= mstream->hdr_buf->used) {
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen /* we need more */
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen mstream->skip_count -= mstream->hdr_buf->used;
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen if (mstream->istream.parent->stream_errno != 0) {
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen if (!mstream->seen_eoh && mstream->add_missing_eoh) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* don't copy eof here because we're only returning headers here.
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen the body will be returned in separate read() call. */
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen /* need more data to finish parsing headers. we may have some
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen data already available though. */
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen /* finished */
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen message_parse_header_deinit(&mstream->hdr_ctx);
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen if (!mstream->header_parsed && mstream->callback != NULL) {
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen /* check if the callback added more headers.
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen this is allowed only of EOH wasn't added yet. */
eecb235c14b49c01774134ea593c266f2d2c2be1Timo Sirainen /* we're at the end of headers. */
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen return i_stream_header_filter_read(&mstream->istream);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainenhandle_end_body_with_lf(struct header_filter_istream *mstream, ssize_t ret)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen struct istream_private *stream = &mstream->istream;
546335814920fb6b5b44c68c7803e654eefeae9dTimo Sirainen const unsigned char *data;
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen data = i_stream_get_data(stream->parent, &size);
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen last_offset = stream->parent->v_offset + size-1;
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen else if (size > 0)
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen if (ret == -1 && stream->parent->eof && !last_lf) {
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen /* missing LF, need to add it */
7e2671b295927b461adc8b6c4ed6a1c4761fb323Timo Sirainen mstream->last_lf_offset = last_lf ? last_offset : (uoff_t)-1;
fcfe85637e1ee14a9fc39c41fd6ceca106301542Timo Sirainenstatic ssize_t i_stream_header_filter_read(struct istream_private *stream)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen stream->istream.v_offset < mstream->header_size.virtual_size) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen v_offset = stream->parent_start_offset + stream->istream.v_offset -
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen ret = i_stream_read_copy_from_parent(&stream->istream);
d81131d3bbb4f0befb62a661d1785cf8c84a17e2Timo Siraineni_stream_header_filter_seek_to_header(struct header_filter_istream *mstream,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mstream->istream.parent->real_stream->access_counter;
0d86aa0d47f7393c669c084b34c0537b193688adTimo Sirainen message_parse_header_deinit(&mstream->hdr_ctx);
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainenstatic void skip_header(struct header_filter_istream *mstream)
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen mstream->istream.parent->real_stream->access_counter) {
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen /* need to re-parse headers */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_stream_header_filter_seek_to_header(mstream, 0);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_stream_read(&mstream->istream.istream) != -1) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen pos = i_stream_get_data_size(&mstream->istream.istream);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_stream_skip(&mstream->istream.istream, pos);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstream_reset_to(struct header_filter_istream *mstream, uoff_t v_offset)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen mstream->istream.skip = mstream->istream.pos = 0;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic void i_stream_header_filter_seek(struct istream_private *stream,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* just reset the input buffer */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* if last_lf_added=TRUE, we're currently at EOF. So reset it only if
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen we're seeking backwards, otherwise we would just add a duplicate */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* seeking to beginning of headers. */
0206dc57f2c04da69599dea5816235cfeb2b897aMartti Rannanjärvi i_stream_header_filter_seek_to_header(mstream, 0);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* if we haven't parsed the whole header yet, we don't know if we
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen want to seek inside header or body. so make sure we've parsed the
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (v_offset < mstream->header_size.virtual_size) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* seek into headers. we'll have to re-parse them, use
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen skip_count to set the wanted position */
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen i_stream_header_filter_seek_to_header(mstream, v_offset);
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen v_offset += mstream->header_size.physical_size -
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Siraineni_stream_header_filter_sync(struct istream_private *stream ATTR_UNUSED)
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen i_panic("istream-header-filter sync() not implemented");
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Siraineni_stream_header_filter_stat(struct istream_private *stream, bool exact)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (i_stream_stat(stream->parent, exact, &st) < 0) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen /* fix the filtered header size */
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Siraineni_stream_create_header_filter(struct istream *input,
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen const char *const *headers,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen header_filter_callback *callback, void *context)
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen unsigned int i, j;
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen i_assert((flags & (HEADER_FILTER_INCLUDE|HEADER_FILTER_EXCLUDE)) != 0);
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainen mstream = i_new(struct header_filter_istream, 1);
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen mstream->pool = pool_alloconly_create(MEMPOOL_GROWING
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen mstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen mstream->headers = headers_count == 0 ? NULL :
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen p_new(mstream->pool, const char *, headers_count);
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen for (i = j = 0; i < headers_count; i++) {
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen strcasecmp(mstream->headers[j-1], headers[i]);
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen /* drop duplicate */
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen mstream->headers[j++] = p_strdup(mstream->pool, headers[i]);
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen mstream->hdr_buf = buffer_create_dynamic(mstream->pool, 1024);
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen mstream->exclude = (flags & HEADER_FILTER_EXCLUDE) != 0;
1eaaa2c9003cf3fbf672d597473e3f84e70d2ee6Timo Sirainen if ((flags & HEADER_FILTER_CRLF_PRESERVE) != 0)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen mstream->hide_body = (flags & HEADER_FILTER_HIDE_BODY) != 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen mstream->add_missing_eoh = (flags & HEADER_FILTER_ADD_MISSING_EOH) != 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen (flags & HEADER_FILTER_END_BODY_WITH_LF) != 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mstream->istream.iostream.destroy = i_stream_header_filter_destroy;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mstream->istream.read = i_stream_header_filter_read;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mstream->istream.seek = i_stream_header_filter_seek;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen mstream->istream.sync = i_stream_header_filter_sync;
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen mstream->istream.stat = i_stream_header_filter_stat;
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen mstream->istream.istream.blocking = input->blocking;
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen mstream->istream.istream.seekable = input->seekable;
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen return i_stream_create(&mstream->istream, input, -1);