index-mail-binary.c revision 6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen/* Copyright (c) 2002-2012 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)
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 static const char *filter_headers[] = {
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. */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail_storage_set_critical(ctx->mail->box->storage,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen "read(%s) failed: %m", i_stream_get_name(ctx->input));
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail_storage_set_error(ctx->mail->box->storage,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen i_stream_seek(ctx->input, part->physical_pos);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* body only */
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* write header with modified content-type */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen block->bin_part.physical_pos = part->physical_pos;
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) {
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen /* single part - write decoded data */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen block->bin_part.physical_pos = part->physical_pos;
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 Sirainen i_error("unlink(%s) failed: %m", str_c(path));
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenstatic void binary_streams_free(struct binary_ctx *ctx)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenbinary_parts_update(struct binary_ctx *ctx, const struct message_part *part,
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct index_mail *mail = (struct index_mail *)ctx->mail;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen unsigned int i, count;
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++) {
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (blocks[i].bin_part.physical_pos != part->physical_pos ||
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenstatic void binary_parts_cache(struct binary_ctx *ctx)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct index_mail *mail = (struct index_mail *)ctx->mail;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 128);
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)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen unsigned int i, count;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen for (i = 0; i < count; i++)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenindex_mail_read_binary_to_cache(struct mail *_mail,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen struct index_mail *mail = (struct 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);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (mail_get_stream(_mail, NULL, NULL, &ctx.input) < 0)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (add_binary_part(&ctx, part, include_hdr) < 0) {
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen cache->to = timeout_add(MAIL_BINARY_CACHE_EXPIRE_MSECS,
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen cache->orig_physical_pos = part->physical_pos;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen cache->input = i_streams_merge(blocks_get_streams(&ctx),
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen if (i_stream_get_size(cache->input, TRUE, &cache->size) < 0) {
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen mail_storage_set_critical(_mail->box->storage,
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen "read(%s) failed: %m",
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen mail_storage_free_binary_cache(_mail->box->storage);
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 const unsigned int field_idx =
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail->ibox->cache_fields[MAIL_CACHE_BINARY_PARTS].idx;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen part_buf = buffer_create_dynamic(pool_datastack_create(), 128);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen ret = index_mail_cache_lookup_field(mail, part_buf, field_idx);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen if (message_binary_part_deserialize(mail->data_pool, part_buf->data,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen mail_cache_set_corrupted(mail->mail.mail.box->cache,
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);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct index_mail *mail = (struct 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,
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);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen mail_set_cache_corrupted(_mail, MAIL_FETCH_MESSAGE_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,
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_binary_cache *cache = &_mail->box->storage->binary_cache;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen return index_mail_get_binary_size(_mail, part,
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 */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen mail_storage_free_binary_cache(_mail->box->storage);
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen i_stream_seek(mail->data.stream, part->physical_pos +
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen input = i_stream_create_crlf(mail->data.stream);