mbox-save.c revision 2d23beeff60058ff85f0a15e7c27588ab62a1593
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (C) 2002 Timo Sirainen */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "lib.h"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include "ioloop.h"
1519df1d514ed508c9708e3d2a4daf89e9c937e0Stephan Bosch#include "array.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "hostpid.h"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include "istream.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "ostream.h"
1519df1d514ed508c9708e3d2a4daf89e9c937e0Stephan Bosch#include "str.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "write-full.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "istream-header-filter.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "ostream-crlf.h"
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen#include "message-parser.h"
b215a8a123623782554a83f3025ef4e771bd8f01Timo Sirainen#include "index-mail.h"
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen#include "mbox-storage.h"
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen#include "mbox-file.h"
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen#include "mbox-from.h"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include "mbox-lock.h"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include "mbox-md5.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "mbox-sync-private.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include <stddef.h>
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include <stdlib.h>
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include <unistd.h>
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include <fcntl.h>
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include <sys/stat.h>
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include <netdb.h>
da7f1a07f583df8905684a7b78469960afd7c78dPhil Carmody
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainenstruct mbox_save_context {
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen struct mail_save_context ctx;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen struct mbox_mailbox *mbox;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct mail_index_transaction *trans;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct mail *mail;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen uoff_t append_offset, mail_offset;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen string_t *headers;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen size_t space_end_idx;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen uint32_t seq, next_uid;
d7952621661d9a9102393d27d061dad3d22083fdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct istream *input;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct ostream *output, *body_output;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen uoff_t extra_hdr_offset, eoh_offset, eoh_input_offset;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen char last_char;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
d09e4ee15faa7b6f1804b78f15d8778030401b4cTimo Sirainen struct mbox_md5_context *mbox_md5_ctx;
98348e3f27a2f59c2f02cd67974004b4cd595c8cTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen unsigned int synced:1;
93a7d1ee4b518b5c85f9721dc6539e4dab6aae00Timo Sirainen unsigned int failed:1;
f7f25f9e1a38678d0e97d2e609beac16285fac6bTimo Sirainen};
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic char my_hostdomain[256] = "";
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic void write_error(struct mbox_save_context *ctx, int error)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen if (ENOSPACE(error)) {
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen mail_storage_set_error(STORAGE(ctx->mbox->storage),
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen "Not enough disk space");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen } else {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen errno = error;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mbox_set_syscall_error(ctx->mbox, "write()");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen ctx->failed = TRUE;
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen}
4f7987384f306ea93b0258623a4cdd69601f2d0eTimo Sirainen
c46d97e6a26a5bacb40a1d817407b94ae3860bddTimo Sirainenstatic int mbox_seek_to_end(struct mbox_save_context *ctx, uoff_t *offset)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
61618d4c58080570f689614fec204ae14e90cef2Timo Sirainen struct stat st;
50e20db49f29917fe9adcf1b56b11badf28bd0e4Timo Sirainen char ch;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen int fd;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (ctx->mbox->mbox_writeonly) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen *offset = 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 0;
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
d8aa10df6d1dae56d3aa485708a34d74e9e31e79Stephan Bosch fd = ctx->mbox->mbox_fd;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (fstat(fd, &st) < 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return mbox_set_syscall_error(ctx->mbox, "fstat()");
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
009217abb57a24a4076092e8e4e165545747839eStephan Bosch *offset = (uoff_t)st.st_size;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (st.st_size == 0)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (lseek(fd, st.st_size-1, SEEK_SET) < 0)
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen return mbox_set_syscall_error(ctx->mbox, "lseek()");
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
c46d97e6a26a5bacb40a1d817407b94ae3860bddTimo Sirainen if (read(fd, &ch, 1) != 1)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return mbox_set_syscall_error(ctx->mbox, "read()");
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (ch != '\n') {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (write_full(fd, "\n", 1) < 0) {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen write_error(ctx, errno);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return -1;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen }
01c7ba8366ceafdbc0752d93079bbe785bebc9e1Timo Sirainen *offset += 1;
556d69b89bf0afd1ba53acce4775097e494b6b8bMartti Rannanjärvi }
556d69b89bf0afd1ba53acce4775097e494b6b8bMartti Rannanjärvi
556d69b89bf0afd1ba53acce4775097e494b6b8bMartti Rannanjärvi return 0;
556d69b89bf0afd1ba53acce4775097e494b6b8bMartti Rannanjärvi}
13eb655174f3857b38f4e3ac8658c82184715fd4Timo Sirainen
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschstatic int mbox_append_lf(struct mbox_save_context *ctx)
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (o_stream_send(ctx->output, "\n", 1) < 0) {
d8aa10df6d1dae56d3aa485708a34d74e9e31e79Stephan Bosch write_error(ctx, ctx->output->stream_errno);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return 0;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainenstatic int write_from_line(struct mbox_save_context *ctx, time_t received_date,
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen const char *from_envelope)
1519df1d514ed508c9708e3d2a4daf89e9c937e0Stephan Bosch{
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen const char *line, *name;
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen int ret;
e9e5e84ffb2ce2e606a24ce6d930580367562ff0Timo Sirainen
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen if (*my_hostdomain == '\0') {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct hostent *hent;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen hent = gethostbyname(my_hostname);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen name = hent != NULL ? hent->h_name : NULL;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (name == NULL) {
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen /* failed, use just the hostname */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen name = my_hostname;
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen }
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen strocpy(my_hostdomain, name, sizeof(my_hostdomain));
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen t_push();
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen if (from_envelope == NULL) {
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen from_envelope =
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen t_strconcat(INDEX_STORAGE(ctx->mbox->storage)->user,
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen "@", my_hostdomain, NULL);
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen }
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen /* save in local timezone, no matter what it was given with */
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen line = mbox_from_create(from_envelope, received_date);
1c3e6a4a7557f23f02abacbef9847dd4346f8553Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen if ((ret = o_stream_send_str(ctx->output, line)) < 0)
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen write_error(ctx, ctx->output->stream_errno);
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen t_pop();
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen return ret;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen}
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainenstatic int mbox_write_content_length(struct mbox_save_context *ctx)
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen{
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen uoff_t end_offset;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen const char *str;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen size_t len;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen int ret = 0;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen if (ctx->mbox->mbox_writeonly) {
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen /* we can't seek, don't set Content-Length */
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen return 0;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen }
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen end_offset = ctx->output->offset;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen /* write Content-Length headers */
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen t_push();
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen str = t_strdup_printf("\nContent-Length: %s",
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen dec2str(end_offset - ctx->eoh_offset));
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen len = strlen(str);
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen if (o_stream_seek(ctx->output, ctx->extra_hdr_offset +
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen ctx->space_end_idx - len) < 0) {
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen mbox_set_syscall_error(ctx->mbox, "o_stream_seek()");
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen ret = -1;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen } else if (o_stream_send(ctx->output, str, len) < 0) {
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen write_error(ctx, ctx->output->stream_errno);
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen ret = -1;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen } else {
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen if (o_stream_seek(ctx->output, end_offset) < 0) {
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen mbox_set_syscall_error(ctx->mbox, "o_stream_seek()");
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen ret = -1;
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen }
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen }
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen t_pop();
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen return ret;
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen}
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainenstatic void mbox_save_init_sync(struct mbox_transaction_context *t)
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen{
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->ictx.ibox;
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen struct mbox_save_context *ctx = t->save_ctx;
704a96fa677763eef7ae62466e14e83a2f535427Timo Sirainen const struct mail_index_header *hdr;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen struct mail_index_view *view;
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen
a8284e999d091cd29210fa75ecdc8076376a7345Timo Sirainen /* open a new view to get the header. this is required if we just
synced the mailbox so we can get updated next_uid. */
view = mail_index_view_open(mbox->ibox.index);
hdr = mail_index_get_header(view);
ctx->next_uid = hdr->next_uid;
ctx->synced = TRUE;
t->mbox_modified = TRUE;
mail_index_view_close(view);
}
static void status_flags_append(string_t *str, enum mail_flags flags,
const struct mbox_flag_type *flags_list)
{
int i;
flags ^= MBOX_NONRECENT_KLUDGE;
for (i = 0; flags_list[i].chr != 0; i++) {
if ((flags & flags_list[i].flag) != 0)
str_append_c(str, flags_list[i].chr);
}
flags ^= MBOX_NONRECENT_KLUDGE;
}
static void mbox_save_append_flag_headers(string_t *str, enum mail_flags flags)
{
if ((flags & STATUS_FLAGS_MASK) != 0) {
str_append(str, "Status: ");
status_flags_append(str, flags, mbox_status_flags);
str_append_c(str, '\n');
}
if ((flags & XSTATUS_FLAGS_MASK) != 0) {
str_append(str, "X-Status: ");
status_flags_append(str, flags, mbox_xstatus_flags);
str_append_c(str, '\n');
}
}
static void
mbox_save_append_keyword_headers(struct mbox_save_context *ctx,
struct mail_keywords *keywords)
{
unsigned char space[MBOX_HEADER_PADDING+1 +
sizeof("Content-Length: \n")-1 + MAX_INT_STRLEN];
const array_t *keyword_names_list;
ARRAY_SET_TYPE(keyword_names_list, const char *);
const char *const *keyword_names;
unsigned int i, count, keyword_names_count;
keyword_names_list = mail_index_get_keywords(ctx->mbox->ibox.index);
keyword_names = array_get(keyword_names_list, &keyword_names_count);
str_append(ctx->headers, "X-Keywords:");
count = keywords == NULL ? 0 : keywords->count;
for (i = 0; i < count; i++) {
i_assert(keywords->idx[i] < keyword_names_count);
str_append_c(ctx->headers, ' ');
str_append(ctx->headers, keyword_names[keywords->idx[i]]);
}
memset(space, ' ', sizeof(space));
str_append_n(ctx->headers, space, sizeof(space));
ctx->space_end_idx = str_len(ctx->headers);
str_append_c(ctx->headers, '\n');
}
static int
mbox_save_init_file(struct mbox_save_context *ctx,
struct mbox_transaction_context *t, int want_mail)
{
struct mbox_mailbox *mbox = ctx->mbox;
int ret;
if (ctx->mbox->mbox_readonly || ctx->mbox->ibox.readonly) {
mail_storage_set_error(STORAGE(ctx->mbox->storage),
"Read-only mbox");
return -1;
}
if (ctx->append_offset == (uoff_t)-1) {
/* first appended mail in this transaction */
if (mbox->mbox_lock_type != F_WRLCK) {
if (mbox_lock(mbox, F_WRLCK, &t->mbox_lock_id) <= 0)
return -1;
}
if (mbox->mbox_fd == -1) {
if (mbox_file_open(mbox) < 0)
return -1;
}
if (!want_mail) {
/* assign UIDs only if mbox doesn't require syncing */
ret = mbox_sync_has_changed(mbox, TRUE);
if (ret < 0)
return -1;
if (ret == 0)
mbox_save_init_sync(t);
}
if (mbox_seek_to_end(ctx, &ctx->append_offset) < 0)
return -1;
ctx->output = o_stream_create_file(mbox->mbox_fd, default_pool,
0, FALSE);
}
if (!ctx->synced && want_mail) {
/* we'll need to assign UID for the mail immediately. */
if (mbox_sync(mbox, 0) < 0)
return -1;
mbox_save_init_sync(t);
}
return 0;
}
static void save_header_callback(struct message_header_line *hdr,
int *matched, void *context)
{
struct mbox_save_context *ctx = context;
if (!*matched && ctx->mbox_md5_ctx && hdr != NULL)
mbox_md5_continue(ctx->mbox_md5_ctx, hdr);
if ((hdr == NULL && ctx->eoh_input_offset == (uoff_t)-1) ||
(hdr != NULL && hdr->eoh))
ctx->eoh_input_offset = ctx->input->v_offset;
if (ctx->mail != NULL) {
index_mail_parse_header(NULL, hdr,
(struct index_mail *)ctx->mail);
}
}
struct mail_save_context *
mbox_save_init(struct mailbox_transaction_context *_t,
enum mail_flags flags, struct mail_keywords *keywords,
time_t received_date, int timezone_offset __attr_unused__,
const char *from_envelope, struct istream *input, int want_mail)
{
struct mbox_transaction_context *t =
(struct mbox_transaction_context *)_t;
struct mbox_mailbox *mbox = (struct mbox_mailbox *)t->ictx.ibox;
struct mbox_save_context *ctx = t->save_ctx;
enum mail_flags save_flags;
uint64_t offset;
i_assert((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
/* FIXME: we could write timezone_offset to From-line.. */
if (received_date == (time_t)-1)
received_date = ioloop_time;
if (ctx == NULL) {
ctx = t->save_ctx = i_new(struct mbox_save_context, 1);
ctx->ctx.transaction = &t->ictx.mailbox_ctx;
ctx->mbox = mbox;
ctx->trans = t->ictx.trans;
ctx->append_offset = (uoff_t)-1;
ctx->headers = str_new(default_pool, 512);
ctx->mail_offset = (uoff_t)-1;
}
ctx->failed = FALSE;
ctx->seq = 0;
if (mbox_save_init_file(ctx, t, want_mail) < 0) {
ctx->failed = TRUE;
return &ctx->ctx;
}
save_flags = (flags & ~MAIL_RECENT) | MAIL_RECENT;
str_truncate(ctx->headers, 0);
if (ctx->synced) {
str_printfa(ctx->headers, "X-UID: %u\n", ctx->next_uid);
if (!mbox->ibox.keep_recent)
save_flags &= ~MAIL_RECENT;
mail_index_append(ctx->trans, ctx->next_uid, &ctx->seq);
mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
save_flags);
if (keywords != NULL) {
mail_index_update_keywords(ctx->trans, ctx->seq,
MODIFY_REPLACE, keywords);
}
offset = ctx->output->offset == 0 ? 0 :
ctx->output->offset - 1;
mail_index_update_ext(ctx->trans, ctx->seq,
mbox->mbox_ext_idx, &offset, NULL);
ctx->next_uid++;
/* parse and cache the mail headers as we read it */
if (ctx->mail == NULL)
ctx->mail = index_mail_alloc(_t, 0, NULL);
mail_set_seq(ctx->mail, ctx->seq);
index_mail_parse_header_init((struct index_mail *)ctx->mail,
NULL);
}
mbox_save_append_flag_headers(ctx->headers, save_flags);
mbox_save_append_keyword_headers(ctx, keywords);
str_append_c(ctx->headers, '\n');
i_assert(mbox->mbox_lock_type == F_WRLCK);
ctx->mail_offset = ctx->output->offset;
ctx->eoh_input_offset = (uoff_t)-1;
ctx->eoh_offset = (uoff_t)-1;
ctx->last_char = '\n';
if (write_from_line(ctx, received_date, from_envelope) < 0)
ctx->failed = TRUE;
else {
ctx->input =
i_stream_create_header_filter(input,
HEADER_FILTER_EXCLUDE |
HEADER_FILTER_NO_CR,
mbox_hide_headers,
mbox_hide_headers_count,
save_header_callback,
ctx);
ctx->body_output =
(STORAGE(mbox->storage)->flags &
MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ?
o_stream_create_crlf(default_pool, ctx->output) :
o_stream_create_lf(default_pool, ctx->output);
if (ctx->mbox->mbox_save_md5 && ctx->synced)
ctx->mbox_md5_ctx = mbox_md5_init();
}
return &ctx->ctx;
}
int mbox_save_continue(struct mail_save_context *_ctx)
{
struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
const unsigned char *data;
size_t size;
ssize_t ret;
if (ctx->failed)
return -1;
if (ctx->eoh_offset != (uoff_t)-1) {
/* writing body */
if (o_stream_send_istream(ctx->body_output, ctx->input) < 0) {
write_error(ctx, ctx->body_output->stream_errno);
return -1;
}
return 0;
}
while ((ret = i_stream_read(ctx->input)) != -1) {
if (ret == 0)
return 0;
data = i_stream_get_data(ctx->input, &size);
if (ctx->eoh_input_offset != (uoff_t)-1 &&
ctx->input->v_offset + size >= ctx->eoh_input_offset) {
/* found end of headers. write the rest of them. */
size = ctx->eoh_input_offset - ctx->input->v_offset;
if (o_stream_send(ctx->output, data, size) < 0) {
write_error(ctx, ctx->output->stream_errno);
return -1;
}
if (size > 0)
ctx->last_char = data[size-1];
i_stream_skip(ctx->input, size + 1);
break;
}
if (o_stream_send(ctx->output, data, size) < 0) {
write_error(ctx, ctx->output->stream_errno);
return -1;
}
ctx->last_char = data[size-1];
i_stream_skip(ctx->input, size);
}
if (ctx->last_char != '\n') {
if (o_stream_send(ctx->output, "\n", 1) < 0) {
write_error(ctx, ctx->output->stream_errno);
return -1;
}
}
if (ctx->mbox_md5_ctx) {
unsigned char hdr_md5_sum[16];
mbox_md5_finish(ctx->mbox_md5_ctx, hdr_md5_sum);
mail_index_update_ext(ctx->trans, ctx->seq,
ctx->mbox->ibox.md5hdr_ext_idx,
hdr_md5_sum, NULL);
}
/* append our own headers and ending empty line */
ctx->extra_hdr_offset = ctx->output->offset;
if (o_stream_send(ctx->output, str_data(ctx->headers),
str_len(ctx->headers)) < 0) {
write_error(ctx, ctx->output->stream_errno);
return -1;
}
ctx->eoh_offset = ctx->output->offset;
/* write body */
(void)i_stream_get_data(ctx->input, &size);
return ctx->input->eof && size == 0 ? 0 : mbox_save_continue(_ctx);
}
int mbox_save_finish(struct mail_save_context *_ctx, struct mail *dest_mail)
{
struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
if (!ctx->failed) {
if (mbox_write_content_length(ctx) < 0 ||
mbox_append_lf(ctx) < 0)
ctx->failed = TRUE;
}
if (ctx->input != NULL) {
i_stream_unref(ctx->input);
ctx->input = NULL;
}
if (ctx->body_output != NULL) {
o_stream_unref(ctx->body_output);
ctx->body_output = NULL;
}
if (ctx->failed && ctx->mail_offset != (uoff_t)-1) {
/* saving this mail failed - truncate back to beginning of it */
if (ftruncate(ctx->mbox->mbox_fd, (off_t)ctx->mail_offset) < 0)
mbox_set_syscall_error(ctx->mbox, "ftruncate()");
ctx->mail_offset = (uoff_t)-1;
}
if (ctx->failed)
return -1;
if (dest_mail != NULL) {
i_assert(ctx->seq != 0);
if (mail_set_seq(dest_mail, ctx->seq) < 0)
return -1;
}
return 0;
}
void mbox_save_cancel(struct mail_save_context *_ctx)
{
struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
ctx->failed = TRUE;
(void)mbox_save_finish(_ctx, NULL);
}
static void mbox_transaction_save_deinit(struct mbox_save_context *ctx)
{
i_assert(ctx->body_output == NULL);
if (ctx->output != NULL)
o_stream_unref(ctx->output);
if (ctx->mail != NULL)
index_mail_free(ctx->mail);
str_free(ctx->headers);
i_free(ctx);
}
int mbox_transaction_save_commit(struct mbox_save_context *ctx)
{
int ret = 0;
if (ctx->synced) {
mail_index_update_header(ctx->trans,
offsetof(struct mail_index_header, next_uid),
&ctx->next_uid, sizeof(ctx->next_uid), FALSE);
}
if (!ctx->synced && ctx->mbox->mbox_fd != -1 &&
!ctx->mbox->mbox_writeonly) {
if (fdatasync(ctx->mbox->mbox_fd) < 0) {
mbox_set_syscall_error(ctx->mbox, "fdatasync()");
ret = -1;
}
}
mbox_transaction_save_deinit(ctx);
return ret;
}
void mbox_transaction_save_rollback(struct mbox_save_context *ctx)
{
struct mbox_mailbox *mbox = ctx->mbox;
if (ctx->append_offset != (uoff_t)-1 && mbox->mbox_fd != -1) {
i_assert(mbox->mbox_lock_type == F_WRLCK);
/* failed, truncate file back to original size.
output stream needs to be flushed before truncating
so unref() won't write anything. */
o_stream_flush(ctx->output);
if (ftruncate(mbox->mbox_fd, (off_t)ctx->append_offset) < 0)
mbox_set_syscall_error(mbox, "ftruncate()");
}
mbox_transaction_save_deinit(ctx);
}