view src/lib-storage/index/mbox/mbox-sync-update.c @ 2955:d5ee1bbd15f0 HEAD

Fixed mbox corruption in certain situations. Added more asserts to catch similiar problems if there still happens to be any.
author Timo Sirainen <tss@iki.fi>
date Tue, 07 Dec 2004 22:26:00 +0200
parents 9099bb8ee151
children 61c8d205d887
line wrap: on
line source

/* Copyright (C) 2004 Timo Sirainen */

#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "str.h"
#include "message-parser.h"
#include "index-storage.h"
#include "mbox-storage.h"
#include "mbox-sync-private.h"

static void status_flags_append(struct mbox_sync_mail_context *ctx,
				const struct mbox_flag_type *flags_list)
{
	int i;

	for (i = 0; flags_list[i].chr != 0; i++) {
		if ((ctx->mail.flags & flags_list[i].flag) != 0)
			str_append_c(ctx->header, flags_list[i].chr);
	}
}

void mbox_sync_move_buffer(struct mbox_sync_mail_context *ctx,
			   size_t pos, size_t need, size_t have)
{
	ssize_t diff = (ssize_t)need - (ssize_t)have;
	int i;

	i_assert(have < SSIZE_T_MAX);

	if (diff == 0) {
		if (ctx->header_last_change < pos + have ||
		    ctx->header_last_change == (size_t)-1)
			ctx->header_last_change = pos + have;
	} else {
		/* FIXME: if (diff < ctx->space && pos < ctx->offset) then
		   move the data only up to space offset and give/take the
		   space from there. update header_last_change accordingly.
		   (except pos and offset can't be compared directly) */
		ctx->header_last_change = (size_t)-1;
		for (i = 0; i < MBOX_HDR_COUNT; i++) {
			if (ctx->hdr_pos[i] > pos &&
			    ctx->hdr_pos[i] != (size_t)-1)
				ctx->hdr_pos[i] += diff;
		}

		if (diff < 0)
			str_delete(ctx->header, pos, -diff);
		else {
			ctx->header_last_change = (size_t)-1;
			buffer_copy(ctx->header, pos + diff,
				    ctx->header, pos, (size_t)-1);
		}
	}
}

static void status_flags_replace(struct mbox_sync_mail_context *ctx, size_t pos,
				 const struct mbox_flag_type *flags_list)
{
	unsigned char *data;
	size_t size;
	int i, need, have;

	if (ctx->header_first_change > pos)
		ctx->header_first_change = pos;

	/* how many bytes do we need? */
	for (i = 0, need = 0; flags_list[i].chr != 0; i++) {
		if ((ctx->mail.flags & flags_list[i].flag) != 0)
			need++;
	}

	/* how many bytes do we have now? */
	data = buffer_get_modifyable_data(ctx->header, &size);
	for (have = 0; pos < size; pos++) {
		if (data[pos] == '\n')
			break;

		/* see if this is unknown flag for us */
		for (i = 0; flags_list[i].chr != 0; i++) {
			if (flags_list[i].chr == data[pos])
				break;
		}

		if (flags_list[i].chr != 0)
			have++;
		else {
			/* save this one */
			data[pos-have] = data[pos];
		}
	}
	pos -= have;
        mbox_sync_move_buffer(ctx, pos, need, have);

	/* @UNSAFE */
	data = buffer_get_space_unsafe(ctx->header, pos, need);
	for (i = 0, need = 0; flags_list[i].chr != 0; i++) {
		if ((ctx->mail.flags & flags_list[i].flag) != 0)
			*data++ = flags_list[i].chr;
	}
}

static void keywords_append(struct mbox_sync_mail_context *ctx,
			    keywords_mask_t keywords)
{
	// FIXME
}

