On Thu, 2010-11-25 at 15:33 +0000, Timo Sirainen wrote:

> Attached a program to de-maildirify a dbox. You have to run it
> separately for each directory containing dbox-Mails:

Minor update: it shouldn't warn about non-existing maildir files.
/*
   export DOVECOT=~/cvs/dovecot-2.0
   gcc -g -Wall -I$DOVECOT -I$DOVECOT/src/lib \
     -I$DOVECOT/src/lib-storage/index/dbox-common -DHAVE_CONFIG_H \
     dbox-dehybrid.c -o dbox-dehybrid $DOVECOT/src/lib/.libs/liblib.a
*/

#include "lib.h"
#include "array.h"
#include "str.h"
#include "strnum.h"
#include "hex-dec.h"
#include "istream.h"
#include "istream-crlf.h"
#include "ostream.h"
#include "dbox-file.h"

#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <utime.h>
#include <sys/stat.h>

struct mail {
	const char *fname;
	uint32_t uid;
};

static void dbox_header_write(struct ostream *output)
{
	const char *hdr;

	hdr = t_strdup_printf("%u %c%x %c%x\n", DBOX_VERSION,
			      DBOX_HEADER_MSG_HEADER_SIZE,
			      (unsigned int)sizeof(struct dbox_message_header),
			      DBOX_HEADER_CREATE_STAMP,
			      (unsigned int)time(NULL));
	o_stream_send_str(output, hdr);
}

static void dbox_msg_header_write(struct ostream *output, uoff_t message_size)
{
	struct dbox_message_header dbox_msg_hdr;

	memset(&dbox_msg_hdr, ' ', sizeof(dbox_msg_hdr));
	memcpy(dbox_msg_hdr.magic_pre, DBOX_MAGIC_PRE,
	       sizeof(dbox_msg_hdr.magic_pre));
	dbox_msg_hdr.type = DBOX_MESSAGE_TYPE_NORMAL;
	dec2hex(dbox_msg_hdr.message_size_hex, message_size,
		sizeof(dbox_msg_hdr.message_size_hex));
	dbox_msg_hdr.save_lf = '\n';
	o_stream_send(output, &dbox_msg_hdr, sizeof(dbox_msg_hdr));
}

static void dbox_write_metadata(struct ostream *output, time_t received_date,
				uoff_t vsize, const char *guid)
{
	struct dbox_metadata_header metadata_hdr;
	string_t *str;

	memset(&metadata_hdr, 0, sizeof(metadata_hdr));
	memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST,
	       sizeof(metadata_hdr.magic_post));
	o_stream_send(output, &metadata_hdr, sizeof(metadata_hdr));

	str = t_str_new(256);
	str_printfa(str, "%c%lx\n", DBOX_METADATA_RECEIVED_TIME,
		    (unsigned long)received_date);
	str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
		    (unsigned long long)vsize);
	str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid);
	str_append_c(str, '\n');
	o_stream_send(output, str_data(str), str_len(str));
}

static int dbox_convert(const struct mail *mail, const char *dir)
{
	const char *srcpath, *destpath;
	struct istream *input, *crlf;
	struct ostream *output;
	size_t size;
	uoff_t vsize;
	struct stat st;
	int fd, ret = -1;

	srcpath = t_strdup_printf("%s/%s", dir, mail->fname);
	destpath = t_strdup_printf("%s/u.%u", dir, mail->uid);
	if (stat(destpath, &st) == 0) {
		if (stat(srcpath, &st) < 0 && errno == ENOENT)
			return 0;
		i_warning("%s exists already, skipping %s", destpath, srcpath);
		return -1;
	}

	if (stat(srcpath, &st) < 0)
		i_fatal("stat(%s) failed: %m", srcpath);

	fd = creat(destpath, st.st_mode);
	if (fd == -1)
		i_fatal("creat(%s) failed: %m", destpath);
	output = o_stream_create_fd_file(fd, 0, TRUE);

	fd = open(srcpath, O_RDONLY);
	if (fd == -1)
		i_fatal("open(%s) failed: %m", srcpath);
	input = i_stream_create_fd(fd, (size_t)-1, TRUE);

	vsize = 0;
	crlf = i_stream_create_crlf(input);
	while (i_stream_read(crlf) > 0) {
		i_stream_get_data(crlf, &size);
		i_stream_skip(crlf, size);
		vsize += size;
	}
	i_stream_destroy(&crlf);
	i_stream_seek(input, 0);

	dbox_header_write(output);
	dbox_msg_header_write(output, st.st_size);
	o_stream_send_istream(output, input);
	i_assert(i_stream_is_eof(input));
	dbox_write_metadata(output, st.st_mtime, vsize,
			    t_strcut(mail->fname, ':'));

	if (output->last_failed_errno != 0) {
		errno = output->last_failed_errno;
		i_error("write(%s) failed: %m", destpath);
		unlink(destpath);
	} else {
		struct utimbuf ut;

		ut.actime = time(NULL);
		ut.modtime = st.st_mtime;
		utime(destpath, &ut);
		unlink(srcpath);
		ret = 0;
	}

	i_stream_destroy(&input);
	o_stream_destroy(&output);
	return ret;
}

int main(int argc, char *argv[])
{
	ARRAY_DEFINE(mails, struct mail);
	const struct mail *mail;
	struct mail newmail;
	const char *dir = argv[1], *path, *line;
	struct istream *input;
	int fd, ret = 0;

	lib_init();

	path = t_strconcat(dir, "/dbox.index", NULL);
	t_array_init(&mails, 100);
	fd = open(path, O_RDONLY);
	if (fd == -1)
		i_fatal("open(%s) failed: %m", path);
	input = i_stream_create_fd(fd, (size_t)-1, TRUE);
	(void)i_stream_read_next_line(input);
	while ((line = i_stream_read_next_line(input)) != NULL) {
		const char *const *args = t_strsplit(line, " ");

		i_assert(args[3][0] == ':');
		if (str_to_uint32(args[2], &newmail.uid) < 0)
			i_unreached();
		newmail.fname = t_strdup(args[3] + 1);
		array_append(&mails, &newmail, 1);
	}
	i_stream_destroy(&input);

	array_foreach(&mails, mail) {
		T_BEGIN {
			if (dbox_convert(mail, dir) < 0)
				ret = -1;
		} T_END;
	}

	if (ret == 0) {
		unlink(t_strconcat(dir, "/dbox.index", NULL));
		unlink(t_strconcat(dir, "/dovecot-uidlist", NULL));
		unlink(t_strconcat(dir, "/dovecot-keywords", NULL));
	}

	return 0;
}

Reply via email to