1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "str.h"
5 #include "array.h"
6 #include "istream.h"
7 #include "istream-crlf.h"
8 #include "istream-nonuls.h"
9 #include "istream-base64.h"
10 #include "istream-header-filter.h"
11 #include "istream-qp.h"
12 #include "ostream.h"
13 #include "message-parser.h"
14 #include "message-decoder.h"
15 #include "mail-storage-private.h"
16 #include "mail-namespace.h"
17 #include "imap-bodystructure.h"
18 #include "imap-parser.h"
19 #include "imap-msgpart.h"
20 
21 enum fetch_type {
22 	FETCH_FULL,
23 	FETCH_MIME,
24 	FETCH_MIME_BODY,
25 	FETCH_HEADER,
26 	FETCH_HEADER_FIELDS,
27 	FETCH_HEADER_FIELDS_NOT,
28 	FETCH_BODY
29 };
30 
31 struct imap_msgpart {
32 	pool_t pool;
33 
34 	/* "" for root, otherwise e.g. "1.2.3". the .MIME, .HEADER, etc.
35 	   suffix not included */
36 	const char *section_number;
37 	enum fetch_type fetch_type;
38 	enum mail_fetch_field wanted_fields;
39 
40 	/* HEADER.FIELDS[.NOT] (list of headers) */
41         struct mailbox_header_lookup_ctx *header_ctx;
42 	const char *const *headers;
43 
44 	/* which part of the message part to fetch (default: 0..UOFF_T_MAX) */
45 	uoff_t partial_offset, partial_size;
46 
47 	bool decode_cte_to_binary:1;
48 };
49 
50 struct imap_msgpart_open_ctx {
51 	/* from matching message_part, set after opening: */
52 	uoff_t physical_pos;
53 	struct message_size mime_hdr_size;
54 	struct message_size mime_body_size;
55 };
56 
imap_msgpart_type(enum fetch_type fetch_type)57 static struct imap_msgpart *imap_msgpart_type(enum fetch_type fetch_type)
58 {
59 	struct imap_msgpart *msgpart;
60 	pool_t pool;
61 
62 	pool = pool_alloconly_create("imap msgpart", sizeof(*msgpart)+32);
63 	msgpart = p_new(pool, struct imap_msgpart, 1);
64 	msgpart->pool = pool;
65 	msgpart->partial_size = UOFF_T_MAX;
66 	msgpart->fetch_type = fetch_type;
67 	msgpart->section_number = "";
68 	if (fetch_type == FETCH_HEADER || fetch_type == FETCH_FULL)
69 		msgpart->wanted_fields |= MAIL_FETCH_STREAM_HEADER;
70 	if (fetch_type == FETCH_BODY || fetch_type == FETCH_FULL)
71 		msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY;
72 	return msgpart;
73 }
74 
imap_msgpart_full(void)75 struct imap_msgpart *imap_msgpart_full(void)
76 {
77 	return imap_msgpart_type(FETCH_FULL);
78 }
79 
imap_msgpart_header(void)80 struct imap_msgpart *imap_msgpart_header(void)
81 {
82 	return imap_msgpart_type(FETCH_HEADER);
83 }
84 
imap_msgpart_body(void)85 struct imap_msgpart *imap_msgpart_body(void)
86 {
87 	return imap_msgpart_type(FETCH_BODY);
88 }
89 
90 static struct message_part *
imap_msgpart_find(struct message_part * parts,const char * section)91 imap_msgpart_find(struct message_part *parts, const char *section)
92 {
93 	struct message_part *part = parts;
94 	const char *path;
95 	unsigned int num;
96 
97 	path = section;
98 	while (*path >= '0' && *path <= '9' && part != NULL) {
99 		/* get part number, we have already verified its validity */
100 		num = 0;
101 		while (*path != '\0' && *path != '.') {
102 			i_assert(*path >= '0' && *path <= '9');
103 
104 			num = num*10 + (*path - '0');
105 			path++;
106 		}
107 
108 		if (*path == '.')
109 			path++;
110 
111 		if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
112 			/* find the part */
113 			part = part->children;
114 			for (; num > 1 && part != NULL; num--)
115 				part = part->next;
116 		} else {
117 			/* only 1 allowed with non-multipart messages.
118 			   if the child isn't message/rfc822, the path must be
119 			   finished after this. */
120 			if (num != 1)
121 				part = NULL;
122 			else if (*path != '\0' &&
123 				 (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0)
124 				part = NULL;
125 		}
126 
127 		if (part != NULL &&
128 		    (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0 &&
129 		    (*path >= '0' && *path <= '9')) {
130 			/* if we continue inside the message/rfc822, skip this
131 			   body part */
132 			part = part->children;
133 		}
134 	}
135 	i_assert(part == NULL || *path == '\0');
136 	return part;
137 }
138 
139 static int
imap_msgpart_get_header_fields(pool_t pool,const char * header_list,ARRAY_TYPE (const_string)* fields)140 imap_msgpart_get_header_fields(pool_t pool, const char *header_list,
141 			       ARRAY_TYPE(const_string) *fields)
142 {
143 	struct istream *input;
144 	struct imap_parser *parser;
145 	const struct imap_arg *args, *hdr_list;
146 	unsigned int list_count;
147 	unsigned int i;
148 	int result = 0;
149 
150 	input = i_stream_create_from_data(header_list, strlen(header_list));
151 	parser = imap_parser_create(input, NULL, SIZE_MAX);
152 
153 	if (imap_parser_finish_line(parser, 0, 0, &args) > 0 &&
154 	    imap_arg_get_list_full(args, &hdr_list, &list_count) &&
155 	    args[1].type == IMAP_ARG_EOL &&
156 	    list_count > 0) {
157 		const char *value;
158 
159 		p_array_init(fields, pool, list_count);
160 		for (i = 0; i < list_count; i++) {
161 			if (!imap_arg_get_astring(&hdr_list[i], &value)) {
162 				result = -1;
163 				break;
164 			}
165 
166 			value = p_strdup(pool, t_str_ucase(value));
167 			array_push_back(fields, &value);
168 		}
169 		/* istream-header-filter requires headers to be sorted */
170 		array_sort(fields, i_strcasecmp_p);
171 	} else {
172 		result = -1;
173 	}
174 
175 	imap_parser_unref(&parser);
176 	i_stream_unref(&input);
177 	return result;
178 }
179 
180 static int
imap_msgpart_parse_header_fields(struct imap_msgpart * msgpart,const char * header_list)181 imap_msgpart_parse_header_fields(struct imap_msgpart *msgpart,
182 				 const char *header_list)
183 {
184 	ARRAY_TYPE(const_string) fields;
185 
186 	if (header_list[0] == ' ')
187 		header_list++;
188 
189 	/* HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */
190 	if (imap_msgpart_get_header_fields(msgpart->pool, header_list,
191 					   &fields) < 0)
192 		return -1;
193 
194 	array_append_zero(&fields);
195 	msgpart->headers = array_front(&fields);
196 	return 0;
197 }
198 
imap_msgpart_parse(const char * section,struct imap_msgpart ** msgpart_r)199 int imap_msgpart_parse(const char *section, struct imap_msgpart **msgpart_r)
200 {
201 	struct imap_msgpart *msgpart;
202 	pool_t pool;
203 	unsigned int i;
204 	bool next_digit;
205 	int ret;
206 
207 	pool = pool_alloconly_create("imap msgpart", 1024);
208 	msgpart = *msgpart_r = p_new(pool, struct imap_msgpart, 1);
209 	msgpart->pool = pool;
210 	msgpart->partial_size = UOFF_T_MAX;
211 
212 	/* get the section number */
213 	next_digit = TRUE;
214 	for (i = 0; section[i] != '\0'; i++) {
215 		if (section[i] >= '0' && section[i] <= '9') {
216 			next_digit = FALSE;
217 		} else if (!next_digit && section[i] == '.') {
218 			next_digit = TRUE;
219 		} else {
220 			break;
221 		}
222 	}
223 	if (i == 0) {
224 		/* [], [HEADER], etc. */
225 		msgpart->section_number = "";
226 	} else if (section[i] == '\0') {
227 		/* [1.2.3] */
228 		if (i > 0 && section[i-1] == '.') {
229 			pool_unref(&pool);
230 			return -1;
231 		}
232 		msgpart->section_number = p_strdup(pool, section);
233 		section = "";
234 	} else {
235 		/* [1.2.3.MIME], [1.2.3.HEADER], etc */
236 		if (section[i-1] != '.') {
237 			pool_unref(&pool);
238 			return -1;
239 		}
240 		msgpart->section_number = p_strndup(pool, section, i-1);
241 		section += i;
242 	}
243 
244 	if (*section == '\0') {
245 		msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY;
246 		if (*msgpart->section_number == '\0') {
247 			/* BODY[] - header+body */
248 			msgpart->fetch_type = FETCH_FULL;
249 			msgpart->wanted_fields |= MAIL_FETCH_STREAM_HEADER;
250 		} else {
251 			/* BODY[1] - body only */
252 			msgpart->fetch_type = FETCH_MIME_BODY;
253 		}
254 		return 0;
255 	}
256 	section = t_str_ucase(section);
257 
258 	if (strcmp(section, "MIME") == 0) {
259 		if (msgpart->section_number[0] == '\0')
260 			return -1;
261 		msgpart->fetch_type = FETCH_MIME;
262 		msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY;
263 	} else if (strcmp(section, "TEXT") == 0) {
264 		/* body (for root or for message/rfc822) */
265 		msgpart->fetch_type = FETCH_BODY;
266 		msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY;
267 	} else if (str_begins(section, "HEADER")) {
268 		/* header (for root or for message/rfc822) */
269 		if (section[6] == '\0') {
270 			msgpart->fetch_type = FETCH_HEADER;
271 			ret = 0;
272 		} else if (str_begins(section, "HEADER.FIELDS.NOT")) {
273 			msgpart->fetch_type = FETCH_HEADER_FIELDS_NOT;
274 			ret = imap_msgpart_parse_header_fields(msgpart,
275 							       section+17);
276 		} else if (str_begins(section, "HEADER.FIELDS")) {
277 			msgpart->fetch_type = FETCH_HEADER_FIELDS;
278 			ret = imap_msgpart_parse_header_fields(msgpart,
279 							       section+13);
280 		} else {
281 			ret = -1;
282 		}
283 		if (ret < 0) {
284 			imap_msgpart_free(&msgpart);
285 			return -1;
286 		}
287 		if (msgpart->fetch_type == FETCH_HEADER_FIELDS) {
288 			/* we may be able to get this from cache, don't give a
289 			   wanted_fields hint */
290 		} else if (*msgpart->section_number == '\0')
291 			msgpart->wanted_fields |= MAIL_FETCH_STREAM_HEADER;
292 		else
293 			msgpart->wanted_fields |= MAIL_FETCH_STREAM_BODY;
294 	} else {
295 		imap_msgpart_free(&msgpart);
296 		return -1;
297 	}
298 	return 0;
299 }
300 
imap_msgpart_free(struct imap_msgpart ** _msgpart)301 void imap_msgpart_free(struct imap_msgpart **_msgpart)
302 {
303 	struct imap_msgpart *msgpart = *_msgpart;
304 
305 	*_msgpart = NULL;
306 
307 	imap_msgpart_close_mailbox(msgpart);
308 	pool_unref(&msgpart->pool);
309 }
310 
imap_msgpart_contains_body(const struct imap_msgpart * msgpart)311 bool imap_msgpart_contains_body(const struct imap_msgpart *msgpart)
312 {
313 	switch (msgpart->fetch_type) {
314 	case FETCH_HEADER:
315 	case FETCH_HEADER_FIELDS:
316 	case FETCH_HEADER_FIELDS_NOT:
317 		return FALSE;
318 	case FETCH_FULL:
319 	case FETCH_MIME:
320 	case FETCH_MIME_BODY:
321 	case FETCH_BODY:
322 		break;
323 	}
324 	return TRUE;
325 }
326 
imap_msgpart_set_decode_to_binary(struct imap_msgpart * msgpart)327 void imap_msgpart_set_decode_to_binary(struct imap_msgpart *msgpart)
328 {
329 	msgpart->decode_cte_to_binary = TRUE;
330 }
331 
imap_msgpart_set_partial(struct imap_msgpart * msgpart,uoff_t offset,uoff_t size)332 void imap_msgpart_set_partial(struct imap_msgpart *msgpart,
333 			      uoff_t offset, uoff_t size)
334 {
335 	msgpart->partial_offset = offset;
336 	msgpart->partial_size = size;
337 }
338 
imap_msgpart_get_partial_offset(struct imap_msgpart * msgpart)339 uoff_t imap_msgpart_get_partial_offset(struct imap_msgpart *msgpart)
340 {
341 	return msgpart->partial_offset;
342 }
343 
imap_msgpart_get_partial_size(struct imap_msgpart * msgpart)344 uoff_t imap_msgpart_get_partial_size(struct imap_msgpart *msgpart)
345 {
346 	return msgpart->partial_size;
347 }
348 
imap_msgpart_get_fetch_data(struct imap_msgpart * msgpart)349 enum mail_fetch_field imap_msgpart_get_fetch_data(struct imap_msgpart *msgpart)
350 {
351 	return msgpart->wanted_fields;
352 }
353 
imap_msgpart_get_wanted_headers(struct imap_msgpart * msgpart,ARRAY_TYPE (const_string)* headers)354 void imap_msgpart_get_wanted_headers(struct imap_msgpart *msgpart,
355 				     ARRAY_TYPE(const_string) *headers)
356 {
357 	unsigned int i;
358 
359 	if (msgpart->fetch_type != FETCH_HEADER_FIELDS)
360 		return;
361 
362 	for (i = 0; msgpart->headers[i] != NULL; i++)
363 		array_push_back(headers, &msgpart->headers[i]);
364 }
365 
366 static int
imap_msgpart_get_partial_header(struct mail * mail,struct istream * mail_input,const struct imap_msgpart * msgpart,uoff_t * virtual_size_r,bool * have_crlfs_r,struct imap_msgpart_open_result * result_r)367 imap_msgpart_get_partial_header(struct mail *mail, struct istream *mail_input,
368 				const struct imap_msgpart *msgpart,
369 				uoff_t *virtual_size_r, bool *have_crlfs_r,
370 				struct imap_msgpart_open_result *result_r)
371 {
372 	const char *const *hdr_fields = msgpart->headers;
373 	unsigned int hdr_count = str_array_length(hdr_fields);
374 	struct message_size hdr_size;
375 	struct istream *input;
376 	bool has_nuls;
377 
378 	if (msgpart->fetch_type != FETCH_HEADER_FIELDS) {
379 		i_assert(msgpart->fetch_type == FETCH_HEADER_FIELDS_NOT);
380 		input = i_stream_create_header_filter(mail_input,
381 						      HEADER_FILTER_EXCLUDE |
382 						      HEADER_FILTER_HIDE_BODY,
383 						      hdr_fields, hdr_count,
384 						      *null_header_filter_callback,
385 						      NULL);
386 	} else if (msgpart->section_number[0] != '\0') {
387 		/* fetching partial headers for a message/rfc822 part. */
388 		input = i_stream_create_header_filter(mail_input,
389 						      HEADER_FILTER_INCLUDE |
390 						      HEADER_FILTER_HIDE_BODY,
391 						      hdr_fields, hdr_count,
392 						      *null_header_filter_callback,
393 						      NULL);
394 	} else {
395 		/* mail_get_header_stream() already filtered out the
396 		   unwanted headers. */
397 		input = mail_input;
398 		i_stream_ref(input);
399 	}
400 
401 	if (message_get_header_size(input, &hdr_size, &has_nuls) < 0) {
402 		mail_set_critical(mail,
403 			"read(%s) failed: %s", i_stream_get_name(input),
404 			i_stream_get_error(input));
405 		i_stream_unref(&input);
406 		return -1;
407 	}
408 	i_stream_seek(input, 0);
409 	result_r->input = input;
410 	result_r->size = hdr_size.virtual_size;
411 	result_r->size_field = 0;
412 	*virtual_size_r = hdr_size.virtual_size;
413 	*have_crlfs_r = hdr_size.physical_size == hdr_size.virtual_size;
414 	return 0;
415 }
416 
417 static struct istream *
imap_msgpart_crlf_seek(struct mail * mail,struct istream * input,const struct imap_msgpart * msgpart)418 imap_msgpart_crlf_seek(struct mail *mail, struct istream *input,
419 		       const struct imap_msgpart *msgpart)
420 {
421 	struct mail_msgpart_partial_cache *cache = &mail->box->partial_cache;
422 	struct istream *crlf_input, *errinput;
423 	uoff_t physical_start = input->v_offset;
424 	uoff_t virtual_skip = msgpart->partial_offset;
425 	bool cr_skipped;
426 
427 	if (virtual_skip == 0) {
428 		/* no need to seek */
429 	} else if (mail->uid > 0 && cache->uid == mail->uid &&
430 		   cache->physical_start == physical_start &&
431 		   cache->virtual_pos < virtual_skip) {
432 		/* use cache */
433 		i_stream_seek(input, physical_start + cache->physical_pos);
434 		virtual_skip -= cache->virtual_pos;
435 	}
436 	if (message_skip_virtual(input, virtual_skip, &cr_skipped) < 0) {
437 		errinput = i_stream_create_error_str(errno, "%s", i_stream_get_error(input));
438 		i_stream_set_name(errinput, i_stream_get_name(input));
439 		i_stream_unref(&input);
440 		return errinput;
441 	}
442 
443 	if (mail->uid > 0 &&
444 	    (msgpart->partial_offset != 0 ||
445 	     msgpart->partial_size != UOFF_T_MAX) && !input->eof) {
446 		/* update cache */
447 		cache->uid = mail->uid;
448 		cache->physical_start = physical_start;
449 		cache->physical_pos = input->v_offset - physical_start;
450 		cache->virtual_pos = msgpart->partial_offset;
451 		if (cr_skipped) {
452 			/* the physical_pos points to virtual CRLF, but
453 			   virtual_pos already skipped CR. that can't work,
454 			   so seek back the virtual CR */
455 			cache->virtual_pos--;
456 		}
457 	}
458 	crlf_input = i_stream_create_crlf(input);
459 	if (cr_skipped)
460 		i_stream_skip(crlf_input, 1);
461 	i_stream_unref(&input);
462 	return crlf_input;
463 }
464 
465 static void
imap_msgpart_get_partial(struct mail * mail,const struct imap_msgpart * msgpart,bool convert_nuls,bool use_partial_cache,uoff_t virtual_size,bool have_crlfs,struct imap_msgpart_open_result * result)466 imap_msgpart_get_partial(struct mail *mail, const struct imap_msgpart *msgpart,
467 			 bool convert_nuls, bool use_partial_cache,
468 			 uoff_t virtual_size, bool have_crlfs,
469 			 struct imap_msgpart_open_result *result)
470 {
471 	struct istream *input2;
472 	uoff_t bytes_left;
473 
474 	/* input is already seeked to the beginning of the wanted data */
475 
476 	if (msgpart->partial_offset >= virtual_size) {
477 		/* can't seek past the MIME part */
478 		i_stream_unref(&result->input);
479 		result->input = i_stream_create_from_data("", 0);
480 		result->size = 0;
481 		return;
482 	}
483 
484 	if (have_crlfs) {
485 		/* input has CRLF linefeeds, we can quickly seek to
486 		   wanted position */
487 		i_stream_skip(result->input, msgpart->partial_offset);
488 	} else {
489 		/* input has LF linefeeds. it can be slow to seek to wanted
490 		   position, so try to do caching whenever possible */
491 		i_assert(use_partial_cache);
492 		result->input = imap_msgpart_crlf_seek(mail, result->input,
493 						       msgpart);
494 	}
495 
496 	bytes_left = virtual_size - msgpart->partial_offset;
497 	if (msgpart->partial_size <= bytes_left) {
498 		/* limit output to specified number of bytes */
499 		result->size = msgpart->partial_size;
500 	} else {
501 		/* send all bytes */
502 		result->size = bytes_left;
503 	}
504 
505 	if (!mail->has_no_nuls && convert_nuls) {
506 		/* IMAP literals must not contain NULs. change them to
507 		   0x80 characters. */
508 		input2 = i_stream_create_nonuls(result->input, '\x80');
509 		i_stream_unref(&result->input);
510 		result->input = input2;
511 	}
512 	input2 = i_stream_create_limit(result->input, result->size);
513 	i_stream_unref(&result->input);
514 	result->input = input2;
515 }
516 
517 static int
imap_msgpart_find_part(struct mail * mail,const struct imap_msgpart * msgpart,struct message_part ** part_r)518 imap_msgpart_find_part(struct mail *mail, const struct imap_msgpart *msgpart,
519 		       struct message_part **part_r)
520 {
521 	struct message_part *parts, *part = NULL;
522 
523 	if (*msgpart->section_number == '\0') {
524 		*part_r = NULL;
525 		return 1;
526 	}
527 
528 	if (mail_get_parts(mail, &parts) < 0)
529 		return -1;
530 	part = imap_msgpart_find(parts, msgpart->section_number);
531 	if (part == NULL) {
532 		/* MIME part not found. */
533 		*part_r = NULL;
534 		return 0;
535 	}
536 
537 	switch (msgpart->fetch_type) {
538 	case FETCH_MIME:
539 		/* What to do if this is a message/rfc822? Does it have
540 		   MIME headers or not? Possibilities are: a) no, return
541 		   empty string (UW-IMAP does this), b) return the same as
542 		   HEADER. Dovecot has done b) for a long time and it's not
543 		   very clear which one is correct, so we'll just continue
544 		   with b) */
545 	case FETCH_FULL:
546 	case FETCH_MIME_BODY:
547 		break;
548 	case FETCH_HEADER:
549 	case FETCH_HEADER_FIELDS:
550 	case FETCH_HEADER_FIELDS_NOT:
551 	case FETCH_BODY:
552 		/* fetching message/rfc822 part's header/body */
553 		if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0) {
554 			*part_r = NULL;
555 			return 0;
556 		}
557 		i_assert(part->children != NULL &&
558 			 part->children->next == NULL);
559 		part = part->children;
560 		break;
561 	}
562 	*part_r = part;
563 	return 1;
564 }
565 
566 static int
imap_msgpart_open_normal(struct mail * mail,struct imap_msgpart * msgpart,const struct message_part * part,uoff_t * virtual_size_r,bool * have_crlfs_r,struct imap_msgpart_open_result * result_r)567 imap_msgpart_open_normal(struct mail *mail, struct imap_msgpart *msgpart,
568 			 const struct message_part *part,
569 			 uoff_t *virtual_size_r, bool *have_crlfs_r,
570 			 struct imap_msgpart_open_result *result_r)
571 {
572 	struct message_size hdr_size, body_size, part_size;
573 	struct istream *input = NULL;
574 	bool unknown_crlfs = FALSE;
575 
576 	i_zero(&hdr_size);
577 	i_zero(&body_size);
578 	i_zero(&part_size);
579 
580 	if (*msgpart->section_number != '\0') {
581 		/* find the MIME part */
582 		i_assert(part != NULL);
583 
584 		if (mail_get_stream_because(mail, NULL, NULL, "MIME part", &input) < 0)
585 			return -1;
586 
587 		i_stream_seek(input, part->physical_pos);
588 		hdr_size = part->header_size;
589 		body_size = part->body_size;
590 	} else switch (msgpart->fetch_type) {
591 	case FETCH_FULL:
592 		/* fetch the whole message */
593 		if (mail_get_stream_because(mail, NULL, NULL, "full mail", &input) < 0 ||
594 		    mail_get_virtual_size(mail, &body_size.virtual_size) < 0)
595 			return -1;
596 		result_r->size_field = MAIL_FETCH_VIRTUAL_SIZE;
597 
598 		i_assert(mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER);
599 		mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL;
600 		if (mail_get_physical_size(mail, &body_size.physical_size) < 0)
601 			unknown_crlfs = TRUE;
602 		mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER;
603 		break;
604 	case FETCH_MIME:
605 	case FETCH_MIME_BODY:
606 		i_unreached();
607 	case FETCH_HEADER:
608 	case FETCH_HEADER_FIELDS_NOT:
609 		/* fetch the message's header */
610 		if (mail_get_hdr_stream(mail, &hdr_size, &input) < 0)
611 			return -1;
612 		result_r->size_field = MAIL_FETCH_MESSAGE_PARTS;
613 		break;
614 	case FETCH_HEADER_FIELDS:
615 		/* try to lookup the headers from cache */
616 		if (msgpart->header_ctx == NULL) {
617 			msgpart->header_ctx =
618 				mailbox_header_lookup_init(mail->box,
619 							   msgpart->headers);
620 		}
621 		if (mail_get_header_stream(mail, msgpart->header_ctx,
622 					   &input) < 0)
623 			return -1;
624 		result_r->size_field = 0;
625 		break;
626 	case FETCH_BODY:
627 		/* fetch the message's body */
628 		if (mail_get_stream_because(mail, &hdr_size, &body_size,
629 					    "mail body", &input) < 0)
630 			return -1;
631 		result_r->size_field = MAIL_FETCH_MESSAGE_PARTS;
632 		break;
633 	}
634 
635 	if (msgpart->headers != NULL) {
636 		/* return specific headers */
637 		return imap_msgpart_get_partial_header(mail, input, msgpart,
638 						       virtual_size_r,
639 						       have_crlfs_r, result_r);
640 	}
641 
642 	switch (msgpart->fetch_type) {
643 	case FETCH_FULL:
644 		part_size.physical_size += body_size.physical_size;
645 		part_size.virtual_size += body_size.virtual_size;
646 		/* fall through */
647 	case FETCH_MIME:
648 	case FETCH_HEADER:
649 		part_size.physical_size += hdr_size.physical_size;
650 		part_size.virtual_size += hdr_size.virtual_size;
651 		break;
652 	case FETCH_HEADER_FIELDS:
653 	case FETCH_HEADER_FIELDS_NOT:
654 		i_unreached();
655 	case FETCH_BODY:
656 	case FETCH_MIME_BODY:
657 		i_stream_skip(input, hdr_size.physical_size);
658 		part_size.physical_size += body_size.physical_size;
659 		part_size.virtual_size += body_size.virtual_size;
660 		break;
661 	}
662 
663 	result_r->input = input;
664 	i_stream_ref(input);
665 	*virtual_size_r = part_size.virtual_size;
666 	*have_crlfs_r = !unknown_crlfs &&
667 		part_size.virtual_size == part_size.physical_size;
668 	return 0;
669 }
670 
imap_msgpart_open(struct mail * mail,struct imap_msgpart * msgpart,struct imap_msgpart_open_result * result_r)671 int imap_msgpart_open(struct mail *mail, struct imap_msgpart *msgpart,
672 		      struct imap_msgpart_open_result *result_r)
673 {
674 	struct message_part *part;
675 	uoff_t virtual_size;
676 	bool include_hdr, binary, use_partial_cache, have_crlfs;
677 	int ret;
678 
679 	i_zero(result_r);
680 
681 	if ((ret = imap_msgpart_find_part(mail, msgpart, &part)) < 0)
682 		return -1;
683 	if (ret == 0) {
684 		/* MIME part not found. return an empty part. */
685 		result_r->input = i_stream_create_from_data("", 0);
686 		return 0;
687 	}
688 
689 	if (msgpart->decode_cte_to_binary &&
690 	    (msgpart->fetch_type == FETCH_FULL ||
691 	     msgpart->fetch_type == FETCH_BODY ||
692 	     msgpart->fetch_type == FETCH_MIME_BODY)) {
693 		/* binary fetch */
694 		include_hdr = msgpart->fetch_type == FETCH_FULL;
695 		if (part == NULL) {
696 			if (mail_get_parts(mail, &part) < 0)
697 				return -1;
698 		}
699 		if (mail_get_binary_stream(mail, part, include_hdr,
700 					   &virtual_size, &binary,
701 					   &result_r->input) < 0)
702 			return -1;
703 		have_crlfs = TRUE;
704 		use_partial_cache = FALSE;
705 	} else {
706 		if (imap_msgpart_open_normal(mail, msgpart, part, &virtual_size,
707 					     &have_crlfs, result_r) < 0)
708 			return -1;
709 		binary = FALSE;
710 		use_partial_cache = TRUE;
711 	}
712 
713 	if (binary && msgpart->decode_cte_to_binary)
714 		result_r->binary_decoded_input_has_nuls = TRUE;
715 
716 	imap_msgpart_get_partial(mail, msgpart, !binary, use_partial_cache,
717 				 virtual_size, have_crlfs, result_r);
718 	return 0;
719 }
720 
imap_msgpart_size(struct mail * mail,struct imap_msgpart * msgpart,uoff_t * size_r)721 int imap_msgpart_size(struct mail *mail, struct imap_msgpart *msgpart,
722 		      uoff_t *size_r)
723 {
724 	struct imap_msgpart_open_result result;
725 	struct message_part *part;
726 	bool include_hdr;
727 	unsigned int lines;
728 	int ret;
729 
730 	if (!msgpart->decode_cte_to_binary ||
731 	    (msgpart->fetch_type != FETCH_FULL &&
732 	     msgpart->fetch_type != FETCH_BODY &&
733 	     msgpart->fetch_type != FETCH_MIME_BODY)) {
734 		/* generic implementation */
735 		if (imap_msgpart_open(mail, msgpart, &result) < 0)
736 			return -1;
737 		i_stream_unref(&result.input);
738 		*size_r = result.size;
739 		return 0;
740 	}
741 
742 	/* binary-optimized implementation: */
743 	if ((ret = imap_msgpart_find_part(mail, msgpart, &part)) < 0)
744 		return -1;
745 	if (ret == 0) {
746 		/* MIME part not found. return an empty part. */
747 		*size_r = 0;
748 		return 0;
749 	}
750 	if (part == NULL) {
751 		if (mail_get_parts(mail, &part) < 0)
752 			return -1;
753 	}
754 	include_hdr = msgpart->fetch_type == FETCH_FULL;
755 	return mail_get_binary_size(mail, part, include_hdr, size_r, &lines);
756 }
757 
758 static int
imap_msgpart_parse_bodystructure(struct mail * mail,struct message_part * all_parts)759 imap_msgpart_parse_bodystructure(struct mail *mail,
760 				 struct message_part *all_parts)
761 {
762 	struct mail_private *pmail = (struct mail_private *)mail;
763 	const char *bodystructure, *error;
764 
765 	if (mail_get_special(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE,
766 			     &bodystructure) < 0)
767 		return -1;
768 	if (all_parts->context != NULL) {
769 		/* we just parsed the bodystructure */
770 		return 0;
771 	}
772 
773 	if (imap_bodystructure_parse(bodystructure, pmail->data_pool,
774 				     all_parts, &error) < 0) {
775 		mail_set_cache_corrupted(mail,
776 			MAIL_FETCH_IMAP_BODYSTRUCTURE, t_strdup_printf(
777 			"Invalid message_part/BODYSTRUCTURE %s: %s",
778 			bodystructure, error));
779 		return -1;
780 	}
781 	return 0;
782 }
783 
784 static int
imap_msgpart_vsizes_to_binary(struct mail * mail,const struct message_part * part,struct message_part ** binpart_r)785 imap_msgpart_vsizes_to_binary(struct mail *mail, const struct message_part *part,
786 			      struct message_part **binpart_r)
787 {
788 	struct message_part **pos;
789 	uoff_t size;
790 	unsigned int lines;
791 
792 	if (mail_get_binary_size(mail, part, FALSE, &size, &lines) < 0)
793 		return -1;
794 
795 	*binpart_r = t_new(struct message_part, 1);
796 	**binpart_r = *part;
797 	(*binpart_r)->body_size.virtual_size = size;
798 	(*binpart_r)->body_size.lines = lines;
799 
800 	pos = &(*binpart_r)->children;
801 	for (part = part->children; part != NULL; part = part->next) {
802 		if (imap_msgpart_vsizes_to_binary(mail, part, pos) < 0)
803 			return -1;
804 		pos = &(*pos)->next;
805 	}
806 	return 0;
807 }
808 
imap_msgpart_bodypartstructure(struct mail * mail,struct imap_msgpart * msgpart,const char ** bpstruct_r)809 int imap_msgpart_bodypartstructure(struct mail *mail,
810 				   struct imap_msgpart *msgpart,
811 				   const char **bpstruct_r)
812 {
813 	struct message_part *all_parts, *part;
814 	string_t *bpstruct;
815 	const char *error;
816 	int ret;
817 
818 	/* if we start parsing the body in here, make sure we also parse the
819 	   BODYSTRUCTURE */
820 	mail_add_temp_wanted_fields(mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, NULL);
821 
822 	if ((ret = imap_msgpart_find_part(mail, msgpart, &part)) < 0)
823 		return -1;
824 	if (ret == 0) {
825 		/* MIME part not found. */
826 		*bpstruct_r = NULL;
827 		return 0;
828 	}
829 
830 	if (mail_get_parts(mail, &all_parts) < 0)
831 		return -1;
832 	if (all_parts->context == NULL) {
833 		if (imap_msgpart_parse_bodystructure(mail, all_parts) < 0)
834 			return -1;
835 	}
836 	if (part == NULL)
837 		part = all_parts;
838 
839 	if (msgpart->decode_cte_to_binary)
840 		ret = imap_msgpart_vsizes_to_binary(mail, part, &part);
841 
842 	if (ret >= 0) {
843 		bpstruct = t_str_new(256);
844 		if (imap_bodystructure_write(part, bpstruct, TRUE, &error) < 0) {
845 			error = t_strdup_printf(
846 				"Invalid message_part/BODYSTRUCTURE: %s", error);
847 			mail_set_cache_corrupted(mail, MAIL_FETCH_MESSAGE_PARTS,
848 						 error);
849 			return -1;
850 		}
851 		*bpstruct_r = str_c(bpstruct);
852 	}
853 	return ret < 0 ? -1 : 1;
854 }
855 
856 
imap_msgpart_close_mailbox(struct imap_msgpart * msgpart)857 void imap_msgpart_close_mailbox(struct imap_msgpart *msgpart)
858 {
859 	mailbox_header_lookup_unref(&msgpart->header_ctx);
860 }
861