summaryrefslogtreecommitdiffstats
path: root/src/lib-mail/test-istream-dot.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib-mail/test-istream-dot.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/lib-mail/test-istream-dot.c b/src/lib-mail/test-istream-dot.c
new file mode 100644
index 0000000..b77f364
--- /dev/null
+++ b/src/lib-mail/test-istream-dot.c
@@ -0,0 +1,230 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "istream.h"
+#include "istream-dot.h"
+#include "test-common.h"
+
+struct dot_test {
+ const char *input;
+ const char *output;
+ const char *parent_input;
+};
+
+static void test_istream_dot_one(const struct dot_test *test,
+ bool send_last_lf, bool test_bufsize)
+{
+ struct istream *test_input, *input;
+ const unsigned char *data;
+ size_t size;
+ unsigned int i;
+ size_t outsize, input_len, output_len;
+ string_t *str;
+ uoff_t offset;
+ int ret;
+
+ test_input = test_istream_create(test->input);
+ input = i_stream_create_dot(test_input, send_last_lf);
+
+ input_len = strlen(test->input);
+ output_len = strlen(test->output);
+ if (!send_last_lf &&
+ (test->input[input_len-1] == '\n' ||
+ strstr(test->input, "\n.\n") != NULL ||
+ strstr(test->input, "\n.\r\n") != NULL)) {
+ if (output_len > 0 &&
+ test->output[output_len-1] == '\n') {
+ output_len--;
+ if (output_len > 0 &&
+ test->output[output_len-1] == '\r')
+ output_len--;
+ }
+ }
+
+ str = t_str_new(256);
+ if (!test_bufsize) {
+ outsize = 1; i = 0;
+ i_stream_set_max_buffer_size(input, outsize);
+ test_istream_set_size(test_input, 1);
+ while ((ret = i_stream_read(input)) != -1) {
+ switch (ret) {
+ case -2:
+ i_stream_set_max_buffer_size(input, ++outsize);
+ offset = test_input->v_offset;
+ /* seek one byte backwards so stream gets
+ reset */
+ i_stream_seek(test_input, offset - 1);
+ /* go back to original position */
+ test_istream_set_size(test_input, offset);
+ i_stream_skip(test_input, 1);
+ /* and finally allow reading one more byte */
+ test_istream_set_size(test_input, offset + 1);
+ break;
+ case 0:
+ test_istream_set_size(test_input, ++i);
+ break;
+ default:
+ test_assert(ret > 0);
+
+ data = i_stream_get_data(input, &size);
+ str_append_data(str, data, size);
+ i_stream_skip(input, size);
+ }
+ }
+ test_istream_set_size(test_input, input_len);
+ (void)i_stream_read(test_input);
+ } else {
+ test_istream_set_size(test_input, input_len);
+ size = 0;
+ for (i = 1; i < output_len; i++) {
+ i_stream_set_max_buffer_size(input, i);
+ test_assert(i_stream_read(input) == 1);
+ test_assert(i_stream_read(input) == -2);
+ data = i_stream_get_data(input, &size);
+ test_assert(memcmp(data, test->output, size) == 0);
+ }
+ i_stream_set_max_buffer_size(input, i+2);
+ if (size < output_len)
+ test_assert(i_stream_read(input) == 1);
+ test_assert(i_stream_read(input) == -1);
+
+ data = i_stream_get_data(input, &size);
+ if (size > 0)
+ str_append_data(str, data, size);
+ }
+ test_assert(input->stream_errno == 0);
+ test_assert(str_len(str) == output_len);
+ test_assert(memcmp(str_data(str), test->output, output_len) == 0);
+
+ /* read the data after the '.' line and verify it's still there */
+ i_stream_set_max_buffer_size(test_input, SIZE_MAX);
+ (void)i_stream_read(test_input);
+ data = i_stream_get_data(test_input, &size);
+ test_assert(size == strlen(test->parent_input));
+ if (size > 0)
+ test_assert(memcmp(data, test->parent_input, size) == 0);
+
+ i_stream_unref(&test_input);
+ i_stream_unref(&input);
+}
+
+static void test_istream_dot_error(const char *input_str, bool test_bufsize)
+{
+ struct istream *test_input, *input;
+ unsigned int i;
+ size_t outsize, input_len;
+ uoff_t offset;
+ int ret;
+
+ test_input = test_istream_create(input_str);
+ input = i_stream_create_dot(test_input, FALSE);
+
+ input_len = strlen(input_str);
+
+ if (!test_bufsize) {
+ outsize = 1; i = 0;
+ i_stream_set_max_buffer_size(input, outsize);
+ test_istream_set_size(test_input, 1);
+ while ((ret = i_stream_read(input)) != -1) {
+ switch (ret) {
+ case -2:
+ i_stream_set_max_buffer_size(input, ++outsize);
+ offset = test_input->v_offset;
+ /* seek one byte backwards so stream gets
+ reset */
+ i_stream_seek(test_input, offset - 1);
+ /* go back to original position */
+ test_istream_set_size(test_input, offset);
+ i_stream_skip(test_input, 1);
+ /* and finally allow reading one more byte */
+ test_istream_set_size(test_input, offset + 1);
+ break;
+ case 0:
+ test_istream_set_size(test_input, ++i);
+ break;
+ default:
+ test_assert(ret > 0);
+ }
+ }
+ test_istream_set_size(test_input, input_len);
+ (void)i_stream_read(test_input);
+ } else {
+ test_istream_set_size(test_input, input_len);
+ for (i = 1; i <= input_len; i++) {
+ i_stream_set_max_buffer_size(input, i);
+ (void)i_stream_read(input);
+ (void)i_stream_read(input);
+ }
+ i_stream_set_max_buffer_size(input, i+1);
+ (void)i_stream_read(input);
+ }
+ test_assert(input->stream_errno == EPIPE);
+
+ i_stream_unref(&test_input);
+ i_stream_unref(&input);
+}
+
+static void test_istream_dot(void)
+{
+ static struct dot_test tests[] = {
+ { "..foo\n..\n.foo\n.\nfoo", ".foo\n.\nfoo\n", "foo" },
+ { "..foo\r\n..\r\n.foo\r\n.\r\nfoo", ".foo\r\n.\r\nfoo\r\n", "foo" },
+ { "\r.\r\n.\r\n", "\r.\r\n", "" },
+ { "\n\r.\r\r\n.\r\n", "\n\r.\r\r\n", "" },
+ { "\r\n.\rfoo\n.\n", "\r\n\rfoo\n", "" },
+ { "\r\n.\r\n", "\r\n", "" },
+ { "\n.\r\n", "\n", "" },
+ { "\n.\n", "\n", "" },
+ { ".\r\n", "", "" },
+ { ".\n", "", "" }
+ };
+ static const char *error_tests[] = {
+ "",
+ ".",
+ "..",
+ ".\r",
+ ".\rx",
+ "..\r\n",
+ "\r.",
+ "\r.\r",
+ "\r.\rx",
+ "\r.\r\n",
+ "\r.\n",
+ "\r..\n",
+ "\r\n",
+ "\r\n.",
+ "\r\n.\r",
+ "\r\n.\rx",
+ "\r\n.\rx\n",
+ "\r\n..\r\n",
+ "\n",
+ "\n.",
+ "\n.\r",
+ "\n.\rx",
+ "\n..\r\n"
+ };
+ unsigned int i;
+
+ test_begin("dot istream");
+ for (i = 0; i < N_ELEMENTS(tests); i++) {
+ test_istream_dot_one(&tests[i], TRUE, TRUE);
+ test_istream_dot_one(&tests[i], TRUE, FALSE);
+ test_istream_dot_one(&tests[i], FALSE, TRUE);
+ test_istream_dot_one(&tests[i], FALSE, FALSE);
+ }
+ for (i = 0; i < N_ELEMENTS(error_tests); i++) {
+ test_istream_dot_error(error_tests[i], FALSE);
+ test_istream_dot_error(error_tests[i], TRUE);
+ }
+ test_end();
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_istream_dot,
+ NULL
+ };
+ return test_run(test_functions);
+}