view src/lib-storage/index/mbox/mbox-save.c @ 764:f57c52738f90 HEAD

Renamed IBuffer and OBuffer to IStream and OStream which describes their functionality better. I tried to keep the variable names and comments also sensible.
author Timo Sirainen <tss@iki.fi>
date Fri, 06 Dec 2002 03:09:22 +0200
parents b7aefd0d7611
children 35abd7a5d381
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "hostpid.h"
#include "ostream.h"
#include "write-full.h"
#include "mbox-index.h"
#include "mbox-lock.h"
#include "mbox-storage.h"

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <netdb.h>

static char my_hostdomain[256] = "";

static int write_error(MailStorage *storage, const char *mbox_path)
{
	if (errno == ENOSPC)
		mail_storage_set_error(storage, "Not enough disk space");
	else {
		mail_storage_set_critical(storage,
			"Error writing to mbox file %s: %m", mbox_path);
	}

	return FALSE;
}

static int mbox_seek_to_end(MailStorage *storage, int fd,
			    const char *mbox_path, off_t *pos)
{
	struct stat st;
	char ch;

	if (fstat(fd, &st) < 0) {
		mail_storage_set_critical(storage,
			"fstat() failed for mbox file %s: %m", mbox_path);
		return FALSE;
	}

	*pos = st.st_size;
	if (st.st_size == 0)
		return TRUE;

	if (lseek(fd, st.st_size-1, SEEK_SET) < 0) {
		mail_storage_set_critical(storage,
			"lseek() failed for mbox file %s: %m", mbox_path);
		return FALSE;
	}

	if (read(fd, &ch, 1) != 1) {
		mail_storage_set_critical(storage,
			"read() failed for mbox file %s: %m", mbox_path);
		return FALSE;
	}

	if (ch != '\n') {
		if (write_full(fd, "\n", 1) < 0)
			return write_error(storage, mbox_path);
		*pos += 1;
	}

	return TRUE;
}

static int mbox_append_lf(MailStorage *storage, OStream *output,
			  const char *mbox_path)
{
	if (o_stream_send(output, "\n", 1) < 0)
		return write_error(storage, mbox_path);

	return TRUE;
}

static int write_from_line(MailStorage *storage, OStream *output,
			   const char *mbox_path, time_t internal_date)
{
	const char *sender, *line, *name;
	size_t len;

	if (*my_hostdomain == '\0') {
		struct hostent *hent;

		hostpid_init();
		hent = gethostbyname(my_hostname);

		name = hent != NULL ? hent->h_name : NULL;
		if (name == NULL) {
			/* failed, use just the hostname */
			name = my_hostname;
		}

		strncpy(my_hostdomain, name, 255);
		my_hostdomain[255] = '\0';
	}

	sender = t_strconcat(storage->user, "@", my_hostdomain, NULL);

	/* save in local timezone, no matter what it was given with */
	line = mbox_from_create(sender, internal_date);
	len = strlen(line);

	if (o_stream_send(output, line, len) < 0)
		return write_error(storage, mbox_path);

	return TRUE;
}

static int write_flags(MailStorage *storage, OStream *output,
		       const char *mbox_path,
		       MailFlags flags, const char *custom_flags[])
{
	const char *str;
	unsigned int field;
	int i;

	if (flags == 0)
		return TRUE;

	if (flags & MAIL_SEEN) {
		if (o_stream_send(output, "Status: R\n", 10) < 0)
			return write_error(storage, mbox_path);
	}

	if (flags & (MAIL_ANSWERED|MAIL_DRAFT|MAIL_FLAGGED|MAIL_DELETED)) {
		str = t_strconcat("X-Status: ",
				  (flags & MAIL_ANSWERED) ? "A" : "",
				  (flags & MAIL_DRAFT) ? "D" : "",
				  (flags & MAIL_FLAGGED) ? "F" : "",
				  (flags & MAIL_DELETED) ? "T" : "",
				  "\n", NULL);

		if (o_stream_send(output, str, strlen(str)) < 0)
			return write_error(storage, mbox_path);
	}

	if (flags & MAIL_CUSTOM_FLAGS_MASK) {
		if (o_stream_send(output, "X-Keywords:", 11) < 0)
			return write_error(storage, mbox_path);

		field = 1 << MAIL_CUSTOM_FLAG_1_BIT;
		for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++, field <<= 1) {
			if ((flags & field) && custom_flags[i] != NULL) {
				if (o_stream_send(output, " ", 1) < 0)
					return write_error(storage, mbox_path);

				if (o_stream_send(output, custom_flags[i],
						  strlen(custom_flags[i])) < 0)
					return write_error(storage, mbox_path);
			}
		}

		if (o_stream_send(output, "\n", 1) < 0)
			return write_error(storage, mbox_path);
	}

	return TRUE;
}

int mbox_storage_save(Mailbox *box, MailFlags flags, const char *custom_flags[],
		      time_t internal_date, int timezone_offset __attr_unused__,
		      IStream *data, uoff_t data_size)
{
	IndexMailbox *ibox = (IndexMailbox *) box;
	MailIndex *index;
	MailFlags real_flags;
	const char *mbox_path;
	OStream *output;
	int failed;
	off_t pos;

	if (box->readonly) {
		mail_storage_set_error(box->storage, "Mailbox is read-only");
		return FALSE;
	}

	/* we don't need the real flag positions, easier to keep using our own.
	   they need to be checked/added though. */
	real_flags = flags;
	if (!index_mailbox_fix_custom_flags(ibox, &real_flags, custom_flags))
		return FALSE;

	if (!index_storage_sync_and_lock(ibox, FALSE, MAIL_LOCK_EXCLUSIVE))
		return FALSE;

	index = ibox->index;
	mbox_path = index->mbox_path;
	if (!mbox_seek_to_end(box->storage, index->mbox_fd, mbox_path, &pos))
		failed = TRUE;
	else {
		failed = FALSE;

		t_push();
		output = o_stream_create_file(index->mbox_fd,
					      data_stack_pool, 4096,
					      0, FALSE);
		o_stream_set_blocking(output, 60000, NULL, NULL);

		if (!write_from_line(box->storage, output, mbox_path,
				     internal_date) ||
		    !write_flags(box->storage, output, mbox_path, flags,
				 custom_flags) ||
		    !index_storage_save(box->storage, mbox_path,
					data, output, data_size) ||
		    !mbox_append_lf(box->storage, output, mbox_path)) {
			/* failed, truncate file back to original size.
			   output stream needs to be flushed before truncating
			   so unref() won't write anything. */
			o_stream_flush(output);
			(void)ftruncate(index->mbox_fd, pos);
			failed = TRUE;
		}
		o_stream_unref(output);
		t_pop();
	}

	/* kludgy.. for copying inside same mailbox. */
	if (!ibox->delay_save_unlocking) {
		if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
			return FALSE;
	}

	return !failed;
}