view src/lib-storage/index/dbox/dbox-sync-full.c @ 5448:beabd433cdae HEAD

Moved delete/rename operations to mailbox_list API. Fixed mbox/maildir to work with either fs/maildir++ directory layout. They can be changed by appending :LAYOUT=fs|maildir++ to mail_location.
author Timo Sirainen <tss@iki.fi>
date Thu, 29 Mar 2007 10:59:11 +0300
parents bf4e98a0de3f
children 78eaf595359c
line wrap: on
line source

/* Copyright (C) 2005 Timo Sirainen */

#include "lib.h"
#include "array.h"
#include "seq-range-array.h"
#include "dbox-storage.h"
#include "dbox-uidlist.h"
#include "dbox-file.h"
#include "dbox-keywords.h"
#include "dbox-sync.h"

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>

static int
dbox_mail_get_keywords(struct dbox_mailbox *mbox, struct dbox_file *file,
		       ARRAY_TYPE(keyword_indexes) *keywords)
{
	const unsigned int *map;
	unsigned int i, count;

	if (!array_is_created(&file->file_idx_keywords)) {
		if (dbox_file_read_keywords(mbox, file) < 0)
			return -1;
	}

	map = array_get(&file->file_idx_keywords, &count);
	for (i = 0; i < count; i++) {
		if (file->seeked_keywords[i] != '0')
			array_append(keywords, &map[i], 1);
	}

	return 0;
}

static int dbox_sync_full_mail(struct dbox_sync_context *ctx, uint32_t *seq_r)
{
	struct dbox_mailbox *mbox = ctx->mbox;
	const struct dbox_mail_header *hdr = &mbox->file->seeked_mail_header;
	enum mail_flags flags;
        struct mail_keywords *keywords;
	ARRAY_TYPE(keyword_indexes) keywords_arr;
	uint32_t seq;
	uint64_t hdr_offset = mbox->file->seeked_offset;

	/* FIXME: mails can be in two places at the same time if we crashed
	   during copying expunge */

	i_assert(hdr->expunged != '1');

	if (mbox->file->seeked_uid >= ctx->mail_index_next_uid) {
		/* new mail. append it. */
		mail_index_append(ctx->trans, mbox->file->seeked_uid, &seq);
		*seq_r = 0;
	} else {
		if (mail_index_lookup_uid_range(ctx->sync_view,
						mbox->file->seeked_uid,
						mbox->file->seeked_uid,
						&seq, &seq) < 0) {
			mail_storage_set_index_error(&ctx->mbox->ibox);
			return -1;
		}
		if (seq == 0) {
			/* not found. it should have been there. */
			mail_storage_set_critical(STORAGE(mbox->storage),
				"dbox %s sync: "
				"UID %u inserted in the middle of mailbox",
				mbox->path, mbox->file->seeked_uid);
			mail_index_mark_corrupted(mbox->ibox.index);
			return -1;
		}
		*seq_r = seq;
	}

	flags = 0;
	if (hdr->answered == '1')
		flags |= MAIL_ANSWERED;
	if (hdr->flagged == '1')
		flags |= MAIL_FLAGGED;
	if (hdr->deleted == '1')
		flags |= MAIL_DELETED;
	if (hdr->seen == '1')
		flags |= MAIL_SEEN;
	if (hdr->draft == '1')
		flags |= MAIL_DRAFT;
	mail_index_update_flags(ctx->trans, seq, MODIFY_REPLACE, flags);

	t_push();
	t_array_init(&keywords_arr, mbox->file->keyword_count);
	if (dbox_mail_get_keywords(mbox, mbox->file, &keywords_arr) < 0) {
		t_pop();
		return -1;
	}
	keywords = mail_index_keywords_create_from_indexes(ctx->trans,
							   &keywords_arr);
	mail_index_update_keywords(ctx->trans, seq, MODIFY_REPLACE, keywords);
	mail_index_keywords_free(&keywords);
	t_pop();

	mail_index_update_ext(ctx->trans, seq, mbox->dbox_file_ext_idx,
			      &mbox->file->file_seq, NULL);
	mail_index_update_ext(ctx->trans, seq, mbox->dbox_offset_ext_idx,
			      &hdr_offset, NULL);
	return 0;
}

