1 /* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "test-lib.h"
4 #include "str.h"
5 #include "istream-private.h"
6 #include "istream-jsonstr.h"
7 
8 static const struct {
9 	const char *input;
10 	const char *output;
11 	int stream_errno;
12 } tests[] = {
13 	{ "foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\uffff\"",
14 	  "foo\\\"\b\f\n\r\t\001\xEF\xBF\xBF", 0 },
15 	{ "\\ud801\\udc37\"", "\xf0\x90\x90\xb7", 0 }, /* valid codepoint */
16 	{ "\"", "", 0 },
17 	{ "foo\\?\"", "foo", EINVAL },
18 	{ "foo\\?\"", "foo", EINVAL },
19 	{ "", "", EPIPE },
20 	{ "\\\"", "\"", EPIPE },
21 	{ "foo", "foo", EPIPE },
22 	{ "\\ud801", "", EPIPE }, /* high surrogate alone */
23 	{ "\\udced\\udc37\"", "", EINVAL }, /* low surrogate before high */
24 	{ "\\ud8011\\udc37\"", "", EINVAL }, /* has extra 1 in middle */
25 	{ "hello \\udc37\"", "hello ", EINVAL }, /* low surrogate before high with valid prefix*/
26 	{ "hello \\ud801", "hello ", EPIPE }, /* high surrogate alone with valid prefix */
27 	{ "\\uabcg", "", EINVAL }, /* invalid hex value */
28 };
29 
30 static void
run_test_buffer(const char * json_input,const char * output,int stream_errno,unsigned int skip_count)31 run_test_buffer(const char *json_input, const char *output, int stream_errno,
32 		unsigned int skip_count)
33 {
34 	size_t json_input_len = strlen(json_input);
35 	struct istream *input_data, *input;
36 	const unsigned char *data;
37 	size_t i, size;
38 	ssize_t ret = 0;
39 
40 	input_data = test_istream_create_data(json_input, json_input_len);
41 	test_istream_set_allow_eof(input_data, FALSE);
42 	input = i_stream_create_jsonstr(input_data);
43 
44 	for (i = 1; i < json_input_len;) {
45 		test_istream_set_size(input_data, i);
46 		while ((ret = i_stream_read(input)) > 0) ;
47 		if (ret == -1 && stream_errno != 0)
48 			break;
49 		test_assert_idx(ret == 0, i);
50 		if (i + skip_count < json_input_len)
51 			i += skip_count;
52 		else
53 			i++;
54 	}
55 	test_istream_set_allow_eof(input_data, TRUE);
56 	test_istream_set_size(input_data, json_input_len);
57 	ret = i_stream_read(input);
58 	while (ret > 0 && stream_errno != 0)
59 		ret = i_stream_read(input);
60 	test_assert(ret == -1);
61 	test_assert(input->stream_errno == stream_errno);
62 
63 	if (stream_errno == 0) {
64 		data = i_stream_get_data(input, &size);
65 		test_assert(size == strlen(output));
66 		if (size > 0)
67 			test_assert(memcmp(data, output, size) == 0);
68 	}
69 	i_stream_unref(&input);
70 	i_stream_unref(&input_data);
71 }
72 
73 static void
run_test(const char * json_input,const char * output,int stream_errno)74 run_test(const char *json_input, const char *output, int stream_errno)
75 {
76 	for (unsigned int i = 1; i <= 5; i++)
77 		run_test_buffer(json_input, output, stream_errno, i);
78 }
79 
test_istream_jsonstr_autoretry(void)80 static void test_istream_jsonstr_autoretry(void)
81 {
82 	const char *json_input = "\\u0001\"";
83 	const size_t json_input_len = strlen(json_input);
84 	struct istream *input_data, *input;
85 
86 	test_begin("istream-jsonstr autoretry");
87 	input_data = test_istream_create_data(json_input, json_input_len);
88 	input = i_stream_create_jsonstr(input_data);
89 
90 	test_istream_set_size(input_data, 2);
91 	test_assert(i_stream_read(input_data) == 2);
92 	test_istream_set_size(input_data, json_input_len);
93 	test_assert(i_stream_read(input) == 1);
94 	test_assert(i_stream_read(input) == -1);
95 
96 	i_stream_unref(&input);
97 	i_stream_unref(&input_data);
98 	test_end();
99 }
100 
test_istream_jsonstr_partial(void)101 static void test_istream_jsonstr_partial(void)
102 {
103 	size_t len = 0;
104 	const char *json_input = "hello\\u0060x\"";
105 	const char *output = "hello`x";
106 	const size_t json_input_len = strlen(json_input);
107 	struct istream *input_data, *input;
108 
109 	test_begin("istream-jsonstr partial");
110 
111 	input_data = test_istream_create_data(json_input, json_input_len);
112 	input = i_stream_create_jsonstr(input_data);
113 	test_istream_set_size(input_data, 9);
114 	test_assert(i_stream_read(input) == 5);
115 	test_istream_set_size(input_data, json_input_len);
116 	test_assert(i_stream_read(input) == 2);
117 	test_assert(i_stream_read(input) == -1);
118 
119 	test_assert(memcmp(i_stream_get_data(input, &len), output, I_MIN(len, strlen(output))) == 0 &&
120 		    len == strlen(output));
121 
122 	i_stream_unref(&input);
123 	i_stream_unref(&input_data);
124 
125 	test_end();
126 }
127 
test_istream_jsonstr(void)128 void test_istream_jsonstr(void)
129 {
130 	unsigned int i;
131 
132 	for (i = 0; i < N_ELEMENTS(tests); i++) {
133 		test_begin(t_strdup_printf("istream-jsonstr %u", i+1));
134 		run_test(tests[i].input, tests[i].output, tests[i].stream_errno);
135 		test_end();
136 	}
137 	test_istream_jsonstr_autoretry();
138 	test_istream_jsonstr_partial();
139 }
140