~ubuntu-branches/ubuntu/trusty/dovecot/trusty-updates

1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "istream-private.h"
5
#include "ostream.h"
6
#include "base64.h"
7
#include "buffer.h"
8
#include "str.h"
9
#include "hash-format.h"
10
#include "rfc822-parser.h"
11
#include "message-parser.h"
12
#include "istream-attachment-extractor.h"
13
14
#define BASE64_ATTACHMENT_MAX_EXTRA_BYTES 1024
15
16
enum mail_attachment_state {
17
	MAIL_ATTACHMENT_STATE_NO,
18
	MAIL_ATTACHMENT_STATE_MAYBE,
19
	MAIL_ATTACHMENT_STATE_YES
20
};
21
22
enum base64_state {
23
	BASE64_STATE_0 = 0,
24
	BASE64_STATE_1,
25
	BASE64_STATE_2,
26
	BASE64_STATE_3,
27
	BASE64_STATE_CR,
28
	BASE64_STATE_EOB,
29
	BASE64_STATE_EOM
30
};
31
32
struct attachment_istream_part {
33
	char *content_type, *content_disposition;
34
	enum mail_attachment_state state;
35
	/* start offset of the message part in the original input stream */
36
	uoff_t start_offset;
37
38
	/* for saving attachments base64-decoded: */
39
	enum base64_state base64_state;
40
	unsigned int base64_line_blocks, cur_base64_blocks;
41
	uoff_t base64_bytes;
42
	bool base64_have_crlf; /* CRLF linefeeds */
43
	bool base64_failed;
44
45
	int temp_fd;
46
	struct ostream *temp_output;
47
	buffer_t *part_buf;
48
};
49
50
struct attachment_istream {
51
	struct istream_private istream;
52
	pool_t pool;
53
54
	struct istream_attachment_settings set;
55
	void *context;
56
57
	struct message_parser_ctx *parser;
58
	struct message_part *cur_part;
59
	struct attachment_istream_part part;
60
61
	bool retry_read;
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
62
	bool failed;
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
63
};
64
65
static void stream_add_data(struct attachment_istream *astream,
66
			    const void *data, size_t size)
67
{
68
	if (size > 0) {
69
		memcpy(i_stream_alloc(&astream->istream, size), data, size);
70
		astream->istream.pos += size;
71
	}
72
}
73
74
static void parse_content_type(struct attachment_istream *astream,
75
			       const struct message_header_line *hdr)
76
{
77
	struct rfc822_parser_context parser;
78
	string_t *content_type;
79
80
	rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
81
	rfc822_skip_lwsp(&parser);
82
83
	T_BEGIN {
84
		content_type = t_str_new(64);
85
		if (rfc822_parse_content_type(&parser, content_type) >= 0) {
86
			i_free(astream->part.content_type);
87
			astream->part.content_type =
88
				i_strdup(str_c(content_type));
89
		}
90
	} T_END;
91
}
92
93
static void
94
parse_content_disposition(struct attachment_istream *astream,
95
			  const struct message_header_line *hdr)
96
{
97
	/* just pass it without parsing to is_attachment() callback */
98
	i_free(astream->part.content_disposition);
99
	astream->part.content_disposition =
100
		i_strndup(hdr->full_value, hdr->full_value_len);
101
}
102
103
static void astream_parse_header(struct attachment_istream *astream,
104
				 struct message_header_line *hdr)
105
{
106
	if (!hdr->continued) {
107
		stream_add_data(astream, hdr->name, hdr->name_len);
108
		stream_add_data(astream, hdr->middle, hdr->middle_len);
109
	}
110
	stream_add_data(astream, hdr->value, hdr->value_len);
111
	if (!hdr->no_newline) {
112
		if (hdr->crlf_newline)
113
			stream_add_data(astream, "\r\n", 2);
114
		else
115
			stream_add_data(astream, "\n", 1);
116
	}
117
118
	if (hdr->continues) {
119
		hdr->use_full_value = TRUE;
120
		return;
121
	}
122
123
	if (strcasecmp(hdr->name, "Content-Type") == 0)
124
		parse_content_type(astream, hdr);
125
	else if (strcasecmp(hdr->name, "Content-Disposition") == 0)
126
		parse_content_disposition(astream, hdr);
127
}
128
129
static bool astream_want_attachment(struct attachment_istream *astream,
130
				    struct message_part *part)
