1
/* Copyright (c) 2007-2013 Dovecot authors, see the included COPYING file */
7
#include "hash-format.h"
8
#include "safe-mkstemp.h"
10
#include "istream-crlf.h"
11
#include "istream-attachment-extractor.h"
12
#include "istream-attachment-connector.h"
14
#include "test-common.h"
19
#define BINARY_TEXT_LONG "we have\ra lot \nof \0binary stuff in here\n" \
20
"b adjig sadjg jasidgjiaehga3wht8a3w8ghxjc dsgad hasdghsd gasd ds" \
21
"jdsoga sjdga0w3tjhawjgsertniq3n5oqerjqw2r89q23h awhrqh835r8a"
22
#define BINARY_TEXT_LONG_BASE64 \
23
"d2UgaGF2ZQ1hIGxvdCAKb2YgAGJpbmFyeSBzdHVmZiBpbiBoZXJlCmIgYWRqaWcgc2FkamcgamFz\r\n" \
24
"aWRnamlhZWhnYTN3aHQ4YTN3OGdoeGpjIGRzZ2FkIGhhc2RnaHNkIGdhc2QgZHNqZHNvZ2Egc2pk\r\n" \
25
"Z2EwdzN0amhhd2pnc2VydG5pcTNuNW9xZXJqcXcycjg5cTIzaCBhd2hycWg4MzVyOGE="
27
#define BINARY_TEXT_SHORT "eh"
28
#define BINARY_TEXT_SHORT_BASE64 "ZWg="
30
static const char mail_input[] =
31
"MIME-Version: 1.0\r\n"
32
"Content-Type: multipart/alternative;\r\n boundary=\"bound\"\r\n"
36
"Content-Transfer-Encoding: base64\r\n"
37
"Content-Type: text/plain\r\n"
39
BINARY_TEXT_LONG_BASE64
41
"Content-Type: text/plain\r\n"
42
"Content-Transfer-Encoding: base64\r\n"
44
BINARY_TEXT_SHORT_BASE64
47
static const char mail_output[] =
48
"MIME-Version: 1.0\r\n"
49
"Content-Type: multipart/alternative;\r\n boundary=\"bound\"\r\n"
53
"Content-Transfer-Encoding: base64\r\n"
54
"Content-Type: text/plain\r\n"
57
"Content-Type: text/plain\r\n"
58
"Content-Transfer-Encoding: base64\r\n"
65
uoff_t encoded_size, decoded_size;
66
unsigned int base64_blocks_per_line;
69
static buffer_t *attachment_data;
70
static ARRAY(struct attachment) attachments;
72
static int test_open_temp_fd(void *context ATTR_UNUSED)
74
string_t *str = t_str_new(128);
77
str_append(str, "/tmp/dovecot-test.");
78
fd = safe_mkstemp(str, 0600, (uid_t)-1, (gid_t)-1);
80
i_fatal("safe_mkstemp(%s) failed: %m", str_c(str));
81
(void)unlink(str_c(str));
85
static int test_open_attachment_ostream(struct istream_attachment_info *info,
86
struct ostream **output_r,
87
void *context ATTR_UNUSED)
91
if (attachment_data == NULL)
92
attachment_data = buffer_create_dynamic(default_pool, 1024);
93
if (!array_is_created(&attachments))
94
i_array_init(&attachments, 8);
95
a = array_append_space(&attachments);
96
a->buffer_offset = attachment_data->used;
97
a->start_offset = info->start_offset;
98
a->encoded_size = info->encoded_size;
99
a->base64_blocks_per_line = info->base64_blocks_per_line;
100
test_assert(strlen(info->hash) == 160/8*2); /* sha1 size */
102
*output_r = o_stream_create_buffer(attachment_data);
103
if (o_stream_seek(*output_r, a->buffer_offset) < 0)
108
static int test_close_attachment_ostream(struct ostream *output, bool success,
109
void *context ATTR_UNUSED)
111
struct attachment *a;
115
a = array_idx_modifiable(&attachments, array_count(&attachments)-1);
116
a->decoded_size = output->offset - a->buffer_offset;
118
if (o_stream_nfinish(output) < 0)
120
o_stream_destroy(&output);
124
static struct istream *
125
test_build_original_istream(struct istream *base_input, uoff_t msg_size)
127
struct istream_attachment_connector *conn;
128
const unsigned char *data = attachment_data->data;
129
const struct attachment *a;
130
struct istream *input;
131
uoff_t data_size = attachment_data->used;
134
conn = istream_attachment_connector_begin(base_input, msg_size);
135
array_foreach(&attachments, a) {
136
input = i_stream_create_from_data(data, a->decoded_size);
137
if (istream_attachment_connector_add(conn, input,
138
a->start_offset, a->encoded_size,
139
a->base64_blocks_per_line, TRUE, &error) < 0)
141
i_stream_unref(&input);
143
i_assert(a->decoded_size <= data_size);
144
data += a->decoded_size;
145
data_size -= a->decoded_size;
147
i_assert(data_size == 0);
148
return istream_attachment_connector_finish(&conn);
152
get_istream_attachment_settings(struct istream_attachment_settings *set_r)
156
memset(set_r, 0, sizeof(*set_r));
158
set_r->drain_parent_input = TRUE;
159
set_r->open_temp_fd = test_open_temp_fd;
160
set_r->open_attachment_ostream = test_open_attachment_ostream;
161
set_r->close_attachment_ostream= test_close_attachment_ostream;
162
if (hash_format_init("%{sha1}", &set_r->hash_format, &error) < 0)
166
static int test_input_stream(struct istream *file_input)
168
struct istream_attachment_settings set;
169
struct istream *input, *input2;
170
const unsigned char *data;
172
struct sha1_ctxt hash;
175
unsigned char hash_file[SHA1_RESULTLEN], hash_attached[SHA1_RESULTLEN];
178
/* get hash when directly reading input */
179
input = i_stream_create_crlf(file_input);
181
while (i_stream_read_data(input, &data, &size, 0) > 0) {
182
sha1_loop(&hash, data, size);
183
i_stream_skip(input, size);
185
sha1_result(&hash, hash_file);
186
msg_size = input->v_offset;
187
i_stream_unref(&input);
189
/* read through attachment extractor */
190
get_istream_attachment_settings(&set);
192
i_stream_seek(file_input, 0);
193
input = i_stream_create_crlf(file_input);
194
input2 = i_stream_create_attachment_extractor(input, &set, NULL);
195
i_stream_unref(&input);
196
base_buf = buffer_create_dynamic(default_pool, 1024);
197
while (i_stream_read_data(input2, &data, &size, 0) > 0) {
198
buffer_append(base_buf, data, size);
199
i_stream_skip(input2, size);
201
i_stream_unref(&input2);
203
/* rebuild the original stream and see if the hash matches */
204
input2 = i_stream_create_from_data(base_buf->data, base_buf->used);
205
input = test_build_original_istream(input2, msg_size);
206
i_stream_unref(&input2);
209
while (i_stream_read_data(input, &data, &size, 0) > 0) {
210
sha1_loop(&hash, data, size);
211
i_stream_skip(input, size);
213
sha1_result(&hash, hash_attached);
214
i_stream_unref(&input);
216
ret = memcmp(hash_file, hash_attached, SHA1_RESULTLEN) == 0 ? 0 : -1;
218
i_stream_unref(&file_input);
219
buffer_free(&base_buf);
220
if (attachment_data != NULL)
221
buffer_free(&attachment_data);
222
if (array_is_created(&attachments))
223
array_free(&attachments);
227
static void test_istream_attachment(void)
229
struct istream_attachment_settings set;
230
struct istream *datainput, *input;
231
const unsigned char *data;
235
test_begin("istream attachment");
236
datainput = test_istream_create_data(mail_input, sizeof(mail_input));
237
test_istream_set_allow_eof(datainput, FALSE);
239
get_istream_attachment_settings(&set);
240
input = i_stream_create_attachment_extractor(datainput, &set, NULL);
242
for (i = 1; i <= sizeof(mail_input); i++) {
243
test_istream_set_size(datainput, i);
244
while ((ret = i_stream_read(input)) > 0) ;
245
test_assert(ret == 0);
247
test_istream_set_allow_eof(datainput, TRUE);
248
while ((ret = i_stream_read(input)) > 0) ;
249
test_assert(ret == -1);
251
data = i_stream_get_data(input, &size);
252
test_assert(size == sizeof(mail_output) &&
253
memcmp(data, mail_output, size) == 0);
255
data = attachment_data->data;
256
test_assert(attachment_data->used ==
257
sizeof(BINARY_TEXT_LONG)-1 + strlen(BINARY_TEXT_SHORT));
258
test_assert(memcmp(data, BINARY_TEXT_LONG, sizeof(BINARY_TEXT_LONG)-1) == 0);
259
test_assert(memcmp(data + sizeof(BINARY_TEXT_LONG)-1,
260
BINARY_TEXT_SHORT, strlen(BINARY_TEXT_SHORT)) == 0);
261
i_stream_unref(&input);
262
i_stream_unref(&datainput);
266
static int test_input_file(const char *path)
268
struct istream *file_input;
273
file_input = i_stream_create_file(path, 64);
274
if (test_input_stream(file_input) < 0) {
275
fprintf(stderr, "istream-attachment-extractor: mismatch on file %s\n",
279
i_stream_unref(&file_input);
285
int main(int argc, char *argv[])
287
static void (*test_functions[])(void) = {
288
test_istream_attachment,
292
return test_input_file(argv[1]) < 0 ? 1 : 0;
294
return test_run(test_functions);