view src/lib-storage/index/index-messageset.c @ 222:cf4d065f2f85 HEAD

lots of cleanups. also index/datafile is now capable of staying in memory, as long as it's noticed while opening the index.
author Timo Sirainen <tss@iki.fi>
date Sat, 14 Sep 2002 14:09:42 +0300
parents 4716cf66c2cc
children 276b7a53c264
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "mail-index.h"
#include "mail-index-util.h"
#include "mail-hash.h"
#include "mail-modifylog.h"
#include "index-messageset.h"

static unsigned int get_next_number(const char **str)
{
	unsigned int num;

	num = 0;
	while (**str != '\0') {
		if (**str < '0' || **str > '9')
			break;

		num = num*10 + (**str - '0');
		(*str)++;
	}

	return num;
}

static int mail_index_foreach(MailIndex *index,
			      unsigned int seq, unsigned int seq2,
			      MsgsetForeachFunc func, void *context,
			      const char **error)
{
	MailIndexRecord *rec;
	const unsigned int *expunges;
	unsigned int expunges_before;
	int expunges_found;

	if (seq > seq2) {
		/* Second sequence can't be smaller than first - we could swap
		   them but I think it's a bug in client if it does this,
		   and better complain about it immediately than later let
		   them wonder why it doesn't work with other imapds.. */
		*error = t_strdup_printf("Invalid messageset range: %u > %u",
					 seq, seq2);
		return -2;
	}

	/* get list of expunged messages in our range. the expunges_before
	   can be used to calculate the current real sequence position */
	expunges = mail_modifylog_seq_get_expunges(index->modifylog, seq, seq2,
						   &expunges_before);
	i_assert(expunges_before < seq);
	expunges_found = *expunges != '\0';

	/* Reset index errors, since we later rely on it to check if failed */
	index_reset_error(index);

	/* get the first non-expunged message. note that if all messages
	   were expunged in the range, this points outside wanted range. */
	rec = index->lookup(index, seq - expunges_before);
	for (; rec != NULL; seq++) {
		/* skip expunged sequences */
		i_assert(rec->uid != 0);

		while (*expunges != 0 && *expunges < rec->uid) {
			expunges++;
			seq++;
		}
		i_assert(*expunges != rec->uid);

		if (seq > seq2)
			break;

		t_push();
		if (!func(index, rec, seq, context)) {
			t_pop();
			return 0;
		}
		t_pop();

		rec = index->next(index, rec);
	}

	if (rec == NULL && index->get_last_error(index) != NULL) {
		/* error occured */
		return -1;
	}

	return !expunges_found && seq > seq2 ? 1 : 2;
}

static int mail_index_messageset_foreach(MailIndex *index,
					 const char *messageset,
					 unsigned int messages_count,
					 MsgsetForeachFunc func, void *context,
					 const char **error)
{
	const char *input;
	unsigned int seq, seq2;
	int ret, all_found;

	i_assert(index->lock_type != MAIL_LOCK_UNLOCK);

	*error = NULL;
	if (messages_count == 0) {
		/* no messages in mailbox */
		return 1;
	}

	all_found = TRUE;
	input = messageset;
	while (*input != '\0') {
		if (*input == '*') {
			/* last message */
			seq = messages_count;
			input++;
		} else {
			seq = get_next_number(&input);
			if (seq == 0) {
				*error = t_strconcat("Invalid messageset: ",
						     messageset, NULL);
				return -2;
			}
		}

		if (*input != ':')
			seq2 = seq;
		else {
			/* first:last range */
			input++;

			if (*input != '*') {
				seq2 = get_next_number(&input);
				if (seq2 == 0) {
					*error = t_strconcat("Invalid "
							     "messageset: ",
							     messageset, NULL);
					return -2;
				}

				if (seq2 > messages_count) {
					/* too large .. ignore silently */
					seq2 = messages_count;
				}
			} else {
				seq2 = messages_count;
				input++;
			}
		}

		if (*input == ',')
			input++;
		else if (*input != '\0') {
			*error = t_strdup_printf("Unexpected char '%c' "
						 "with messageset: %s",
						 *input, messageset);
			return -2;
		}

		if (seq > messages_count) {
			/* too large .. ignore silently */
		} else {
			ret = mail_index_foreach(index, seq, seq2,
						 func, context, error);
			if (ret <= 0)
				return ret;
			if (ret == 2)
				all_found = FALSE;
		}
	}

	return all_found ? 1 : 2;
}

