mbox-rewrite.c revision 74a02d35d21807b9f9069964514d8c09e3631ea4
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch/* Copyright (C) 2002 Timo Sirainen */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include "lib.h"
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include "iobuffer.h"
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include "temp-string.h"
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include "write-full.h"
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include "mbox-index.h"
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include "mail-index-util.h"
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include "mail-custom-flags.h"
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include <stdio.h>
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include <stdlib.h>
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include <unistd.h>
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch#include <fcntl.h>
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschtypedef struct {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch IOBuffer *outbuf;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch int failed;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch unsigned int seq;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch unsigned int msg_flags;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch const char **custom_flags;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch const char *status, *x_status, *x_keywords;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch unsigned int uid_validity;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch unsigned int uid_last;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch} MboxRewriteContext;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch/* Remove dirty flag from all messages */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschstatic void reset_dirty_flags(MailIndex *index)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch{
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch MailIndexRecord *rec;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch rec = index->lookup(index, 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch while (rec != NULL) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch rec->index_flags &= ~INDEX_MAIL_FLAG_DIRTY;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch rec = index->next(index, rec);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch index->header->flags &= ~MAIL_INDEX_FLAG_DIRTY_MESSAGES;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch}
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschstatic int mbox_write(MailIndex *index, IOBuffer *inbuf, IOBuffer *outbuf,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch uoff_t end_offset)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch{
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch i_assert(inbuf->offset <= end_offset);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (io_buffer_send_iobuffer(outbuf, inbuf,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch end_offset - inbuf->offset) < 0)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return FALSE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (inbuf->offset < end_offset) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* fsck should have noticed it.. */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch index_set_error(index, "Error rewriting mbox file %s: "
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch "Unexpected end of file", index->mbox_path);
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch return FALSE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch}
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschstatic const char *strip_chars(const char *value, size_t value_len,
4d955db590c3d76a631dfc5d37bcdf578a43e55aStephan Bosch const char *list)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch{
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* leave only unknown flags, very likely none */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch char *ret, *p;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch unsigned int i;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ret = p = t_buffer_get(value_len+1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch for (i = 0; i < value_len; i++) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (strchr(list, value[i]) == NULL)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch *p++ = value[i];
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (ret == p)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return NULL;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch *p = '\0';
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch t_buffer_alloc((size_t) (p-ret)+1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return ret;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch}
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschstatic void update_stripped_custom_flags(const char *value, size_t len,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch int index, void *context)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch{
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch TempString *str = context;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (index < 0) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* not found, keep it */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (str->len != 0)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch t_string_append_c(str, ' ');
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch t_string_append_n(str, value, len);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch}
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschstatic const char *strip_custom_flags(const char *value, size_t len,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch MboxRewriteContext *ctx)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch{
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch TempString *str;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch str = t_string_new(len+1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch mbox_keywords_parse(value, len, ctx->custom_flags,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch update_stripped_custom_flags, str);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return str->str;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch}
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschstatic void header_func(MessagePart *part __attr_unused__,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch const char *name, size_t name_len,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch const char *value, size_t value_len,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch void *context)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch{
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch MboxRewriteContext *ctx = context;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch char *end;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (ctx->failed)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (name_len == 6 && strncasecmp(name, "Status", 6) == 0)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx->status = strip_chars(value, value_len, "RO");
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch else if (name_len == 8 && strncasecmp(name, "X-Status", 8) == 0)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx->x_status = strip_chars(value, value_len, "ADFT");
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch else if (name_len == 10 && strncasecmp(name, "X-Keywords", 10) == 0)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx->x_keywords = strip_custom_flags(value, value_len, ctx);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch else if (name_len == 10 && strncasecmp(name, "X-IMAPbase", 10) == 0) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (ctx->seq == 1) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* temporarily copy the value to make sure we
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch don't overflow it */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch t_push();
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch value = t_strndup(value, value_len);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx->uid_validity = strtoul(value, &end, 10);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch while (*end == ' ') end++;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx->uid_last = strtoul(end, &end, 10);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch t_pop();
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch } else {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* save this header */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(ctx->outbuf, name, name_len);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(ctx->outbuf, ": ", 2);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(ctx->outbuf, value, value_len);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(ctx->outbuf, "\n", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (ctx->outbuf->closed)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx->failed = TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch}
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschstatic int mbox_write_header(MailIndex *index,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch MailIndexRecord *rec, unsigned int seq,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch IOBuffer *inbuf, IOBuffer *outbuf,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch uoff_t end_offset)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch{
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* We need to update fields that define message flags. Standard fields
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch are stored in Status and X-Status. For custom flags we use
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch uw-imapd compatible format, by first listing them in first message's
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch X-IMAPbase field and actually defining them in X-Keywords field.
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch Format of X-IMAPbase is: <UID validity> <last used UID> <flag names>
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch We don't want to sync our UIDs with the mbox file, so the UID
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch validity is always kept different from our internal UID validity.
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch Last used UID is also not updated, and set to 0 initially.
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch MboxRewriteContext ctx;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch MessageSize hdr_size;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch const char *str, *flags, **custom_flags;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch unsigned int field;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch int i;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (inbuf->offset >= end_offset) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* fsck should have noticed it.. */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch index_set_error(index, "Error rewriting mbox file %s: "
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch "Unexpected end of file", index->mbox_path);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return FALSE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch t_push();
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch custom_flags = mail_custom_flags_list_get(index->custom_flags);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* parse the header, write the fields we don't want to change */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch memset(&ctx, 0, sizeof(ctx));
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.outbuf = outbuf;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.seq = seq;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.msg_flags = rec->msg_flags;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.custom_flags = custom_flags;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch message_parse_header(NULL, inbuf, &hdr_size, header_func, &ctx);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch i_assert(hdr_size.physical_size == rec->header_size);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* append the flag fields */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (seq == 1) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* write X-IMAPbase header to first message */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (ctx.uid_validity == 0)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.uid_validity = index->header->uid_validity-1;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch str = t_strdup_printf("X-IMAPbase: %u %u",
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.uid_validity, ctx.uid_last);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, str, strlen(str));
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (custom_flags[i] != NULL) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, " ", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, custom_flags[i],
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch strlen(custom_flags[i]));
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, "\n", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if ((rec->msg_flags & MAIL_CUSTOM_FLAGS_MASK) ||
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.x_keywords != NULL) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* write X-Keywords header containing custom flags */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, "X-Keywords:", 11);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch field = 1 << MAIL_CUSTOM_FLAG_1_BIT;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++, field <<= 1) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if ((rec->msg_flags & field) &&
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch custom_flags[i] != NULL) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, " ", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, custom_flags[i],
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch strlen(custom_flags[i]));
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (ctx.x_keywords != NULL && ctx.x_keywords[0] != '\0') {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* X-Keywords that aren't custom flags */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, " ", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, ctx.x_keywords,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch strlen(ctx.x_keywords));
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, "\n", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* Status field */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch flags = (rec->msg_flags & MAIL_SEEN) ? "Status: RO" : "Status: O";
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch flags = t_strconcat(flags, ctx.status, NULL);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, flags, strlen(flags));
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, "\n", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* X-Status field */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if ((rec->msg_flags & (MAIL_SYSTEM_FLAGS_MASK^MAIL_SEEN)) != 0 ||
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.x_status != NULL) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch flags = t_strconcat("X-Status: ",
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (rec->msg_flags & MAIL_ANSWERED) ? "A" : "",
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (rec->msg_flags & MAIL_DRAFT) ? "D" : "",
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (rec->msg_flags & MAIL_FLAGGED) ? "F" : "",
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (rec->msg_flags & MAIL_DELETED) ? "T" : "",
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch ctx.x_status, NULL);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, flags, strlen(flags));
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, "\n", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch t_pop();
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch mail_custom_flags_list_unref(index->custom_flags);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* empty line ends headers */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, "\n", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch}
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Boschint mbox_index_rewrite(MailIndex *index)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch{
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* Write it to temp file and then rename() to real file.
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch easier and much safer than moving data inside the file.
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch This rewriting relies quite a lot on valid header/body sizes
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch which fsck() should have ensured. */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch MailIndexRecord *rec;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch IOBuffer *inbuf, *outbuf;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch uoff_t offset;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch const char *path;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch unsigned int seq;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch int in_fd, out_fd, failed;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if ((index->header->flags & MAIL_INDEX_FLAG_DIRTY_MESSAGES) == 0) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* no need to rewrite */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (!mbox_index_fsck(index))
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return FALSE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch in_fd = open(index->mbox_path, O_RDWR);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (in_fd == -1)
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return mbox_set_syscall_error(index, "open()");
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch inbuf = io_buffer_create_mmap(in_fd, default_pool,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch MAIL_MMAP_BLOCK_SIZE, 0);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch out_fd = mail_index_create_temp_file(index, &path);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (out_fd == -1) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)close(in_fd);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch io_buffer_destroy(inbuf);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return FALSE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch outbuf = io_buffer_create_file(out_fd, default_pool, 8192);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch failed = FALSE; seq = 1;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch rec = index->lookup(index, 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch while (rec != NULL) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* get offset to beginning of mail headers */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (!mbox_mail_get_start_offset(index, rec, &offset)) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* fsck should have fixed it */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch failed = TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch break;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (offset + rec->header_size + rec->body_size > inbuf->size) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch index_set_corrupted(index, "Invalid message size");
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch failed = TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch break;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* write the From-line */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (!mbox_write(index, inbuf, outbuf, offset)) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch failed = TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch break;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* write header, updating flag fields */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch offset += rec->header_size;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (!mbox_write_header(index, rec, seq, inbuf, outbuf,
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch offset)) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch failed = TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch break;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* write body */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch offset += rec->body_size;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (!mbox_write(index, inbuf, outbuf, offset)) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch failed = TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch break;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch seq++;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch rec = index->next(index, rec);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* always end with a \n */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)io_buffer_send(outbuf, "\n", 1);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (outbuf->closed) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch errno = outbuf->buf_errno;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch mbox_set_syscall_error(index, "write()");
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch failed = TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (!failed) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if (rename(path, index->mbox_path) == 0) {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch /* all ok, we need to fsck the index next time.
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch use set_flags because set_lock() would remove it
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch if we modified it directly */
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch index->set_flags |= MAIL_INDEX_FLAG_FSCK;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch reset_dirty_flags(index);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch } else {
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch index_set_error(index, "rename(%s, %s) failed: %m",
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch path, index->mbox_path);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch failed = TRUE;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch }
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)close(out_fd);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)close(in_fd);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch (void)unlink(path);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch io_buffer_destroy(outbuf);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch io_buffer_destroy(inbuf);
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch return failed;
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch}
6ae6496c225238a2c55a8cd96744ad976c44a726Stephan Bosch