static void mbox_sync_add_missing_headers(struct mbox_sync_mail_context *ctx)
{
	size_t old_hdr_size, new_hdr_size;
	int i, have_keywords;

	old_hdr_size = ctx->body_offset - ctx->hdr_offset;
	new_hdr_size = str_len(ctx->header);

	if (new_hdr_size > 0 &&
	    str_data(ctx->header)[new_hdr_size-1] != '\n') {
		/* broken header - doesn't end with \n. fix it. */
		str_append_c(ctx->header, '\n');
	}

	if (ctx->sync_ctx->dest_first_mail &&
	    ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] == (size_t)-1) {
		if (ctx->sync_ctx->base_uid_validity == 0) {
			ctx->sync_ctx->base_uid_validity =
				ctx->sync_ctx->hdr->uid_validity == 0 ?
				(uint32_t)ioloop_time :
				ctx->sync_ctx->hdr->uid_validity;
		}

		str_append(ctx->header, "X-IMAPbase: ");
		ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] = str_len(ctx->header);
		str_printfa(ctx->header, "%u %010u",
			    ctx->sync_ctx->base_uid_validity,
			    ctx->sync_ctx->next_uid-1);
		//FIXME:keywords_append(ctx, all_keywords);
		str_append_c(ctx->header, '\n');
	}

	if (ctx->hdr_pos[MBOX_HDR_X_UID] == (size_t)-1 && !ctx->pseudo) {
		str_append(ctx->header, "X-UID: ");
		ctx->hdr_pos[MBOX_HDR_X_UID] = str_len(ctx->header);
		str_printfa(ctx->header, "%u\n", ctx->mail.uid);
	}

	if (ctx->hdr_pos[MBOX_HDR_STATUS] == (size_t)-1 &&
	    (ctx->mail.flags & STATUS_FLAGS_MASK) != 0) {
		str_append(ctx->header, "Status: ");
		ctx->hdr_pos[MBOX_HDR_STATUS] = str_len(ctx->header);
		status_flags_append(ctx, mbox_status_flags);
		str_append_c(ctx->header, '\n');
	}

	if (ctx->hdr_pos[MBOX_HDR_X_STATUS] == (size_t)-1 &&
	    (ctx->mail.flags & XSTATUS_FLAGS_MASK) != 0) {
		str_append(ctx->header, "X-Status: ");
		ctx->hdr_pos[MBOX_HDR_X_STATUS] = str_len(ctx->header);
		status_flags_append(ctx, mbox_xstatus_flags);
		str_append_c(ctx->header, '\n');
	}

	have_keywords = FALSE;
	for (i = 0; i < INDEX_KEYWORDS_BYTE_COUNT; i++) {
		if (ctx->mail.keywords[i] != 0) {
			have_keywords = TRUE;
			break;
		}
	}

	if (ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] == (size_t)-1 && have_keywords) {
		str_append(ctx->header, "X-Keywords: ");
		ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] = str_len(ctx->header);
		keywords_append(ctx, ctx->mail.keywords);
		str_append_c(ctx->header, '\n');
	}

	if (ctx->content_length == (uoff_t)-1 &&
	    ctx->mail.body_size >= MBOX_MIN_CONTENT_LENGTH_SIZE) {
		str_printfa(ctx->header, "Content-Length: %"PRIuUOFF_T"\n",
			    ctx->mail.body_size);
	}

	if (str_len(ctx->header) != new_hdr_size) {
		if (ctx->header_first_change == (size_t)-1)
			ctx->header_first_change = new_hdr_size;
		ctx->header_last_change = (size_t)-1;
	}

	if (ctx->have_eoh)
		str_append_c(ctx->header, '\n');
}

static void mbox_sync_update_status(struct mbox_sync_mail_context *ctx)
{
	if (ctx->hdr_pos[MBOX_HDR_STATUS] != (size_t)-1) {
		status_flags_replace(ctx, ctx->hdr_pos[MBOX_HDR_STATUS],
				     mbox_status_flags);
	}
}

static void mbox_sync_update_xstatus(struct mbox_sync_mail_context *ctx)
{
	if (ctx->hdr_pos[MBOX_HDR_X_STATUS] != (size_t)-1) {
		status_flags_replace(ctx, ctx->hdr_pos[MBOX_HDR_X_STATUS],
				     mbox_xstatus_flags);
	}
}

static void mbox_sync_update_xkeywords(struct mbox_sync_mail_context *ctx)
{
}

static void mbox_sync_update_line(struct mbox_sync_mail_context *ctx,
				  size_t pos, string_t *new_line)
{
	const char *hdr, *p;

	if (ctx->header_first_change > pos)
		ctx->header_first_change = pos;

	hdr = str_c(ctx->header) + pos;
	p = strchr(hdr, '\n');

	if (p == NULL) {
		/* shouldn't really happen, but allow anyway.. */
		ctx->header_last_change = (size_t)-1;
		str_truncate(ctx->header, pos);
		str_append_str(ctx->header, new_line);
	} else {
		mbox_sync_move_buffer(ctx, pos, str_len(new_line), p - hdr + 1);
		buffer_copy(ctx->header, pos, new_line, 0, (size_t)-1);
	}
}

static void mbox_sync_update_x_imap_base(struct mbox_sync_mail_context *ctx)
{
	string_t *str;

	if (!ctx->sync_ctx->dest_first_mail ||
	    ctx->hdr_pos[MBOX_HDR_X_IMAPBASE] == (size_t)-1 ||
	    ctx->sync_ctx->update_base_uid_last == 0 ||
	    ctx->sync_ctx->update_base_uid_last < ctx->sync_ctx->base_uid_last)
		return;

	/* update uid-last field in X-IMAPbase */
	t_push();

	str = t_str_new(200);
	str_printfa(str, "%u %010u", ctx->sync_ctx->base_uid_validity,
		    ctx->sync_ctx->update_base_uid_last);
	//FIXME:keywords_append(ctx, all_keywords);
	str_append_c(str, '\n');

        mbox_sync_update_line(ctx, ctx->hdr_pos[MBOX_HDR_X_IMAPBASE], str);
	t_pop();
}