static int mail_index_uid_foreach(MailIndex *index,
				  unsigned int uid, unsigned int uid2,
				  MsgsetForeachFunc func, void *context,
				  const char **error)
{
	MailIndexRecord *rec;
	const unsigned int *expunges;
	unsigned int seq;
	int expunges_found;

	if (uid > uid2) {
		/* not allowed - see mail_index_foreach() */
		*error = t_strdup_printf("Invalid uidset range: %u > %u",
					 uid, uid2);
		return -2;
	}

	/* get list of expunged messages in our range. */
	expunges = mail_modifylog_uid_get_expunges(index->modifylog, uid, uid2);
	expunges_found = *expunges != '\0';

	/* skip expunged messages at the beginning */
	while (*expunges == uid) {
		expunges++;

		if (uid++ == uid2) {
			/* all were expunged */
			return 2;
		}
	}

	rec = index->lookup_uid_range(index, uid, uid2);
	if (rec == NULL)
		return expunges_found ? 2 : 1;

	seq = index->get_sequence(index, rec);
	while (rec != NULL && rec->uid <= uid2) {
		uid = rec->uid;
		while (*expunges != 0 && *expunges < rec->uid) {
			expunges++;
			seq++;
		}
		i_assert(*expunges != rec->uid);

		t_push();
		if (!func(index, rec, seq, context)) {
			t_pop();
			return 0;
		}
		t_pop();

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

	if (rec == NULL && index->get_last_error(index) != NULL) {
		/* error occured */
		return -1;
	}

	return expunges_found ? 2 : 1;
}

static int mail_index_uidset_foreach(MailIndex *index, const char *uidset,
				     unsigned int messages_count,
				     MsgsetForeachFunc func, void *context,
				     const char **error)
{
	MailIndexRecord *rec;
	const char *input;
	unsigned int uid, uid2;
	int ret, all_found;

	i_assert(index->lock_type != MAIL_LOCK_UNLOCK);

	*error = NULL;

	all_found = TRUE;
	input = uidset;
	while (*input != '\0') {
		if (*input == '*') {
			/* last message */
			if (messages_count == 0)
				uid = 0;
			else {
				rec = index->lookup(index, messages_count);
				uid = rec == NULL ? 0 : rec->uid;
			}
			input++;
		} else {
			uid = get_next_number(&input);
			if (uid == 0) {
				*error = t_strconcat("Invalid uidset: ",
						     uidset, NULL);
				return -2;
			}
		}

		if (*input != ':')
			uid2 = uid;
		else {
			/* first:last range */
			input++;

			if (*input != '*') {
				uid2 = get_next_number(&input);
				if (uid2 == 0) {
					*error = t_strconcat("Invalid uidset: ",
							     uidset, NULL);
					return -2;
				}
			} else {
				uid2 = index->header->next_uid-1;
				if (uid2 < uid) {
					/* allow requesting "n:*" where n is
					   larger than the actual (synced)
					   messages count */
					uid2 = uid;
				}
				input++;
			}
		}

		if (*input == ',')
			input++;
		else if (*input != '\0') {
			*error = t_strdup_printf("Unexpected char '%c' with "
						 "uidset: %s", *input, uidset);
			return -2;
		}

		if (uid >= index->header->next_uid) {
			/* too large .. ignore silently */
		} else {
			ret = mail_index_uid_foreach(index, uid, uid2,
						     func, context, error);
			if (ret <= 0)
				return ret;
			if (ret == 2)
				all_found = FALSE;
		}
	}

	return all_found ? 1 : 2;
}

int index_messageset_foreach(IndexMailbox *ibox,
			     const char *messageset, int uidset,
			     MsgsetForeachFunc func, void *context)
{
	const char *error;
	int ret;

	if (uidset) {
		ret = mail_index_uidset_foreach(ibox->index, messageset,
						ibox->synced_messages_count,
						func, context, &error);
	} else {
		ret = mail_index_messageset_foreach(ibox->index, messageset,
						    ibox->synced_messages_count,
						    func, context, &error);
	}

	if (ret < 0) {
		if (ret == -2) {
			/* user error */
			mail_storage_set_error(ibox->box.storage, "%s", error);
		} else {
			mail_storage_set_index_error(ibox);
		}
	}

	return ret;
}