diff src/lib-index/mbox/mbox-append.c @ 0:3b1985cbc908 HEAD

Initial revision
author Timo Sirainen <tss@iki.fi>
date Fri, 09 Aug 2002 12:15:38 +0300
parents
children 1b34ec11fff8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-index/mbox/mbox-append.c	Fri Aug 09 12:15:38 2002 +0300
@@ -0,0 +1,269 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "lib.h"
+#include "mmap-util.h"
+#include "ioloop.h"
+#include "mbox-index.h"
+#include "mail-index-util.h"
+
+#include <time.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+static const char *months[] = {
+	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static MailIndexRecord *
+mail_index_record_append(MailIndex *index, time_t internal_date,
+			 size_t full_virtual_size)
+{
+	MailIndexRecord trec, *rec;
+
+	memset(&trec, 0, sizeof(MailIndexRecord));
+	trec.internal_date = internal_date;
+	trec.full_virtual_size = full_virtual_size;
+
+	rec = &trec;
+	if (!index->append(index, &rec))
+		return NULL;
+
+	return rec;
+}
+
+static time_t from_line_parse_date(const char *msg, size_t size)
+{
+	const char *msg_end;
+	struct tm tm;
+	int i;
+
+	/* From <sender> <date> <moreinfo> */
+	if (strncmp(msg, "From ", 5) != 0)
+		return 0;
+
+	msg_end = msg + size;
+
+	/* skip sender */
+	msg += 5;
+	while (*msg != ' ' && msg < msg_end) msg++;
+	while (*msg == ' ' && msg < msg_end) msg++;
+
+	/* next 24 chars are the date in asctime() format,
+	   eg. "Thu Nov 29 22:33:52 2001" */
+	if (msg+24 > msg_end)
+		return 0;
+
+	memset(&tm, 0, sizeof(tm));
+
+	/* skip weekday */
+	msg += 4;
+
+	/* month */
+	for (i = 0; i < 12; i++) {
+		if (strncasecmp(months[i], msg, 3) == 0) {
+			tm.tm_mon = i;
+			break;
+		}
+	}
+
+	if (i == 12 || msg[3] != ' ')
+		return 0;
+	msg += 4;
+
+	/* day */
+	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ' ')
+		return 0;
+	tm.tm_mday = (msg[0]-'0') * 10 + (msg[1]-'0');
+	msg += 3;
+
+	/* hour */
+	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ':')
+		return 0;
+	tm.tm_hour = (msg[0]-'0') * 10 + (msg[1]-'0');
+	msg += 3;
+
+	/* minute */
+	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ':')
+		return 0;
+	tm.tm_min = (msg[0]-'0') * 10 + (msg[1]-'0');
+	msg += 3;
+
+	/* second */
+	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ' ')
+		return 0;
+	tm.tm_sec = (msg[0]-'0') * 10 + (msg[1]-'0');
+	msg += 3;
+
+	/* year */
+	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) ||
+	    !i_isdigit(msg[2]) || !i_isdigit(msg[3]))
+		return 0;
+	tm.tm_year = (msg[0]-'0') * 1000 + (msg[1]-'0') * 100 +
+		(msg[2]-'0') * 10 + (msg[3]-'0') - 1900;
+
+	tm.tm_isdst = -1;
+	return mktime(&tm);
+}
+
+static void header_func(MessagePart *part __attr_unused__,
+			const char *name, unsigned int name_len,
+			const char *value, unsigned int value_len,
+			void *user_data)
+{
+	MailIndexRecord *rec = user_data;
+
+	rec->msg_flags |= mbox_header_get_flags(name, name_len,
+						value, value_len);
+}
+
+static int mbox_index_append_data(MailIndex *index, const char *msg,
+				  off_t offset, size_t physical_size,
+				  size_t virtual_size)
+{
+	MailIndexRecord *rec;
+	MailIndexUpdate *update;
+	time_t internal_date;
+	char location[MAX_INT_STRLEN];
+	unsigned int i;
+
+	internal_date = from_line_parse_date(msg, physical_size);
+	if (internal_date <= 0)
+		internal_date = ioloop_time;
+
+	/* skip the From-line */
+	for (i = 0; i < physical_size; i++) {
+		if (msg[i] == '\n') {
+			i++;
+			break;
+		}
+	}
+
+	if (i == physical_size)
+		return FALSE;
+
+	msg += i;
+	offset += i;
+	physical_size -= i;
+	virtual_size -= i;
+	if (i > 0 && msg[i-1] != '\r')
+		virtual_size--;
+
+	rec = mail_index_record_append(index, internal_date, virtual_size);
+	if (rec == NULL)
+		return FALSE;
+
+	update = index->update_begin(index, rec);
+
+	/* location = offset to beginning of message */
+	i_snprintf(location, sizeof(location), "%lu", (unsigned long) offset);
+	index->update_field(update, FIELD_TYPE_LOCATION, location, 0);
+
+	/* parse the header and add cache wanted fields */
+	mail_index_update_headers(update, msg, physical_size, header_func, rec);
+
+	if (!index->update_end(update)) {
+		/* failed - delete the record */
+		(void)index->expunge(index, rec, 0, FALSE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+int mbox_index_append_mmaped(MailIndex *index, const char *data,
+			     size_t data_size, off_t start_offset)
+{
+	const char *data_start, *data_end, *start, *cr;
+	size_t size, vsize;
+	off_t pos;
+	int missing_cr_count;
+
+	/* we should start with "From ". if we don't, something's messed up
+	   and we should check the whole file instead. */
+	if (strncmp(data, "From ", 5) != 0) {
+		index->set_flags |= MAIL_INDEX_FLAG_FSCK;
+		return FALSE;
+	}
+
+	/* each message ends at "\nFrom ". first get the size of the message,
+	   then parse it. calculate the missing CR count as well. */
+	start = data; cr = NULL; missing_cr_count = 0;
+
+	data_start = data;
+	data_end = data + data_size;
+	for (; data != data_end; data++) {
+		if (*data == '\r')
+			cr = data;
+		else if (*data == '\n') {
+			if (cr != data-1)
+				missing_cr_count++;
+
+			if (data+6 < data_end && data[1] == 'F' &&
+			    data[2] == 'r' && data[3] == 'o' &&
+			    data[4] == 'm' && data[5] == ' ') {
+				/* end of message */
+				pos = (off_t) (start - data_start) +
+					start_offset;
+				size = (size_t) (data - start) + 1;
+				vsize = size + missing_cr_count;
+				if (!mbox_index_append_data(index, start, pos,
+							    size, vsize))
+					return FALSE;
+
+				missing_cr_count = 0;
+				start = data+1;
+			}
+		}
+	}
+
+	/* last message */
+	pos = (off_t) (start - data_start);
+	size = (size_t) (data - start);
+	vsize = size + missing_cr_count;
+	return mbox_index_append_data(index, start, pos, size, vsize);
+}
+
+int mbox_index_append(MailIndex *index, int fd, const char *path)
+{
+	void *mmap_base;
+	size_t mmap_length;
+	off_t pos, end_pos;
+	int ret;
+
+	/* get our current position */
+	pos = lseek(fd, 0, SEEK_CUR);
+
+	/* get the size of the file */
+	end_pos = lseek(fd, 0, SEEK_END);
+
+	if (pos == (off_t)-1 || end_pos == (off_t)-1) {
+		index_set_error(index, "lseek() failed with mbox file %s: %m",
+				path);
+		return FALSE;
+	}
+
+	if (pos == end_pos) {
+		/* no new data */
+		return TRUE;
+	}
+
+	if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
+		return FALSE;
+
+	/* mmap() the file */
+	mmap_length = end_pos-pos;
+	mmap_base = mmap(NULL, mmap_length, PROT_READ, MAP_SHARED, fd, pos);
+	if (mmap_base == MAP_FAILED) {
+		index_set_error(index, "mmap() failed with mbox file %s: %m",
+				path);
+		return FALSE;
+	}
+
+	(void)madvise(mmap_base, mmap_length, MADV_SEQUENTIAL);
+
+	ret = mbox_index_append_mmaped(index, mmap_base, mmap_length, pos);
+	(void)munmap(mmap_base, mmap_length);
+	return ret;
+}