test-istream-header-filter.c revision d4488f314d0c799c5f5b8a7890e17eef868f7a2c
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "str.h"
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen#include "istream.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "message-header-parser.h"
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen#include "istream-header-filter.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "test-common.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstruct run_ctx {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen header_filter_callback *callback;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen bool null_hdr_seen;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen bool callback_called;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen};
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainenstatic void run_callback(struct header_filter_istream *input,
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen struct message_header_line *hdr,
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen bool *matched, struct run_ctx *ctx)
c1faff067b29fb48426cb84260adba563e93189aTimo Sirainen{
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen if (hdr == NULL)
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen ctx->null_hdr_seen = TRUE;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen if (ctx->callback != NULL)
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen ctx->callback(input, hdr, matched, NULL);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen ctx->callback_called = TRUE;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen}
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainenstatic void
ef2b2ef2e6a6eb5e4667f2e63faae8a3b646e8baTimo Sirainentest_istream_run(struct istream *test_istream,
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen unsigned int input_len, const char *output,
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen enum header_filter_flags flags,
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen header_filter_callback *callback)
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen{
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen struct run_ctx run_ctx = { .callback = callback, .null_hdr_seen = FALSE };
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen struct istream *filter;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen unsigned int i, output_len = strlen(output);
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen const struct stat *st;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const unsigned char *data;
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen size_t size;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter = i_stream_create_header_filter(test_istream, flags, NULL, 0,
f162f350e1eacce12288a565657e769d7976dd7fTimo Sirainen run_callback, &run_ctx);
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen for (i = 1; i < input_len; i++) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_istream_set_size(test_istream, i);
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen test_assert(i_stream_read(filter) >= 0);
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_istream_set_size(test_istream, input_len);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_assert(i_stream_read(filter) > 0);
f162f350e1eacce12288a565657e769d7976dd7fTimo Sirainen test_assert(i_stream_read(filter) == -1);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
4ea6c43a08b37f270bd54b5809142246fd118263Timo Sirainen test_assert(run_ctx.null_hdr_seen);
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen run_ctx.null_hdr_seen = FALSE;
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen run_ctx.callback_called = FALSE;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainen data = i_stream_get_data(filter, &size);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_assert(size == output_len && memcmp(data, output, size) == 0);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* run again to make sure it's still correct the second time */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_skip(filter, size);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_seek(filter, 0);
4ea6c43a08b37f270bd54b5809142246fd118263Timo Sirainen while (i_stream_read(filter) > 0) ;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_assert(run_ctx.null_hdr_seen == run_ctx.callback_called);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen data = i_stream_get_data(filter, &size);
bffaca7da2825c2a77aa46022358e2f8400a0f94Timo Sirainen test_assert(size == output_len && memcmp(data, output, size) == 0);
f162f350e1eacce12288a565657e769d7976dd7fTimo Sirainen test_assert(i_stream_stat(filter, TRUE, &st) == 0 &&
f162f350e1eacce12288a565657e769d7976dd7fTimo Sirainen (uoff_t)st->st_size == size);
bffaca7da2825c2a77aa46022358e2f8400a0f94Timo Sirainen i_stream_unref(&filter);
bffaca7da2825c2a77aa46022358e2f8400a0f94Timo Sirainen}
f162f350e1eacce12288a565657e769d7976dd7fTimo Sirainen
2e07e3182f355cf04a1461dd7f893d0ebc818764Timo Sirainenstatic void ATTR_NULL(3)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenfilter_callback(struct header_filter_istream *input ATTR_UNUSED,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct message_header_line *hdr,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool *matched, void *context ATTR_UNUSED)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen if (hdr != NULL && (hdr->name_offset == 0 ||
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen strcmp(hdr->name, "X-Drop") == 0)) {
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen /* drop 1) first header, 2) X-Drop header */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen *matched = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
7d87a87b360ecac47fe10e7ca5c7e1433dd63004Timo Sirainen
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainenstatic void test_istream_filter(void)
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen static const char *exclude_headers[] = { "Subject", "To" };
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const char *input = "From: foo\nFrom: abc\nTo: bar\nSubject: plop\nX-Drop: 1\n\nhello world\n";
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const char *output = "From: abc\n\nhello world\n";
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct istream *istream, *filter, *filter2;
7d87a87b360ecac47fe10e7ca5c7e1433dd63004Timo Sirainen unsigned int i, input_len = strlen(input);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned int output_len = strlen(output);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen const unsigned char *data;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const struct stat *st;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen size_t size;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
ef2b2ef2e6a6eb5e4667f2e63faae8a3b646e8baTimo Sirainen test_begin("i_stream_create_header_filter: exclude");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen istream = test_istream_create(input);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter = i_stream_create_header_filter(istream,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen HEADER_FILTER_EXCLUDE |
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen HEADER_FILTER_NO_CR,
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen exclude_headers,
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen N_ELEMENTS(exclude_headers),
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen filter_callback, (void *)NULL);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter2 = i_stream_create_header_filter(filter,
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen HEADER_FILTER_EXCLUDE |
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen HEADER_FILTER_NO_CR,
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen exclude_headers,
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen N_ELEMENTS(exclude_headers),
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen *null_header_filter_callback,
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen (void *)NULL);
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen i_stream_unref(&filter);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter = filter2;
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen for (i = 1; i < input_len; i++) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_istream_set_size(istream, i);
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen test_assert(i_stream_read(filter) >= 0);
3f7583189249a28892cd5bd69e15681c7a8a619bTimo Sirainen }
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen test_istream_set_size(istream, input_len);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen test_assert(i_stream_read(filter) > 0);
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen test_assert(i_stream_read(filter) == -1);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen data = i_stream_get_data(filter, &size);
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen test_assert(size == output_len && memcmp(data, output, size) == 0);
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen test_assert(i_stream_stat(filter, TRUE, &st) == 0 &&
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen (uoff_t)st->st_size == size);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen i_stream_skip(filter, size);
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen i_stream_seek(filter, 0);
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen while (i_stream_read(filter) > 0) ;
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen data = i_stream_get_data(filter, &size);
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen test_assert(size == output_len && memcmp(data, output, size) == 0);
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen test_assert(i_stream_stat(filter, TRUE, &st) == 0 &&
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen (uoff_t)st->st_size == size);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen i_stream_unref(&filter);
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen i_stream_unref(&istream);
d337f4291d9ea9cd1adee0f468bc0c3baeabb4feTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_end();
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainenstatic void add_random_text(string_t *dest, unsigned int count)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen{
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen unsigned int i;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen for (i = 0; i < count; i++)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen str_append_c(dest, rand() % ('z'-'a'+1) + 'a');
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen}
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainenstatic void ATTR_NULL(3)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainenfilter2_callback(struct header_filter_istream *input ATTR_UNUSED,
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen struct message_header_line *hdr,
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen bool *matched, bool *null_hdr_seen)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen{
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen if (hdr == NULL)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen *null_hdr_seen = TRUE;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen else if (strcmp(hdr->name, "To") == 0)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen *matched = TRUE;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen}
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainenstatic void test_istream_filter_large_buffer(void)
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen{
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen string_t *input, *output;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct istream *istream, *filter;
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen const struct stat *st;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const unsigned char *data;
10ccd0e45768923d69be459e87ef6cd2574cec60Timo Sirainen size_t size, prefix_len;
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen const char *p;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned int i;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen bool null_hdr_seen = FALSE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_begin("i_stream_create_header_filter: large buffer");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen input = str_new(default_pool, 1024*128);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen output = str_new(default_pool, 1024*128);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append(input, "From: ");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen add_random_text(input, 1024*31);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append(input, "\nTo: ");
5a37e34b1b5acf453372cd112c70bb4e46b4bee2Timo Sirainen add_random_text(input, 1024*32);
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen str_append(input, "\nSubject: ");
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen add_random_text(input, 1024*34);
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen str_append(input, "\n\nbody\n");
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen istream = test_istream_create_data(str_data(input), str_len(input));
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen test_istream_set_max_buffer_size(istream, 8192);
f4e66312c54f8f21df984e3b17c0cc752e019ec5Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter = i_stream_create_header_filter(istream,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen HEADER_FILTER_EXCLUDE |
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen HEADER_FILTER_NO_CR,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen NULL, 0,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter2_callback,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen &null_hdr_seen);
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen for (i = 0; i < 2; i++) {
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen for (;;) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ssize_t ret = i_stream_read(filter);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(ret != 0);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (ret == -1)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen break;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (ret == -2) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen data = i_stream_get_data(filter, &size);
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen str_append_n(output, data, size);
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen i_stream_skip(filter, size);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* callbacks are called only once */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_assert(null_hdr_seen == (i == 0));
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen data = i_stream_get_data(filter, &size);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_assert(size <= 8192);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append_n(output, data, size);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen p = strstr(str_c(input), "To: ");
e5dca7edde333c2759b0e6b1d0d00b94ea303322Timo Sirainen i_assert(p != NULL);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen prefix_len = p - str_c(input);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen test_assert(strncmp(str_c(input), str_c(output), prefix_len) == 0);
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen p = strchr(p, '\n');
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen i_assert(p != NULL);
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen test_assert(strcmp(p+1, str_c(output) + prefix_len) == 0);
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_assert(i_stream_stat(filter, TRUE, &st) == 0 &&
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (uoff_t)st->st_size == filter->v_offset + size);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* seek back and retry once with caching and different
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen buffer size */
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen i_stream_seek(filter, 0);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_truncate(output, 0);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen test_istream_set_max_buffer_size(istream, 4096);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen null_hdr_seen = FALSE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_free(&input);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_free(&output);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_unref(&filter);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i_stream_unref(&istream);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen test_end();
2c719bcb92302f45df4badb71d1d97f57235d0ccTimo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic void test_istream_filter_large_buffer2(void)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen static const char *wanted_headers[] = { "References" };
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen string_t *input, *output;
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen struct istream *istream, *filter;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const struct stat *st;
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen const unsigned char *data;
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen size_t size;
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen unsigned int i;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int ret;
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen test_begin("i_stream_create_header_filter: large buffer2");
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen input = str_new(default_pool, 1024*128);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen output = str_new(default_pool, 1024*128);
f8da06de93e28b5d3e039a427cdde7e1e15daec8Timo Sirainen str_append(input, "References: ");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen add_random_text(input, 1024*64);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append(input, "\r\n\r\n");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen istream = test_istream_create_data(str_data(input), str_len(input));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen test_istream_set_max_buffer_size(istream, 8192);
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen filter = i_stream_create_header_filter(istream,
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen HEADER_FILTER_INCLUDE | HEADER_FILTER_HIDE_BODY,
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen wanted_headers, N_ELEMENTS(wanted_headers),
8552b0cad8ffe9ccb8270577ba28b8010c89af11Timo Sirainen *null_header_filter_callback, (void *)NULL);
for (i = 0; i < 2; i++) {
while ((ret = i_stream_read_more(filter, &data, &size)) > 0) {
str_append_n(output, data, size);
i_stream_skip(filter, size);
}
test_assert(ret == -1);
test_assert(filter->stream_errno == 0);
test_assert(strcmp(str_c(input), str_c(output)) == 0);
test_assert(i_stream_stat(filter, TRUE, &st) == 0 &&
(uoff_t)st->st_size == filter->v_offset + size);
/* seek back and retry once with caching and different
buffer size */
i_stream_seek(filter, 0);
str_truncate(output, 0);
test_istream_set_max_buffer_size(istream, 4096);
}
str_free(&input);
str_free(&output);
i_stream_unref(&filter);
i_stream_unref(&istream);
test_end();
}
static void
filter3_callback(struct header_filter_istream *input ATTR_UNUSED,
struct message_header_line *hdr,
bool *matched ATTR_UNUSED, string_t *dest)
{
if (hdr != NULL)
message_header_line_write(dest, hdr);
}
static void test_istream_callbacks(void)
{
string_t *input, *output;
const struct stat *st;
struct istream *istream, *filter;
unsigned int i;
test_begin("i_stream_create_header_filter: callbacks");
input = str_new(default_pool, 1024*128);
output = str_new(default_pool, 1024*128);
str_append(input, "From: first line\n ");
add_random_text(input, 1024*31);
str_append(input, "\nTo: first line\n\tsecond line\n\t");
add_random_text(input, 1024*32);
str_append(input, "\n last line\nSubject: ");
add_random_text(input, 1024*34);
str_append(input, "\n");
istream = test_istream_create_data(str_data(input), str_len(input));
test_istream_set_max_buffer_size(istream, 8192);
filter = i_stream_create_header_filter(istream,
HEADER_FILTER_EXCLUDE |
HEADER_FILTER_NO_CR,
NULL, 0,
filter3_callback,
output);
/* callback should be called exactly once for all the header input */
for (i = 0; i < 2; i++) {
while (i_stream_read(filter) != -1)
i_stream_skip(filter, i_stream_get_data_size(filter));
}
test_assert(i_stream_stat(filter, TRUE, &st) == 0 &&
(uoff_t)st->st_size == str_len(output));
test_assert(strcmp(str_c(output), str_c(input)) == 0);
str_free(&input);
str_free(&output);
i_stream_unref(&filter);
i_stream_unref(&istream);
test_end();
}
static void ATTR_NULL(3)
edit_callback(struct header_filter_istream *input,
struct message_header_line *hdr,
bool *matched, void *context ATTR_UNUSED)
{
if (hdr == NULL)
return;
if (hdr->eoh) {
/* add a new header */
const char *new_hdr = "Added: header\n\n";
i_stream_header_filter_add(input, new_hdr, strlen(new_hdr));
*matched = TRUE;
} else if (strcasecmp(hdr->name, "To") == 0) {
/* modify To header */
const char *new_to = "To: 123\n";
*matched = TRUE;
i_stream_header_filter_add(input, new_to, strlen(new_to));
}
}
static void test_istream_edit(void)
{
const char *input = "From: foo\nTo: bar\n\nhello world\n";
const char *output = "From: foo\nTo: 123\nAdded: header\n\nhello world\n";
struct istream *istream;
test_begin("i_stream_create_header_filter: edit headers");
istream = test_istream_create(input);
test_istream_run(istream, strlen(input), output,
HEADER_FILTER_EXCLUDE |
HEADER_FILTER_NO_CR,
edit_callback);
i_stream_unref(&istream);
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";
const struct stat *st;
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,
(void *)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);
test_assert(i_stream_stat(filter, TRUE, &st) == 0 &&
(uoff_t)st->st_size == filter->v_offset + size);
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);
if (size > 0)
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();
}
static void test_istream_add_missing_eoh(void)
{
struct {
const char *input;
const char *output;
unsigned int extra;
} tests[] = {
{ "From: foo", "From: foo\n\n", 1 },
{ "From: foo\n", "From: foo\n\n", 1 },
{ "From: foo\n\n", "From: foo\n\n", 1 },
{ "From: foo\n\nbar", "From: foo\n\nbar", 0 },
{ "From: foo\r\n", "From: foo\r\n\r\n", 1 },
{ "From: foo\r\n\r\n", "From: foo\r\n\r\n", 0 },
{ "From: foo\r\n\r\nbar", "From: foo\r\n\r\nbar", 0 }
};
struct istream *istream;
unsigned int i;
test_begin("i_stream_create_header_filter: add missing EOH");
for (i = 0; i < N_ELEMENTS(tests); i++) {
istream = test_istream_create(tests[i].input);
test_istream_run(istream,
strlen(tests[i].input) + tests[i].extra,
tests[i].output,
HEADER_FILTER_EXCLUDE |
HEADER_FILTER_CRLF_PRESERVE |
HEADER_FILTER_ADD_MISSING_EOH,
*null_header_filter_callback);
i_stream_unref(&istream);
}
test_end();
}
static void test_istream_hide_body(void)
{
struct {
const char *input;
const char *output;
int extra;
} tests[] = {
{ "From: foo", "From: foo", 0 },
{ "From: foo\n", "From: foo\n", 0 },
{ "From: foo\n\n", "From: foo\n\n", 1 },
{ "From: foo\n\nbar", "From: foo\n\n", -2 },
{ "From: foo\r\n", "From: foo\r\n", 0 },
{ "From: foo\r\n\r\n", "From: foo\r\n\r\n", 0 },
{ "From: foo\r\n\r\nbar", "From: foo\r\n\r\n", -3 }
};
struct istream *istream;
unsigned int i;
test_begin("i_stream_create_header_filter: hide body");
for (i = 0; i < N_ELEMENTS(tests); i++) {
istream = test_istream_create(tests[i].input);
test_istream_run(istream,
strlen(tests[i].input) + tests[i].extra,
tests[i].output,
HEADER_FILTER_EXCLUDE |
HEADER_FILTER_CRLF_PRESERVE |
HEADER_FILTER_HIDE_BODY,
*null_header_filter_callback);
i_stream_unref(&istream);
}
test_end();
}
static void ATTR_NULL(3)
strip_eoh_callback(struct header_filter_istream *input ATTR_UNUSED,
struct message_header_line *hdr,
bool *matched, void *context ATTR_UNUSED)
{
if (hdr != NULL && hdr->eoh)
*matched = TRUE;
}
static void test_istream_strip_eoh(void)
{
const char *input = "From: foo\nTo: bar\n\nhello world\n";
const char *output = "From: foo\nTo: bar\nhello world\n";
struct istream *istream;
test_begin("i_stream_create_header_filter: strip_eoh");
istream = test_istream_create(input);
test_istream_run(istream, strlen(input), output,
HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR,
strip_eoh_callback);
i_stream_unref(&istream);
test_end();
}
static void ATTR_NULL(3)
missing_eoh_callback(struct header_filter_istream *input ATTR_UNUSED,
struct message_header_line *hdr,
bool *matched ATTR_UNUSED, void *context ATTR_UNUSED)
{
if (hdr == NULL) {
const char *new_hdr = "Subject: added\n\n";
i_stream_header_filter_add(input, new_hdr, strlen(new_hdr));
}
}
static void test_istream_missing_eoh_callback(void)
{
const char *input = "From: foo\nTo: bar\n";
const char *output = "From: foo\nTo: bar\nSubject: added\n\n";
struct istream *istream;
test_begin("i_stream_create_header_filter: add headers when EOH is missing");
istream = test_istream_create(input);
test_istream_run(istream, strlen(input) + 1, output,
HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR,
missing_eoh_callback);
i_stream_unref(&istream);
test_end();
}
static void test_istream_empty_missing_eoh_callback(void)
{
const char *input = "";
const char *output = "Subject: added\n\n";
struct istream *istream;
test_begin("i_stream_create_header_filter: add headers when mail is empty");
istream = test_istream_create(input);
test_istream_run(istream, strlen(input)+1, output,
HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR,
missing_eoh_callback);
i_stream_unref(&istream);
test_end();
}
int main(void)
{
static void (*test_functions[])(void) = {
test_istream_filter,
test_istream_filter_large_buffer,
test_istream_filter_large_buffer2,
test_istream_callbacks,
test_istream_edit,
test_istream_add_missing_eoh,
test_istream_end_body_with_lf,
test_istream_hide_body,
test_istream_strip_eoh,
test_istream_missing_eoh_callback,
test_istream_empty_missing_eoh_callback,
NULL
};
return test_run(test_functions);
}