From 1e302b4e07d5229869821aa6240520420304d3f1 Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Thu, 9 Apr 2020 16:37:28 -0400 Subject: Export mbox files --- Makefile | 1 + archive.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ archive.h | 9 +++++++++ mbox.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 mbox.c diff --git a/Makefile b/Makefile index 6a72dd9..20b440f 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ LDLIBS = -ltls OBJS += archive.o OBJS += imap.o +OBJS += mbox.o dev: tags all diff --git a/archive.c b/archive.c index cdfd982..497b5a6 100644 --- a/archive.c +++ b/archive.c @@ -75,7 +75,13 @@ static enum Atom fetchNew(FILE *imap, struct List threads) { for (size_t i = 0; i < uids.len; ++i) { fprintf(imap, "%s%" PRIu32, (i ? "," : ""), uids.ptr[i].number); } - fprintf(imap, " (UID ENVELOPE)\r\n"); + fprintf( + imap, + " (" + "UID ENVELOPE " + "BODY[HEADER.FIELDS (" MBOX_HEADERS ")] BODY[TEXT]" + ")\r\n" + ); done: listFree(uids); @@ -167,19 +173,37 @@ static struct Envelope parseEnvelope(struct List list) { } static void exportMessage(struct List items) { - static enum Atom AtomUID; - static enum Atom AtomEnvelope; + static enum Atom AtomUID, AtomEnvelope, AtomBody; + static enum Atom AtomHeaderFields, AtomText; if (!AtomUID) AtomUID = atom("UID"); if (!AtomEnvelope) AtomEnvelope = atom("ENVELOPE"); + if (!AtomBody) AtomBody = atom("BODY"); + if (!AtomHeaderFields) AtomHeaderFields = atom("HEADER.FIELDS"); + if (!AtomText) AtomText = atom("TEXT"); uint32_t uid = 0; struct Envelope envelope = {0}; + char *header = NULL; + char *body = NULL; + for (size_t i = 0; i + 1 < items.len; i += 2) { - if (items.ptr[i].type != Atom) { + enum Atom name; + if (items.ptr[i].type == Atom) { + name = items.ptr[i].atom; + } else if ( + items.ptr[i].type == List && + items.ptr[i].list.len && + items.ptr[i].list.ptr[0].type == Atom + ) { + name = items.ptr[i].list.ptr[0].atom; + } else { errx(EX_PROTOCOL, "invalid data item name"); } - enum Atom name = items.ptr[i].atom; - if (name == AtomUID) { + + if (name == AtomBody) { + i--; + continue; + } else if (name == AtomUID) { if (items.ptr[i + 1].type != Number) { errx(EX_PROTOCOL, "invalid UID data item value"); } @@ -189,10 +213,33 @@ static void exportMessage(struct List items) { errx(EX_PROTOCOL, "invalid ENVELOPE data item value"); } envelope = parseEnvelope(items.ptr[i + 1].list); + } else if (name == AtomHeaderFields) { + if (items.ptr[i + 1].type != String) { + errx(EX_PROTOCOL, "invalid BODY[HEADER.FIELDS] data item value"); + } + header = items.ptr[i + 1].string; + } else if (name == AtomText) { + if (items.ptr[i + 1].type != String) { + errx(EX_PROTOCOL, "invalid BODY[TEXT] data item value"); + } + body = items.ptr[i + 1].string; } } + if (!uid) errx(EX_PROTOCOL, "missing UID data item"); if (!envelope.subject) errx(EX_PROTOCOL, "missing ENVELOPE data item"); + if (!header) errx(EX_PROTOCOL, "missing BODY[HEADER.FIELDS] data item"); + if (!body) errx(EX_PROTOCOL, "missing BODY[TEXT] data item"); + + const char *path = uidPath(uid, "mbox"); + FILE *file = fopen(path, "w"); + if (!file) err(EX_CANTCREAT, "%s", path); + + int error = mboxFrom(file) + || mboxHeader(file, header) + || mboxBody(file, body) + || fclose(file); + if (error) err(EX_IOERR, "%s", path); envelopeFree(envelope); } diff --git a/archive.h b/archive.h index 9ec1f89..d804021 100644 --- a/archive.h +++ b/archive.h @@ -55,3 +55,12 @@ static inline void envelopeFree(struct Envelope envelope) { free(envelope.cc.addrs); free(envelope.bcc.addrs); } + +#define MBOX_HEADERS \ + "Date Subject From Sender Reply-To To Cc Bcc " \ + "Message-Id In-Reply-To References " \ + "MIME-Version Content-Type Content-Disposition Content-Transfer-Encoding" + +int mboxFrom(FILE *file); +int mboxHeader(FILE *file, char *header); +int mboxBody(FILE *file, char *body); diff --git a/mbox.c b/mbox.c new file mode 100644 index 0000000..f8684de --- /dev/null +++ b/mbox.c @@ -0,0 +1,54 @@ +/* Copyright (C) 2020 C. McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "archive.h" + +int mboxFrom(FILE *file) { + int n = fprintf(file, "From mboxrd@z Thu Jan 1 00:00:00 1970\n"); + return (n < 0 ? n : 0); +} + +int mboxHeader(FILE *file, char *header) { + for (char *crlf; (crlf = strstr(header, "\r\n")); header = &crlf[2]) { + *crlf = '\0'; + int n = fprintf(file, "%s\n", header); + if (n < 0) return n; + } + return 0; +} + +int mboxBody(FILE *file, char *body) { + int n; + for (char *crlf; (crlf = strstr(body, "\r\n")); body = &crlf[2]) { + *crlf = '\0'; + char *from = body; + while (*from == '>') from++; + if (!strncmp(from, "From ", 5)) { + n = fprintf(file, ">%s\n", body); + } else { + n = fprintf(file, "%s\n", body); + } + if (n < 0) return n; + } + n = fprintf(file, "\n"); + return (n < 0 ? n : 0); +} -- cgit 1.4.1