dbox-save.c revision 7cd7bd65aba6d84f4e4f5066d248437eaa4e5e54
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2007-2010 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"
c68f28e2cf5f9621511bece0414335e551dc82c6Timo Sirainen#include "dbox-attachment.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo 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{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen enum mail_flags save_flags;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen save_flags = ctx->ctx.flags & ~MAIL_RECENT;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_append(ctx->trans, ctx->ctx.uid, &ctx->seq);
e42e27fcc497c7b4a5cc0b6ff304abca5ccfcb4fTimo Sirainen mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
e42e27fcc497c7b4a5cc0b6ff304abca5ccfcb4fTimo Sirainen save_flags);
e42e27fcc497c7b4a5cc0b6ff304abca5ccfcb4fTimo Sirainen if (ctx->ctx.keywords != NULL) {
e42e27fcc497c7b4a5cc0b6ff304abca5ccfcb4fTimo Sirainen mail_index_update_keywords(ctx->trans, ctx->seq,
e42e27fcc497c7b4a5cc0b6ff304abca5ccfcb4fTimo Sirainen MODIFY_REPLACE, ctx->ctx.keywords);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ctx->ctx.min_modseq != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_update_modseq(ctx->trans, ctx->seq,
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen ctx->ctx.min_modseq);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenvoid dbox_save_begin(struct dbox_save_context *ctx, struct istream *input)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen struct mail_save_context *_ctx = &ctx->ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_storage *_storage = _ctx->transaction->box->storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo 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);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
804fa3f03bd9170272168a5ad214053bbe3160c7Josef 'Jeff' Sipek 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 }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_set_seq(_ctx->dest_mail, ctx->seq);
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen _ctx->dest_mail->saving = TRUE;
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 */
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch 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) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mail_storage_set_critical(_storage,
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi "o_stream_send(%s) failed: %m",
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi ctx->cur_file->cur_path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ctx->failed = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen _ctx->output = ctx->dbox_output;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen if (_ctx->received_date == (time_t)-1)
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen _ctx->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{
804fa3f03bd9170272168a5ad214053bbe3160c7Josef 'Jeff' Sipek 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;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (_ctx->attach != NULL)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return index_attachment_save_continue(_ctx);
c68f28e2cf5f9621511bece0414335e551dc82c6Timo Sirainen
c68f28e2cf5f9621511bece0414335e551dc82c6Timo Sirainen do {
c68f28e2cf5f9621511bece0414335e551dc82c6Timo Sirainen if (o_stream_send_istream(_ctx->output, ctx->input) < 0) {
c68f28e2cf5f9621511bece0414335e551dc82c6Timo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
c68f28e2cf5f9621511bece0414335e551dc82c6Timo Sirainen mail_storage_set_critical(storage,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "o_stream_send_istream(%s) failed: %m",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ctx->cur_file->cur_path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen ctx->failed = TRUE;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen return -1;
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen }
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen index_mail_cache_parse_continue(_ctx->dest_mail);
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen /* both tee input readers may consume data from our primary
48325adac125d7ff275ec69b05b7a92be9637630Timo Sirainen input stream. we'll have to make sure we don't return with
48325adac125d7ff275ec69b05b7a92be9637630Timo Sirainen one of the streams still having data in them. */
1b6c4fdd2bb4234b5711874b3845547f49649744Timo Sirainen } while (i_stream_read(ctx->input) > 0);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainenvoid dbox_save_end(struct dbox_save_context *ctx)
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen{
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen struct ostream *dbox_output = ctx->dbox_output;
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen if (ctx->ctx.attach != NULL) {
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen if (index_attachment_save_finish(&ctx->ctx) < 0)
d1ba8ecbb936ace90179d2292952546708d68f71Timo Sirainen ctx->failed = TRUE;
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen }
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen if (ctx->ctx.output == dbox_output)
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi return;
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi /* e.g. zlib plugin had changed this */
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi o_stream_ref(dbox_output);
efb83f10b2a557d7427c311da52d768fb91e1b47Timo Sirainen o_stream_destroy(&ctx->ctx.output);
efb83f10b2a557d7427c311da52d768fb91e1b47Timo Sirainen ctx->ctx.output = dbox_output;
c25dfa96bc32e8841c9a8cf5ba02fffba4290160Timo Sirainen}
3177b410680f3915549719f84a4acbffd4f9c561Timo Sirainen
3177b410680f3915549719f84a4acbffd4f9c561Timo Sirainenvoid dbox_save_write_metadata(struct mail_save_context *_ctx,
3177b410680f3915549719f84a4acbffd4f9c561Timo Sirainen struct ostream *output, uoff_t output_msg_size,
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen const char *orig_mailbox_name,
c3d9da3955043aef88c17b71f2081e894186aa6bTimo Sirainen uint8_t guid_128[MAIL_GUID_128_SIZE])
c25dfa96bc32e8841c9a8cf5ba02fffba4290160Timo Sirainen{
c25dfa96bc32e8841c9a8cf5ba02fffba4290160Timo Sirainen struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen struct dbox_metadata_header metadata_hdr;
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen const char *guid;
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen string_t *str;
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen uoff_t vsize;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen memset(&metadata_hdr, 0, sizeof(metadata_hdr));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST,
804fa3f03bd9170272168a5ad214053bbe3160c7Josef 'Jeff' Sipek sizeof(metadata_hdr.magic_post));
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen o_stream_send(output, &metadata_hdr, sizeof(metadata_hdr));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str = t_str_new(256);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (output_msg_size != ctx->input->v_offset) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* a plugin changed the data written to disk, so the
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "message size" dbox header doesn't contain the actual
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch "physical" message size. we need to save it as a
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen separate metadata header. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_printfa(str, "%c%llx\n", DBOX_METADATA_PHYSICAL_SIZE,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen (unsigned long long)ctx->input->v_offset);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_printfa(str, "%c%lx\n", DBOX_METADATA_RECEIVED_TIME,
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen (unsigned long)_ctx->received_date);
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen if (mail_get_virtual_size(_ctx->dest_mail, &vsize) < 0)
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen i_unreached();
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen (unsigned long long)vsize);
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen if (_ctx->pop3_uidl != NULL) {
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen i_assert(strchr(_ctx->pop3_uidl, '\n') == NULL);
b58aafbd21b365117538f73f306d22f75acd91f1Timo Sirainen str_printfa(str, "%c%s\n", DBOX_METADATA_POP3_UIDL,
f89eb8f2cda0bd6d40a9f96db1c92517f0593871Martti Rannanjärvi _ctx->pop3_uidl);
f89eb8f2cda0bd6d40a9f96db1c92517f0593871Martti Rannanjärvi }
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen guid = _ctx->guid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (guid != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_generate_guid_128_hash(guid, guid_128);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen else {
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen mail_generate_guid_128(guid_128);
a8bc64d2ec8babb5109fa23aa3c90383de61cd69Timo Sirainen guid = binary_to_hex(guid_128, MAIL_GUID_128_SIZE);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen }
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid);
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen if (orig_mailbox_name != NULL &&
a8bc64d2ec8babb5109fa23aa3c90383de61cd69Timo Sirainen strchr(orig_mailbox_name, '\r') == NULL &&
147a788fea2a88f7125b27226451271d55cf5b01Timo Sirainen strchr(orig_mailbox_name, '\n') == NULL) {
147a788fea2a88f7125b27226451271d55cf5b01Timo Sirainen /* save the original mailbox name so if mailbox indexes get
147a788fea2a88f7125b27226451271d55cf5b01Timo Sirainen corrupted we can place at least some (hopefully most) of
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen the messages to correct mailboxes. */
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen str_printfa(str, "%c%s\n", DBOX_METADATA_ORIG_MAILBOX,
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen orig_mailbox_name);
147a788fea2a88f7125b27226451271d55cf5b01Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen dbox_attachment_save_write_metadata(_ctx, str);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_append_c(str, '\n');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_send(output, str_data(str), str_len(str));
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen}
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen