mbox-save.c revision ed9782b21a020daad7fed0b765e9a39a691e7e05
c636315472e4f87313af7be30b7fbcad4b8ca8a4Stephen Gallagher/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
c636315472e4f87313af7be30b7fbcad4b8ca8a4Stephen Gallagher
fd5a4eacd56700ffb08a73121aeacdc806cb0132Sumit Bose#include "lib.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "ioloop.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "array.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "base64.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "hostpid.h"
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include "randgen.h"
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher#include "istream.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "ostream.h"
df4cc3a83c5d6700b6a09ff96cb4a6b1949b1aa9Stephen Gallagher#include "str.h"
df4cc3a83c5d6700b6a09ff96cb4a6b1949b1aa9Stephen Gallagher#include "write-full.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "istream-header-filter.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "istream-crlf.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "istream-concat.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "message-parser.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "index-mail.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "mbox-storage.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "mbox-file.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "mbox-from.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "mbox-lock.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "mbox-md5.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "mbox-sync-private.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include <stddef.h>
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include <stdlib.h>
2a552e43581c74f51205c7141ec9f6e9542509f8Stephen Gallagher#include <unistd.h>
2a552e43581c74f51205c7141ec9f6e9542509f8Stephen Gallagher#include <fcntl.h>
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include <sys/stat.h>
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include <utime.h>
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
d921c1eba437662437847279f251a0a5d8f70127Maxim#define MBOX_DELIVERY_ID_RAND_BYTES (64/8)
2cbdd12983eb85eddb90f64cfafb24eae5b448f4Jakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstruct mbox_save_context {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_save_context ctx;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mbox_mailbox *mbox;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_index_transaction *trans;
eb2e21b764d03544d8161e9956d7f70b07b75f77Simo Sorce struct mail *mail;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher uoff_t append_offset, mail_offset;
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek time_t orig_atime;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher string_t *headers;
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher size_t space_end_idx;
4b6a0d0b3d42e5fdb457f47d9adfa5e66b160256Stephen Gallagher uint32_t seq, next_uid, uid_validity, first_saved_uid;
e124844907ed6973915e4d56f5442ecd07535a12Jakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct istream *input;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher struct ostream *output;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher uoff_t extra_hdr_offset, eoh_offset;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher char last_char;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mbox_md5_context *mbox_md5_ctx;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher char *x_delivery_id_header;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int synced:1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int failed:1;
be1ef1c62ad13612be5e1f879476c24452a5d6d0Stephen Gallagher unsigned int finished:1;
be1ef1c62ad13612be5e1f879476c24452a5d6d0Stephen Gallagher};
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int write_error(struct mbox_save_context *ctx)
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher{
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher mbox_set_syscall_error(ctx->mbox, "write()");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->failed = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
558998ce664055a75595371118f818084d8f2b23Jan Cholasta}
558998ce664055a75595371118f818084d8f2b23Jan Cholasta
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholastastatic int mbox_seek_to_end(struct mbox_save_context *ctx, uoff_t *offset)
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta{
558998ce664055a75595371118f818084d8f2b23Jan Cholasta struct stat st;
558998ce664055a75595371118f818084d8f2b23Jan Cholasta char ch;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int fd;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->mbox->mbox_writeonly) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *offset = 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher fd = ctx->mbox->mbox_fd;
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny if (fstat(fd, &st) < 0)
f1828234a850dd28465425248a83a993f262918fPavel Březina return mbox_set_syscall_error(ctx->mbox, "fstat()");
f1828234a850dd28465425248a83a993f262918fPavel Březina
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->orig_atime = st.st_atime;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *offset = (uoff_t)st.st_size;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (st.st_size == 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (lseek(fd, st.st_size-1, SEEK_SET) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return mbox_set_syscall_error(ctx->mbox, "lseek()");
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina if (read(fd, &ch, 1) != 1)
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina return mbox_set_syscall_error(ctx->mbox, "read()");
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (ch != '\n') {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (write_full(fd, "\n", 1) < 0)
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta return write_error(ctx);
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta *offset += 1;
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mbox_append_lf(struct mbox_save_context *ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (o_stream_send(ctx->output, "\n", 1) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return write_error(ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int write_from_line(struct mbox_save_context *ctx, time_t received_date,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *from_envelope)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
69aaef8719c5cf33ed1c4090fa313ba281bf8a02Jakub Hrozek int ret;
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher
fe60346714a73ac3987f786731389320633dd245Pavel Březina T_BEGIN {
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose const char *line;
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (from_envelope == NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_storage *storage =
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher &ctx->mbox->storage->storage;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher from_envelope = t_strconcat(storage->ns->user->username,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "@", my_hostdomain(), NULL);
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher }
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher /* save in local timezone, no matter what it was given with */
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek line = mbox_from_create(from_envelope, received_date);
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher if ((ret = o_stream_send_str(ctx->output, line)) < 0)
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher write_error(ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher } T_END;
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher return ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mbox_write_content_length(struct mbox_save_context *ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher uoff_t end_offset;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *str;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher size_t len;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->mbox->mbox_writeonly) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* we can't seek, don't set Content-Length */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher end_offset = ctx->output->offset;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* write Content-Length headers */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str = t_strdup_printf("\nContent-Length: %s",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher dec2str(end_offset - ctx->eoh_offset));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher len = strlen(str);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* flush manually here so that we don't confuse seek() errors with
c7919a4fe41133cc466aa3d9431bfceee5784e7bJan Cholasta buffer flushing errors */
b35f20cd8ecdc8308a3201e55752fb0443ec6ae4Jan Cholasta if (o_stream_flush(ctx->output) < 0)
c7919a4fe41133cc466aa3d9431bfceee5784e7bJan Cholasta return write_error(ctx);
69aaef8719c5cf33ed1c4090fa313ba281bf8a02Jakub Hrozek if (o_stream_seek(ctx->output, ctx->extra_hdr_offset +
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek ctx->space_end_idx - len) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return mbox_set_syscall_error(ctx->mbox, "o_stream_seek()");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (o_stream_send(ctx->output, str, len) < 0 ||
c7919a4fe41133cc466aa3d9431bfceee5784e7bJan Cholasta o_stream_flush(ctx->output) < 0)
b35f20cd8ecdc8308a3201e55752fb0443ec6ae4Jan Cholasta return write_error(ctx);
c7919a4fe41133cc466aa3d9431bfceee5784e7bJan Cholasta
69aaef8719c5cf33ed1c4090fa313ba281bf8a02Jakub Hrozek if (o_stream_seek(ctx->output, end_offset) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return mbox_set_syscall_error(ctx->mbox, "o_stream_seek()");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void mbox_save_init_sync(struct mbox_transaction_context *t)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->ictx.ibox;
3b1df539835367cb81cd5ff0f9959947d5642e55Stephen Gallagher struct mbox_save_context *ctx = t->save_ctx;
3b1df539835367cb81cd5ff0f9959947d5642e55Stephen Gallagher const struct mail_index_header *hdr;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_index_view *view;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* open a new view to get the header. this is required if we just
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher synced the mailbox so we can get updated next_uid. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (void)mail_index_refresh(mbox->ibox.index);
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek view = mail_index_view_open(mbox->ibox.index);
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek hdr = mail_index_get_header(view);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->next_uid = hdr->next_uid;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->first_saved_uid = ctx->next_uid;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->uid_validity = hdr->uid_validity;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->synced = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher t->mails_saved = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
15b266d9f14dad26da8678a79019749d0f69532eStephen Gallagher mail_index_view_close(&view);
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozek}
b97595ae059c69b1960a6e7e56d74660388a683bJan Zeleny
667db40da4db362d7ca0a1f7f1c4ba40fb71795aJakub Hrozekstatic void status_flags_append(string_t *str, enum mail_flags flags,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const struct mbox_flag_type *flags_list)
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int i;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher flags ^= MBOX_NONRECENT_KLUDGE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (i = 0; flags_list[i].chr != 0; i++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if ((flags & flags_list[i].flag) != 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append_c(str, flags_list[i].chr);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher flags ^= MBOX_NONRECENT_KLUDGE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void mbox_save_append_flag_headers(string_t *str, enum mail_flags flags)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* write the Status: header always. It always gets added soon anyway. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append(str, "Status: ");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher status_flags_append(str, flags, mbox_status_flags);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append_c(str, '\n');
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if ((flags & XSTATUS_FLAGS_MASK) != 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append(str, "X-Status: ");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher status_flags_append(str, flags, mbox_xstatus_flags);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append_c(str, '\n');
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghermbox_save_append_keyword_headers(struct mbox_save_context *ctx,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_keywords *keywords)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned char space[MBOX_HEADER_PADDING+1 +
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher sizeof("Content-Length: \n")-1 + MAX_INT_STRLEN];
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const ARRAY_TYPE(keywords) *keyword_names_list;
bfbf5cb0f00c60c0f000f56c282377b13b9a89abSumit Bose const char *const *keyword_names;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher unsigned int i, count, keyword_names_count;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher keyword_names_list = mail_index_get_keywords(ctx->mbox->ibox.index);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher keyword_names = array_get(keyword_names_list, &keyword_names_count);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append(ctx->headers, "X-Keywords:");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher count = keywords == NULL ? 0 : keywords->count;
eb2e21b764d03544d8161e9956d7f70b07b75f77Simo Sorce for (i = 0; i < count; i++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(keywords->idx[i] < keyword_names_count);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append_c(ctx->headers, ' ');
0ef783e186ef1c9f60e61a4e8e54c44cb366fdfePavel Březina str_append(ctx->headers, keyword_names[keywords->idx[i]]);
2cbdd12983eb85eddb90f64cfafb24eae5b448f4Jakub Hrozek }
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher memset(space, ' ', sizeof(space));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append_n(ctx->headers, space, sizeof(space));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->space_end_idx = str_len(ctx->headers);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append_c(ctx->headers, '\n');
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghermbox_save_init_file(struct mbox_save_context *ctx,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mbox_transaction_context *t, bool want_mail)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny{
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny struct mbox_mailbox *mbox = ctx->mbox;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_storage *storage = &mbox->storage->storage;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher bool empty = FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->mbox->mbox_readonly) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mail_storage_set_error(storage, MAIL_ERROR_PERM,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "Read-only mbox");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if ((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS) != 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher want_mail = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->append_offset == (uoff_t)-1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* first appended mail in this transaction */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox->mbox_lock_type != F_WRLCK) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox->mbox_lock_type == F_RDLCK) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* FIXME: we shouldn't fail here. it's just
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher a locking issue that should be possible to
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher fix.. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mail_storage_set_error(storage,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher MAIL_ERROR_NOTPOSSIBLE,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "Can't copy mails inside same mailbox");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox_lock(mbox, F_WRLCK, &t->mbox_lock_id) <= 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox->mbox_fd == -1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox_file_open(mbox) < 0)
126c9338cf12a3e4404c36bbe4ec14b18f23537cMaxim return -1;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* update mbox_sync_dirty state */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = mbox_sync_has_changed_full(mbox, TRUE, &empty);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!want_mail && ret == 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* we're not required to assign UIDs for the appended
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mails immediately. do it only if it doesn't require
2e6087c6cc903d5164b9a1d5e3d791fd046001d9Jakub Hrozek syncing. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_save_init_sync(t);
126c9338cf12a3e4404c36bbe4ec14b18f23537cMaxim }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!ctx->synced && (want_mail || empty)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* we'll need to assign UID for the mail immediately. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox_sync(mbox, 0) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_save_init_sync(t);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* the syncing above could have changed the append offset */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->append_offset == (uoff_t)-1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox_seek_to_end(ctx, &ctx->append_offset) < 0)
bfbf5cb0f00c60c0f000f56c282377b13b9a89abSumit Bose return -1;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->output = o_stream_create_fd_file(mbox->mbox_fd,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->append_offset,
2e6087c6cc903d5164b9a1d5e3d791fd046001d9Jakub Hrozek FALSE);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher o_stream_cork(ctx->output);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 0;
2e6087c6cc903d5164b9a1d5e3d791fd046001d9Jakub Hrozek}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void save_header_callback(struct message_header_line *hdr,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher bool *matched, struct mbox_save_context *ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (hdr != NULL) {
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek if (strncmp(hdr->name, "From ", 5) == 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* we can't allow From_-lines in headers. there's no
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher legitimate reason for allowing them in any case,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher so just drop them. */
29be7d76c949b82350c7603cfd362a1fcb47eb1bJan Zeleny *matched = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return;
72e60fd4eabcfbcdbfe01e8c38b94052bc6c2067Jakub Hrozek }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
823a5b3f4375f12b6edae4dd5169ee01771baebeJan Zeleny if (!*matched && ctx->mbox_md5_ctx != NULL)
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher mbox_md5_continue(ctx->mbox_md5_ctx, hdr);
748ba184db97b7534254f97018fa04e8aa458faeJan Cholasta }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void mbox_save_x_delivery_id(struct mbox_save_context *ctx)
5a70b84cb66fb8c7a3fce0e3f2e4b61e0b2ea9d4Simo Sorce{
5f90993426fa2bdc3b3d994c9e85e0805bb92bbcSimo Sorce unsigned char md5_result[MD5_RESULTLEN];
9959c512ac3ba36f7a0db7614f0357ce0bae748fJakub Hrozek buffer_t *buf;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher string_t *str;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher void *randbuf;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher buf = buffer_create_dynamic(pool_datastack_create(), 256);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher buffer_append(buf, &ioloop_time, sizeof(ioloop_time));
d844aab866ae237844360cea70e2dccdc90c783dStephen Gallagher buffer_append(buf, &ioloop_timeval.tv_usec,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher sizeof(ioloop_timeval.tv_usec));
ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15Stephen Gallagher
ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15Stephen Gallagher randbuf = buffer_append_space_unsafe(buf, MBOX_DELIVERY_ID_RAND_BYTES);
8c3a4809b3420657289b42f028a1c9019b112991Stephen Gallagher random_fill_weak(randbuf, MBOX_DELIVERY_ID_RAND_BYTES);
eb2e21b764d03544d8161e9956d7f70b07b75f77Simo Sorce
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher md5_get_digest(buf->data, buf->used, md5_result);
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek str = t_str_new(128);
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta str_append(str, "X-Delivery-ID: ");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher base64_encode(md5_result, sizeof(md5_result), str);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_append_c(str, '\n');
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->x_delivery_id_header = i_strdup(str_c(str));
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
1f1e6cbc59868f06dee3ab4b3df660fcb77ce1c8Jakub Hrozek
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystatic struct istream *
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghermbox_save_get_input_stream(struct mbox_save_context *ctx, struct istream *input)
4c1bf6607060cea867fccf667063c028dfd51e96Stephen Gallagher{
1a7d1977037864e52858058777af8ff8401547ddJan Cholasta struct istream *filter, *ret, *cache_input, *streams[3];
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* filter out unwanted headers and keep track of headers' MD5 sum */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher filter = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE |
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher HEADER_FILTER_NO_CR |
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher HEADER_FILTER_ADD_MISSING_EOH,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_save_drop_headers,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_save_drop_headers_count,
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher save_header_callback, ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if ((ctx->mbox->storage->storage.flags &
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) != 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* we're using MD5 sums to generate POP3 UIDLs.
65e8f538ad35ba7d86cd9e60a3d86aec34537027Stephen Gallagher clients don't like it much if there are duplicates,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher so make sure that there can't be any by appending
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher our own X-Delivery-ID header. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *hdr;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher T_BEGIN {
4af1d1869d659fec84c518c26844132fa1df8f64Jakub Hrozek mbox_save_x_delivery_id(ctx);
eb54e05c9658a7274e3238813c54dd0c6577d3ecPavel Březina } T_END;
e9eeb4302e0e426c6cc1a4e65b95a6f7066e80b9Pavel Březina hdr = ctx->x_delivery_id_header;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher streams[0] = i_stream_create_from_data(hdr, strlen(hdr));
65e8f538ad35ba7d86cd9e60a3d86aec34537027Stephen Gallagher streams[1] = filter;
ae8d047122c7ba8123f72b2eac68944868ac37d4Stephen Gallagher streams[2] = NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = i_stream_create_concat(streams);
213ce2a78b1abe3921d8dc13c949a28130d00aecJan Zeleny i_stream_unref(&filter);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher filter = ret;
1a853121ca2ba8ede6df429ee76942131ffb0f65Jan Zeleny }
ad07ed37b6b51ef134d4524edaf2259e19ac984fJan Zeleny
1a853121ca2ba8ede6df429ee76942131ffb0f65Jan Zeleny /* convert linefeeds to wanted format */
1a853121ca2ba8ede6df429ee76942131ffb0f65Jan Zeleny ret = (ctx->mbox->storage->storage.flags &
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ?
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_stream_create_crlf(filter) : i_stream_create_lf(filter);
81165faf5d951aca69f410713730c26ff048ec44Sumit Bose i_stream_unref(&filter);
3a62a99faf8e12965100d0b26fc9e07752bd3e2dStephen Gallagher
1a7d1977037864e52858058777af8ff8401547ddJan Cholasta if (ctx->mail != NULL) {
65e8f538ad35ba7d86cd9e60a3d86aec34537027Stephen Gallagher /* caching creates a tee stream */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher cache_input = index_mail_cache_parse_init(ctx->mail, ret);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_stream_unref(&ret);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = cache_input;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
347f7c4d1e8e83fc7ffcaf9524a67e8b3ad5d7c5Jan Cholastastruct mail_save_context *
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bosembox_save_alloc(struct mailbox_transaction_context *_t)
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose{
347f7c4d1e8e83fc7ffcaf9524a67e8b3ad5d7c5Jan Cholasta struct mbox_transaction_context *t =
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (struct mbox_transaction_context *)_t;
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->ictx.ibox;
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek struct mbox_save_context *ctx;
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek i_assert((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
374bf54785365273b20690bd3792c25a44738041Pavel Březina
374bf54785365273b20690bd3792c25a44738041Pavel Březina if (t->save_ctx != NULL)
374bf54785365273b20690bd3792c25a44738041Pavel Březina return &t->save_ctx->ctx;
374bf54785365273b20690bd3792c25a44738041Pavel Březina
374bf54785365273b20690bd3792c25a44738041Pavel Březina ctx = t->save_ctx = i_new(struct mbox_save_context, 1);
374bf54785365273b20690bd3792c25a44738041Pavel Březina ctx->ctx.transaction = &t->ictx.mailbox_ctx;
374bf54785365273b20690bd3792c25a44738041Pavel Březina ctx->mbox = mbox;
374bf54785365273b20690bd3792c25a44738041Pavel Březina ctx->trans = t->ictx.trans;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->append_offset = (uoff_t)-1;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->headers = str_new(default_pool, 512);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->mail_offset = (uoff_t)-1;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return &ctx->ctx;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher}
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagherint mbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher{
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher struct mbox_transaction_context *t =
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher (struct mbox_transaction_context *)_ctx->transaction;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher enum mail_flags save_flags;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher uint64_t offset;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher /* FIXME: we could write timezone_offset to From-line.. */
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (_ctx->received_date == (time_t)-1)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher _ctx->received_date = ioloop_time;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
c2352a73f52f600d95966ebe0b0819649ba923faStephen Gallagher ctx->failed = FALSE;
4c1bf6607060cea867fccf667063c028dfd51e96Stephen Gallagher ctx->seq = 0;
1f1e6cbc59868f06dee3ab4b3df660fcb77ce1c8Jakub Hrozek
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny if (mbox_save_init_file(ctx, t, _ctx->dest_mail != NULL) < 0) {
817b1bcafff27cc67630dd0cbd36df708c05fcccStephen Gallagher ctx->failed = TRUE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return -1;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher save_flags = (_ctx->flags & ~MAIL_RECENT) | MAIL_RECENT;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher str_truncate(ctx->headers, 0);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (ctx->synced) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (ctx->mbox->mbox_save_md5)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->mbox_md5_ctx = mbox_md5_init();
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (ctx->output->offset == 0) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher /* writing the first mail. Insert X-IMAPbase as well. */
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher str_printfa(ctx->headers, "X-IMAPbase: %u %010u\n",
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->uid_validity, ctx->next_uid);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher str_printfa(ctx->headers, "X-UID: %u\n", ctx->next_uid);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher mail_index_append(ctx->trans, ctx->next_uid, &ctx->seq);
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
29be7d76c949b82350c7603cfd362a1fcb47eb1bJan Zeleny save_flags & ~MAIL_RECENT);
dbea04f585a30d001b574317c068cd03a4fa332bJakub Hrozek if (_ctx->keywords != NULL) {
5a70b84cb66fb8c7a3fce0e3f2e4b61e0b2ea9d4Simo Sorce mail_index_update_keywords(ctx->trans, ctx->seq,
823a5b3f4375f12b6edae4dd5169ee01771baebeJan Zeleny MODIFY_REPLACE,
9959c512ac3ba36f7a0db7614f0357ce0bae748fJakub Hrozek _ctx->keywords);
20d0bc6d587f346238062df4da5edfde815e59b1Jan Zeleny }
20d0bc6d587f346238062df4da5edfde815e59b1Jan Zeleny
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher offset = ctx->output->offset == 0 ? 0 :
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->output->offset - 1;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher mail_index_update_ext(ctx->trans, ctx->seq,
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher ctx->mbox->mbox_ext_idx, &offset, NULL);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->next_uid++;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* parse and cache the mail headers as we read it */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (_ctx->dest_mail == NULL) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (ctx->mail == NULL) {
1a7d1977037864e52858058777af8ff8401547ddJan Cholasta ctx->mail = mail_alloc(_ctx->transaction,
748ba184db97b7534254f97018fa04e8aa458faeJan Cholasta 0, NULL);
748ba184db97b7534254f97018fa04e8aa458faeJan Cholasta }
748ba184db97b7534254f97018fa04e8aa458faeJan Cholasta _ctx->dest_mail = ctx->mail;
1a7d1977037864e52858058777af8ff8401547ddJan Cholasta }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mail_set_seq(_ctx->dest_mail, ctx->seq);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose }
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher mbox_save_append_flag_headers(ctx->headers, save_flags);
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher mbox_save_append_keyword_headers(ctx, _ctx->keywords);
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher str_append_c(ctx->headers, '\n');
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher i_assert(ctx->mbox->mbox_lock_type == F_WRLCK);
a72e9289fe001c85a17acd667ca31d692fd99605Stephen Gallagher
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher ctx->mail_offset = ctx->output->offset;
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher ctx->eoh_offset = (uoff_t)-1;
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose ctx->last_char = '\n';
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose if (write_from_line(ctx, _ctx->received_date, _ctx->from_envelope) < 0)
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose ctx->failed = TRUE;
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose else
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose ctx->input = mbox_save_get_input_stream(ctx, input);
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose return ctx->failed ? -1 : 0;
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose}
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bosestatic int mbox_save_body_input(struct mbox_save_context *ctx)
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose{
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher const unsigned char *data;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher size_t size;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher data = i_stream_get_data(ctx->input, &size);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (size > 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (o_stream_send(ctx->output, data, size) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return write_error(ctx);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->last_char = data[size-1];
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_stream_skip(ctx->input, size);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return 0;
e369fc08906383e6d5c39832f31bb6600a33f887Simo Sorce}
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int mbox_save_body(struct mbox_save_context *ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ssize_t ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15Stephen Gallagher while ((ret = i_stream_read(ctx->input)) != -1) {
8c3a4809b3420657289b42f028a1c9019b112991Stephen Gallagher if (ctx->mail != NULL) {
eb2e21b764d03544d8161e9956d7f70b07b75f77Simo Sorce /* i_stream_read() may have returned 0 at EOF
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher because of this parser */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_mail_cache_parse_continue(ctx->mail);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (ret == 0)
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher return 0;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox_save_body_input(ctx) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->last_char != '\n') {
d844aab866ae237844360cea70e2dccdc90c783dStephen Gallagher /* if mail doesn't end with LF, we'll do that.
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher otherwise some mbox parsers don't like the result.
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher this makes it impossible to save a mail that doesn't
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher end with LF though. */
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher const char *linefeed =
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher (ctx->mbox->storage->storage.flags &
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ?
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina "\r\n" : "\n";
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina if (o_stream_send_str(ctx->output, linefeed) < 0)
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina return write_error(ctx);
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina }
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina return 0;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina}
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
41ef946f3f74a46b9e26118116e4811e259b30efPavel Březinaint mbox_save_continue(struct mail_save_context *_ctx)
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina{
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina const unsigned char *data;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina size_t i, size;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina ssize_t ret;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (ctx->failed)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek return -1;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (ctx->eoh_offset != (uoff_t)-1) {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek /* writing body */
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek return mbox_save_body(ctx);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek }
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek while ((ret = i_stream_read(ctx->input)) > 0) {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek if (ctx->mail != NULL)
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek index_mail_cache_parse_continue(ctx->mail);
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta data = i_stream_get_data(ctx->input, &size);
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta for (i = 0; i < size; i++) {
b42b5d5aaf4da165582e73ad985fdff6e34e61e4Jakub Hrozek if (data[i] == '\n' &&
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta ((i == 0 && ctx->last_char == '\n') ||
bd03e67c9d2fc4ad0275e7a573385ee5b7b9307aJan Cholasta (i > 0 && data[i-1] == '\n'))) {
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta /* end of headers. we don't need to worry about
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta CRs because they're dropped */
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta break;
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta }
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (i != size) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* found end of headers. write the rest of them
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher (not including the finishing empty line) */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (o_stream_send(ctx->output, data, i) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return write_error(ctx);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->last_char = '\n';
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher i_stream_skip(ctx->input, i + 1);
d248b68f90e60a1dd1cca1f694cc51bc3007c8b1Jan Engelhardt break;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (o_stream_send(ctx->output, data, size) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return write_error(ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->last_char = data[size-1];
4b0309363dbfb9a1409e082b3a84f17b53a751c1Stephen Gallagher i_stream_skip(ctx->input, size);
4b0309363dbfb9a1409e082b3a84f17b53a751c1Stephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret == 0)
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek return 0;
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek
52e0894fd65bff4715c88330eb62b28e1635228fStephen Gallagher if (ctx->last_char != '\n') {
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek if (o_stream_send(ctx->output, "\n", 1) < 0)
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek return write_error(ctx);
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek }
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozek if (ctx->mbox_md5_ctx) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned char hdr_md5_sum[16];
8a07521b413a3b5879f824e1872c5770c92ee5c0Stephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->x_delivery_id_header != NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct message_header_line hdr;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher memset(&hdr, 0, sizeof(hdr));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.name = ctx->x_delivery_id_header;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.name_len = sizeof("X-Delivery-ID")-1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.middle = (const unsigned char *)hdr.name +
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.name_len;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.middle_len = 2;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.value = hdr.full_value =
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.middle + hdr.middle_len;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr.value_len = strlen((const char *)hdr.value);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_md5_continue(ctx->mbox_md5_ctx, &hdr);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_md5_finish(ctx->mbox_md5_ctx, hdr_md5_sum);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher mail_index_update_ext(ctx->trans, ctx->seq,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->mbox->ibox.md5hdr_ext_idx,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher hdr_md5_sum, NULL);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* append our own headers and ending empty line */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->extra_hdr_offset = ctx->output->offset;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (o_stream_send(ctx->output, str_data(ctx->headers),
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher str_len(ctx->headers)) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return write_error(ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->eoh_offset = ctx->output->offset;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* write body */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox_save_body_input(ctx) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return -1;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return ctx->input->eof ? 0 : mbox_save_body(ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherint mbox_save_finish(struct mail_save_context *_ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (ctx->output != NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* make sure everything is written */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (o_stream_flush(ctx->output) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher write_error(ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->finished = TRUE;
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (!ctx->failed) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher T_BEGIN {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mbox_write_content_length(ctx) < 0 ||
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_append_lf(ctx) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->failed = TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher } T_END;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->mail != NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher index_mail_cache_parse_deinit(ctx->mail, ctx->ctx.received_date,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher !ctx->failed);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->input != NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_stream_destroy(&ctx->input);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->failed && ctx->mail_offset != (uoff_t)-1) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* saving this mail failed - truncate back to beginning of it */
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny (void)o_stream_flush(ctx->output);
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny if (ftruncate(ctx->mbox->mbox_fd, (off_t)ctx->mail_offset) < 0)
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny mbox_set_syscall_error(ctx->mbox, "ftruncate()");
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny o_stream_seek(ctx->output, ctx->mail_offset);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher ctx->mail_offset = (uoff_t)-1;
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny }
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny
f1828234a850dd28465425248a83a993f262918fPavel Březina index_save_context_free(_ctx);
f1828234a850dd28465425248a83a993f262918fPavel Březina return ctx->failed ? -1 : 0;
f1828234a850dd28465425248a83a993f262918fPavel Březina}
f1828234a850dd28465425248a83a993f262918fPavel Březina
f1828234a850dd28465425248a83a993f262918fPavel Březinavoid mbox_save_cancel(struct mail_save_context *_ctx)
f1828234a850dd28465425248a83a993f262918fPavel Březina{
f1828234a850dd28465425248a83a993f262918fPavel Březina struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina ctx->failed = TRUE;
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina (void)mbox_save_finish(_ctx);
70e59ed31c5a9c9ed02d9065ddf92be87c887efbJakub Hrozek}
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina
70e59ed31c5a9c9ed02d9065ddf92be87c887efbJakub Hrozekstatic void mbox_transaction_save_deinit(struct mbox_save_context *ctx)
70e59ed31c5a9c9ed02d9065ddf92be87c887efbJakub Hrozek{
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina if (ctx->output != NULL)
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina o_stream_destroy(&ctx->output);
558998ce664055a75595371118f818084d8f2b23Jan Cholasta if (ctx->mail != NULL)
558998ce664055a75595371118f818084d8f2b23Jan Cholasta mail_free(&ctx->mail);
558998ce664055a75595371118f818084d8f2b23Jan Cholasta str_free(&ctx->headers);
748ba184db97b7534254f97018fa04e8aa458faeJan Cholasta i_free(ctx);
558998ce664055a75595371118f818084d8f2b23Jan Cholasta}
558998ce664055a75595371118f818084d8f2b23Jan Cholasta
558998ce664055a75595371118f818084d8f2b23Jan Cholastaint mbox_transaction_save_commit(struct mbox_save_context *ctx)
558998ce664055a75595371118f818084d8f2b23Jan Cholasta{
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta struct mbox_transaction_context *t =
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta (struct mbox_transaction_context *)ctx->ctx.transaction;
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta struct mbox_mailbox *mbox = ctx->mbox;
748ba184db97b7534254f97018fa04e8aa458faeJan Cholasta struct stat st;
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta int ret = 0;
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta i_assert(ctx->finished);
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta
558998ce664055a75595371118f818084d8f2b23Jan Cholasta if (fstat(mbox->mbox_fd, &st) < 0) {
558998ce664055a75595371118f818084d8f2b23Jan Cholasta mbox_set_syscall_error(mbox, "fstat()");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose if (ctx->synced) {
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose *t->ictx.saved_uid_validity = ctx->uid_validity;
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose *t->ictx.first_saved_uid = ctx->first_saved_uid;
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose *t->ictx.last_saved_uid = ctx->next_uid - 1;
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose
21f28bdbab10881b9fb0b890dfa15af429326606Sumit Bose mail_index_update_header(ctx->trans,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher offsetof(struct mail_index_header, next_uid),
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher &ctx->next_uid, sizeof(ctx->next_uid), FALSE);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret == 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox->mbox_hdr.sync_mtime = st.st_mtime;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox->mbox_hdr.sync_size = st.st_size;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mail_index_update_header_ext(ctx->trans,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox->mbox_ext_idx,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher 0, &mbox->mbox_hdr,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher sizeof(mbox->mbox_hdr));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (ret == 0 && ctx->orig_atime != st.st_atime) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* try to set atime back to its original value */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct utimbuf buf;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher buf.modtime = st.st_mtime;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher buf.actime = ctx->orig_atime;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (utime(mbox->path, &buf) < 0)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher mbox_set_syscall_error(mbox, "utime()");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!ctx->synced && mbox->mbox_fd != -1 &&
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher !mbox->mbox_writeonly && !mbox->ibox.fsync_disable) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (fdatasync(mbox->mbox_fd) < 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_set_syscall_error(mbox, "fdatasync()");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_transaction_save_deinit(ctx);
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher return ret;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid mbox_transaction_save_rollback(struct mbox_save_context *ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mbox_mailbox *mbox = ctx->mbox;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
5843ad321944a028f6dee7e1fd4f9381c4953d07Sumit Bose if (!ctx->finished)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_save_cancel(&ctx->ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->append_offset != (uoff_t)-1 && mbox->mbox_fd != -1) {
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher i_assert(mbox->mbox_lock_type == F_WRLCK);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* failed, truncate file back to original size.
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher output stream needs to be flushed before truncating
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher so unref() won't write anything. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->output != NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher o_stream_flush(ctx->output);
c8b8901b05da9e31dba320f305ec20301e928cfbSumit Bose
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ftruncate(mbox->mbox_fd, (off_t)ctx->append_offset) < 0)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher mbox_set_syscall_error(mbox, "ftruncate()");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mbox_transaction_save_deinit(ctx);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher