mbox-save.c revision aeab2134d21723e782b788616b83b4f4e17626ef
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2013 Dovecot authors, see the included COPYING file */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen#include "ioloop.h"
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen#include "array.h"
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen#include "base64.h"
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen#include "hostpid.h"
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen#include "randgen.h"
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen#include "istream.h"
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen#include "ostream.h"
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen#include "str.h"
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen#include "write-full.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "istream-header-filter.h"
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen#include "istream-crlf.h"
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen#include "istream-concat.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "message-parser.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "mail-user.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "index-mail.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "mbox-storage.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "mbox-file.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "mbox-from.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "mbox-lock.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "mbox-md5.h"
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen#include "mbox-sync-private.h"
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include <stddef.h>
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include <stdlib.h>
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen#include <unistd.h>
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include <fcntl.h>
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include <sys/stat.h>
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#include <utime.h>
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen#define MBOX_DELIVERY_ID_RAND_BYTES (64/8)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstruct mbox_save_context {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct mail_save_context ctx;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen struct mbox_mailbox *mbox;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen struct mail_index_transaction *trans;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen struct mail *mail;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen uoff_t append_offset, mail_offset;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen time_t orig_atime;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen string_t *headers;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen size_t space_end_idx;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen uint32_t seq, next_uid, uid_validity;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen struct istream *input;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen struct ostream *output;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen uoff_t extra_hdr_offset, eoh_offset;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen char last_char;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen struct mbox_md5_context *mbox_md5_ctx;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen char *x_delivery_id_header;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned int synced:1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned int failed:1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen unsigned int finished:1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen};
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainenstatic void write_error(struct mbox_save_context *ctx)
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen{
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen mbox_set_syscall_error(ctx->mbox, "write()");
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen ctx->failed = TRUE;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen}
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenstatic int mbox_seek_to_end(struct mbox_save_context *ctx, uoff_t *offset)
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen{
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen struct stat st;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen char ch;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen int fd;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (ctx->mbox->mbox_writeonly) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen *offset = 0;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return 0;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen fd = ctx->mbox->mbox_fd;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (fstat(fd, &st) < 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen mbox_set_syscall_error(ctx->mbox, "fstat()");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ctx->orig_atime = st.st_atime;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen *offset = (uoff_t)st.st_size;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (st.st_size == 0)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return 0;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (lseek(fd, st.st_size-1, SEEK_SET) < 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen mbox_set_syscall_error(ctx->mbox, "lseek()");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (read(fd, &ch, 1) != 1) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen mbox_set_syscall_error(ctx->mbox, "read()");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (ch != '\n') {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (write_full(fd, "\n", 1) < 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen write_error(ctx);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen *offset += 1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return 0;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen}
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic int mbox_append_lf(struct mbox_save_context *ctx)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (o_stream_send(ctx->output, "\n", 1) < 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen write_error(ctx);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return 0;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen}
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic int write_from_line(struct mbox_save_context *ctx, time_t received_date,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const char *from_envelope)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen int ret;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen T_BEGIN {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen const char *line;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (from_envelope == NULL) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen struct mail_storage *storage =
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen &ctx->mbox->storage->storage;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen from_envelope =
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen strchr(storage->user->username, '@') != NULL ?
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen storage->user->username :
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen t_strconcat(storage->user->username,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen "@", my_hostdomain(), NULL);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen } else if (*from_envelope == '\0') {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* can't write empty envelope */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen from_envelope = "MAILER-DAEMON";
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* save in local timezone, no matter what it was given with */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen line = mbox_from_create(from_envelope, received_date);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if ((ret = o_stream_send_str(ctx->output, line)) < 0)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen write_error(ctx);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen } T_END;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return ret;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen}
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenstatic int mbox_write_content_length(struct mbox_save_context *ctx)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen{
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen uoff_t end_offset;
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen const char *str;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen size_t len;
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen i_assert(ctx->eoh_offset != (uoff_t)-1);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (ctx->mbox->mbox_writeonly) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* we can't seek, don't set Content-Length */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return 0;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen end_offset = ctx->output->offset;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen /* write Content-Length headers */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen str = t_strdup_printf("\nContent-Length: %s",
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen dec2str(end_offset - ctx->eoh_offset));
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen len = strlen(str);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
303a87c31cb4aa198326694e231df53a043e63c7Timo Sirainen /* flush manually here so that we don't confuse seek() errors with
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen buffer flushing errors */
f2bd9e507b8befdd95a983f86664febf5c19bd95Timo Sirainen if (o_stream_flush(ctx->output) < 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen write_error(ctx);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (o_stream_seek(ctx->output, ctx->extra_hdr_offset +
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ctx->space_end_idx - len) < 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen mbox_set_syscall_error(ctx->mbox, "lseek()");
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (o_stream_send(ctx->output, str, len) < 0 ||
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen o_stream_flush(ctx->output) < 0) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen write_error(ctx);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (o_stream_seek(ctx->output, end_offset) < 0) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen mbox_set_syscall_error(ctx->mbox, "lseek()");
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return -1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return 0;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen}
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic void mbox_save_init_sync(struct mailbox_transaction_context *t)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->box;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct mbox_save_context *ctx = (struct mbox_save_context *)t->save_ctx;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const struct mail_index_header *hdr;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct mail_index_view *view;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* open a new view to get the header. this is required if we just
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen synced the mailbox so we can get updated next_uid. */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen mail_index_refresh(mbox->box.index);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen view = mail_index_view_open(mbox->box.index);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen hdr = mail_index_get_header(view);
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen ctx->next_uid = hdr->next_uid;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen ctx->uid_validity = hdr->uid_validity;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen ctx->synced = TRUE;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen mail_index_view_close(&view);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen}
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic void status_flags_append(string_t *str, enum mail_flags flags,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const struct mbox_flag_type *flags_list)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen int i;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen flags ^= MBOX_NONRECENT_KLUDGE;
39e6fcc3e8b1ccb13087c232cb6bdea04d1a20a4Timo Sirainen for (i = 0; flags_list[i].chr != 0; i++) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if ((flags & flags_list[i].flag) != 0)
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen str_append_c(str, flags_list[i].chr);
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen }
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen}
3398d5e2b883812de5d569721c8294b581e1d9e6Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainenstatic void mbox_save_append_flag_headers(string_t *str, enum mail_flags flags)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
ba5c8b0ae7460752adaf911901bf263788f62c72Phil Carmody /* write the Status: header always. It always gets added soon anyway. */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen str_append(str, "Status: ");
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen status_flags_append(str, flags, mbox_status_flags);
bc3698b8892df8003b410daea6f5bbcd20433808Timo Sirainen str_append_c(str, '\n');
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if ((flags & XSTATUS_FLAGS_MASK) != 0) {
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen str_append(str, "X-Status: ");
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen status_flags_append(str, flags, mbox_xstatus_flags);
06e72c658de3ce1252594b151313df90acf73271Timo Sirainen str_append_c(str, '\n');
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen}
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenstatic void
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainenmbox_save_append_keyword_headers(struct mbox_save_context *ctx,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct mail_keywords *keywords)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen unsigned char space[MBOX_HEADER_PADDING+1 +
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen sizeof("Content-Length: \n")-1 + MAX_INT_STRLEN];
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen const ARRAY_TYPE(keywords) *keyword_names_list;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen const char *const *keyword_names;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen unsigned int i, count, keyword_names_count;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen keyword_names_list = mail_index_get_keywords(ctx->mbox->box.index);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen keyword_names = array_get(keyword_names_list, &keyword_names_count);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen str_append(ctx->headers, "X-Keywords:");
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen count = keywords == NULL ? 0 : keywords->count;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen for (i = 0; i < count; i++) {
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen i_assert(keywords->idx[i] < keyword_names_count);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen str_append_c(ctx->headers, ' ');
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen str_append(ctx->headers, keyword_names[keywords->idx[i]]);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen memset(space, ' ', sizeof(space));
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen str_append_n(ctx->headers, space, sizeof(space));
07974f50bd55b06fd6d465f2c0e491794786e2faTimo Sirainen ctx->space_end_idx = str_len(ctx->headers);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen str_append_c(ctx->headers, '\n');
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen}
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainenstatic int
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainenmbox_save_init_file(struct mbox_save_context *ctx,
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen struct mbox_transaction_context *t, bool want_mail)
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen{
07974f50bd55b06fd6d465f2c0e491794786e2faTimo Sirainen struct mailbox_transaction_context *_t = &t->t;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct mbox_mailbox *mbox = ctx->mbox;
07974f50bd55b06fd6d465f2c0e491794786e2faTimo Sirainen struct mail_storage *storage = &mbox->storage->storage;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen bool empty = FALSE;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen int ret;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen if (mbox_is_backend_readonly(ctx->mbox)) {
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen mail_storage_set_error(storage, MAIL_ERROR_PERM,
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen "Read-only mbox");
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen return -1;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen }
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
2d9644d34a78b24dc7769cd96497e700a0fb1cf1Timo Sirainen if ((_t->flags & MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS) != 0 ||
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen ctx->ctx.data.uid != 0)
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen want_mail = TRUE;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (ctx->append_offset == (uoff_t)-1) {
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen /* first appended mail in this transaction */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (t->write_lock_id == 0) {
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen if (mbox_lock(mbox, F_WRLCK, &t->write_lock_id) <= 0)
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen return -1;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen }
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
270f00aeab7bede38764291e21a314211b884ab4Timo Sirainen if (mbox->mbox_fd == -1) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (mbox_file_open(mbox) < 0)
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* update mbox_sync_dirty state */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ret = mbox_sync_has_changed_full(mbox, TRUE, &empty);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (ret < 0)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return -1;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (!want_mail && ret == 0) {
7891195e3975d554df183670dba1fcecfa0a30c3Timo Sirainen /* we're not required to assign UIDs for the appended
7891195e3975d554df183670dba1fcecfa0a30c3Timo Sirainen mails immediately. do it only if it doesn't require
7891195e3975d554df183670dba1fcecfa0a30c3Timo Sirainen syncing. */
7891195e3975d554df183670dba1fcecfa0a30c3Timo Sirainen mbox_save_init_sync(_t);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (!ctx->synced && (want_mail || empty)) {
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen /* we'll need to assign UID for the mail immediately. */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (mbox_sync(mbox, 0) < 0)
89b3a53140d31bfa0f34378bf7ee1f52da954961Timo Sirainen return -1;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen mbox_save_init_sync(_t);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen }
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* the syncing above could have changed the append offset */
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (ctx->append_offset == (uoff_t)-1) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (mbox_seek_to_end(ctx, &ctx->append_offset) < 0)
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen return -1;
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen ctx->output = o_stream_create_fd_file(mbox->mbox_fd,
bd74402ca1a39ec303075fefb1212d7e18a71531Timo Sirainen ctx->append_offset,
505561860cdfee4eac51469fd27a59983ef72e8eTimo Sirainen FALSE);
505561860cdfee4eac51469fd27a59983ef72e8eTimo Sirainen o_stream_cork(ctx->output);
505561860cdfee4eac51469fd27a59983ef72e8eTimo Sirainen }
505561860cdfee4eac51469fd27a59983ef72e8eTimo Sirainen return 0;
505561860cdfee4eac51469fd27a59983ef72e8eTimo Sirainen}
505561860cdfee4eac51469fd27a59983ef72e8eTimo Sirainen
505561860cdfee4eac51469fd27a59983ef72e8eTimo Sirainenstatic void
270f00aeab7bede38764291e21a314211b884ab4Timo Sirainensave_header_callback(struct header_filter_istream *input ATTR_UNUSED,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct message_header_line *hdr,
c6ae908f6a2313573625d782bdd4e0ff3882c44aTimo Sirainen bool *matched, struct mbox_save_context *ctx)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (hdr != NULL) {
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen if (strncmp(hdr->name, "From ", 5) == 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* we can't allow From_-lines in headers. there's no
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen legitimate reason for allowing them in any case,
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen so just drop them. */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen *matched = TRUE;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return;
17da42c31202b1b3e7e308121ea17d922c24da1bTimo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
ef11d3930c3602fc86349a4e3a53442df470b601Timo Sirainen if (!*matched && ctx->mbox_md5_ctx != NULL)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ctx->mbox->md5_v.more(ctx->mbox_md5_ctx, hdr);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen}
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic void mbox_save_x_delivery_id(struct mbox_save_context *ctx)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen unsigned char md5_result[MD5_RESULTLEN];
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen buffer_t *buf;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen string_t *str;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen void *randbuf;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 256);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen buffer_append(buf, &ioloop_time, sizeof(ioloop_time));
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen buffer_append(buf, &ioloop_timeval.tv_usec,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen sizeof(ioloop_timeval.tv_usec));
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen randbuf = buffer_append_space_unsafe(buf, MBOX_DELIVERY_ID_RAND_BYTES);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen random_fill_weak(randbuf, MBOX_DELIVERY_ID_RAND_BYTES);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen md5_get_digest(buf->data, buf->used, md5_result);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen str = t_str_new(128);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen str_append(str, "X-Delivery-ID: ");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen base64_encode(md5_result, sizeof(md5_result), str);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen str_append_c(str, '\n');
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ctx->x_delivery_id_header = i_strdup(str_c(str));
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen}
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic struct istream *
270f00aeab7bede38764291e21a314211b884ab4Timo Sirainenmbox_save_get_input_stream(struct mbox_save_context *ctx, struct istream *input)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen{
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct istream *filter, *ret, *cache_input, *streams[3];
59714981ae172b5113be7ca9b8be518b759fc86dTimo Sirainen
59714981ae172b5113be7ca9b8be518b759fc86dTimo Sirainen /* filter out unwanted headers and keep track of headers' MD5 sum */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen filter = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE |
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen HEADER_FILTER_NO_CR |
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen HEADER_FILTER_ADD_MISSING_EOH |
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen HEADER_FILTER_END_BODY_WITH_LF,
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen mbox_save_drop_headers,
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen mbox_save_drop_headers_count,
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen save_header_callback, ctx);
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if ((ctx->mbox->storage->storage.flags &
ccb77e2f63626ec46e5745ef4f38baa8e8e504fcTimo Sirainen MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) != 0) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen /* we're using MD5 sums to generate POP3 UIDLs.
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen clients don't like it much if there are duplicates,
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen so make sure that there can't be any by appending
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen our own X-Delivery-ID header. */
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen const char *hdr;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen T_BEGIN {
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen mbox_save_x_delivery_id(ctx);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen } T_END;
6384258c2f84e635d8ceffc3eeddad71f7538040Timo Sirainen hdr = ctx->x_delivery_id_header;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen streams[0] = i_stream_create_from_data(hdr, strlen(hdr));
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen streams[1] = filter;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen streams[2] = NULL;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen ret = i_stream_create_concat(streams);
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen i_stream_unref(&filter);
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen filter = ret;
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen }
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen /* convert linefeeds to wanted format */
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen ret = ctx->mbox->storage->storage.set->mail_save_crlf ?
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen i_stream_create_crlf(filter) : i_stream_create_lf(filter);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen i_stream_unref(&filter);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (ctx->ctx.dest_mail != NULL) {
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen /* caching creates a tee stream */
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen cache_input =
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen index_mail_cache_parse_init(ctx->ctx.dest_mail, ret);
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen i_stream_unref(&ret);
45c763dbebee268eda4a1e8bcc1ff82606b5ed0dTimo Sirainen ret = cache_input;
59714981ae172b5113be7ca9b8be518b759fc86dTimo Sirainen }
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen return ret;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen}
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainenstruct mail_save_context *
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainenmbox_save_alloc(struct mailbox_transaction_context *t)
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen{
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->box;
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen struct mbox_save_context *ctx;
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (t->save_ctx == NULL) {
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen ctx = i_new(struct mbox_save_context, 1);
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen ctx->ctx.transaction = t;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen ctx->mbox = mbox;
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen ctx->trans = t->itrans;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen ctx->append_offset = (uoff_t)-1;
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen ctx->headers = str_new(default_pool, 512);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen ctx->mail_offset = (uoff_t)-1;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen t->save_ctx = &ctx->ctx;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen }
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen return t->save_ctx;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen}
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainenint mbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen{
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen struct mail_save_data *mdata = &_ctx->data;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen struct mbox_transaction_context *t =
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen (struct mbox_transaction_context *)_ctx->transaction;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen enum mail_flags save_flags;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen uint64_t offset;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* FIXME: we could write timezone_offset to From-line.. */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (mdata->received_date == (time_t)-1)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen mdata->received_date = ioloop_time;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen ctx->failed = FALSE;
c8625391da3ee51b31e69b88895708a3d149dd1bTimo Sirainen ctx->seq = 0;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (mbox_save_init_file(ctx, t, _ctx->dest_mail != NULL) < 0) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen ctx->failed = TRUE;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen return -1;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen save_flags = mdata->flags;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (mdata->uid == 0)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen save_flags |= MAIL_RECENT;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen str_truncate(ctx->headers, 0);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (ctx->synced) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (ctx->mbox->mbox_save_md5)
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen ctx->mbox_md5_ctx = ctx->mbox->md5_v.init();
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (ctx->next_uid < mdata->uid) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* we can use the wanted UID */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen ctx->next_uid = mdata->uid;
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (ctx->output->offset == 0) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen /* writing the first mail. Insert X-IMAPbase as well. */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen str_printfa(ctx->headers, "X-IMAPbase: %u %010u\n",
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen ctx->uid_validity, ctx->next_uid);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen str_printfa(ctx->headers, "X-UID: %u\n", ctx->next_uid);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen mail_index_append(ctx->trans, ctx->next_uid, &ctx->seq);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen save_flags & ~MAIL_RECENT);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (mdata->keywords != NULL) {
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen mail_index_update_keywords(ctx->trans, ctx->seq,
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen MODIFY_REPLACE,
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen mdata->keywords);
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen }
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen if (mdata->min_modseq != 0) {
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen mail_index_update_modseq(ctx->trans, ctx->seq,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen mdata->min_modseq);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen offset = ctx->output->offset == 0 ? 0 :
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ctx->output->offset - 1;
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen mail_index_update_ext(ctx->trans, ctx->seq,
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen ctx->mbox->mbox_ext_idx, &offset, NULL);
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen ctx->next_uid++;
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen /* parse and cache the mail headers as we read it */
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen if (_ctx->dest_mail == NULL) {
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen if (ctx->mail == NULL) {
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen ctx->mail = mail_alloc(_ctx->transaction,
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen 0, NULL);
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen }
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen _ctx->dest_mail = ctx->mail;
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen }
270f00aeab7bede38764291e21a314211b884ab4Timo Sirainen mail_set_seq_saving(_ctx->dest_mail, ctx->seq);
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen }
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen mbox_save_append_flag_headers(ctx->headers, save_flags);
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen mbox_save_append_keyword_headers(ctx, mdata->keywords);
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen str_append_c(ctx->headers, '\n');
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen i_assert(ctx->mbox->mbox_lock_type == F_WRLCK);
270f00aeab7bede38764291e21a314211b884ab4Timo Sirainen
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen ctx->mail_offset = ctx->output->offset;
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen ctx->eoh_offset = (uoff_t)-1;
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen ctx->last_char = '\n';
a4bc2c3962b94f83c7bb7bebe7af364f4dee7883Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (write_from_line(ctx, mdata->received_date, mdata->from_envelope) < 0)
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen ctx->failed = TRUE;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen else
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ctx->input = mbox_save_get_input_stream(ctx, input);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return ctx->failed ? -1 : 0;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen}
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainenstatic int mbox_save_body_input(struct mbox_save_context *ctx)
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen{
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen const unsigned char *data;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen size_t size;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen data = i_stream_get_data(ctx->input, &size);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (size > 0) {
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen if (o_stream_send(ctx->output, data, size) < 0) {
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen write_error(ctx);
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen return -1;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen ctx->last_char = data[size-1];
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen i_stream_skip(ctx->input, size);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen }
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen return 0;
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen}
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen
10cfe8a2bdc5ccfc05380689c71c27209327538fTimo Sirainenstatic int mbox_save_body(struct mbox_save_context *ctx)
10cfe8a2bdc5ccfc05380689c71c27209327538fTimo Sirainen{
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen ssize_t ret;
10cfe8a2bdc5ccfc05380689c71c27209327538fTimo Sirainen
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen while ((ret = i_stream_read(ctx->input)) != -1) {
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen if (ctx->ctx.dest_mail != NULL) {
a54be2bd26d6f0860d194d3aeedfa6b7fc14d24cTimo Sirainen /* i_stream_read() may have returned 0 at EOF
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen because of this parser */
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen index_mail_cache_parse_continue(ctx->ctx.dest_mail);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen }
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen if (ret == 0)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return 0;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (mbox_save_body_input(ctx) < 0)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen i_assert(ctx->last_char == '\n');
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return 0;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen}
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainenstatic int mbox_save_finish_headers(struct mbox_save_context *ctx)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen{
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen i_assert(ctx->eoh_offset == (uoff_t)-1);
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen /* append our own headers and ending empty line */
8039af9679af6fb56116b353fe44f7dd4c08f031Timo Sirainen ctx->extra_hdr_offset = ctx->output->offset;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (o_stream_send(ctx->output, str_data(ctx->headers),
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen str_len(ctx->headers)) < 0) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen write_error(ctx);
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen return -1;
85ee28daca146e18a99a22f46c0d639e57a6ac95Timo Sirainen }
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen ctx->eoh_offset = ctx->output->offset;
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen return 0;
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen}
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainenint mbox_save_continue(struct mail_save_context *_ctx)
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen{
d42c9a8f362b76740418c4f9f9441eb7fc661e57Timo Sirainen struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen const unsigned char *data;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen size_t i, size;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen ssize_t ret;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (ctx->failed)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (ctx->eoh_offset != (uoff_t)-1) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* writing body */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return mbox_save_body(ctx);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen while ((ret = i_stream_read(ctx->input)) > 0) {
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen if (ctx->ctx.dest_mail != NULL)
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen index_mail_cache_parse_continue(ctx->ctx.dest_mail);
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen data = i_stream_get_data(ctx->input, &size);
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen for (i = 0; i < size; i++) {
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen if (data[i] == '\n' &&
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen ((i == 0 && ctx->last_char == '\n') ||
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen (i > 0 && data[i-1] == '\n'))) {
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen /* end of headers. we don't need to worry about
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen CRs because they're dropped */
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen break;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen }
0b7651dc6ad21cce8579b5957252ae0daf972668Timo Sirainen if (i != size) {
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen /* found end of headers. write the rest of them
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen (not including the finishing empty line) */
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen if (o_stream_send(ctx->output, data, i) < 0) {
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen write_error(ctx);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
270f00aeab7bede38764291e21a314211b884ab4Timo Sirainen ctx->last_char = '\n';
270f00aeab7bede38764291e21a314211b884ab4Timo Sirainen i_stream_skip(ctx->input, i + 1);
270f00aeab7bede38764291e21a314211b884ab4Timo Sirainen break;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (o_stream_send(ctx->output, data, size) < 0) {
68efcccb384f2d6871164b072457e87473502c51Timo Sirainen write_error(ctx);
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen return -1;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen ctx->last_char = data[size-1];
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen i_stream_skip(ctx->input, size);
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen }
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen if (ret == 0)
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen return 0;
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen if (ctx->input->stream_errno != 0) {
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen i_error("read(%s) failed: %m", i_stream_get_name(ctx->input));
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen ctx->failed = TRUE;
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen return -1;
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen }
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen i_assert(ctx->last_char == '\n');
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (ctx->mbox_md5_ctx) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen unsigned char hdr_md5_sum[16];
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (ctx->x_delivery_id_header != NULL) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen struct message_header_line hdr;
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen memset(&hdr, 0, sizeof(hdr));
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen hdr.name = ctx->x_delivery_id_header;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen hdr.name_len = sizeof("X-Delivery-ID")-1;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen hdr.middle = (const unsigned char *)hdr.name +
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen hdr.name_len;
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen hdr.middle_len = 2;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen hdr.value = hdr.full_value =
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen hdr.middle + hdr.middle_len;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen hdr.value_len = strlen((const char *)hdr.value);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen ctx->mbox->md5_v.more(ctx->mbox_md5_ctx, &hdr);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen ctx->mbox->md5_v.finish(ctx->mbox_md5_ctx, hdr_md5_sum);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mail_index_update_ext(ctx->trans, ctx->seq,
88286b0527bcc0711e312e9db65ca121a45213e3Timo Sirainen ctx->mbox->md5hdr_ext_idx,
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen hdr_md5_sum, NULL);
28cd2599128e102198758cf6080588305feb6bcdTimo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (mbox_save_finish_headers(ctx) < 0)
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return -1;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* write body */
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen if (mbox_save_body_input(ctx) < 0)
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen return -1;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen return ctx->input->eof ? 0 : mbox_save_body(ctx);
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen}
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainenint mbox_save_finish(struct mail_save_context *_ctx)
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen{
3419b088ffe531799bdb47b3ff3fce85c8b7569aTimo Sirainen struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (!ctx->failed && ctx->eoh_offset == (uoff_t)-1)
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen (void)mbox_save_finish_headers(ctx);
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainen
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainen if (ctx->output != NULL) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* make sure everything is written */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (o_stream_nfinish(ctx->output) < 0)
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainen write_error(ctx);
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen ctx->finished = TRUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (!ctx->failed) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen i_assert(ctx->output != NULL);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen T_BEGIN {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (mbox_write_content_length(ctx) < 0 ||
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mbox_append_lf(ctx) < 0)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen ctx->failed = TRUE;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen } T_END;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ctx->ctx.dest_mail != NULL) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen index_mail_cache_parse_deinit(ctx->ctx.dest_mail,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen ctx->ctx.data.received_date,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen !ctx->failed);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ctx->input != NULL)
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen i_stream_destroy(&ctx->input);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen if (ctx->failed && ctx->mail_offset != (uoff_t)-1) {
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen /* saving this mail failed - truncate back to beginning of it */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen (void)o_stream_nfinish(ctx->output);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ftruncate(ctx->mbox->mbox_fd, (off_t)ctx->mail_offset) < 0)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mbox_set_syscall_error(ctx->mbox, "ftruncate()");
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen (void)o_stream_seek(ctx->output, ctx->mail_offset);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen ctx->mail_offset = (uoff_t)-1;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
2372e16bfae0a245bfde6e908cde0919aa11ee0bTimo Sirainen if (ctx->seq != 0 && ctx->failed) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mail_index_expunge(ctx->trans, ctx->seq);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* currently we can't just drop pending cache updates for this
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen one specific record, so we'll reset the whole cache
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen transaction. */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mail_cache_transaction_reset(ctx->ctx.transaction->cache_trans);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen index_save_context_free(_ctx);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen return ctx->failed ? -1 : 0;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen}
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenvoid mbox_save_cancel(struct mail_save_context *_ctx)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen{
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen ctx->failed = TRUE;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen (void)mbox_save_finish(_ctx);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen}
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenstatic void mbox_transaction_save_deinit(struct mbox_save_context *ctx)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen{
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ctx->output != NULL)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen o_stream_destroy(&ctx->output);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ctx->mail != NULL)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mail_free(&ctx->mail);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen str_free(&ctx->headers);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen}
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenstatic void mbox_save_truncate(struct mbox_save_context *ctx)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen{
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ctx->append_offset == (uoff_t)-1 || ctx->mbox->mbox_fd == -1)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen return;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen i_assert(ctx->mbox->mbox_lock_type == F_WRLCK);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* failed, truncate file back to original size. output stream needs to
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen be flushed before truncating so unref() won't write anything. */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ctx->output != NULL)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen (void)o_stream_flush(ctx->output);
2d340205d897e23fbecb40c8e63a4ca49bd6739bTimo Sirainen
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen if (ftruncate(ctx->mbox->mbox_fd, (off_t)ctx->append_offset) < 0)
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen mbox_set_syscall_error(ctx->mbox, "ftruncate()");
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen}
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainenint mbox_transaction_save_commit_pre(struct mail_save_context *_ctx)
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainen{
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen struct mailbox_transaction_context *_t = _ctx->transaction;
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen struct mbox_mailbox *mbox = ctx->mbox;
5a7acd67806132cbc1ec9578df60d712d307e4beTimo Sirainen struct stat st;
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen int ret = 0;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen i_assert(ctx->finished);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
567b56c2d6a1063cad997c956f3ed1d9d735f14eTimo Sirainen if (fstat(mbox->mbox_fd, &st) < 0) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mbox_set_syscall_error(mbox, "fstat()");
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen ret = -1;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen }
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen
c4267cf4c40fb1f866b5958ff122ef836b8c5dfbTimo Sirainen if (ctx->synced) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen _t->changes->uid_validity = ctx->uid_validity;
362db49a9acfb297266cb9a09600689dcffce3ccTimo Sirainen mail_index_append_finish_uids(ctx->trans, 0,
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen &_t->changes->saved_uids);
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen mail_index_update_header(ctx->trans,
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen offsetof(struct mail_index_header, next_uid),
ccd83028a34cc6e2b6370eb7ecf1cf25e717c2d3Timo Sirainen &ctx->next_uid, sizeof(ctx->next_uid), FALSE);
362db49a9acfb297266cb9a09600689dcffce3ccTimo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ret == 0) {
294f1a51763015cda0e2d874c5027d6fe7a2cd54Timo Sirainen mbox->mbox_hdr.sync_mtime = st.st_mtime;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mbox->mbox_hdr.sync_size = st.st_size;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mail_index_update_header_ext(ctx->trans,
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen mbox->mbox_ext_idx,
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen 0, &mbox->mbox_hdr,
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen sizeof(mbox->mbox_hdr));
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ret == 0 && ctx->orig_atime != st.st_atime) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* try to set atime back to its original value.
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen (it'll fail with EPERM for shared mailboxes where we aren't
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen the file's owner) */
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct utimbuf buf;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen buf.modtime = st.st_mtime;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen buf.actime = ctx->orig_atime;
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen if (utime(mailbox_get_path(&mbox->box), &buf) < 0 &&
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen errno != EPERM)
1939d1843ee6c7ca5e5baa3967b0332341440005Timo Sirainen mbox_set_syscall_error(mbox, "utime()");
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen if (ctx->output != NULL) {
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen /* flush the final LF */
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen if (o_stream_nfinish(ctx->output) < 0)
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen write_error(ctx);
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainen }
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainen if (mbox->mbox_fd != -1 && !mbox->mbox_writeonly &&
71caf493c651b8eab5adb170db0237f293928e92Timo Sirainen mbox->storage->storage.set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (fdatasync(mbox->mbox_fd) < 0) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mbox_set_syscall_error(mbox, "fdatasync()");
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen mbox_save_truncate(ctx);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen ret = -1;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen }
d2470b3dfe91ca07459185384ee25080b42a1636Timo Sirainen }
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen mbox_transaction_save_deinit(ctx);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen if (ret < 0)
5a7acd67806132cbc1ec9578df60d712d307e4beTimo Sirainen i_free(ctx);
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen return ret;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen}
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainenvoid mbox_transaction_save_commit_post(struct mail_save_context *_ctx,
5735ada0f82788ee1b5228978d5bd8dad5a04219Timo Sirainen struct mail_index_transaction_commit_result *result ATTR_UNUSED)
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen{
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen i_assert(ctx->mbox->mbox_lock_type == F_WRLCK);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen if (ctx->synced) {
c96eb61168670cfdd7596baba18856d3f086a093Timo Sirainen /* after saving mails with UIDs we need to update
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen the last-uid */
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen (void)mbox_sync(ctx->mbox, MBOX_SYNC_HEADER |
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen MBOX_SYNC_REWRITE);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen }
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen i_free(ctx);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen}
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainenvoid mbox_transaction_save_rollback(struct mail_save_context *_ctx)
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen{
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen if (!ctx->finished)
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen mbox_save_cancel(&ctx->ctx);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen mbox_save_truncate(ctx);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen mbox_transaction_save_deinit(ctx);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen i_free(ctx);
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen}
279b22f320f6139da5c1b0e2a5ead6692e7db947Timo Sirainen