static void mbox_sync_update_x_uid(struct mbox_sync_mail_context *ctx)
{
	string_t *str;

	if (ctx->hdr_pos[MBOX_HDR_X_UID] == (size_t)-1 ||
	    ctx->mail.uid == ctx->parsed_uid)
		return;

	t_push();
	str = t_str_new(64);
	str_printfa(str, "%u\n", ctx->mail.uid);
	mbox_sync_update_line(ctx, ctx->hdr_pos[MBOX_HDR_X_UID], str);
	t_pop();
}

void mbox_sync_update_header(struct mbox_sync_mail_context *ctx,
			     buffer_t *syncs_buf)
{
	const struct mail_index_sync_rec *sync;
	size_t size, i;
	uint8_t old_flags;
	keywords_mask_t old_keywords;

	i_assert(ctx->mail.uid != 0 || ctx->pseudo);

	sync = buffer_get_data(syncs_buf, &size);
	size /= sizeof(*sync);

	old_flags = ctx->mail.flags;

	if (size != 0) {
		memcpy(old_keywords, ctx->mail.keywords, sizeof(old_keywords));

		for (i = 0; i < size; i++) {
			if (sync[i].type != MAIL_INDEX_SYNC_TYPE_FLAGS)
				continue;

			mail_index_sync_flags_apply(&sync[i], &ctx->mail.flags,
						    ctx->mail.keywords);
		}

		/* keep our old recent flag. especially because we use it
		   negatively as non-recent */
		ctx->mail.flags = (ctx->mail.flags & ~MAIL_RECENT) |
			(old_flags & MAIL_RECENT);

		if ((old_flags & XSTATUS_FLAGS_MASK) !=
		    (ctx->mail.flags & XSTATUS_FLAGS_MASK))
			mbox_sync_update_xstatus(ctx);
		if (memcmp(old_keywords, ctx->mail.keywords,
			   INDEX_KEYWORDS_BYTE_COUNT) != 0)
			mbox_sync_update_xkeywords(ctx);
	}

	if (!ctx->sync_ctx->ibox->keep_recent)
		ctx->mail.flags |= MBOX_NONRECENT;

	if ((old_flags & STATUS_FLAGS_MASK) !=
	    (ctx->mail.flags & STATUS_FLAGS_MASK))
		mbox_sync_update_status(ctx);

	mbox_sync_update_x_imap_base(ctx);
	mbox_sync_update_x_uid(ctx);
	mbox_sync_add_missing_headers(ctx);
	ctx->updated = TRUE;
}

void mbox_sync_update_header_from(struct mbox_sync_mail_context *ctx,
				  const struct mbox_sync_mail *mail)
{
	if ((ctx->mail.flags & STATUS_FLAGS_MASK) !=
	    (mail->flags & STATUS_FLAGS_MASK) ||
	    (ctx->mail.flags & MBOX_NONRECENT) == 0) {
		ctx->mail.flags = (ctx->mail.flags & ~STATUS_FLAGS_MASK) |
			(mail->flags & STATUS_FLAGS_MASK);
		if (!ctx->sync_ctx->ibox->keep_recent)
                        ctx->mail.flags |= MBOX_NONRECENT;
		mbox_sync_update_status(ctx);
	}
	if ((ctx->mail.flags & XSTATUS_FLAGS_MASK) !=
	    (mail->flags & XSTATUS_FLAGS_MASK)) {
		ctx->mail.flags = (ctx->mail.flags & ~XSTATUS_FLAGS_MASK) |
			(mail->flags & XSTATUS_FLAGS_MASK);
		mbox_sync_update_xstatus(ctx);
	}
	if (memcmp(ctx->mail.keywords, mail->keywords,
		   INDEX_KEYWORDS_BYTE_COUNT) != 0) {
		memcpy(ctx->mail.keywords, mail->keywords,
		       INDEX_KEYWORDS_BYTE_COUNT);
		mbox_sync_update_xkeywords(ctx);
	}

	i_assert(ctx->mail.uid == 0 || ctx->mail.uid == mail->uid);
	ctx->mail.uid = mail->uid;

	mbox_sync_update_x_imap_base(ctx);
	mbox_sync_update_x_uid(ctx);
	mbox_sync_add_missing_headers(ctx);
}