dbox-save.c revision 147a788fea2a88f7125b27226451271d55cf5b01
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2007-2013 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream-crlf.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ostream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "hex-binary.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "index-mail.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#include "dbox-attachment.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "dbox-file.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "dbox-save.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid dbox_save_add_to_index(struct dbox_save_context *ctx)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen struct mail_save_data *mdata = &ctx->ctx.data;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen enum mail_flags save_flags;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen save_flags = mdata->flags & ~MAIL_RECENT;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen mail_index_append(ctx->trans, mdata->uid, &ctx->seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen save_flags);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (mdata->keywords != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_update_keywords(ctx->trans, ctx->seq,
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen MODIFY_REPLACE, mdata->keywords);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (mdata->min_modseq != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_update_modseq(ctx->trans, ctx->seq,
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen mdata->min_modseq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid dbox_save_begin(struct dbox_save_context *ctx, struct istream *input)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_save_context *_ctx = &ctx->ctx;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct mail_storage *_storage = _ctx->transaction->box->storage;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct dbox_storage *storage = (struct dbox_storage *)_storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct dbox_message_header dbox_msg_hdr;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct istream *crlf_input;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen dbox_save_add_to_index(ctx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (_ctx->dest_mail == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ctx->mail == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ctx->mail = mail_alloc(_ctx->transaction, 0, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen _ctx->dest_mail = ctx->mail;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen mail_set_seq_saving(_ctx->dest_mail, ctx->seq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen crlf_input = i_stream_create_lf(input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ctx->input = index_mail_cache_parse_init(_ctx->dest_mail, crlf_input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_unref(&crlf_input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* write a dummy header. it'll get rewritten when we're finished */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memset(&dbox_msg_hdr, 0, sizeof(dbox_msg_hdr));
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen o_stream_cork(ctx->dbox_output);
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen if (o_stream_send(ctx->dbox_output, &dbox_msg_hdr,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sizeof(dbox_msg_hdr)) < 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen mail_storage_set_critical(_storage, "write(%s) failed: %m",
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_get_name(ctx->dbox_output));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ctx->failed = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen _ctx->data.output = ctx->dbox_output;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (_ctx->data.received_date == (time_t)-1)
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen _ctx->data.received_date = ioloop_time;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen index_attachment_save_begin(_ctx, storage->attachment_fs, ctx->input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint dbox_save_continue(struct mail_save_context *_ctx)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_storage *storage = _ctx->transaction->box->storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ctx->failed)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (_ctx->data.attach != NULL)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return index_attachment_save_continue(_ctx);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen do {
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_critical(storage,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen "write(%s) failed: %m",
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen o_stream_get_name(_ctx->data.output));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ctx->failed = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen index_mail_cache_parse_continue(_ctx->dest_mail);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* both tee input readers may consume data from our primary
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen input stream. we'll have to make sure we don't return with
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen one of the streams still having data in them. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } while (i_stream_read(ctx->input) > 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainenvoid dbox_save_end(struct dbox_save_context *ctx)
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen{
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen struct mail_save_data *mdata = &ctx->ctx.data;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen struct ostream *dbox_output = ctx->dbox_output;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
1b6c4fdd2bb4234b5711874b3845547f49649744Timo Sirainen if (mdata->attach != NULL && !ctx->failed) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (index_attachment_save_finish(&ctx->ctx) < 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ctx->failed = TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (o_stream_nfinish(mdata->output) < 0) {
efb83f10b2a557d7427c311da52d768fb91e1b47Timo Sirainen mail_storage_set_critical(ctx->ctx.transaction->box->storage,
efb83f10b2a557d7427c311da52d768fb91e1b47Timo Sirainen "write(%s) failed: %m",
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen o_stream_get_name(mdata->output));
efb83f10b2a557d7427c311da52d768fb91e1b47Timo Sirainen ctx->failed = TRUE;
efb83f10b2a557d7427c311da52d768fb91e1b47Timo Sirainen }
c25dfa96bc32e8841c9a8cf5ba02fffba4290160Timo Sirainen if (mdata->output != dbox_output) {
afaf4f293964cbe3c3cbab1249490f8d68c28904Timo Sirainen if (mdata->output != NULL) {
dcf4259a9de2a5c86cea67e5bb1321ff5222746dTimo Sirainen /* e.g. zlib plugin had changed this */
dcf4259a9de2a5c86cea67e5bb1321ff5222746dTimo Sirainen o_stream_ref(dbox_output);
afaf4f293964cbe3c3cbab1249490f8d68c28904Timo Sirainen o_stream_destroy(&mdata->output);
afaf4f293964cbe3c3cbab1249490f8d68c28904Timo Sirainen mdata->output = dbox_output;
dcf4259a9de2a5c86cea67e5bb1321ff5222746dTimo Sirainen } else {
dcf4259a9de2a5c86cea67e5bb1321ff5222746dTimo Sirainen i_assert(ctx->failed);
dcf4259a9de2a5c86cea67e5bb1321ff5222746dTimo Sirainen }
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen }
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen index_mail_cache_parse_deinit(ctx->ctx.dest_mail,
c25dfa96bc32e8841c9a8cf5ba02fffba4290160Timo Sirainen ctx->ctx.data.received_date,
c25dfa96bc32e8841c9a8cf5ba02fffba4290160Timo Sirainen !ctx->failed);
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen}
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainenvoid dbox_save_write_metadata(struct mail_save_context *_ctx,
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen struct ostream *output, uoff_t output_msg_size,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *orig_mailbox_name,
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen guid_128_t guid_128)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen struct mail_save_data *mdata = &ctx->ctx.data;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct dbox_metadata_header metadata_hdr;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *guid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen string_t *str;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen uoff_t vsize;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memset(&metadata_hdr, 0, sizeof(metadata_hdr));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sizeof(metadata_hdr.magic_post));
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, &metadata_hdr, sizeof(metadata_hdr));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str = t_str_new(256);
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen if (output_msg_size != ctx->input->v_offset) {
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen /* a plugin changed the data written to disk, so the
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen "message size" dbox header doesn't contain the actual
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen "physical" message size. we need to save it as a
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen separate metadata header. */
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen str_printfa(str, "%c%llx\n", DBOX_METADATA_PHYSICAL_SIZE,
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen (unsigned long long)ctx->input->v_offset);
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_printfa(str, "%c%lx\n", DBOX_METADATA_RECEIVED_TIME,
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen (unsigned long)mdata->received_date);
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen if (mail_get_virtual_size(_ctx->dest_mail, &vsize) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_unreached();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (unsigned long long)vsize);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (mdata->pop3_uidl != NULL) {
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen i_assert(strchr(mdata->pop3_uidl, '\n') == NULL);
a8bc64d2ec8babb5109fa23aa3c90383de61cd69Timo Sirainen str_printfa(str, "%c%s\n", DBOX_METADATA_POP3_UIDL,
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen mdata->pop3_uidl);
a8bc64d2ec8babb5109fa23aa3c90383de61cd69Timo Sirainen }
147a788fea2a88f7125b27226451271d55cf5b01Timo Sirainen if (mdata->pop3_order != 0) {
147a788fea2a88f7125b27226451271d55cf5b01Timo Sirainen str_printfa(str, "%c%u\n", DBOX_METADATA_POP3_ORDER,
147a788fea2a88f7125b27226451271d55cf5b01Timo Sirainen mdata->pop3_order);
147a788fea2a88f7125b27226451271d55cf5b01Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen guid = mdata->guid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (guid != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_generate_guid_128_hash(guid, guid_128);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else {
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen guid_128_generate(guid_128);
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen guid = guid_128_to_string(guid_128);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (orig_mailbox_name != NULL &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strchr(orig_mailbox_name, '\r') == NULL &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strchr(orig_mailbox_name, '\n') == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* save the original mailbox name so if mailbox indexes get
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen corrupted we can place at least some (hopefully most) of
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen the messages to correct mailboxes. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_printfa(str, "%c%s\n", DBOX_METADATA_ORIG_MAILBOX,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen orig_mailbox_name);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen dbox_attachment_save_write_metadata(_ctx, str);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append_c(str, '\n');
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}