131
{
132
	struct istream_attachment_header ahdr;
133
134
	if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
135
		/* multiparts may contain attachments as children,
136
		   but they're never themselves */
137
		return FALSE;
138
	}
139
	if (astream->set.want_attachment == NULL)
140
		return TRUE;
141
142
	memset(&ahdr, 0, sizeof(ahdr));
143
	ahdr.part = part;
144
	ahdr.content_type = astream->part.content_type;
145
	ahdr.content_disposition = astream->part.content_disposition;
146
	return astream->set.want_attachment(&ahdr, astream->context);
147
}
148
149
static int astream_base64_decode_lf(struct attachment_istream_part *part)
150
{
151
	part->base64_state = BASE64_STATE_0;
152
	if (part->cur_base64_blocks < part->base64_line_blocks) {
153
		/* last line */
154
		part->base64_state = BASE64_STATE_EOM;
155
		return 0;
156
	} else if (part->base64_line_blocks == 0) {
157
		/* first line */
158
		if (part->cur_base64_blocks == 0)
159
			return -1;
160
		part->base64_line_blocks = part->cur_base64_blocks;
161
	} else if (part->cur_base64_blocks == part->base64_line_blocks) {
162
		/* line is ok */
163
	} else {
164
		return -1;
165
	}
166
	part->cur_base64_blocks = 0;
167
	return 1;
168
}
169
170
static int
171
astream_try_base64_decode_char(struct attachment_istream_part *part,
172
			       size_t pos, char chr)
173
{
174
	switch (part->base64_state) {
175
	case BASE64_STATE_0:
176
		if (base64_is_valid_char(chr))
177
			part->base64_state++;
178
		else if (chr == '\r')
179
			part->base64_state = BASE64_STATE_CR;
180
		else if (chr == '\n') {
181
			return astream_base64_decode_lf(part);
182
		} else {
183
			return -1;
184
		}
185
		break;
186
	case BASE64_STATE_1:
187
		if (!base64_is_valid_char(chr))
188
			return -1;
189
		part->base64_state++;
190
		break;
191
	case BASE64_STATE_2:
192
		if (base64_is_valid_char(chr))
193
			part->base64_state++;
194
		else if (chr == '=')
195
			part->base64_state = BASE64_STATE_EOB;
196
		else
197
			return -1;
198
		break;
199
	case BASE64_STATE_3:
200
		part->base64_bytes = part->temp_output->offset + pos + 1;
201
		if (base64_is_valid_char(chr)) {
202
			part->base64_state = BASE64_STATE_0;
203
			part->cur_base64_blocks++;
204
		} else if (chr == '=') {
205
			part->base64_state = BASE64_STATE_EOM;
206
			part->cur_base64_blocks++;
207
			return 0;
208
		} else {
209
			return -1;
210
		}
211
		break;
212
	case BASE64_STATE_CR:
213
		if (chr != '\n')
214
			return -1;
215
		part->base64_have_crlf = TRUE;
216
		return astream_base64_decode_lf(part);
217
	case BASE64_STATE_EOB:
218
		if (chr != '=')
219
			return -1;
220
221
		part->base64_bytes = part->temp_output->offset + pos + 1;
222
		part->base64_state = BASE64_STATE_EOM;
223
		part->cur_base64_blocks++;
224
		return 0;
225
	case BASE64_STATE_EOM:
226
		i_unreached();
227
	}
228
	return 1;
229
}
230
231
static void
232
astream_try_base64_decode(struct attachment_istream_part *part,
233
			  const unsigned char *data, size_t size)
234
{
235
	size_t i;
236
	int ret;
237
238
	if (part->base64_failed || part->base64_state == BASE64_STATE_EOM)
239
		return;
240
241
	for (i = 0; i < size; i++) {
242
		ret = astream_try_base64_decode_char(part, i, (char)data[i]);
243
		if (ret <= 0) {
244
			if (ret < 0)
245
				part->base64_failed = TRUE;
246
			break;
247
		}
248
	}
249
}
250
251
static int astream_open_output(struct attachment_istream *astream)
252
{
253
	int fd;
254
255
	i_assert(astream->part.temp_fd == -1);
256
257
	fd = astream->set.open_temp_fd(astream->context);
258
	if (fd == -1)
259
		return -1;
260
261
	astream->part.temp_fd = fd;
262
	astream->part.temp_output = o_stream_create_fd(fd, 0, FALSE);
263
	o_stream_cork(astream->part.temp_output);
264
	return 0;
265
}
266
267
static void astream_add_body(struct attachment_istream *astream,
268
			     const struct message_block *block)
