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; }