static int dbox_sync_full_file(struct dbox_sync_context *ctx, uint32_t file_seq)
{
	struct dbox_mailbox *mbox = ctx->mbox;
	struct dbox_uidlist_entry entry;
	uint32_t seq;
	int ret;

	if ((ret = dbox_file_seek(mbox, file_seq, 0, FALSE)) < 0) {
		/* error / broken file */
		return -1;
	}
	if (ret == 0) {
		/* broken file, but without any useful data in it */
		if (unlink(mbox->file->path) < 0) {
			mail_storage_set_critical(STORAGE(mbox->storage),
				"unlink(%s) failed: %m", mbox->file->path);
			return -1;
		}
		return 0;
	}

	memset(&entry, 0, sizeof(entry));
	entry.file_seq = file_seq;
	t_array_init(&entry.uid_list, 64);

	if (mbox->file->seeked_mail_header.expunged != '0') {
		/* first mail expunged */
		ret = dbox_file_seek_next_nonexpunged(mbox);
	}
	while (ret > 0) {
		if (dbox_sync_full_mail(ctx, &seq) < 0)
			return -1;

		/* add to this file's uid list */
		seq_range_array_add(&entry.uid_list, 0,
				    ctx->mbox->file->seeked_uid);
		if (seq != 0) {
			/* add to the whole mailbox's exist list so we can
			   expunge the mails that weren't found. seq=0 is
			   given for newly appended mails */
			seq_range_array_add(&ctx->exists, 0, seq);
		}

		ret = dbox_file_seek_next_nonexpunged(mbox);
	}
	if (ret == 0 && array_count(&entry.uid_list) == 0) {
		/* all mails expunged in the file */
		if (unlink(mbox->file->path) < 0) {
			mail_storage_set_critical(STORAGE(mbox->storage),
				"unlink(%s) failed: %m", mbox->file->path);
			return -1;
		}
	} else {
		dbox_uidlist_sync_append(ctx->uidlist_sync_ctx, &entry);
	}
	return ret;
}

static void dbox_sync_full_expunge_nonfound(struct dbox_sync_context *ctx)
{
	const struct seq_range *exists;
	const struct mail_index_header *hdr;
	unsigned int i, count;
	uint32_t seq = 1;

	exists = array_get(&ctx->exists, &count);
	for (i = 0; i < count; i++) {
		/* expunge seq .. exists[i]-1 */
		while (seq < exists[i].seq1) {
			mail_index_expunge(ctx->trans, seq);
			seq++;
		}
		seq = exists[i].seq2 + 1;
	}

	hdr = mail_index_get_header(ctx->sync_view);
	while (seq <= hdr->messages_count) {
		mail_index_expunge(ctx->trans, seq);
		seq++;
	}
}

int dbox_sync_full(struct dbox_sync_context *ctx)
{
	struct dbox_mailbox *mbox = ctx->mbox;
	const struct mail_index_header *hdr;
	unsigned int file_prefix_len = strlen(DBOX_MAIL_FILE_PREFIX);
	uint32_t file_seq;
	DIR *dirp;
	struct dirent *dp;
	int ret = 0;

	/* go through msg.* files, sync them to index and based on it
	   write dbox's index file */
	dirp = opendir(mbox->path);
	if (dirp == NULL) {
		mail_storage_set_critical(STORAGE(mbox->storage),
					  "opendir(%s) failed: %m", mbox->path);
		return -1;
	}

	hdr = mail_index_get_header(ctx->sync_view);
	ctx->mail_index_next_uid = hdr->next_uid;

	dbox_uidlist_sync_from_scratch(ctx->uidlist_sync_ctx);
	i_array_init(&ctx->exists, 128);

	while ((dp = readdir(dirp)) != NULL) {
		if (strncmp(dp->d_name, DBOX_MAIL_FILE_PREFIX,
			    file_prefix_len) != 0 ||
		    !is_numeric(dp->d_name + file_prefix_len, '\0'))
			continue;

		file_seq = (uint32_t)strtoul(dp->d_name + file_prefix_len,
					     NULL, 10);
		t_push();
		ret = dbox_sync_full_file(ctx, file_seq);
		t_pop();
		if (ret < 0)
			break;
	}
	if (closedir(dirp) < 0) {
		mail_storage_set_critical(STORAGE(mbox->storage),
			"closedir(%s) failed: %m", mbox->path);
		ret = -1;
	}

	if (ret == 0)
		dbox_sync_full_expunge_nonfound(ctx);

	array_free(&ctx->exists);
	return ret;
}