269
{
270
	struct attachment_istream_part *part = &astream->part;
271
	buffer_t *part_buf;
272
	size_t new_size;
273
274
	switch (part->state) {
275
	case MAIL_ATTACHMENT_STATE_NO:
276
		stream_add_data(astream, block->data, block->size);
277
		break;
278
	case MAIL_ATTACHMENT_STATE_MAYBE:
279
		/* we'll write data to in-memory buffer until we reach
280
		   attachment min_size */
281
		if (part->part_buf == NULL) {
282
			part->part_buf =
283
				buffer_create_dynamic(default_pool,
284
						      astream->set.min_size);
285
		}
286
		part_buf = part->part_buf;
287
		new_size = part_buf->used + block->size;
288
		if (new_size < astream->set.min_size) {
289
			buffer_append(part_buf, block->data, block->size);
290
			break;
291
		}
292
		/* attachment is large enough. we'll first copy the buffered
293
		   data from memory to temp file */
294
		if (astream_open_output(astream) < 0) {
295
			/* failed, fallback to just saving it inline */
296
			part->state = MAIL_ATTACHMENT_STATE_NO;
297
			stream_add_data(astream, part_buf->data, part_buf->used);
298
			stream_add_data(astream, block->data, block->size);
299
			break;
300
		}
301
		part->state = MAIL_ATTACHMENT_STATE_YES;
302
		astream_try_base64_decode(part, part_buf->data, part_buf->used);
303
		hash_format_loop(astream->set.hash_format,
304
				 part_buf->data, part_buf->used);
305
		o_stream_nsend(part->temp_output,
306
			       part_buf->data, part_buf->used);
307
		buffer_set_used_size(part_buf, 0);
308
		/* fall through to write the new data to temp file */
309
	case MAIL_ATTACHMENT_STATE_YES:
310
		astream_try_base64_decode(part, block->data, block->size);
311
		hash_format_loop(astream->set.hash_format,
312
				 block->data, block->size);
313
		o_stream_nsend(part->temp_output, block->data, block->size);
314
		break;
315
	}
316
}
317
318
static int astream_decode_base64(struct attachment_istream *astream)
319
{
320
	struct attachment_istream_part *part = &astream->part;
321
	buffer_t *extra_buf = NULL;
322
	struct istream *input, *base64_input;
323
	struct ostream *output;
324
	const unsigned char *data;
325
	size_t size;
326
	ssize_t ret;
327
	buffer_t *buf;
328
	int outfd;
329
	bool failed = FALSE;
330
331
	if (part->base64_bytes < astream->set.min_size ||
332
	    part->temp_output->offset > part->base64_bytes +
333
	    				BASE64_ATTACHMENT_MAX_EXTRA_BYTES) {
334
		/* only a small part of the MIME part is base64-encoded. */
335
		return -1;
336
	}
337
338
	if (part->base64_line_blocks == 0) {
339
		/* only one line of base64 */
340
		part->base64_line_blocks = part->cur_base64_blocks;
341
		i_assert(part->base64_line_blocks > 0);
342
	}
343
344
	/* decode base64 data and write it to another temp file */
345
	outfd = astream->set.open_temp_fd(astream->context);
346
	if (outfd == -1)
347
		return -1;
348
349
	buf = buffer_create_dynamic(default_pool, 1024);
350
	input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE, FALSE);
351
	base64_input = i_stream_create_limit(input, part->base64_bytes);
352
	output = o_stream_create_fd_file(outfd, 0, FALSE);
353
	o_stream_cork(output);
354
355
	hash_format_reset(astream->set.hash_format);
356
	while ((ret = i_stream_read(base64_input)) > 0) {
357
		data = i_stream_get_data(base64_input, &size);
358
		buffer_set_used_size(buf, 0);
359
		if (base64_decode(data, size, &size, buf) < 0) {
360
			i_error("istream-attachment: BUG: "
361
				"Attachment base64 data unexpectedly broke");
362
			failed = TRUE;
363
			break;
364
		}
365
		i_stream_skip(base64_input, size);
366
		o_stream_nsend(output, buf->data, buf->used);
367
		hash_format_loop(astream->set.hash_format,
368
				 buf->data, buf->used);
369
	}
