view src/lib-index/maildir/maildir-index.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 41ec8cadd238
children 40629cdd51fb
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "str.h"
#include "maildir-index.h"
#include "mail-index-data.h"
#include "mail-index-util.h"

#include <stdio.h>
#include <sys/stat.h>

extern struct mail_index maildir_index;

enum mail_flags maildir_filename_get_flags(const char *fname,
					   enum mail_flags default_flags)
{
	const char *info;
	enum mail_flags flags;

	info = strchr(fname, ':');
	if (info == NULL || info[1] != '2' || info[2] != ',')
		return default_flags;

	flags = 0;
	for (info += 3; *info != '\0' && *info != ','; info++) {
		switch (*info) {
		case 'R': /* replied */
			flags |= MAIL_ANSWERED;
			break;
		case 'S': /* seen */
			flags |= MAIL_SEEN;
			break;
		case 'T': /* trashed */
			flags |= MAIL_DELETED;
			break;
		case 'D': /* draft */
			flags |= MAIL_DRAFT;
			break;
		case 'F': /* flagged */
			flags |= MAIL_FLAGGED;
			break;
		default:
			if (*info >= 'a' && *info <= 'z') {
				/* custom flag */
				flags |= 1 << (MAIL_CUSTOM_FLAG_1_BIT +
					       *info-'a');
				break;
			}

			/* unknown flag - ignore */
			break;
		}
	}

	return flags;
}

const char *maildir_filename_set_flags(const char *fname, enum mail_flags flags)
{
	string_t *flags_str;
	const char *info, *oldflags;
	int i, nextflag;

	/* remove the old :info from file name, and get the old flags */
	info = strrchr(fname, ':');
	if (info != NULL && strrchr(fname, '/') > info)
		info = NULL;

	oldflags = "";
	if (info != NULL) {
		fname = t_strdup_until(fname, info);
		if (info[1] == '2' && info[2] == ',')
			oldflags = info+3;
	}

	/* insert the new flags between old flags. flags must be sorted by
	   their ASCII code. unknown flags are kept. */
	flags_str = t_str_new(256);
	str_append(flags_str, fname);
	str_append(flags_str, ":2,");
	for (;;) {
		/* skip all known flags */
		while (*oldflags == 'D' || *oldflags == 'F' ||
		       *oldflags == 'R' || *oldflags == 'S' ||
		       *oldflags == 'T' ||
		       (*oldflags >= 'a' && *oldflags <= 'z'))
			oldflags++;

		nextflag = *oldflags == '\0' || *oldflags == ',' ? 256 :
			(unsigned char) *oldflags;

		if ((flags & MAIL_DRAFT) && nextflag > 'D') {
			str_append_c(flags_str, 'D');
			flags &= ~MAIL_DRAFT;
		}
		if ((flags & MAIL_FLAGGED) && nextflag > 'F') {
			str_append_c(flags_str, 'F');
			flags &= ~MAIL_FLAGGED;
		}
		if ((flags & MAIL_ANSWERED) && nextflag > 'R') {
			str_append_c(flags_str, 'R');
			flags &= ~MAIL_ANSWERED;
		}
		if ((flags & MAIL_SEEN) && nextflag > 'S') {
			str_append_c(flags_str, 'S');
			flags &= ~MAIL_SEEN;
		}
		if ((flags & MAIL_DELETED) && nextflag > 'T') {
			str_append_c(flags_str, 'T');
			flags &= ~MAIL_DELETED;
		}

		if ((flags & MAIL_CUSTOM_FLAGS_MASK) && nextflag > 'a') {
			for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++) {
				if (flags & (1 << (i + MAIL_CUSTOM_FLAG_1_BIT)))
					str_append_c(flags_str, 'a' + i);
			}
			flags &= ~MAIL_CUSTOM_FLAGS_MASK;
		}

		if (*oldflags == '\0' || *oldflags == ',')
			break;

		str_append_c(flags_str, *oldflags);
		oldflags++;
	}

	if (*oldflags == ',') {
		/* another flagset, we don't know about these, just keep them */
		while (*oldflags != '\0')
			str_append_c(flags_str, *oldflags++);
	}

	return str_c(flags_str);
}

