view src/lib-storage/index/mbox/mbox-expunge.c @ 1870:c972ea085643 HEAD

istream rewrite. instead of directly setting any limits to stream, you now have to use i_stream_create_limit() to existing stream. this should make the istreams much easier to create and understand how they work.
author Timo Sirainen <tss@iki.fi>
date Sun, 09 Nov 2003 20:26:25 +0200
parents e42d97a85653
children 79790750c349
line wrap: on
line source

/* Copyright (C) 2002-2003 Timo Sirainen */

#include "lib.h"
#include "istream.h"
#include "ostream.h"
#include "mbox-index.h"
#include "mbox-storage.h"
#include "mbox-lock.h"
#include "index-expunge.h"

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

struct mbox_expunge_context {
	struct mail_expunge_context *ctx;

        struct index_mailbox *ibox;
	struct istream *input;
	struct ostream *output;
	int failed, expunges;

	uoff_t from_offset, move_offset;
};

struct mail_expunge_context *
mbox_storage_expunge_init(struct mailbox *box,
			  enum mail_fetch_field wanted_fields, int expunge_all)
{
	struct index_mailbox *ibox = (struct index_mailbox *) box;
	struct mbox_expunge_context *ctx;
	struct mail_expunge_context *mctx;
	struct istream *input;

	mctx = index_storage_expunge_init(box, wanted_fields, expunge_all);
	if (mctx == NULL)
		return NULL;

	/* mbox must be already opened, synced and locked at this point.
	   we just want the istream. */
	input = mbox_get_stream(ibox->index, MAIL_LOCK_EXCLUSIVE);
	if (input == NULL)
		return NULL;

	i_assert(ibox->index->mbox_sync_counter ==
		 ibox->index->mbox_lock_counter);

	ctx = i_new(struct mbox_expunge_context, 1);
	ctx->ctx = mctx;
	ctx->ibox = ibox;
	ctx->input = input;
	ctx->output = o_stream_create_file(ibox->index->mbox_fd, default_pool,
					   4096, FALSE);
	ctx->from_offset = (uoff_t)-1;
	ctx->move_offset = (uoff_t)-1;
	o_stream_set_blocking(ctx->output, 60000, NULL, NULL);
	return (struct mail_expunge_context *) ctx;
}

static int mbox_move_data(struct mbox_expunge_context *ctx)
{
	struct istream *input;
	const unsigned char *data;
	size_t size;
	int failed;

	i_stream_seek(ctx->input, ctx->move_offset);

	if (ctx->output->offset == 0) {
		/* we're writing to beginning of mbox, so we
		   don't want the [\r]\n there */
		(void)i_stream_read_data(ctx->input, &data, &size, 1);
		if (size > 0 && data[0] == '\n')
			i_stream_skip(ctx->input, 1);
		else if (size > 1 && data[0] == '\r' &&
			 data[1] == '\n')
			i_stream_skip(ctx->input, 2);
	}

	if (ctx->from_offset == 0)
		failed = o_stream_send_istream(ctx->output, ctx->input) < 0;
	else {
		input = i_stream_create_limit(default_pool, ctx->input,
					      0, ctx->from_offset);
		failed = o_stream_send_istream(ctx->output, ctx->input) < 0;
		i_stream_unref(input);
	}

	return !failed;
}

int mbox_storage_expunge_deinit(struct mail_expunge_context *_ctx)
{
	struct mbox_expunge_context *ctx = (struct mbox_expunge_context *) _ctx;
	int failed = ctx->failed;

	if (ctx->expunges) {
		if (!failed && ctx->move_offset != (uoff_t)-1) {
			ctx->from_offset = 0;
			if (!mbox_move_data(ctx))
				failed = TRUE;
		} else if (failed && ctx->output->offset > 0) {
			/* we moved some of the data. move the rest as well
			   so there won't be invalid holes in mbox file */
			(void)o_stream_send_istream(ctx->output, ctx->input);
		}

		if (ftruncate(ctx->ibox->index->mbox_fd,
			      (off_t)ctx->output->offset) < 0) {
			mail_storage_set_error(ctx->ibox->box.storage,
				"ftruncate() failed for mbox file %s: %m",
				ctx->ibox->index->mailbox_path);
			failed = TRUE;
		}
	}

	if (!index_storage_expunge_deinit(ctx->ctx))
		failed = TRUE;

	o_stream_unref(ctx->output);
	i_free(ctx);
	return !failed;
}

static int get_from_offset(struct mbox_expunge_context *ctx,
			   struct mail_index_record *rec, uoff_t *offset_r)
{
	struct message_size hdr_size;
	uoff_t offset, body_size;

	if (!mbox_mail_get_location(ctx->ibox->index, rec, &offset, &body_size))
		return FALSE;

	i_stream_seek(ctx->input, offset);
	message_get_header_size(ctx->input, &hdr_size, NULL);

	*offset_r = offset + hdr_size.physical_size + body_size;
	return TRUE;
}

struct mail *mbox_storage_expunge_fetch_next(struct mail_expunge_context *_ctx)
{
	struct mbox_expunge_context *ctx =
		(struct mbox_expunge_context *) _ctx;
	struct mail_expunge_context *mctx = ctx->ctx;
	struct mail_index *index = ctx->ibox->index;

	if (mctx->rec == NULL)
		return NULL;

	if (mctx->fetch_next) {
                mctx->fetch_next = FALSE;
		do {
			if (!get_from_offset(ctx, mctx->rec,
					     &ctx->from_offset)) {
				ctx->failed = TRUE;
				return NULL;
			}

			mctx->seq++;
			mctx->rec = index->next(index, mctx->rec);
			if (mctx->rec == NULL)
				return NULL;
		} while ((mctx->rec->msg_flags & MAIL_DELETED) == 0 &&
			 !mctx->expunge_all);
	}

	return index_storage_expunge_fetch_next(ctx->ctx);
}

static int get_prev_from_offset(struct mbox_expunge_context *ctx,
				unsigned int seq)
{
	struct mail_index_record *rec;

	if (seq == 1)
		ctx->from_offset = 0;
	else {
		rec = ctx->ibox->index->lookup(ctx->ibox->index, seq-1);

		if (!get_from_offset(ctx, rec, &ctx->from_offset))
			return FALSE;
	}

	return TRUE;
}

int mbox_storage_expunge(struct mail *mail, struct mail_expunge_context *_ctx,
			 unsigned int *seq_r, int notify)
{
	struct mbox_expunge_context *ctx = (struct mbox_expunge_context *) _ctx;
	struct index_mail *imail = (struct index_mail *) mail;

	if (ctx->from_offset == (uoff_t)-1) {
		if (!get_prev_from_offset(ctx, imail->data.idx_seq))
			return FALSE;
	}

	if (!ctx->expunges) {
		/* first expunged message */
		if (o_stream_seek(ctx->output, ctx->from_offset) < 0)
			return FALSE;
		ctx->expunges = TRUE;
	} else if (ctx->move_offset != ctx->from_offset) {
		if (!mbox_move_data(ctx))
			return FALSE;
	}

	if (!get_from_offset(ctx, imail->data.rec, &ctx->move_offset))
		return FALSE;

	return index_storage_expunge(mail, ctx->ctx, seq_r, notify);
}