370
	if (ret != -1) {
371
		i_assert(failed);
372
	} else if (base64_input->stream_errno != 0) {
373
		i_error("istream-attachment: read(%s) failed: %m",
374
			i_stream_get_name(base64_input));
375
		failed = TRUE;
376
	}
377
	if (o_stream_nfinish(output) < 0) {
378
		i_error("istream-attachment: write(%s) failed: %m",
379
			o_stream_get_name(output));
380
		failed = TRUE;
381
	}
382
383
	buffer_free(&buf);
384
	i_stream_unref(&base64_input);
385
	o_stream_unref(&output);
386
387
	if (input->v_offset != part->temp_output->offset && !failed) {
388
		/* write the rest of the data to the message stream */
389
		extra_buf = buffer_create_dynamic(default_pool, 1024);
390
		while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
391
			buffer_append(extra_buf, data, size);
392
			i_stream_skip(input, size);
393
		}
394
		i_assert(ret == -1);
395
		if (input->stream_errno != 0) {
396
			i_error("istream-attachment: read(%s) failed: %m",
397
				i_stream_get_name(base64_input));
398
			failed = TRUE;
399
		}
400
	}
401
	i_stream_unref(&input);
402
403
	if (failed) {
404
		i_close_fd(&outfd);
405
		return -1;
406
	}
407
408
	/* successfully wrote it. switch to using it. */
409
	o_stream_destroy(&part->temp_output);
410
	i_close_fd(&part->temp_fd);
411
	part->temp_fd = outfd;
412
413
	if (extra_buf != NULL) {
414
		stream_add_data(astream, extra_buf->data, extra_buf->used);
415
		buffer_free(&extra_buf);
416
	}
417
	return 0;
418
}
419
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
420
static int
421
astream_part_finish(struct attachment_istream *astream, const char **error_r)
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
422
{
423
	struct attachment_istream_part *part = &astream->part;
424
	struct istream_attachment_info info;
425
	struct istream *input;
426
	struct ostream *output;
427
	string_t *digest_str;
428
	const unsigned char *data;
429
	size_t size;
430
	int ret = 0;
431
432
	if (o_stream_nfinish(part->temp_output) < 0) {
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
433
		*error_r = t_strdup_printf("write(%s) failed: %s",
434
					   o_stream_get_name(part->temp_output),
435
					   o_stream_get_error(part->temp_output));
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
436
		return -1;
437
	}
438
439
	memset(&info, 0, sizeof(info));
440
	info.start_offset = astream->part.start_offset;
441
	/* base64_bytes contains how many valid base64 bytes there are so far.
442
	   if the base64 ends properly, it'll specify how much of the MIME part
443
	   is saved as an attachment. the rest of the data (typically
444
	   linefeeds) is added back to main stream */
445
	info.encoded_size = part->base64_bytes;
446
	/* get the hash before base64-decoder resets it */
447
	digest_str = t_str_new(128);
448
	hash_format_write(astream->set.hash_format, digest_str);
449
	info.hash = str_c(digest_str);
450
451
	/* if it looks like we can decode base64 without any data loss,
452
	   do it and write the decoded data to another temp file. */
453
	if (!part->base64_failed) {
454
		if (part->base64_state == BASE64_STATE_0 &&
455
		    part->base64_bytes > 0) {
456
			/* there is no trailing LF or '=' characters,
457
			   but it's not completely empty */
458
			part->base64_state = BASE64_STATE_EOM;
459
		}
460
		if (part->base64_state == BASE64_STATE_EOM) {
461
			/* base64 data looks ok. */
462
			if (astream_decode_base64(astream) < 0)
463
				part->base64_failed = TRUE;
464
		} else {
465
			part->base64_failed = TRUE;
466
		}
467
	}
468
469
	/* open attachment output file */
470
	info.part = astream->cur_part;
471
	if (!part->base64_failed) {
472
		info.base64_blocks_per_line = part->base64_line_blocks;
473
		info.base64_have_crlf = part->base64_have_crlf;
474
		/* base64-decoder updated the hash, use it */
475
		str_truncate(digest_str, 0);
476
		hash_format_write(astream->set.hash_format, digest_str);
477
		info.hash = str_c(digest_str);
478
	} else {
479
		/* couldn't decode base64, so write the entire MIME part
480
		   as attachment */
481
		info.encoded_size = part->temp_output->offset;
482
	}
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
483
	if (astream->set.open_attachment_ostream(&info, &output, error_r,
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
484
						 astream->context) < 0)
485
		return -1;
486
487
	/* copy data to attachment from temp file */
488
	input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE, FALSE);
