mbox-rewrite.c revision 62505210a7e6d1b2e35fac335a6c875a7c98ccfb
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "lib.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "iobuffer.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "temp-string.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "write-full.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "mbox-index.h"
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen#include "mbox-lock.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "mail-index-util.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include "mail-custom-flags.h"
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <stdio.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <stdlib.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <unistd.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen#include <fcntl.h>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainentypedef struct {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen IOBuffer *outbuf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen int failed;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen uoff_t content_length;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int seq;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int msg_flags;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char **custom_flags;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int uid_validity;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int uid_last;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int ximapbase_found:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int xkeywords_found:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int status_found:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int xstatus_found:1;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen unsigned int content_length_found:1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen} MboxRewriteContext;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen/* Remove dirty flag from all messages */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void reset_dirty_flags(MailIndex *index)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MailIndexRecord *rec;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rec = index->lookup(index, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen while (rec != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rec->index_flags &= ~INDEX_MAIL_FLAG_DIRTY;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rec = index->next(index, rec);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index->header->flags &= ~(MAIL_INDEX_FLAG_DIRTY_MESSAGES |
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int mbox_write(MailIndex *index, IOBuffer *inbuf, IOBuffer *outbuf,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uoff_t end_offset)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(inbuf->offset <= end_offset);
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send_iobuffer(outbuf, inbuf,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen end_offset - inbuf->offset) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (inbuf->offset < end_offset) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* fsck should have noticed it.. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_set_error(index, "Error rewriting mbox file %s: "
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "Unexpected end of file", index->mbox_path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int mbox_write_ximapbase(MboxRewriteContext *ctx)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *str;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen int i;
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = t_strdup_printf("X-IMAPbase: %u %u",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->uid_validity, ctx->uid_last);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->custom_flags[i] != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, " ", 1) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, ctx->custom_flags[i],
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen strlen(ctx->custom_flags[i])) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, "\n", 1) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int mbox_write_xkeywords(MboxRewriteContext *ctx, const char *x_keywords)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int field;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen int i;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((ctx->msg_flags & MAIL_CUSTOM_FLAGS_MASK) == 0 &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen x_keywords == NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, "X-Keywords:", 11) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen field = 1 << MAIL_CUSTOM_FLAG_1_BIT;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++, field <<= 1) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((ctx->msg_flags & field) && ctx->custom_flags[i] != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, " ", 1) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (io_buffer_send(ctx->outbuf, ctx->custom_flags[i],
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen strlen(ctx->custom_flags[i])) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (x_keywords != NULL) {
049da065aa64c1a5ed46eed6cde7382b011612a9Timo Sirainen /* X-Keywords that aren't custom flags */
049da065aa64c1a5ed46eed6cde7382b011612a9Timo Sirainen if (io_buffer_send(ctx->outbuf, " ", 1) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (io_buffer_send(ctx->outbuf, x_keywords,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen strlen(x_keywords)) < 0)
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, "\n", 1) < 0)
27a44fcfd8d19bffe0f267f20a2b5d3fe7600fddTimo Sirainen return FALSE;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int mbox_write_status(MboxRewriteContext *ctx, const char *status)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *str;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = (ctx->msg_flags & MAIL_SEEN) ? "Status: RO" : "Status: O";
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (status != NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = t_strconcat(str, status, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, "\n", 1) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int mbox_write_xstatus(MboxRewriteContext *ctx, const char *x_status)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *str;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* X-Status field */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((ctx->msg_flags & (MAIL_SYSTEM_FLAGS_MASK^MAIL_SEEN)) == 0 &&
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen x_status == NULL)
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen return TRUE;
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = t_strconcat("X-Status: ",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (ctx->msg_flags & MAIL_ANSWERED) ? "A" : "",
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen (ctx->msg_flags & MAIL_DRAFT) ? "D" : "",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (ctx->msg_flags & MAIL_FLAGGED) ? "F" : "",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (ctx->msg_flags & MAIL_DELETED) ? "T" : "",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen x_status, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, "\n", 1) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int mbox_write_content_length(MboxRewriteContext *ctx)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen char str[MAX_LARGEST_T_STRLEN+30];
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_snprintf(str, sizeof(str), "Content-Length: %"PRIuUOFF_T"\n",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->content_length);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic const char *strip_chars(const char *value, size_t value_len,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *list)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* leave only unknown flags, very likely none */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen char *ret, *p;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int i;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ret = p = t_buffer_get(value_len+1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0; i < value_len; i++) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (strchr(list, value[i]) == NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *p++ = value[i];
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ret == p)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return NULL;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen *p = '\0';
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_buffer_alloc((size_t) (p-ret)+1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return ret;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void update_stripped_custom_flags(const char *value, size_t len,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen int index, void *context)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen TempString *str = context;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (index < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* not found, keep it */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (str->len != 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_string_append_c(str, ' ');
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_string_append_n(str, value, len);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic const char *strip_custom_flags(const char *value, size_t len,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MboxRewriteContext *ctx)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen TempString *str;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = t_string_new(len+1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mbox_keywords_parse(value, len, ctx->custom_flags,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen update_stripped_custom_flags, str);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return str->len == 0 ? NULL : str->str;
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen}
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainenstatic void header_func(MessagePart *part __attr_unused__,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *name, size_t name_len,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *value, size_t value_len,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen void *context)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MboxRewriteContext *ctx = context;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *str;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen char *end;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->failed)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (name_len == 6 && strncasecmp(name, "Status", 6) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->status_found = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = strip_chars(value, value_len, "RO");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_status(ctx, str);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (name_len == 8 && strncasecmp(name, "X-Status", 8) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->xstatus_found = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = strip_chars(value, value_len, "ADFT");
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen (void)mbox_write_xstatus(ctx, str);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (name_len == 10 && strncasecmp(name, "X-Keywords", 10) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->ximapbase_found = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen str = strip_custom_flags(value, value_len, ctx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_xkeywords(ctx, str);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (name_len == 10 && strncasecmp(name, "X-IMAPbase", 10) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->seq == 1) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* temporarily copy the value to make sure we
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen don't overflow it */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_push();
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen value = t_strndup(value, value_len);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->uid_validity = strtoul(value, &end, 10);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen while (*end == ' ') end++;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->uid_last = strtoul(end, &end, 10);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_pop();
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->ximapbase_found = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_ximapbase(ctx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (name_len == 14 &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen strncasecmp(name, "Content-Length", 14) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->content_length_found = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_content_length(ctx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (name_len > 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* save this header */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)io_buffer_send(ctx->outbuf, name, name_len);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)io_buffer_send(ctx->outbuf, ": ", 2);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)io_buffer_send(ctx->outbuf, value, value_len);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)io_buffer_send(ctx->outbuf, "\n", 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ctx->outbuf->closed)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx->failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int mbox_write_header(MailIndex *index,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MailIndexRecord *rec, unsigned int seq,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen IOBuffer *inbuf, IOBuffer *outbuf,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uoff_t end_offset)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* We need to update fields that define message flags. Standard fields
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen are stored in Status and X-Status. For custom flags we use
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uw-imapd compatible format, by first listing them in first message's
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen X-IMAPbase field and actually defining them in X-Keywords field.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen Format of X-IMAPbase is: <UID validity> <last used UID> <flag names>
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8c4bbf6b1415e9d0845bc8f1cd6d19b76ab0392Timo Sirainen We don't want to sync our UIDs with the mbox file, so the UID
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen validity is always kept different from our internal UID validity.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen Last used UID is also not updated, and set to 0 initially.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MboxRewriteContext ctx;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MessageSize hdr_size;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (inbuf->offset >= end_offset) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* fsck should have noticed it.. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_set_error(index, "Error rewriting mbox file %s: "
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "Unexpected end of file", index->mbox_path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_push();
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* parse the header, write the fields we don't want to change */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen memset(&ctx, 0, sizeof(ctx));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx.outbuf = outbuf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx.seq = seq;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx.content_length = rec->body_size;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx.msg_flags = rec->msg_flags;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx.uid_validity = index->header->uid_validity-1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx.custom_flags = mail_custom_flags_list_get(index->custom_flags);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen io_buffer_set_read_limit(inbuf, inbuf->offset + rec->header_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen message_parse_header(NULL, inbuf, &hdr_size, header_func, &ctx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen io_buffer_set_read_limit(inbuf, 0);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(hdr_size.physical_size == rec->header_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* append the flag fields */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (seq == 1 && !ctx.ximapbase_found) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* write X-IMAPbase header to first message */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_ximapbase(&ctx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!ctx.xkeywords_found)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_xkeywords(&ctx, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!ctx.status_found)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_status(&ctx, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!ctx.xstatus_found)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_xstatus(&ctx, NULL);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!ctx.content_length_found)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_write_content_length(&ctx);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_pop();
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_custom_flags_list_unref(index->custom_flags);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* empty line ends headers */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)io_buffer_send(outbuf, "\n", 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int fd_copy(int in_fd, int out_fd, uoff_t out_offset)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen IOBuffer *inbuf, *outbuf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen int ret;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(out_offset <= OFF_T_MAX);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (lseek(out_fd, (off_t)out_offset, SEEK_SET) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return -1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_push();
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen inbuf = io_buffer_create_mmap(in_fd, data_stack_pool,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen 1024*256, 0, 0, 0);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen outbuf = io_buffer_create_file(out_fd, data_stack_pool, 1024, FALSE);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ret = io_buffer_send_iobuffer(outbuf, inbuf, inbuf->size);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (ret < 0)
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen errno = outbuf->buf_errno;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen else {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* we may have shrinked the file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(out_offset + inbuf->size <= OFF_T_MAX);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen ret = ftruncate(out_fd, (off_t) (out_offset + inbuf->size));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen io_buffer_unref(outbuf);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen io_buffer_unref(inbuf);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_pop();
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return ret;
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen}
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenint mbox_index_rewrite(MailIndex *index)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen{
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* Write messages beginning from the first dirty one to temp file,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen then copy it over the mbox file. This may create data loss if
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen interrupted (see below). This rewriting relies quite a lot on
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen valid header/body sizes which fsck() should have ensured. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MailIndexRecord *rec;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen IOBuffer *inbuf, *outbuf;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uoff_t offset, dirty_offset;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char *path;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int seq;
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen int tmp_fd, failed, dirty_found, locked, rewrite;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((index->header->flags & MAIL_INDEX_FLAG_DIRTY_MESSAGES) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* no need to rewrite */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen tmp_fd = -1; locked = FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = TRUE; rewrite = FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen do {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* lock before fscking to prevent race conditions between
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen fsck's unlock and our lock. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen inbuf = mbox_file_open(index, 0, TRUE);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (inbuf == NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen break;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!mbox_lock_write(index))
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen break;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen locked = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!mbox_index_fsck(index))
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen break;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((index->header->flags &
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MAIL_INDEX_FLAG_DIRTY_MESSAGES) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* fsck() figured out there's no dirty messages
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen after all */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = FALSE; rewrite = FALSE;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen break;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen tmp_fd = mail_index_create_temp_file(index, &path);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (tmp_fd == -1)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen break;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = FALSE; rewrite = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } while (0);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!rewrite) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (locked)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_unlock(index);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (inbuf != NULL)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen io_buffer_unref(inbuf);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return !failed;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (index->header->flags & MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* need to update X-IMAPbase in first message */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen dirty_found = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen dirty_found = FALSE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen dirty_offset = 0;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen t_push();
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen outbuf = io_buffer_create_file(tmp_fd, data_stack_pool, 8192, FALSE);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen failed = FALSE; seq = 1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rec = index->lookup(index, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen while (rec != NULL) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (dirty_found || (rec->index_flags & INDEX_MAIL_FLAG_DIRTY)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* get offset to beginning of mail headers */
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen if (!mbox_mail_get_start_offset(index, rec, &offset)) {
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen /* fsck should have fixed it */
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen failed = TRUE;
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen break;
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen }
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen if (offset + rec->header_size + rec->body_size > inbuf->size) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_set_corrupted(index,
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen "Invalid message size");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen break;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else {
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen offset = 0;
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen if (!dirty_found &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (rec->index_flags & INDEX_MAIL_FLAG_DIRTY)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* first dirty message */
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen dirty_found = TRUE;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen dirty_offset = offset;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen io_buffer_seek(inbuf, dirty_offset);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (dirty_found) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* write the From-line */
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (!mbox_write(index, inbuf, outbuf, offset)) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen failed = TRUE;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen break;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* write header, updating flag fields */
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen offset += rec->header_size;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!mbox_write_header(index, rec, seq, inbuf, outbuf,
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen offset)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen break;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* write body */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen offset += rec->body_size;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!mbox_write(index, inbuf, outbuf, offset)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen break;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen seq++;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen rec = index->next(index, rec);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (!dirty_found) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_set_error(index, "Expected dirty messages not found "
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "from mbox file %s", index->mbox_path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* always end with a \n */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)io_buffer_send(outbuf, "\n", 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (outbuf->closed) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen errno = outbuf->buf_errno;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mbox_set_syscall_error(index, "write()");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = TRUE;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen }
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen io_buffer_unref(inbuf);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen io_buffer_unref(outbuf);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t_pop();
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (!failed) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* POSSIBLE DATA LOSS HERE. We're writing to the mbox file,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen so if we get killed here before finished, we'll lose some
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen bytes. I can't really think of any way to fix this,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rename() is problematic too especially because of file
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen locking issues (new mail could be lost).
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen Usually we're moving the data by just a few bytes, so
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen the data loss should never be more than those few bytes..
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen If we moved more, we could have written the file from end
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen to beginning in blocks (it'd be a bit slow to do it in
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen blocks of ~1-10 bytes which is the usual case, so we don't
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen bother).
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen Also, we might as well be shrinking the file, in which
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen case we can't lose data. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (fd_copy(tmp_fd, index->mbox_fd, dirty_offset) == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* all ok, we need to fsck the index next time.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen use set_flags because set_lock() would remove it
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if we modified it directly */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index->set_flags |= MAIL_INDEX_FLAG_FSCK;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen reset_dirty_flags(index);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mbox_set_syscall_error(index, "fd_copy()");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen failed = TRUE;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen }
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)mbox_unlock(index);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (void)unlink(path);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (close(tmp_fd) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen index_file_set_syscall_error(index, path, "close()");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return failed;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen}
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen