dbox-save.c revision c25dfa96bc32e8841c9a8cf5ba02fffba4290160
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2007-2012 Dovecot authors, see the included COPYING file */
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#include "lib.h"
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#include "istream.h"
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen#include "istream-crlf.h"
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen#include "ostream.h"
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#include "str.h"
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#include "hex-binary.h"
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#include "index-mail.h"
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#include "dbox-attachment.h"
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#include "dbox-file.h"
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#include "dbox-save.h"
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainenvoid dbox_save_add_to_index(struct dbox_save_context *ctx)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen struct mail_save_data *mdata = &ctx->ctx.data;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen enum mail_flags save_flags;
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen save_flags = mdata->flags & ~MAIL_RECENT;
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen mail_index_append(ctx->trans, mdata->uid, &ctx->seq);
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen save_flags);
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen if (mdata->keywords != NULL) {
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen mail_index_update_keywords(ctx->trans, ctx->seq,
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen MODIFY_REPLACE, mdata->keywords);
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (mdata->min_modseq != 0) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen mail_index_update_modseq(ctx->trans, ctx->seq,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen mdata->min_modseq);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid dbox_save_begin(struct dbox_save_context *ctx, struct istream *input)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_save_context *_ctx = &ctx->ctx;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_storage *_storage = _ctx->transaction->box->storage;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen struct dbox_storage *storage = (struct dbox_storage *)_storage;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen struct dbox_message_header dbox_msg_hdr;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen struct istream *crlf_input;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen dbox_save_add_to_index(ctx);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen if (_ctx->dest_mail == NULL) {
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (ctx->mail == NULL)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen ctx->mail = mail_alloc(_ctx->transaction, 0, NULL);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen _ctx->dest_mail = ctx->mail;
3b32bc12710240f86465a00fbb2bd1ef030e6c40Timo Sirainen }
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen mail_set_seq_saving(_ctx->dest_mail, ctx->seq);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen crlf_input = i_stream_create_lf(input);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen ctx->input = index_mail_cache_parse_init(_ctx->dest_mail, crlf_input);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen i_stream_unref(&crlf_input);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen /* write a dummy header. it'll get rewritten when we're finished */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen memset(&dbox_msg_hdr, 0, sizeof(dbox_msg_hdr));
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen o_stream_cork(ctx->dbox_output);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen if (o_stream_send(ctx->dbox_output, &dbox_msg_hdr,
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen sizeof(dbox_msg_hdr)) < 0) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen mail_storage_set_critical(_storage, "write(%s) failed: %m",
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen o_stream_get_name(ctx->dbox_output));
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen ctx->failed = TRUE;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen }
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen _ctx->data.output = ctx->dbox_output;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen if (_ctx->data.received_date == (time_t)-1)
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen _ctx->data.received_date = ioloop_time;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen index_attachment_save_begin(_ctx, storage->attachment_fs, ctx->input);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen}
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenint dbox_save_continue(struct mail_save_context *_ctx)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen{
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
b42697a5749b85659a24316d97f1c208d469e4e8Timo Sirainen struct mail_storage *storage = _ctx->transaction->box->storage;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (ctx->failed)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen return -1;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (_ctx->data.attach != NULL)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen return index_attachment_save_continue(_ctx);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen do {
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) {
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen mail_storage_set_critical(storage,
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen "write(%s) failed: %m",
724b7fcf28c2547eb9c837d0e99241c0501dccf3Timo Sirainen o_stream_get_name(_ctx->data.output));
724b7fcf28c2547eb9c837d0e99241c0501dccf3Timo Sirainen }
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen ctx->failed = TRUE;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen return -1;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen }
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen index_mail_cache_parse_continue(_ctx->dest_mail);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen /* both tee input readers may consume data from our primary
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen input stream. we'll have to make sure we don't return with
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen one of the streams still having data in them. */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen } while (i_stream_read(ctx->input) > 0);
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen return 0;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen}
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen
9ffdc9d18870acef2e4dde99715d8528ff4b080dTimo Sirainenvoid dbox_save_end(struct dbox_save_context *ctx)
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen{
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen struct mail_save_data *mdata = &ctx->ctx.data;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen struct ostream *dbox_output = ctx->dbox_output;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen if (mdata->attach != NULL && !ctx->failed) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (index_attachment_save_finish(&ctx->ctx) < 0)
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen ctx->failed = TRUE;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen }
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen if (o_stream_nfinish(mdata->output) < 0) {
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen mail_storage_set_critical(ctx->ctx.transaction->box->storage,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen "write(%s) failed: %m",
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen o_stream_get_name(mdata->output));
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen ctx->failed = TRUE;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen }
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (mdata->output != dbox_output) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* e.g. zlib plugin had changed this */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen o_stream_ref(dbox_output);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen o_stream_destroy(&mdata->output);
ac26a4607cb12b156f6a42f1ead2881bedd43d94Timo Sirainen mdata->output = dbox_output;
dca6d617a23e3f93af3b8df59acb46478179fe55Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen index_mail_cache_parse_deinit(ctx->ctx.dest_mail,
1108376e39a19912e8394e64e19b1bc6f6691cf6Timo Sirainen ctx->ctx.data.received_date,
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen !ctx->failed);
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen}
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainenvoid dbox_save_write_metadata(struct mail_save_context *_ctx,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen struct ostream *output, uoff_t output_msg_size,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen const char *orig_mailbox_name,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen guid_128_t guid_128)
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen{
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
306cfd77100131c08b243de10f6d40500f4c27c6Timo Sirainen struct mail_save_data *mdata = &ctx->ctx.data;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen struct dbox_metadata_header metadata_hdr;
09c08fad8e7cc694a6c8d1711e67839acd3a2f04Timo Sirainen const char *guid;
438f12d7a776da695019114884b48188d94613efTimo Sirainen string_t *str;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen uoff_t vsize;
9ffdc9d18870acef2e4dde99715d8528ff4b080dTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen memset(&metadata_hdr, 0, sizeof(metadata_hdr));
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen sizeof(metadata_hdr.magic_post));
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen o_stream_nsend(output, &metadata_hdr, sizeof(metadata_hdr));
6469cf211a57433335641725dc236ebb2b9fdd3bTimo Sirainen
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen str = t_str_new(256);
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen if (output_msg_size != ctx->input->v_offset) {
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen /* a plugin changed the data written to disk, so the
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen "message size" dbox header doesn't contain the actual
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen "physical" message size. we need to save it as a
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen separate metadata header. */
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen str_printfa(str, "%c%llx\n", DBOX_METADATA_PHYSICAL_SIZE,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen (unsigned long long)ctx->input->v_offset);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen }
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen str_printfa(str, "%c%lx\n", DBOX_METADATA_RECEIVED_TIME,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen (unsigned long)mdata->received_date);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen if (mail_get_virtual_size(_ctx->dest_mail, &vsize) < 0)
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen i_unreached();
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen (unsigned long long)vsize);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen if (mdata->pop3_uidl != NULL) {
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen i_assert(strchr(mdata->pop3_uidl, '\n') == NULL);
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen str_printfa(str, "%c%s\n", DBOX_METADATA_POP3_UIDL,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen mdata->pop3_uidl);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen }
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen guid = mdata->guid;
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen if (guid != NULL)
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen mail_generate_guid_128_hash(guid, guid_128);
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen else {
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen guid_128_generate(guid_128);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen guid = guid_128_to_string(guid_128);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen }
str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid);
if (orig_mailbox_name != NULL &&
strchr(orig_mailbox_name, '\r') == NULL &&
strchr(orig_mailbox_name, '\n') == NULL) {
/* save the original mailbox name so if mailbox indexes get
corrupted we can place at least some (hopefully most) of
the messages to correct mailboxes. */
str_printfa(str, "%c%s\n", DBOX_METADATA_ORIG_MAILBOX,
orig_mailbox_name);
}
dbox_attachment_save_write_metadata(_ctx, str);
str_append_c(str, '\n');
o_stream_nsend(output, str_data(str), str_len(str));
}