view src/lib-storage/index/mbox/mbox-expunge.c @ 903:fd8888f6f037 HEAD

Naming style changes, finally got tired of most of the typedefs. Also the previous enum -> macro change reverted so that we don't use the highest bit anymore, that's incompatible with old indexes so they will be rebuilt.
author Timo Sirainen <tss@iki.fi>
date Sun, 05 Jan 2003 15:09:51 +0200
parents 86cf24da85f1
children 0dc579b99c9e
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

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

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

static int expunge_real(struct index_mailbox *ibox,
			struct mail_index_record *rec, unsigned int seq,
			struct istream *input, struct ostream *output,
			int notify)
{
	uoff_t offset, hdr_size, body_size;
	uoff_t end_offset, from_offset, copy_size, old_limit;
	const unsigned char *data;
	size_t size;
	int expunges, failed;

	if (seq == 1)
		end_offset = 0;
	else {
		/* we need to find offset to beginning of From-line.
		   not the fastest way maybe, but easiest.. */
		rec = ibox->index->lookup(ibox->index, seq-1);
		
		if (!mbox_mail_get_location(ibox->index, rec, &offset,
					    &hdr_size, &body_size))
			return FALSE;
		end_offset = offset + hdr_size + body_size;

		/* get back to the deleted record */
		rec = ibox->index->next(ibox->index, rec);
	}

	old_limit = input->v_limit;

	expunges = FALSE;
	while (rec != NULL) {
		if (!mbox_mail_get_location(ibox->index, rec, &offset,
					    &hdr_size, &body_size))
			return FALSE;

		from_offset = end_offset;
		end_offset = offset + hdr_size + body_size;

		if (rec->msg_flags & MAIL_DELETED) {
			if (!index_expunge_mail(ibox, rec, seq, notify))
				return FALSE;
			seq--;

			if (!expunges) {
				/* first expunged record, seek to position
				   where we want to begin writing */
				if (!o_stream_seek(output, from_offset))
					return FALSE;
				expunges = TRUE;
			}
		} else if (expunges) {
			/* seek to wanted input position, and copy
			   this messages */
			i_assert(input->v_offset <= from_offset);
			i_stream_skip(input, from_offset - input->v_offset);

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

			i_stream_set_read_limit(input, end_offset);
			failed = o_stream_send_istream(output, input) < 0;
			i_stream_set_read_limit(input, old_limit);

			if (failed || input->v_offset != end_offset)
				return FALSE;
		}

		rec = ibox->index->next(ibox->index, rec);
		seq++;
	}

	i_stream_skip(input, end_offset - input->v_offset);

	/* copy the rest as well, should be only \n but someone might
	   as well just appended more data.. but if we've deleted all mail,
	   don't write the only \n there. */
	copy_size = input->v_size - input->v_offset;
	if (output->offset == 0 && copy_size == 1)
		return TRUE;

	return o_stream_send_istream(output, input) >= 0;
}

int mbox_expunge_locked(struct index_mailbox *ibox, int notify)
{
	struct mail_index_record *rec;
	struct istream *input;
	struct ostream *output;
	unsigned int seq;
	int failed;

	if (!index_expunge_seek_first(ibox, &seq, &rec))
		return FALSE;

	if (rec == NULL) {
		/* no deleted messages */
		return TRUE;
	}

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

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

	t_push();
	output = o_stream_create_file(ibox->index->mbox_fd, data_stack_pool,
				      4096, 0, FALSE);
	o_stream_set_blocking(output, 60000, NULL, NULL);

	failed = !expunge_real(ibox, rec, seq, input, output, notify);

	if (failed && 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(output, input);
	}

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

	o_stream_unref(output);
	t_pop();

	return !failed;
}