/src/dovecot/src/lib-storage/index/index-mail-binary.c
Line | Count | Source |
1 | | /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str.h" |
5 | | #include "safe-mkstemp.h" |
6 | | #include "istream.h" |
7 | | #include "istream-crlf.h" |
8 | | #include "istream-seekable.h" |
9 | | #include "istream-base64.h" |
10 | | #include "istream-qp.h" |
11 | | #include "istream-header-filter.h" |
12 | | #include "ostream.h" |
13 | | #include "message-binary-part.h" |
14 | | #include "message-parser.h" |
15 | | #include "message-decoder.h" |
16 | | #include "mail-user.h" |
17 | | #include "index-storage.h" |
18 | | #include "index-mail.h" |
19 | | |
20 | | #define MAIL_BINARY_CACHE_EXPIRE_MSECS (60*1000) |
21 | | |
22 | | #define IS_CONVERTED_CTE(cte) \ |
23 | 0 | ((cte) == MESSAGE_CTE_QP || (cte) == MESSAGE_CTE_BASE64) |
24 | | |
25 | | struct binary_block { |
26 | | struct istream *input; |
27 | | uoff_t physical_pos; |
28 | | unsigned int body_lines_count; |
29 | | bool converted, converted_hdr; |
30 | | }; |
31 | | |
32 | | struct binary_ctx { |
33 | | struct mail *mail; |
34 | | struct istream *input; |
35 | | bool has_nuls, converted; |
36 | | /* each block is its own input stream. basically each converted MIME |
37 | | body has its own block and the parts between the MIME bodies are |
38 | | unconverted blocks */ |
39 | | ARRAY(struct binary_block) blocks; |
40 | | |
41 | | uoff_t copy_start_offset; |
42 | | }; |
43 | | |
44 | | static void binary_copy_to(struct binary_ctx *ctx, uoff_t end_offset) |
45 | 0 | { |
46 | 0 | struct binary_block *block; |
47 | 0 | struct istream *linput, *cinput; |
48 | 0 | uoff_t orig_offset, size; |
49 | |
|
50 | 0 | i_assert(end_offset >= ctx->copy_start_offset); |
51 | | |
52 | 0 | if (end_offset == ctx->copy_start_offset) |
53 | 0 | return; |
54 | | |
55 | 0 | size = end_offset - ctx->copy_start_offset; |
56 | 0 | orig_offset = ctx->input->v_offset; |
57 | |
|
58 | 0 | i_stream_seek(ctx->input, ctx->copy_start_offset); |
59 | 0 | linput = i_stream_create_limit(ctx->input, size); |
60 | 0 | cinput = i_stream_create_crlf(linput); |
61 | 0 | i_stream_unref(&linput); |
62 | |
|
63 | 0 | block = array_append_space(&ctx->blocks); |
64 | 0 | block->input = cinput; |
65 | |
|
66 | 0 | i_stream_seek(ctx->input, orig_offset); |
67 | 0 | } |
68 | | |
69 | | static void |
70 | | binary_cte_filter_callback(struct header_filter_istream *input, |
71 | | struct message_header_line *hdr, |
72 | | bool *matched ATTR_UNUSED, void *context ATTR_UNUSED) |
73 | 0 | { |
74 | 0 | static const char *cte_binary = "Content-Transfer-Encoding: binary\r\n"; |
75 | |
|
76 | 0 | if (hdr != NULL && hdr->eoh) { |
77 | 0 | i_stream_header_filter_add(input, cte_binary, |
78 | 0 | strlen(cte_binary)); |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | | static int |
83 | | add_binary_part(struct binary_ctx *ctx, const struct message_part *part, |
84 | | bool include_hdr) |
85 | 0 | { |
86 | 0 | static const char *filter_headers[] = { |
87 | 0 | "Content-Transfer-Encoding", |
88 | 0 | }; |
89 | 0 | struct message_header_parser_ctx *parser; |
90 | 0 | struct message_header_line *hdr; |
91 | 0 | struct message_part *child; |
92 | 0 | struct message_size hdr_size; |
93 | 0 | struct istream *linput; |
94 | 0 | struct binary_block *block; |
95 | 0 | enum message_cte cte; |
96 | 0 | uoff_t part_end_offset; |
97 | 0 | int ret; |
98 | | |
99 | | /* first parse the header to find c-t-e. */ |
100 | 0 | i_stream_seek(ctx->input, part->physical_pos); |
101 | |
|
102 | 0 | cte = MESSAGE_CTE_78BIT; |
103 | 0 | parser = message_parse_header_init(ctx->input, &hdr_size, 0); |
104 | 0 | while ((ret = message_parse_header_next(parser, &hdr)) > 0) { |
105 | 0 | if (strcasecmp(hdr->name, "Content-Transfer-Encoding") == 0) |
106 | 0 | cte = message_decoder_parse_cte(hdr); |
107 | 0 | } |
108 | 0 | i_assert(ret < 0); |
109 | 0 | if (message_parse_header_has_nuls(parser)) { |
110 | | /* we're not converting NULs to 0x80 when doing a binary fetch, |
111 | | even if they're in the message header. */ |
112 | 0 | ctx->has_nuls = TRUE; |
113 | 0 | } |
114 | 0 | message_parse_header_deinit(&parser); |
115 | |
|
116 | 0 | if (ctx->input->stream_errno != 0) { |
117 | 0 | mail_set_critical(ctx->mail, |
118 | 0 | "read(%s) failed: %s", i_stream_get_name(ctx->input), |
119 | 0 | i_stream_get_error(ctx->input)); |
120 | 0 | return -1; |
121 | 0 | } |
122 | | |
123 | 0 | if (cte == MESSAGE_CTE_UNKNOWN) { |
124 | 0 | mail_storage_set_error(ctx->mail->box->storage, |
125 | 0 | MAIL_ERROR_CONVERSION, |
126 | 0 | "Unknown Content-Transfer-Encoding"); |
127 | 0 | return -1; |
128 | 0 | } |
129 | | |
130 | 0 | i_stream_seek(ctx->input, part->physical_pos); |
131 | 0 | if (!include_hdr) { |
132 | | /* body only */ |
133 | 0 | } else if (IS_CONVERTED_CTE(cte)) { |
134 | | /* write header with modified content-type */ |
135 | 0 | if (ctx->copy_start_offset != 0) |
136 | 0 | binary_copy_to(ctx, part->physical_pos); |
137 | 0 | block = array_append_space(&ctx->blocks); |
138 | 0 | block->physical_pos = part->physical_pos; |
139 | 0 | block->converted = TRUE; |
140 | 0 | block->converted_hdr = TRUE; |
141 | |
|
142 | 0 | linput = i_stream_create_limit(ctx->input, UOFF_T_MAX); |
143 | 0 | block->input = i_stream_create_header_filter(linput, |
144 | 0 | HEADER_FILTER_EXCLUDE | HEADER_FILTER_HIDE_BODY, |
145 | 0 | filter_headers, N_ELEMENTS(filter_headers), |
146 | 0 | binary_cte_filter_callback, NULL); |
147 | 0 | i_stream_unref(&linput); |
148 | 0 | } else { |
149 | | /* copy everything as-is until the end of this header */ |
150 | 0 | binary_copy_to(ctx, part->physical_pos + |
151 | 0 | part->header_size.physical_size); |
152 | 0 | } |
153 | 0 | ctx->copy_start_offset = part->physical_pos + |
154 | 0 | part->header_size.physical_size; |
155 | 0 | part_end_offset = part->physical_pos + |
156 | 0 | part->header_size.physical_size + |
157 | 0 | part->body_size.physical_size; |
158 | |
|
159 | 0 | if (part->children != NULL) { |
160 | | /* multipart */ |
161 | 0 | for (child = part->children; child != NULL; child = child->next) { |
162 | 0 | if (add_binary_part(ctx, child, TRUE) < 0) |
163 | 0 | return -1; |
164 | 0 | } |
165 | 0 | binary_copy_to(ctx, part_end_offset); |
166 | 0 | ctx->copy_start_offset = part_end_offset; |
167 | 0 | return 0; |
168 | 0 | } |
169 | 0 | if (part->body_size.physical_size == 0) { |
170 | | /* no body */ |
171 | 0 | ctx->copy_start_offset = part_end_offset; |
172 | 0 | return 0; |
173 | 0 | } |
174 | | |
175 | | /* single part - write decoded data */ |
176 | 0 | block = array_append_space(&ctx->blocks); |
177 | 0 | block->physical_pos = part->physical_pos; |
178 | |
|
179 | 0 | i_stream_seek(ctx->input, part->physical_pos + |
180 | 0 | part->header_size.physical_size); |
181 | 0 | linput = i_stream_create_limit(ctx->input, part->body_size.physical_size); |
182 | 0 | switch (cte) { |
183 | 0 | case MESSAGE_CTE_UNKNOWN: |
184 | 0 | i_unreached(); |
185 | 0 | case MESSAGE_CTE_78BIT: |
186 | 0 | case MESSAGE_CTE_BINARY: |
187 | | /* no conversion necessary */ |
188 | 0 | if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) |
189 | 0 | ctx->has_nuls = TRUE; |
190 | 0 | block->input = i_stream_create_crlf(linput); |
191 | 0 | break; |
192 | 0 | case MESSAGE_CTE_QP: |
193 | 0 | block->input = i_stream_create_qp_decoder(linput); |
194 | 0 | ctx->converted = block->converted = TRUE; |
195 | 0 | break; |
196 | 0 | case MESSAGE_CTE_BASE64: |
197 | 0 | block->input = i_stream_create_base64_decoder(linput); |
198 | 0 | ctx->converted = block->converted = TRUE; |
199 | 0 | break; |
200 | 0 | } |
201 | 0 | i_stream_unref(&linput); |
202 | |
|
203 | 0 | ctx->copy_start_offset = part_end_offset; |
204 | 0 | return 0; |
205 | 0 | } |
206 | | |
207 | | static int fd_callback(const char **path_r, void *context) |
208 | 0 | { |
209 | 0 | struct mail *_mail = context; |
210 | 0 | string_t *path; |
211 | 0 | int fd; |
212 | |
|
213 | 0 | path = t_str_new(256); |
214 | 0 | mail_user_set_get_temp_prefix(path, _mail->box->storage->user->set); |
215 | 0 | fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1); |
216 | 0 | if (fd == -1) { |
217 | 0 | e_error(mail_event(_mail), |
218 | 0 | "Temp file creation to %s failed: %m", str_c(path)); |
219 | 0 | return -1; |
220 | 0 | } |
221 | | |
222 | | /* we just want the fd, unlink it */ |
223 | 0 | if (i_unlink(str_c(path)) < 0) { |
224 | | /* shouldn't happen.. */ |
225 | 0 | i_close_fd(&fd); |
226 | 0 | return -1; |
227 | 0 | } |
228 | 0 | *path_r = str_c(path); |
229 | 0 | return fd; |
230 | 0 | } |
231 | | |
232 | | static void binary_streams_free(struct binary_ctx *ctx) |
233 | 0 | { |
234 | 0 | struct binary_block *block; |
235 | |
|
236 | 0 | array_foreach_modifiable(&ctx->blocks, block) |
237 | 0 | i_stream_unref(&block->input); |
238 | 0 | } |
239 | | |
240 | | static void |
241 | | binary_parts_update(struct binary_ctx *ctx, const struct message_part *part, |
242 | | struct message_binary_part **msg_bin_parts) |
243 | 0 | { |
244 | 0 | struct index_mail *mail = INDEX_MAIL(ctx->mail); |
245 | 0 | struct binary_block *blocks; |
246 | 0 | struct message_binary_part bin_part; |
247 | 0 | unsigned int i, count; |
248 | 0 | uoff_t size; |
249 | 0 | bool found; |
250 | |
|
251 | 0 | blocks = array_get_modifiable(&ctx->blocks, &count); |
252 | 0 | for (; part != NULL; part = part->next) { |
253 | 0 | binary_parts_update(ctx, part->children, msg_bin_parts); |
254 | |
|
255 | 0 | i_zero(&bin_part); |
256 | | /* default to unchanged header */ |
257 | 0 | bin_part.binary_hdr_size = part->header_size.virtual_size; |
258 | 0 | bin_part.physical_pos = part->physical_pos; |
259 | 0 | found = FALSE; |
260 | 0 | for (i = 0; i < count; i++) { |
261 | 0 | if (blocks[i].physical_pos != part->physical_pos || |
262 | 0 | !blocks[i].converted) |
263 | 0 | continue; |
264 | | |
265 | 0 | size = blocks[i].input->v_offset; |
266 | 0 | if (blocks[i].converted_hdr) |
267 | 0 | bin_part.binary_hdr_size = size; |
268 | 0 | else |
269 | 0 | bin_part.binary_body_size = size; |
270 | 0 | found = TRUE; |
271 | 0 | } |
272 | 0 | if (found) { |
273 | 0 | bin_part.next = *msg_bin_parts; |
274 | 0 | *msg_bin_parts = p_new(mail->mail.data_pool, |
275 | 0 | struct message_binary_part, 1); |
276 | 0 | **msg_bin_parts = bin_part; |
277 | 0 | } |
278 | 0 | } |
279 | 0 | } |
280 | | |
281 | | static void binary_parts_cache(struct binary_ctx *ctx) |
282 | 0 | { |
283 | 0 | struct index_mail *mail = INDEX_MAIL(ctx->mail); |
284 | 0 | buffer_t *buf; |
285 | |
|
286 | 0 | buf = t_buffer_create(128); |
287 | 0 | message_binary_part_serialize(mail->data.bin_parts, buf); |
288 | 0 | index_mail_cache_add(mail, MAIL_CACHE_BINARY_PARTS, |
289 | 0 | buf->data, buf->used); |
290 | 0 | } |
291 | | |
292 | | static struct istream **blocks_get_streams(struct binary_ctx *ctx) |
293 | 0 | { |
294 | 0 | struct istream **streams; |
295 | 0 | const struct binary_block *blocks; |
296 | 0 | unsigned int i, count; |
297 | |
|
298 | 0 | blocks = array_get(&ctx->blocks, &count); |
299 | 0 | streams = t_new(struct istream *, count+1); |
300 | 0 | for (i = 0; i < count; i++) { |
301 | 0 | streams[i] = blocks[i].input; |
302 | 0 | i_assert(streams[i]->v_offset == 0); |
303 | 0 | } |
304 | 0 | return streams; |
305 | 0 | } |
306 | | |
307 | | static int |
308 | | blocks_count_lines(struct binary_ctx *ctx, struct istream *full_input) |
309 | 0 | { |
310 | 0 | struct binary_block *blocks, *cur_block; |
311 | 0 | unsigned int block_idx, block_count; |
312 | 0 | uoff_t cur_block_offset, cur_block_size; |
313 | 0 | const unsigned char *data, *p; |
314 | 0 | size_t size, skip; |
315 | 0 | ssize_t ret; |
316 | |
|
317 | 0 | blocks = array_get_modifiable(&ctx->blocks, &block_count); |
318 | 0 | cur_block = blocks; |
319 | 0 | cur_block_offset = 0; |
320 | 0 | block_idx = 0; |
321 | | |
322 | | /* count the number of lines each block contains */ |
323 | 0 | while ((ret = i_stream_read_more(full_input, &data, &size)) > 0) { |
324 | 0 | i_assert(cur_block_offset <= cur_block->input->v_offset); |
325 | 0 | if (cur_block->input->eof) { |
326 | | /* this is the last input for this block. the input |
327 | | may also contain the next block's data, which we |
328 | | don't want to include in this block's line count. */ |
329 | 0 | cur_block_size = cur_block->input->v_offset + |
330 | 0 | i_stream_get_data_size(cur_block->input); |
331 | 0 | i_assert(size >= cur_block_size - cur_block_offset); |
332 | 0 | size = cur_block_size - cur_block_offset; |
333 | 0 | } |
334 | 0 | skip = size; |
335 | 0 | while ((p = memchr(data, '\n', size)) != NULL) { |
336 | 0 | size -= p-data+1; |
337 | 0 | data = p+1; |
338 | 0 | cur_block->body_lines_count++; |
339 | 0 | } |
340 | 0 | i_stream_skip(full_input, skip); |
341 | 0 | cur_block_offset += skip; |
342 | |
|
343 | 0 | if (i_stream_read_eof(cur_block->input)) { |
344 | | /* go to the next block */ |
345 | 0 | if (block_idx+1 == block_count) { |
346 | 0 | i_assert(i_stream_read_eof(full_input)); |
347 | 0 | ret = -1; |
348 | 0 | break; |
349 | 0 | } |
350 | 0 | block_idx++; |
351 | 0 | cur_block++; |
352 | 0 | cur_block_offset = 0; |
353 | 0 | } |
354 | 0 | } |
355 | 0 | i_assert(ret == -1); |
356 | 0 | if (full_input->stream_errno != 0) |
357 | 0 | return -1; |
358 | 0 | i_assert(block_count == 0 || !i_stream_have_bytes_left(cur_block->input)); |
359 | 0 | i_assert(block_count == 0 || block_idx+1 == block_count); |
360 | 0 | return 0; |
361 | 0 | } |
362 | | |
363 | | static int |
364 | | index_mail_read_binary_to_cache(struct mail *_mail, |
365 | | const struct message_part *part, |
366 | | bool include_hdr, const char *reason, |
367 | | bool *binary_r, bool *converted_r) |
368 | 0 | { |
369 | 0 | struct index_mail *mail = INDEX_MAIL(_mail); |
370 | 0 | struct mail_binary_cache *cache = &_mail->box->storage->binary_cache; |
371 | 0 | struct binary_ctx ctx; |
372 | 0 | struct istream *is; |
373 | |
|
374 | 0 | i_zero(&ctx); |
375 | 0 | ctx.mail = _mail; |
376 | 0 | t_array_init(&ctx.blocks, 8); |
377 | |
|
378 | 0 | mail_storage_free_binary_cache(_mail->box->storage); |
379 | 0 | if (mail_get_stream_because(_mail, NULL, NULL, reason, &ctx.input) < 0) |
380 | 0 | return -1; |
381 | | |
382 | 0 | if (add_binary_part(&ctx, part, include_hdr) < 0) { |
383 | 0 | binary_streams_free(&ctx); |
384 | 0 | return -1; |
385 | 0 | } |
386 | | |
387 | 0 | if (array_count(&ctx.blocks) != 0) { |
388 | 0 | is = i_streams_merge(blocks_get_streams(&ctx), |
389 | 0 | IO_BLOCK_SIZE, |
390 | 0 | fd_callback, _mail); |
391 | 0 | } else { |
392 | 0 | is = i_stream_create_from_data("", 0); |
393 | 0 | } |
394 | 0 | i_stream_set_name(is, t_strdup_printf( |
395 | 0 | "<binary stream of mailbox %s UID %u>", |
396 | 0 | _mail->box->vname, _mail->uid)); |
397 | 0 | if (blocks_count_lines(&ctx, is) < 0) { |
398 | 0 | if (is->stream_errno == EINVAL) { |
399 | | /* MIME part contains invalid data */ |
400 | 0 | mail_storage_set_error(_mail->box->storage, |
401 | 0 | MAIL_ERROR_INVALIDDATA, |
402 | 0 | "Invalid data in MIME part"); |
403 | 0 | } else { |
404 | 0 | mail_set_critical(_mail, "read(%s) failed: %s", |
405 | 0 | i_stream_get_name(is), |
406 | 0 | i_stream_get_error(is)); |
407 | 0 | } |
408 | 0 | i_stream_unref(&is); |
409 | 0 | binary_streams_free(&ctx); |
410 | 0 | return -1; |
411 | 0 | } |
412 | | |
413 | 0 | if (_mail->uid > 0) { |
414 | 0 | cache->to = timeout_add(MAIL_BINARY_CACHE_EXPIRE_MSECS, |
415 | 0 | mail_storage_free_binary_cache, |
416 | 0 | _mail->box->storage); |
417 | 0 | cache->box = _mail->box; |
418 | 0 | cache->uid = _mail->uid; |
419 | 0 | cache->orig_physical_pos = part->physical_pos; |
420 | 0 | cache->include_hdr = include_hdr; |
421 | 0 | cache->input = is; |
422 | 0 | } |
423 | |
|
424 | 0 | i_assert(!i_stream_have_bytes_left(is)); |
425 | 0 | cache->size = is->v_offset; |
426 | 0 | i_stream_seek(is, 0); |
427 | |
|
428 | 0 | if (part->parent == NULL && include_hdr && |
429 | 0 | mail->data.bin_parts == NULL) { |
430 | 0 | binary_parts_update(&ctx, part, &mail->data.bin_parts); |
431 | 0 | if (_mail->uid > 0) |
432 | 0 | binary_parts_cache(&ctx); |
433 | 0 | } |
434 | 0 | binary_streams_free(&ctx); |
435 | |
|
436 | 0 | *binary_r = ctx.converted ? TRUE : ctx.has_nuls; |
437 | 0 | *converted_r = ctx.converted; |
438 | 0 | return 0; |
439 | 0 | } |
440 | | |
441 | | static bool get_cached_binary_parts(struct index_mail *mail) |
442 | 0 | { |
443 | 0 | const unsigned int field_idx = |
444 | 0 | mail->ibox->cache_fields[MAIL_CACHE_BINARY_PARTS].idx; |
445 | 0 | buffer_t *part_buf; |
446 | 0 | int ret; |
447 | |
|
448 | 0 | if (mail->data.bin_parts != NULL) |
449 | 0 | return TRUE; |
450 | | |
451 | 0 | part_buf = t_buffer_create(128); |
452 | 0 | ret = index_mail_cache_lookup_field(mail, part_buf, field_idx); |
453 | 0 | if (ret <= 0) |
454 | 0 | return FALSE; |
455 | | |
456 | 0 | if (message_binary_part_deserialize(mail->mail.data_pool, |
457 | 0 | part_buf->data, part_buf->used, |
458 | 0 | &mail->data.bin_parts) < 0) { |
459 | 0 | mail_set_mail_cache_corrupted(&mail->mail.mail, |
460 | 0 | "Corrupted cached binary.parts data"); |
461 | 0 | return FALSE; |
462 | 0 | } |
463 | 0 | return TRUE; |
464 | 0 | } |
465 | | |
466 | | static struct message_part * |
467 | | msg_part_find(struct message_part *parts, uoff_t physical_pos) |
468 | 0 | { |
469 | 0 | struct message_part *part, *child; |
470 | |
|
471 | 0 | for (part = parts; part != NULL; part = part->next) { |
472 | 0 | if (part->physical_pos == physical_pos) |
473 | 0 | return part; |
474 | 0 | child = msg_part_find(part->children, physical_pos); |
475 | 0 | if (child != NULL) |
476 | 0 | return child; |
477 | 0 | } |
478 | 0 | return NULL; |
479 | 0 | } |
480 | | |
481 | | static int |
482 | | index_mail_get_binary_properties(struct mail *_mail, |
483 | | const struct message_part *part, |
484 | | bool include_hdr, |
485 | | struct mail_binary_properties *bprops_r) |
486 | 0 | { |
487 | 0 | struct index_mail *mail = INDEX_MAIL(_mail); |
488 | 0 | struct message_part *all_parts, *msg_part; |
489 | 0 | const struct message_binary_part *bin_part, *root_bin_part; |
490 | 0 | uoff_t size, end_offset; |
491 | 0 | unsigned int lines; |
492 | 0 | bool binary, converted; |
493 | |
|
494 | 0 | if (mail_get_parts(_mail, &all_parts) < 0) |
495 | 0 | return -1; |
496 | | |
497 | | /* first lookup from cache */ |
498 | 0 | if (get_cached_binary_parts(mail)) { |
499 | 0 | converted = (mail->data.bin_parts != NULL); |
500 | 0 | binary = converted || message_parts_have_nuls(all_parts); |
501 | 0 | } else { |
502 | | /* not found. parse the whole message */ |
503 | 0 | if (index_mail_read_binary_to_cache(_mail, all_parts, TRUE, |
504 | 0 | "binary.size", &binary, &converted) < 0) |
505 | 0 | return -1; |
506 | 0 | } |
507 | | |
508 | 0 | size = part->header_size.virtual_size + |
509 | 0 | part->body_size.virtual_size; |
510 | | /* note that we assume here that binary translation doesn't change the |
511 | | headers' line counts. this isn't true if the original message |
512 | | contained duplicate Content-Transfer-Encoding lines, but since |
513 | | that's invalid anyway we don't bother trying to handle it. */ |
514 | 0 | lines = part->header_size.lines + part->body_size.lines; |
515 | 0 | end_offset = part->physical_pos + size; |
516 | |
|
517 | 0 | bin_part = mail->data.bin_parts; root_bin_part = NULL; |
518 | 0 | for (; bin_part != NULL; bin_part = bin_part->next) { |
519 | 0 | msg_part = msg_part_find(all_parts, bin_part->physical_pos); |
520 | 0 | if (msg_part == NULL) { |
521 | | /* either binary.parts or mime.parts is broken */ |
522 | 0 | mail_set_cache_corrupted(_mail, MAIL_FETCH_MESSAGE_PARTS, t_strdup_printf( |
523 | 0 | "BINARY part at offset %"PRIuUOFF_T" not found from MIME parts", |
524 | 0 | bin_part->physical_pos)); |
525 | 0 | return -1; |
526 | 0 | } |
527 | 0 | if (msg_part->physical_pos >= part->physical_pos && |
528 | 0 | msg_part->physical_pos < end_offset) { |
529 | 0 | if (msg_part->physical_pos == part->physical_pos) |
530 | 0 | root_bin_part = bin_part; |
531 | 0 | size -= msg_part->header_size.virtual_size + |
532 | 0 | msg_part->body_size.virtual_size; |
533 | 0 | size += bin_part->binary_hdr_size + |
534 | 0 | bin_part->binary_body_size; |
535 | 0 | lines -= msg_part->body_size.lines; |
536 | 0 | lines += bin_part->binary_body_lines_count; |
537 | 0 | } |
538 | 0 | } |
539 | 0 | if (!include_hdr) { |
540 | 0 | if (root_bin_part != NULL) |
541 | 0 | size -= root_bin_part->binary_hdr_size; |
542 | 0 | else |
543 | 0 | size -= part->header_size.virtual_size; |
544 | 0 | lines -= part->header_size.lines; |
545 | 0 | } |
546 | |
|
547 | 0 | if (bprops_r != NULL) { |
548 | 0 | bprops_r->size = size; |
549 | 0 | bprops_r->lines = lines; |
550 | 0 | bprops_r->binary = binary; |
551 | 0 | bprops_r->converted = converted; |
552 | 0 | } |
553 | 0 | return 0; |
554 | 0 | } |
555 | | |
556 | | int index_mail_get_binary_stream(struct mail *_mail, |
557 | | const struct message_part *part, |
558 | | bool include_hdr, |
559 | | struct mail_binary_properties *bprops_r, |
560 | | struct istream **stream_r) |
561 | 0 | { |
562 | 0 | struct index_mail *mail = INDEX_MAIL(_mail); |
563 | 0 | struct mail_binary_cache *cache = &_mail->box->storage->binary_cache; |
564 | 0 | struct istream *input; |
565 | 0 | bool binary, converted; |
566 | |
|
567 | 0 | if (stream_r == NULL) { |
568 | 0 | return index_mail_get_binary_properties(_mail, part, |
569 | 0 | include_hdr, bprops_r); |
570 | 0 | } |
571 | | |
572 | | /* FIXME: always put the header to temp file. skip it when needed. */ |
573 | 0 | if (cache->box == _mail->box && cache->uid == _mail->uid && |
574 | 0 | cache->orig_physical_pos == part->physical_pos && |
575 | 0 | cache->include_hdr == include_hdr) { |
576 | | /* we have this cached already */ |
577 | 0 | i_stream_seek(cache->input, 0); |
578 | 0 | timeout_reset(cache->to); |
579 | 0 | binary = TRUE; |
580 | 0 | converted = TRUE; |
581 | 0 | } else { |
582 | 0 | if (index_mail_read_binary_to_cache(_mail, part, include_hdr, |
583 | 0 | "binary stream", &binary, &converted) < 0) |
584 | 0 | return -1; |
585 | 0 | mail->data.cache_fetch_fields |= MAIL_FETCH_STREAM_BINARY; |
586 | 0 | } |
587 | 0 | if (bprops_r != NULL) { |
588 | 0 | bprops_r->size = cache->size; |
589 | | /* FIXME: lines is a bit complex to calculate in this code path, |
590 | | and current callers don't need it either. */ |
591 | 0 | bprops_r->lines = UINT_MAX; |
592 | 0 | bprops_r->binary = binary; |
593 | 0 | bprops_r->converted = converted; |
594 | 0 | } |
595 | 0 | if (!converted) { |
596 | | /* don't keep this cached. it's exactly the same as |
597 | | the original stream */ |
598 | 0 | i_assert(mail->data.stream != NULL); |
599 | 0 | i_stream_seek(mail->data.stream, part->physical_pos + |
600 | 0 | (include_hdr ? 0 : |
601 | 0 | part->header_size.physical_size)); |
602 | 0 | input = i_stream_create_crlf(mail->data.stream); |
603 | 0 | *stream_r = i_stream_create_limit(input, cache->size); |
604 | 0 | i_stream_unref(&input); |
605 | 0 | mail_storage_free_binary_cache(_mail->box->storage); |
606 | 0 | } else { |
607 | 0 | *stream_r = cache->input; |
608 | 0 | i_stream_ref(cache->input); |
609 | 0 | } |
610 | 0 | return 0; |
611 | 0 | } |