489
	while (i_stream_read_data(input, &data, &size, 0) > 0) {
490
		o_stream_nsend(output, data, size);
491
		i_stream_skip(input, size);
492
	}
493
494
	if (input->stream_errno != 0) {
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
495
		*error_r = t_strdup_printf("read(%s) failed: %s",
496
			i_stream_get_name(input), i_stream_get_error(input));
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
497
		ret = -1;
498
	}
499
	i_stream_destroy(&input);
500
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
501
	if (astream->set.close_attachment_ostream(output, ret == 0, error_r,
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
502
						  astream->context) < 0)
503
		ret = -1;
504
	return ret;
505
}
506
507
static void astream_part_reset(struct attachment_istream *astream)
508
{
509
	struct attachment_istream_part *part = &astream->part;
510
511
	if (part->temp_output != NULL)
512
		o_stream_destroy(&part->temp_output);
513
	if (part->temp_fd != -1)
514
		i_close_fd(&part->temp_fd);
515
516
	i_free_and_null(part->content_type);
517
	i_free_and_null(part->content_disposition);
518
	if (part->part_buf != NULL)
519
		buffer_free(&part->part_buf);
520
521
	memset(part, 0, sizeof(*part));
522
	part->temp_fd = -1;
523
	hash_format_reset(astream->set.hash_format);
524
}
525
526
static int
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
527
astream_end_of_part(struct attachment_istream *astream, const char **error_r)
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
528
{
529
	struct attachment_istream_part *part = &astream->part;
530
	size_t old_size;
531
	int ret = 0;
532
533
	/* MIME part changed. we're now parsing the end of a boundary,
534
	   possibly followed by message epilogue */
535
	switch (part->state) {
536
	case MAIL_ATTACHMENT_STATE_NO:
537
		break;
538
	case MAIL_ATTACHMENT_STATE_MAYBE:
539
		/* MIME part wasn't large enough to be an attachment */
540
		if (part->part_buf != NULL) {
541
			stream_add_data(astream, part->part_buf->data,
542
					part->part_buf->used);
543
			ret = part->part_buf->used > 0 ? 1 : 0;
544
		}
545
		break;
546
	case MAIL_ATTACHMENT_STATE_YES:
547
		old_size = astream->istream.pos - astream->istream.skip;
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
548
		if (astream_part_finish(astream, error_r) < 0)
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
549
			ret = -1;
550
		else {
551
			/* finished base64 may have added a few more trailing
552
			   bytes to the stream */
553
			ret = astream->istream.pos -
554
				astream->istream.skip - old_size;
555
		}
556
		break;
557
	}
558
	part->state = MAIL_ATTACHMENT_STATE_NO;
559
	astream_part_reset(astream);
560
	return ret;
561
}
562
563
static int astream_read_next(struct attachment_istream *astream, bool *retry_r)
564
{
565
	struct istream_private *stream = &astream->istream;
566
	struct message_block block;
567
	size_t old_size, new_size;
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
568
	const char *error;
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
569
	int ret;
570
571
	*retry_r = FALSE;
572
573
	if (stream->pos - stream->skip >= stream->max_buffer_size)
574
		return -2;
575
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
576
	if (astream->failed) {
577
		stream->istream.stream_errno = EINVAL;
578
		return -1;
579
	}
580
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
581
	old_size = stream->pos - stream->skip;
582
	switch (message_parser_parse_next_block(astream->parser, &block)) {
583
	case -1:
584
		/* done / error */
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
585
		ret = astream_end_of_part(astream, &error);
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
586
		if (ret > 0) {
587
			/* final data */
588
			new_size = stream->pos - stream->skip;
589
			return new_size - old_size;
590
		}
591
		stream->istream.eof = TRUE;
592
		stream->istream.stream_errno = stream->parent->stream_errno;
593
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
594
		if (ret < 0) {
595
			io_stream_set_error(&stream->iostream, "%s", error);
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
596
			stream->istream.stream_errno = EINVAL;
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
597
			astream->failed = TRUE;
598
		}
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
599
		astream->cur_part = NULL;
600
		return -1;
601
	case 0:
602
		/* need more data */
603
		return 0;
604
	default:
605
		break;
606
	}
607
608
	if (block.part != astream->cur_part && astream->cur_part != NULL) {
609
		/* end of a MIME part */
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
610
		if (astream_end_of_part(astream, &error) < 0) {
611
			io_stream_set_error(&stream->iostream, "%s", error);
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
612
			stream->istream.stream_errno = EINVAL;
1.16.1 by Jaldhar H. Vyas
Import upstream version 2.2.9
613
			astream->failed = TRUE;
1.13.12 by Jaldhar H. Vyas
Import upstream version 2.2.5
614
			return -1;
615
		}
616
	}
617
	astream->cur_part = block.part;
618
619
	if (block.hdr != NULL) {
620
		/* parsing a header */
621
		astream_parse_header(astream, block.hdr);
622
	} else if (block.size == 0) {
623
		/* end of headers */
624
		if (astream_want_attachment(astream, block.part)) {
625
			astream->part.state = MAIL_ATTACHMENT_STATE_MAYBE;
626
			astream->part.start_offset = stream->parent->v_offset;
627
		}
628
	} else {
629
		astream_add_body(astream, &block);
630
	}
631
	new_size = stream->pos - stream->skip;
632
	*retry_r = new_size == old_size;
633
	return new_size - old_size;
634
}
635
636
static ssize_t
637
i_stream_attachment_extractor_read(struct istream_private *stream)
638
{
639
	struct attachment_istream *astream =
640
		(struct attachment_istream *)stream;
641
	bool retry;
642
	ssize_t ret;
643
644
	do {
645
		ret = astream_read_next(astream, &retry);
646
	} while (retry && astream->set.drain_parent_input);
647
648
	astream->retry_read = retry;
649
	return ret;
650
}
651
652
static void i_stream_attachment_extractor_close(struct iostream_private *stream,
653
						bool close_parent)
