bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen#define MAIL_BINARY_CACHE_EXPIRE_MSECS (60*1000)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen ((cte) == MESSAGE_CTE_QP || (cte) == MESSAGE_CTE_BASE64)
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen /* each block is its own input stream. basically each converted MIME
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen body has its own block and the parts between the MIME bodies are
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen unconverted blocks */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenstatic void binary_copy_to(struct binary_ctx *ctx, uoff_t end_offset)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen i_assert(end_offset >= ctx->copy_start_offset);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen i_stream_seek(ctx->input, ctx->copy_start_offset);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen linput = i_stream_create_limit(ctx->input, size);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenbinary_cte_filter_callback(struct header_filter_istream *input,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen bool *matched ATTR_UNUSED, void *context ATTR_UNUSED)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen static const char *cte_binary = "Content-Transfer-Encoding: binary\r\n";
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenadd_binary_part(struct binary_ctx *ctx, const struct message_part *part,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen "Content-Transfer-Encoding",
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* first parse the header to find c-t-e. */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen i_stream_seek(ctx->input, part->physical_pos);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen parser = message_parse_header_init(ctx->input, &hdr_size, 0);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen while ((ret = message_parse_header_next(parser, &hdr)) > 0) {
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen if (strcasecmp(hdr->name, "Content-Transfer-Encoding") == 0)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* we're not converting NULs to 0x80 when doing a binary fetch,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen even if they're in the message header. */
d052dcfff0c96a0af17a3158e51f709edf4b93a1Timo Sirainen "read(%s) failed: %s", i_stream_get_name(ctx->input),
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail_storage_set_error(ctx->mail->box->storage,
56b134799a457fd55830355f4c8d746d6bb5206fTimo Sirainen "Unknown Content-Transfer-Encoding.");
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen i_stream_seek(ctx->input, part->physical_pos);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* body only */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* write header with modified content-type */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen linput = i_stream_create_limit(ctx->input, (uoff_t)-1);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen block->input = i_stream_create_header_filter(linput,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen HEADER_FILTER_EXCLUDE | HEADER_FILTER_HIDE_BODY,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* copy everything as-is until the end of this header */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* multipart */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen for (child = part->children; child != NULL; child = child->next) {
91dad1ec260a45694c28837c2594ad2ee04beca6Timo Sirainen /* no body */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* single part - write decoded data */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen i_stream_seek(ctx->input, part->physical_pos +
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen linput = i_stream_create_limit(ctx->input, part->body_size.physical_size);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* no conversion necessary */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen block->input = i_stream_create_qp_decoder(linput);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen block->input = i_stream_create_base64_decoder(linput);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenstatic int fd_callback(const char **path_r, void *context)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail_user_set_get_temp_prefix(path, _mail->box->storage->user->set);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen fd = safe_mkstemp_hostpid(path, 0600, (uid_t)-1, (gid_t)-1);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen i_error("Temp file creation to %s failed: %m", str_c(path));
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* we just want the fd, unlink it */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* shouldn't happen.. */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenstatic void binary_streams_free(struct binary_ctx *ctx)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenbinary_parts_update(struct binary_ctx *ctx, const struct message_part *part,
87490012895b4f371635ded00add04c9107dcfefJosef 'Jeff' Sipek struct index_mail *mail = INDEX_MAIL(ctx->mail);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen blocks = array_get_modifiable(&ctx->blocks, &count);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen binary_parts_update(ctx, part->children, msg_bin_parts);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen /* default to unchanged header */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen bin_part.binary_hdr_size = part->header_size.virtual_size;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen for (i = 0; i < count; i++) {
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen if (blocks[i].physical_pos != part->physical_pos ||
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenstatic void binary_parts_cache(struct binary_ctx *ctx)
87490012895b4f371635ded00add04c9107dcfefJosef 'Jeff' Sipek struct index_mail *mail = INDEX_MAIL(ctx->mail);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen message_binary_part_serialize(mail->data.bin_parts, buf);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen index_mail_cache_add(mail, MAIL_CACHE_BINARY_PARTS,
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenstatic struct istream **blocks_get_streams(struct binary_ctx *ctx)
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen for (i = 0; i < count; i++) {
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainenblocks_count_lines(struct binary_ctx *ctx, struct istream *full_input)
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen blocks = array_get_modifiable(&ctx->blocks, &block_count);
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen /* count the number of lines each block contains */
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody while ((ret = i_stream_read_more(full_input, &data, &size)) > 0) {
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen i_assert(cur_block_offset <= cur_block->input->v_offset);
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen /* this is the last input for this block. the input
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen may also contain the next block's data, which we
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen don't want to include in this block's line count. */
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen i_assert(size >= cur_block_size - cur_block_offset);
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen while ((p = memchr(data, '\n', size)) != NULL) {
64e75f0c95e491debf4b3bc7091d0335d0f63646Timo Sirainen /* go to the next block */
e67ccfba723334c3e3cbeebff0627b8072afe0c7Timo Sirainen i_assert(block_count == 0 || !i_stream_have_bytes_left(cur_block->input));
e67ccfba723334c3e3cbeebff0627b8072afe0c7Timo Sirainen i_assert(block_count == 0 || block_idx+1 == block_count);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenindex_mail_read_binary_to_cache(struct mail *_mail,
87490012895b4f371635ded00add04c9107dcfefJosef 'Jeff' Sipek struct index_mail *mail = INDEX_MAIL(_mail);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen struct mail_binary_cache *cache = &_mail->box->storage->binary_cache;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen mail_storage_free_binary_cache(_mail->box->storage);
02b78558dc03daa2e7da2010b63f247b49936a38Timo Sirainen if (mail_get_stream_because(_mail, NULL, NULL, reason, &ctx.input) < 0)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (add_binary_part(&ctx, part, include_hdr) < 0) {
07725eacefe66333b9c7e4c7bfa423f5fa5aa74bTimo Sirainen "<binary stream of mailbox %s UID %u>",
56b134799a457fd55830355f4c8d746d6bb5206fTimo Sirainen /* MIME part contains invalid data */
56b134799a457fd55830355f4c8d746d6bb5206fTimo Sirainen "Invalid data in MIME part");
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mail_set_critical(_mail, "read(%s) failed: %s",
ae5330824a7ca85edd005109a353c3ab6b23b6b8Aki Tuomi cache->to = timeout_add(MAIL_BINARY_CACHE_EXPIRE_MSECS,
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen binary_parts_update(&ctx, part, &mail->data.bin_parts);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen *binary_r = ctx.converted ? TRUE : ctx.has_nuls;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenstatic bool get_cached_binary_parts(struct index_mail *mail)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail->ibox->cache_fields[MAIL_CACHE_BINARY_PARTS].idx;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ret = index_mail_cache_lookup_field(mail, part_buf, field_idx);
5f44975ec6c5755dd74bcd4c47a123a7242ecab3Timo Sirainen if (message_binary_part_deserialize(mail->mail.data_pool,
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen "Corrupted cached binary.parts data");
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenmsg_part_find(struct message_part *parts, uoff_t physical_pos)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen for (part = parts; part != NULL; part = part->next) {
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen child = msg_part_find(part->children, physical_pos);
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen const struct message_part *part, bool include_hdr,
87490012895b4f371635ded00add04c9107dcfefJosef 'Jeff' Sipek struct index_mail *mail = INDEX_MAIL(_mail);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen const struct message_binary_part *bin_part, *root_bin_part;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen /* first lookup from cache */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen /* not found. parse the whole message */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (index_mail_read_binary_to_cache(_mail, all_parts, TRUE,
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen /* note that we assume here that binary translation doesn't change the
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen headers' line counts. this isn't true if the original message
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen contained duplicate Content-Transfer-Encoding lines, but since
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen that's invalid anyway we don't bother trying to handle it. */
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen lines = part->header_size.lines + part->body_size.lines;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen bin_part = mail->data.bin_parts; root_bin_part = NULL;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen for (; bin_part != NULL; bin_part = bin_part->next) {
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen msg_part = msg_part_find(all_parts, bin_part->physical_pos);
8b5b1f6cb19253dfd7821fcef8e9b7e95e6caf3aTimo Sirainen /* either binary.parts or mime.parts is broken */
0206dc57f2c04da69599dea5816235cfeb2b897aMartti Rannanjärvi mail_set_cache_corrupted(_mail, MAIL_FETCH_MESSAGE_PARTS, t_strdup_printf(
8b5b1f6cb19253dfd7821fcef8e9b7e95e6caf3aTimo Sirainen "BINARY part at offset %"PRIuUOFF_T" not found from MIME parts",
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (msg_part->physical_pos >= part->physical_pos &&
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (msg_part->physical_pos == part->physical_pos)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenint index_mail_get_binary_stream(struct mail *_mail,
87490012895b4f371635ded00add04c9107dcfefJosef 'Jeff' Sipek struct index_mail *mail = INDEX_MAIL(_mail);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_binary_cache *cache = &_mail->box->storage->binary_cache;
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen return index_mail_get_binary_size(_mail, part, include_hdr,
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen /* current implementation doesn't bother implementing this,
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen because it's not needed by anything. */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen /* FIXME: always put the header to temp file. skip it when needed. */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (cache->box == _mail->box && cache->uid == _mail->uid &&
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen cache->orig_physical_pos == part->physical_pos &&
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen /* we have this cached already */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (index_mail_read_binary_to_cache(_mail, part, include_hdr,
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen mail->data.cache_fetch_fields |= MAIL_FETCH_STREAM_BINARY;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen /* don't keep this cached. it's exactly the same as
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen the original stream */
471eed36d9aee2f7d99072a4fc5e164a6741194bTimo Sirainen i_stream_seek(mail->data.stream, part->physical_pos +
471eed36d9aee2f7d99072a4fc5e164a6741194bTimo Sirainen input = i_stream_create_crlf(mail->data.stream);
471eed36d9aee2f7d99072a4fc5e164a6741194bTimo Sirainen *stream_r = i_stream_create_limit(input, *size_r);