From 4e0840f8485a6f52ecd316159e22429726b90725 Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Fri, 10 Apr 2020 16:44:04 -0400 Subject: Concatenate mbox threads --- archive.c | 1 + concat.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ export.c | 12 +----------- imap.h | 10 ++++++++++ 4 files changed, 73 insertions(+), 11 deletions(-) diff --git a/archive.c b/archive.c index 93f8355..c84b74e 100644 --- a/archive.c +++ b/archive.c @@ -181,6 +181,7 @@ int main(int argc, char *argv[]) { } createDir("UID"); createDir("message"); + createDir("thread"); threads = resp.data; resp.data = (struct List) {0}; if (exportFetch(imap, export, threads)) { diff --git a/concat.c b/concat.c index 26f24f6..2c58d8a 100644 --- a/concat.c +++ b/concat.c @@ -16,9 +16,11 @@ #include #include +#include #include #include #include +#include #include #include "archive.h" @@ -50,6 +52,43 @@ void concatFetch(FILE *imap, enum Atom tag, struct List threads) { fprintf(imap, " (UID ENVELOPE)\r\n"); } +static const char *uidPath(uint32_t uid, const char *type) { + static char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "UID/%" PRIu32 ".%s", uid, type); + return buf; +} + +static const char *threadPath(const char *messageID, const char *type) { + static char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "thread/%s.%s", messageID, type); + return buf; +} + +static time_t uidNewest(struct List uids, const char *type) { + time_t newest = 0; + for (size_t i = 0; i < uids.len; ++i) { + uint32_t uid = dataCheck(uids.ptr[i], Number).number; + const char *path = uidPath(uid, type); + struct stat file; + int error = stat(path, &file); + if (error) err(EX_DATAERR, "%s", path); + if (file.st_mtime > newest) newest = file.st_mtime; + } + return newest; +} + +static int concatFile(FILE *dst, const char *path) { + char buf[4096]; + FILE *src = fopen(path, "r"); + if (!src) err(EX_DATAERR, "%s", path); + for (size_t len; (len = fread(buf, 1, sizeof(buf), src));) { + size_t n = fwrite(buf, len, 1, dst); + if (!n) return -1; + } + fclose(src); + return 0; +} + void concatData(struct List threads, struct List items) { uint32_t uid = 0; struct Envelope envelope = {0}; @@ -68,4 +107,26 @@ void concatData(struct List threads, struct List items) { if (!envelope.subject) errx(EX_PROTOCOL, "missing ENVELOPE data item"); struct List thread = threadFind(threads, uid); + struct List flat = {0}; + listFlatten(&flat, thread); + + int error; + const char *path; + struct stat file; + + path = threadPath(envelope.messageID, "mbox"); + error = stat(path, &file); + if (error || file.st_mtime < uidNewest(flat, "mbox")) { + FILE *mbox = fopen(path, "w"); + if (!mbox) err(EX_CANTCREAT, "%s", path); + for (size_t i = 0; i < flat.len; ++i) { + uint32_t uid = dataCheck(flat.ptr[i], Number).number; + error = concatFile(mbox, uidPath(uid, "mbox")); + if (error) err(EX_IOERR, "%s", path); + } + error = fclose(mbox); + if (error) err(EX_IOERR, "%s", path); + } + + listFree(flat); } diff --git a/export.c b/export.c index d6c7260..3e0933d 100644 --- a/export.c +++ b/export.c @@ -38,19 +38,9 @@ static const char *messagePath(const char *messageID, const char *type) { return buf; } -static void flatten(struct List *flat, struct List nested) { - for (size_t i = 0; i < nested.len; ++i) { - if (nested.ptr[i].type == List) { - flatten(flat, nested.ptr[i].list); - } else { - listPush(flat, nested.ptr[i]); - } - } -} - bool exportFetch(FILE *imap, enum Atom tag, struct List threads) { struct List uids = {0}; - flatten(&uids, threads); + listFlatten(&uids, threads); for (size_t i = uids.len - 1; i < uids.len; --i) { uint32_t uid = dataCheck(uids.ptr[i], Number).number; int error = 0 diff --git a/imap.h b/imap.h index f3ca668..3a30d5a 100644 --- a/imap.h +++ b/imap.h @@ -125,6 +125,16 @@ static inline void listPush(struct List *list, struct Data data) { list->ptr[list->len++] = data; } +static inline void listFlatten(struct List *flat, struct List nested) { + for (size_t i = 0; i < nested.len; ++i) { + if (nested.ptr[i].type == List) { + listFlatten(flat, nested.ptr[i].list); + } else { + listPush(flat, nested.ptr[i]); + } + } +} + static inline void dataFree(struct Data data); static inline void listFree(struct List list) { -- cgit 1.4.1