struct mail_index *maildir_index_alloc(const char *dir, const char *maildir)
{
	struct mail_index *index;

	i_assert(dir != NULL);
	i_assert(maildir != NULL);

	index = i_new(struct mail_index, 1);
	memcpy(index, &maildir_index, sizeof(struct mail_index));

	index->mailbox_path = i_strdup(maildir);
	mail_index_init(index, dir);
	return index;
}

static void maildir_index_free(struct mail_index *index)
{
	mail_index_close(index);
	i_free(index->dir);
	i_free(index->mailbox_path);
	i_free(index);
}

static time_t maildir_get_internal_date(struct mail_index *index,
					struct mail_index_record *rec)
{
	struct stat st;
	const char *fname;
	time_t date;

	/* try getting it from cache */
	date = mail_get_internal_date(index, rec);
	if (date != (time_t)-1)
		return date;

	/* stat() gives it */
	fname = index->lookup_field(index, rec, DATA_FIELD_LOCATION);
	if (fname == NULL) {
		index_data_set_corrupted(index->data,
			"Missing location field for record %u", rec->uid);
		return (time_t)-1;
	}

	if (stat(fname, &st) < 0) {
		index_file_set_syscall_error(index, fname, "stat()");
		return (time_t)-1;
	}

	return st.st_mtime;
}

static int maildir_index_update_flags(struct mail_index *index,
				      struct mail_index_record *rec,
				      unsigned int seq, enum mail_flags flags,
				      int external_change)
{
	struct mail_index_update *update;
	const char *old_fname, *new_fname;
	const char *old_path, *new_path;

	/* we need to update the flags in the file name */
	old_fname = index->lookup_field(index, rec, DATA_FIELD_LOCATION);
	if (old_fname == NULL) {
		index_data_set_corrupted(index->data,
			"Missing location field for record %u", rec->uid);
		return FALSE;
	}

	new_fname = maildir_filename_set_flags(old_fname, flags);

	if (strcmp(old_fname, new_fname) != 0) {
		old_path = t_strconcat(index->mailbox_path,
				       "/cur/", old_fname, NULL);
		new_path = t_strconcat(index->mailbox_path,
				       "/cur/", new_fname, NULL);

		/* minor problem: new_path is overwritten if it exists.. */
		if (rename(old_path, new_path) < 0) {
			if (errno == ENOSPC)
				index->nodiskspace = TRUE;

			index_set_error(index, "maildir flags update: "
					"rename(%s, %s) failed: %m",
					old_path, new_path);
			return FALSE;
		}

		/* update the filename in index */
		update = index->update_begin(index, rec);
		index->update_field(update, DATA_FIELD_LOCATION, new_fname, 0);

		if (!index->update_end(update))
			return FALSE;
	}

	if (!mail_index_update_flags(index, rec, seq, flags, external_change))
		return FALSE;

	return TRUE;
}

struct mail_index maildir_index = {
	mail_index_open,
	mail_index_open_or_create,
	maildir_index_free,
	mail_index_set_lock,
	mail_index_try_lock,
        mail_index_set_lock_notify_callback,
	maildir_index_rebuild,
	mail_index_fsck,
	maildir_index_sync,
	mail_index_get_header,
	mail_index_lookup,
	mail_index_next,
        mail_index_lookup_uid_range,
	mail_index_lookup_field,
	mail_index_lookup_field_raw,
	mail_index_cache_fields_later,
	maildir_open_mail,
	maildir_get_internal_date,
	mail_index_expunge,
	maildir_index_update_flags,
	mail_index_append_begin,
	mail_index_append_end,
	mail_index_update_begin,
	mail_index_update_end,
	mail_index_update_field,
	mail_index_update_field_raw,
	mail_index_get_last_error,
	mail_index_get_last_error_text,

	MAIL_INDEX_PRIVATE_FILL
};