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