changeset 198:e6e5fd3d4718 HEAD

EXPUNGE works with mbox. finally.
author Timo Sirainen <tss@iki.fi>
date Mon, 09 Sep 2002 10:18:50 +0300
parents 38d77994baa5
children cf1279829ad1
files src/lib-storage/index/mbox/mbox-expunge.c
diffstat 1 files changed, 127 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/mbox/mbox-expunge.c	Mon Sep 09 10:18:37 2002 +0300
+++ b/src/lib-storage/index/mbox/mbox-expunge.c	Mon Sep 09 10:18:50 2002 +0300
@@ -1,22 +1,47 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "iobuffer.h"
+#include "mbox-index.h"
 #include "mbox-storage.h"
+#include "mbox-lock.h"
 
-int mbox_expunge_locked(IndexMailbox *ibox,
+#include <fcntl.h>
+#include <unistd.h>
+
+static int expunge_real(IndexMailbox *ibox, MailIndexRecord *rec,
+			unsigned int seq, IOBuffer *inbuf, IOBuffer *outbuf,
 			MailExpungeFunc expunge_func, void *context)
 {
-	MailIndexRecord *rec;
-	unsigned int seq, uid;
+	uoff_t offset, end_offset, from_offset, copy_size;
+	unsigned int uid;
+	unsigned char *data;
+	size_t size;
+	int expunges;
 
-	/* FIXME: open the mbox file, lock it, and remove the deleted
-	   blocks. probably better to do it in small blocks than to
-	   memmove() megabytes of data.. */
+	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_start_offset(ibox->index, rec, &offset))
+			return FALSE;
+		end_offset = offset + rec->header_size + rec->body_size;
 
-	if (!index_expunge_seek_first(ibox, &seq, &rec))
-		return FALSE;
+		/* get back to the deleted record */
+		rec = ibox->index->next(ibox->index, rec);
+	}
 
+	expunges = FALSE;
 	while (rec != NULL) {
+		if (!mbox_mail_get_start_offset(ibox->index, rec, &offset))
+			return FALSE;
+
+		from_offset = end_offset;
+		end_offset = offset + rec->header_size + rec->body_size;
+
 		if (rec->msg_flags & MAIL_DELETED) {
 			/* save UID before deletion */
 			uid = rec->uid;
@@ -28,10 +53,103 @@
 			if (expunge_func != NULL)
 				expunge_func(&ibox->box, seq, uid, context);
 			seq--;
+
+			if (!expunges) {
+				/* first expunged record, seek to position
+				   where we want to begin writing */
+				if (!io_buffer_seek(outbuf, from_offset))
+					return FALSE;
+				expunges = TRUE;
+			}
+		} else if (expunges) {
+			/* seek to wanted input position, and copy
+			   this messages */
+			i_assert(inbuf->offset <= from_offset);
+			io_buffer_skip(inbuf, from_offset - inbuf->offset);
+
+			if (outbuf->offset == 0) {
+				/* we're writing to beginning of mbox, so we
+				   don't want the [\r]\n there */
+				while (io_buffer_read_data(inbuf, &data,
+							   &size, 1) == 0) ;
+				if (size > 0 && data[0] == '\n')
+					io_buffer_skip(inbuf, 1);
+				else if (size > 1 && data[0] == '\r' &&
+					 data[1] == '\n')
+					io_buffer_skip(inbuf, 2);
+			}
+
+                        copy_size = end_offset - inbuf->offset;
+			if (io_buffer_send_iobuffer(outbuf, inbuf,
+						    copy_size) < 0)
+				return FALSE;
 		}
+
 		rec = ibox->index->next(ibox->index, rec);
 		seq++;
 	}
 
-	return TRUE;
+	/* copy the rest as well, should be only \n but someone might
+	   as well just appended more data.. */
+	copy_size = inbuf->size - inbuf->offset;
+	return io_buffer_send_iobuffer(outbuf, inbuf, copy_size) > 0;
 }
+
+int mbox_expunge_locked(IndexMailbox *ibox,
+			MailExpungeFunc expunge_func, void *context)
+{
+	MailIndexRecord *rec;
+	IOBuffer *inbuf, *outbuf;
+	unsigned int seq;
+	int fd, failed;
+
+	if (!index_expunge_seek_first(ibox, &seq, &rec))
+		return FALSE;
+
+	if (rec == NULL) {
+		/* no deleted messages */
+		return TRUE;
+	}
+
+	fd = open(ibox->index->mbox_path, O_RDWR);
+	if (fd == -1) {
+		mail_storage_set_error(ibox->box.storage,
+				       "Error opening mbox file %s: %m",
+				       ibox->index->mbox_path);
+		return FALSE;
+	}
+
+	if (!mbox_lock(ibox->index, ibox->index->mbox_path, fd)) {
+		(void)close(fd);
+		return FALSE;
+	}
+
+	inbuf = io_buffer_create_mmap(fd, default_pool,
+				      MAIL_MMAP_BLOCK_SIZE, 0);
+	outbuf = io_buffer_create_file(fd, default_pool, 4096);
+
+	failed = !expunge_real(ibox, rec, seq, inbuf, outbuf,
+			       expunge_func, context);
+
+	if (failed && outbuf->offset > 0) {
+		/* we moved some of the data. move the rest as well so there
+		   won't be invalid holes in mbox file */
+		i_assert(inbuf->offset <= inbuf->size);
+		(void)io_buffer_send_iobuffer(outbuf, inbuf,
+					      inbuf->size - inbuf->offset);
+	}
+
+	if (ftruncate(outbuf->fd, outbuf->offset) < 0) {
+		mail_storage_set_error(ibox->box.storage, "ftruncate() failed "
+				       "for mbox file %s: %m",
+				       ibox->index->mbox_path);
+		failed = TRUE;
+	}
+
+	(void)mbox_unlock(ibox->index, ibox->index->mbox_path, fd);
+	(void)close(fd);
+	io_buffer_destroy(inbuf);
+	io_buffer_destroy(outbuf);
+
+	return !failed;
+}