view src/lib-index/mail-messageset.c @ 94:afa320f00439 HEAD

added t_push() + t_pop() around the foreach-function call.
author Timo Sirainen <tss@iki.fi>
date Thu, 29 Aug 2002 02:14:08 +0300
parents d493b9cc265e
children 8d07a46abfdd
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 "mail-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))
			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;
}

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,
				  unsigned int max_sequence,
				  MsgsetForeachFunc func, void *context,
				  const char **error)
{
	MailIndexRecord *rec;
	uoff_t pos;
	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;
		}
	}

	/* since we skipped the known expunged messages at the beginning
	   and our UIDs are contiguously allocated, the first hash lookup
	   _should_ work.. */
	pos = mail_hash_lookup_uid(index->hash, uid);
	if (pos != 0) {
		if (pos + sizeof(MailIndexRecord) > index->mmap_length) {
			/* hash is corrupted */
			index_set_error(index, "Corrupted hash for index %s: "
					"lookup returned offset outside range",
					index->filepath);

			if (!mail_hash_rebuild(index->hash))
				return -1;

			/* lets try again */
			pos = mail_hash_lookup_uid(index->hash, uid);
			if (pos + sizeof(MailIndexRecord) > index->mmap_length)
				return -1;
		}

		rec = (MailIndexRecord *) ((char *) index->mmap_base + pos);
	} else {
		/* ..however if for any reason it doesn't,
		   still handle it properly */
		if (uid == uid2)
			return 2;

		rec = index->lookup_uid_range(index, uid+1, uid2);
		if (rec == NULL)
			return 2;
	}

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

		t_push();
		if (!func(index, rec, seq, context))
			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 && uid == uid2 ? 1 : 2;
}

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;
	if (messages_count == 0) {
		/* no messages in mailbox */
		return 1;
	}

	all_found = TRUE;
	input = uidset;
	while (*input != '\0') {
		if (*input == '*') {
			/* last message */
			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;
				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,
						     messages_count,
						     func, context, error);
			if (ret <= 0)
				return ret;
			if (ret == 2)
				all_found = FALSE;
		}
	}

	return all_found ? 1 : 2;
}