654
{
655
	struct attachment_istream *astream =
656
		(struct attachment_istream *)stream;
657
	struct message_part *parts;
658
	int ret;
659
660
	if (astream->parser != NULL) {
661
		ret = message_parser_deinit(&astream->parser, &parts);
662
		i_assert(ret == 0); /* we didn't use preparsed message_parts */
663
	}
664
	hash_format_deinit_free(&astream->set.hash_format);
665
	if (astream->pool != NULL)
666
		pool_unref(&astream->pool);
667
	if (close_parent)
668
		i_stream_close(astream->istream.parent);
669
}
670
671
struct istream *
672
i_stream_create_attachment_extractor(struct istream *input,
673
				     struct istream_attachment_settings *set,
674
				     void *context)
675
{
676
	struct attachment_istream *astream;
677
678
	i_assert(set->min_size > 0);
679
	i_assert(set->hash_format != NULL);
680
	i_assert(set->open_attachment_ostream != NULL);
681
	i_assert(set->close_attachment_ostream != NULL);
682
683
	astream = i_new(struct attachment_istream, 1);
684
	astream->part.temp_fd = -1;
685
	astream->set = *set;
686
	astream->context = context;
687
	astream->retry_read = TRUE;
688
689
	/* make sure the caller doesn't try to double-free this */
690
	set->hash_format = NULL;
691
692
	astream->istream.max_buffer_size = input->real_stream->max_buffer_size;
693
694
	astream->istream.read = i_stream_attachment_extractor_read;
695
	astream->istream.iostream.close = i_stream_attachment_extractor_close;
696
697
	astream->istream.istream.readable_fd = FALSE;
698
	astream->istream.istream.blocking = input->blocking;
699
	astream->istream.istream.seekable = FALSE;
700
701
	astream->pool = pool_alloconly_create("istream attachment", 1024);
702
	astream->parser = message_parser_init(astream->pool, input, 0,
703
				MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
704
				MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
705
	return i_stream_create(&astream->istream, input,
706
			       i_stream_get_fd(input));
707
}
708
709
bool i_stream_attachment_extractor_can_retry(struct istream *input)
710
{
711
	struct attachment_istream *astream =
712
		(struct attachment_istream *)input->real_stream;
713
714
	return astream->retry_read;
715
}