index-attachment.c revision 4307c886579381dbb1897ea1388ae6978c96f560
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "lib.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "safe-mkstemp.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "fs-api.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "istream.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "ostream.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "base64.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "hash-format.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "str.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "message-parser.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "rfc822-parser.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "mail-user.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "index-mail.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "index-attachment.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#define BASE64_ATTACHMENT_MAX_EXTRA_BYTES 1024
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenenum mail_attachment_state {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen MAIL_ATTACHMENT_STATE_NO,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen MAIL_ATTACHMENT_STATE_MAYBE,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen MAIL_ATTACHMENT_STATE_YES
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenenum base64_state {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen BASE64_STATE_0 = 0,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen BASE64_STATE_1,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen BASE64_STATE_2,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen BASE64_STATE_3,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen BASE64_STATE_CR,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen BASE64_STATE_EOB,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen BASE64_STATE_EOM
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstruct mail_save_attachment_part {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen char *content_type, *content_disposition;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen enum mail_attachment_state state;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* start offset of the message part in the original input stream */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen uoff_t start_offset;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* for saving attachments base64-decoded: */
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen enum base64_state base64_state;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen unsigned int base64_line_blocks, cur_base64_blocks;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen uoff_t base64_bytes;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen bool base64_have_crlf; /* CRLF linefeeds */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen bool base64_failed;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int temp_fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct ostream *output;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct hash_format *part_hash;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_t *part_buf;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstruct mail_save_attachment {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool_t pool;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct message_parser_ctx *parser;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct fs *fs;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct istream *input;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* per-MIME part data */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_save_attachment_part part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct message_part *prev_part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ARRAY_TYPE(mail_attachment_extref) extrefs;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic const char *index_attachment_dir_get(struct mail_storage *storage)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return mail_user_home_expand(storage->user,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen storage->set->mail_attachment_dir);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenvoid index_attachment_save_begin(struct mail_save_context *ctx,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct fs *fs, struct istream *input)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_storage *storage = ctx->transaction->box->storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool_t pool;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(ctx->attach == NULL);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (*storage->set->mail_attachment_dir == '\0')
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool = pool_alloconly_create("save attachment", 1024*4);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach = p_new(pool, struct mail_save_attachment, 1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->pool = pool;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->fs = fs;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->input = input;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->parser =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen message_parser_init(ctx->attach->pool, input, 0, 0);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen p_array_init(&ctx->attach->extrefs, ctx->attach->pool, 8);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void parse_content_type(struct mail_save_context *ctx,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const struct message_header_line *hdr)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct rfc822_parser_context parser;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen string_t *content_type;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen rfc822_parser_init(&parser, hdr->full_value, hdr->full_value_len, NULL);
4e8d6d03c2ff85448df79b181a2ea850fb5d4199Timo Sirainen rfc822_skip_lwsp(&parser);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen T_BEGIN {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen content_type = t_str_new(64);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (rfc822_parse_content_type(&parser, content_type) >= 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_free(ctx->attach->part.content_type);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.content_type =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_strdup(str_c(content_type));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } T_END;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenparse_content_disposition(struct mail_save_context *ctx,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const struct message_header_line *hdr)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* just pass it as-is to backend. */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_free(ctx->attach->part.content_disposition);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.content_disposition =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_strndup(hdr->full_value, hdr->full_value_len);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void index_attachment_save_mail_header(struct mail_save_context *ctx,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct message_header_line *hdr)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (hdr->continues) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hdr->use_full_value = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (strcasecmp(hdr->name, "Content-Type") == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen parse_content_type(ctx, hdr);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (strcasecmp(hdr->name, "Content-Disposition") == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen parse_content_disposition(ctx, hdr);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, hdr->name, hdr->name_len);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, hdr->middle, hdr->middle_len);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, hdr->full_value, hdr->full_value_len);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (!hdr->no_newline) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (hdr->crlf_newline)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, "\r\n", 2);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, "\n", 1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic bool save_is_attachment(struct mail_save_context *ctx,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct message_part *part)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_attachment_part apart;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* multiparts may contain attachments as children,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen but they're never themselves */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return FALSE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen if (ctx->part_is_attachment == NULL)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen memset(&apart, 0, sizeof(apart));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen apart.part = part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen apart.content_type = ctx->attach->part.content_type;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen apart.content_disposition = ctx->attach->part.content_disposition;
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen return ctx->part_is_attachment(ctx, &apart);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int index_attachment_save_temp_open_fd(struct mail_storage *storage)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen string_t *temp_path;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen temp_path = t_str_new(256);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_user_set_get_temp_prefix(temp_path, storage->user->set);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fd = safe_mkstemp_hostpid(temp_path, 0600, (uid_t)-1, (gid_t)-1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (fd == -1) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "safe_mkstemp(%s) failed: %m", str_c(temp_path));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (unlink(str_c(temp_path)) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "unlink(%s) failed: %m", str_c(temp_path));
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen i_close_fd(&fd);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic struct hash_format *
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenindex_attachment_hash_format_init(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_storage *storage = ctx->transaction->box->storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct hash_format *format;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const char *error;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (hash_format_init(storage->set->mail_attachment_hash,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen &format, &error) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* we already checked this when verifying settings */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_panic("mail_attachment_hash=%s unexpectedly failed: %s",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen storage->set->mail_attachment_hash, error);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return format;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int index_attachment_save_temp_open(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fd = index_attachment_save_temp_open_fd(ctx->transaction->box->storage);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (fd == -1)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.temp_fd = fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.output = o_stream_create_fd(fd, 0, FALSE);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_cork(ctx->attach->part.output);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.part_hash = index_attachment_hash_format_init(ctx);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int save_check_write_error(struct mail_storage *storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct ostream *output)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (o_stream_nfinish(output) == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage, "write(%s) failed: %m",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_get_name(output));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int index_attachment_base64_decode(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_save_attachment_part *part = &ctx->attach->part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_storage *storage = ctx->transaction->box->storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_t *extra_buf = NULL;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct istream *input, *base64_input;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct ostream *output;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct hash_format *hash;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const unsigned char *data;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen size_t size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ssize_t ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_t *buf;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int outfd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen bool failed = FALSE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->base64_bytes < storage->set->mail_attachment_min_size ||
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->output->offset > part->base64_bytes +
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen BASE64_ATTACHMENT_MAX_EXTRA_BYTES) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* only a small part of the MIME part is base64-encoded. */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->base64_line_blocks == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* only one line of base64 */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_line_blocks = part->cur_base64_blocks;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(part->base64_line_blocks > 0);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* decode base64 data and write it to another temp file */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen outfd = index_attachment_save_temp_open_fd(storage);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (outfd == -1)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash = index_attachment_hash_format_init(ctx);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buf = buffer_create_dynamic(default_pool, 1024);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE, FALSE);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen base64_input = i_stream_create_limit(input, part->base64_bytes);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen output = o_stream_create_fd_file(outfd, 0, FALSE);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_cork(output);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen while ((ret = i_stream_read(base64_input)) > 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen data = i_stream_get_data(base64_input, &size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_set_used_size(buf, 0);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (base64_decode(data, size, &size, buf) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "Attachment base64 data unexpectedly broke");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen failed = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_stream_skip(base64_input, size);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, buf->data, buf->used);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash_format_loop(hash, buf->data, buf->used);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ret != -1) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(failed);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else if (base64_input->stream_errno != 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "read(attachment-temp) failed: %m");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen failed = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (save_check_write_error(storage, output) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen failed = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_free(&buf);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_stream_unref(&base64_input);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_unref(&output);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (input->v_offset != part->output->offset && !failed) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* write the rest of the data to the message stream */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extra_buf = buffer_create_dynamic(default_pool, 1024);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_append(extra_buf, data, size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_stream_skip(input, size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(ret == -1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (input->stream_errno != 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "read(attachment-temp) failed: %m");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen failed = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_stream_unref(&input);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (failed) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash_format_deinit_free(&hash);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (close(outfd) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "close(attachment-temp) failed: %m");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* successfully wrote it. switch to using it. */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_destroy(&part->output);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (close(part->temp_fd) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "close(attachment-decoded-temp) failed: %m");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->temp_fd = outfd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (extra_buf != NULL) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, extra_buf->data, extra_buf->used);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_free(&extra_buf);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash_format_deinit_free(&part->part_hash);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->part_hash = hash;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int index_attachment_save_finish_part(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_save_attachment_part *part = &ctx->attach->part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_storage *storage = ctx->transaction->box->storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct fs_file *file;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct istream *input;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct ostream *output;
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen guid_128_t guid_128;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const char *attachment_dir, *path, *digest;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen string_t *digest_str;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const unsigned char *data;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen size_t size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen uoff_t attachment_size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen enum fs_open_flags flags = FS_OPEN_FLAG_MKDIR;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret = 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (save_check_write_error(storage, part->output) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen if (!part->base64_failed) {
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen if (part->base64_state == BASE64_STATE_0 &&
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen part->base64_bytes > 0) {
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen /* there is no trailing LF or '=' characters,
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen but it's not completely empty */
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen part->base64_state = BASE64_STATE_EOM;
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen }
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen if (part->base64_state == BASE64_STATE_EOM) {
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen /* base64 data looks ok. */
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen if (index_attachment_base64_decode(ctx) < 0)
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen part->base64_failed = TRUE;
1694d842001a400346eb66298d2113b39c23a0c0Timo Sirainen } else {
1694d842001a400346eb66298d2113b39c23a0c0Timo Sirainen part->base64_failed = TRUE;
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* open the attachment destination file */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen flags |= FS_OPEN_FLAG_FDATASYNC;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen digest_str = t_str_new(128);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash_format_deinit(&part->part_hash, digest_str);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen digest = str_c(digest_str);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (strlen(digest) < 4) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* make sure we can access first 4 bytes without accessing
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen out of bounds memory */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen digest = t_strconcat(digest, "\0\0\0\0", NULL);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen guid_128_generate(guid_128);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen attachment_dir = index_attachment_dir_get(storage);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen path = t_strdup_printf("%s/%c%c/%c%c/%s-%s", attachment_dir,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen digest[0], digest[1],
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen digest[2], digest[3], digest,
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen guid_128_to_string(guid_128));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (fs_open(ctx->attach->fs, path,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen FS_OPEN_MODE_CREATE | flags, &file) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage, "%s",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fs_last_error(ctx->attach->fs));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* copy data to it from temp file */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE, FALSE);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen output = fs_write_stream(file);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen while (i_stream_read_data(input, &data, &size, 0) > 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, data, size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_stream_skip(input, size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (input->stream_errno != 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "read(%s) failed: %m", i_stream_get_name(input));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen attachment_size = !part->base64_failed ?
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_bytes : input->v_offset;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_stream_destroy(&input);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ret < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fs_write_stream_abort(file, &output);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (fs_write_stream_finish(file, &output) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage, "%s",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fs_file_last_error(file));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fs_close(&file);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ret == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_attachment_extref *extref;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extref = array_append_space(&ctx->attach->extrefs);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extref->start_offset = part->start_offset;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extref->size = attachment_size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extref->path = p_strdup(ctx->attach->pool,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen path + strlen(attachment_dir) + 1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extref->base64_blocks_per_line =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_failed ? 0 : part->base64_line_blocks;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen extref->base64_have_crlf = part->base64_have_crlf;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenindex_attachment_try_base64_decode_char(struct mail_save_attachment_part *part,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen size_t pos, char chr)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen switch (part->base64_state) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case BASE64_STATE_0:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (base64_is_valid_char(chr))
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state++;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (chr == '\r')
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state = BASE64_STATE_CR;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (chr == '\n') {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state = BASE64_STATE_0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->cur_base64_blocks <
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_line_blocks) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* last line */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state = BASE64_STATE_EOM;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else if (part->base64_line_blocks == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* first line */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->cur_base64_blocks == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_line_blocks =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->cur_base64_blocks;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else if (part->cur_base64_blocks ==
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_line_blocks) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* line is ok */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->cur_base64_blocks = 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case BASE64_STATE_1:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (!base64_is_valid_char(chr))
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state++;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case BASE64_STATE_2:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (base64_is_valid_char(chr))
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state++;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (chr == '=')
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state = BASE64_STATE_EOB;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case BASE64_STATE_3:
928529ce8d2e60f1af53fae095e82e7d5281a7daTimo Sirainen part->base64_bytes = part->output->offset + pos + 1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (base64_is_valid_char(chr)) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state = BASE64_STATE_0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->cur_base64_blocks++;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else if (chr == '=') {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state = BASE64_STATE_EOM;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->cur_base64_blocks++;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case BASE64_STATE_CR:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (chr != '\n')
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_have_crlf = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case BASE64_STATE_EOB:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (chr != '=')
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_bytes = part->output->offset + pos + 1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_state = BASE64_STATE_EOM;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->cur_base64_blocks++;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case BASE64_STATE_EOM:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_unreached();
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenindex_attachment_try_base64_decode(struct mail_save_attachment_part *part,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const unsigned char *data, size_t size)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen size_t i;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->base64_failed || part->base64_state == BASE64_STATE_EOM)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen for (i = 0; i < size; i++) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = index_attachment_try_base64_decode_char(part, i,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen (char)data[i]);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ret <= 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ret < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->base64_failed = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void index_attachment_save_body(struct mail_save_context *ctx,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const struct message_block *block)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_save_attachment_part *part = &ctx->attach->part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_storage *storage = ctx->transaction->box->storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_t *part_buf;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen size_t new_size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen switch (part->state) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case MAIL_ATTACHMENT_STATE_NO:
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, block->data, block->size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case MAIL_ATTACHMENT_STATE_MAYBE:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->part_buf == NULL) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->part_buf =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_create_dynamic(default_pool,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen storage->set->mail_attachment_min_size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part_buf = part->part_buf;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen new_size = part_buf->used + block->size;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (new_size < storage->set->mail_attachment_min_size) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_append(part_buf, block->data, block->size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* attachment is large enough. we'll first write it to
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen temp file. */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (index_attachment_save_temp_open(ctx) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* failed, fallback to just saving it inline */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->state = MAIL_ATTACHMENT_STATE_NO;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, part_buf->data,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen part_buf->used);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, block->data, block->size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->state = MAIL_ATTACHMENT_STATE_YES;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen index_attachment_try_base64_decode(part, part_buf->data,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part_buf->used);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash_format_loop(part->part_hash,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part_buf->data, part_buf->used);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(part->output, part_buf->data, part_buf->used);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_set_used_size(part_buf, 0);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* fall through */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case MAIL_ATTACHMENT_STATE_YES:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen index_attachment_try_base64_decode(part, block->data,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen block->size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen hash_format_loop(part->part_hash, block->data, block->size);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(part->output, block->data, block->size);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void index_attachment_save_close(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_save_attachment_part *part = &ctx->attach->part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_storage *storage = ctx->transaction->box->storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->output != NULL)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen o_stream_destroy(&part->output);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (close(part->temp_fd) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "close(attachment-temp) failed: %m");
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->temp_fd = -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenindex_attachment_save_body_part_changed(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_save_attachment_part *part = &ctx->attach->part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret = 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* body part changed. we're now parsing the end of a
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen boundary, possibly followed by message epilogue */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen switch (part->state) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case MAIL_ATTACHMENT_STATE_NO:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case MAIL_ATTACHMENT_STATE_MAYBE:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* body part wasn't large enough. write to main file. */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->part_buf != NULL) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(ctx->output, part->part_buf->data,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen part->part_buf->used);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case MAIL_ATTACHMENT_STATE_YES:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (index_attachment_save_finish_part(ctx) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen index_attachment_save_close(ctx);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen part->state = MAIL_ATTACHMENT_STATE_NO;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_free_and_null(part->content_type);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_free_and_null(part->content_disposition);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (part->part_buf != NULL)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen buffer_free(&part->part_buf);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen memset(part, 0, sizeof(*part));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenint index_attachment_save_continue(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_storage *storage = ctx->transaction->box->storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct message_parser_ctx *parser = ctx->attach->parser;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct message_block block;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct ostream *output;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (block.part != ctx->attach->prev_part) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (index_attachment_save_body_part_changed(ctx) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->prev_part = block.part;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (block.hdr != NULL)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen index_attachment_save_mail_header(ctx, block.hdr);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (block.size == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* end of headers */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (save_is_attachment(ctx, block.part)) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.state =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen MAIL_ATTACHMENT_STATE_MAYBE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.start_offset =
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->input->v_offset;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* body */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen index_attachment_save_body(ctx, &block);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen output = ctx->attach->part.output != NULL ?
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.output : ctx->output;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (output->last_failed_errno != 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen index_mail_cache_parse_continue(ctx->dest_mail);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ret == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ctx->attach->input->stream_errno != 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen errno = ctx->attach->input->stream_errno;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage, "read(%s) failed: %m",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_stream_get_name(ctx->attach->input));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ctx->attach->part.output != NULL) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (save_check_write_error(storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach->part.output) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ctx->output != NULL) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (save_check_write_error(storage, ctx->output) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenint index_attachment_save_finish(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct message_part *parts;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret = 0, ret2;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ctx->attach->parser != NULL) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (index_attachment_save_body_part_changed(ctx) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret2 = message_parser_deinit(&ctx->attach->parser, &parts);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(ret2 == 0);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(ctx->attach->part.output == NULL);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenvoid index_attachment_save_free(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (ctx->attach != NULL) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen pool_unref(&ctx->attach->pool);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->attach = NULL;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenconst ARRAY_TYPE(mail_attachment_extref) *
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenindex_attachment_save_get_extrefs(struct mail_save_context *ctx)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ctx->attach == NULL ? NULL :
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen &ctx->attach->extrefs;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenindex_attachment_delete_real(struct mail_storage *storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct fs *fs, const char *name)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const char *path, *p, *attachment_dir;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen path = t_strdup_printf("%s/%s", index_attachment_dir_get(storage), name);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if ((ret = fs_unlink(fs, path)) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage, "%s", fs_last_error(fs));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* if the directory is now empty, rmdir it and its parents
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen until it fails */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen attachment_dir = index_attachment_dir_get(storage);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen while ((p = strrchr(path, '/')) != NULL) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen path = t_strdup_until(path, p);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (strcmp(path, attachment_dir) == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (fs_rmdir(fs, path) == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* success, continue to parent */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else if (errno == ENOTEMPTY || errno == EEXIST) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* there are other entries in this directory */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } else {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen mail_storage_set_critical(storage, "%s",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fs_last_error(fs));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenint index_attachment_delete(struct mail_storage *storage,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct fs *fs, const char *name)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen T_BEGIN {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = index_attachment_delete_real(storage, fs, name);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen } T_END;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}