view src/lib-index/maildir/maildir-build.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 ed0d5b17c7a4
children f25e575bf1ca
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

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

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>

static MailIndexRecord *mail_index_record_append_begin(MailIndex *index,
						       time_t internal_date)
{
	MailIndexRecord trec, *rec;

	memset(&trec, 0, sizeof(MailIndexRecord));
	trec.internal_date = internal_date;

	rec = &trec;
	if (!index->append_begin(index, &rec))
		return NULL;

	return rec;
}

static int maildir_index_append_fd(MailIndex *index, int fd, const char *path,
				   const char *fname)
{
	MailIndexRecord *rec;
	MailIndexUpdate *update;
	struct stat st;
	int failed;

	i_assert(path != NULL);
	i_assert(fname != NULL);

	/* check that file size is somewhat reasonable */
	if (fstat(fd, &st) < 0)
		return index_file_set_syscall_error(index, path, "fstat()");

	if (st.st_size < 10) {
		/* This cannot be a mail file - delete it */
		index_set_error(index, "Invalid size %"PRIuUOFF_T
				" with mail in %s - deleted", st.st_size, path);
		if (unlink(path) < 0)
			index_file_set_syscall_error(index, path, "unlink()");
		return TRUE;
	}

	if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
		return FALSE;

	/* append the file into index */
	rec = mail_index_record_append_begin(index, st.st_mtime);
	if (rec == NULL)
		return FALSE;

	/* set message flags from file name */
	rec->msg_flags = maildir_filename_get_flags(fname, 0);
	mail_index_mark_flag_changes(index, rec, 0, rec->msg_flags);

	update = index->update_begin(index, rec);

	/* set the location */
	index->update_field(update, FIELD_TYPE_LOCATION, fname,
			    MAILDIR_LOCATION_EXTRA_SPACE);

	/* parse the header and update record's fields */
	failed = !maildir_record_update(update, fd, path);

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

	return index->append_end(index, rec);
}

int maildir_index_append_file(MailIndex *index, const char *dir,
			      const char *fname)
{
	const char *path;
	int fd, ret;

	i_assert(index->lock_type != MAIL_LOCK_SHARED);

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

	path = t_strconcat(dir, "/", fname, NULL);
	fd = open(path, O_RDONLY);
	if (fd == -1) {
		/* open() failed - treat it as error unless the error was
		   "file doesn't exist" in which case someone just managed
		   to delete it before we saw it */
		if (errno == EEXIST)
			return TRUE;

		return index_file_set_syscall_error(index, path, "open()");
	}

	ret = maildir_index_append_fd(index, fd, path, fname);
	if (close(fd) < 0)
		return index_file_set_syscall_error(index, path, "close()");
	return ret;
}

int maildir_index_build_dir(MailIndex *index, const char *source_dir,
			    const char *dest_dir)
{
	DIR *dirp;
	const char *final_dir;
	struct dirent *d;
	struct stat st;
	char sourcepath[1024], destpath[1024];
	int failed;

	i_assert(index->lock_type != MAIL_LOCK_SHARED);
	i_assert(source_dir != NULL);

	dirp = opendir(source_dir);
	if (dirp == NULL) {
		return index_file_set_syscall_error(index, source_dir,
						    "opendir()");
	}

	final_dir = dest_dir != NULL ? dest_dir : source_dir;

	failed = FALSE;
	while (!failed && (d = readdir(dirp)) != NULL) {
		if (d->d_name[0] == '.')
			continue;

		if (dest_dir != NULL) {
			/* move the file into dest_dir - abort everything if it
			   already exists, as that should never happen */
			i_snprintf(sourcepath, sizeof(sourcepath), "%s/%s",
				   source_dir, d->d_name);
			i_snprintf(destpath, sizeof(destpath), "%s/%s",
				   dest_dir, d->d_name);
			if (stat(destpath, &st) == 0) {
				index_set_error(index, "Can't move mail %s to "
						"%s: file already exists",
						sourcepath, destpath);
				failed = TRUE;
				break;
			}

			/* race condition here - ignore it as the chance of it
			   happening is pretty much zero */

			if (rename(sourcepath, destpath) < 0) {
				index_set_error(index, "maildir build: "
						"rename(%s, %s) failed: %m",
						sourcepath, destpath);
				failed = TRUE;
				break;
			}
		}

		t_push();
		failed = !maildir_index_append_file(index, final_dir,
						    d->d_name);
		t_pop();
	}

	if (closedir(dirp) < 0)
		index_file_set_syscall_error(index, source_dir, "closedir()");
	return !failed;
}