diff options
author | June McEnroe <june@causal.agency> | 2020-04-09 15:53:31 -0400 |
---|---|---|
committer | June McEnroe <june@causal.agency> | 2020-04-09 15:53:31 -0400 |
commit | cd60193ce40431cd05da4da66fb4156034b8f330 (patch) | |
tree | a433e65f0b01ceb3711fbf29fc277ff389a2f849 | |
parent | Send FETCH for uncached UIDs (diff) | |
download | bubger-cd60193ce40431cd05da4da66fb4156034b8f330.tar.gz bubger-cd60193ce40431cd05da4da66fb4156034b8f330.zip |
Parse envelopes
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | archive.c | 130 | ||||
-rw-r--r-- | archive.h | 57 |
3 files changed, 188 insertions, 1 deletions
diff --git a/Makefile b/Makefile index 81a107a..6a72dd9 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ all: bubger bubger: ${OBJS} ${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@ -${OBJS}: imap.h +${OBJS}: archive.h imap.h tags: *.c *.h ctags -w *.c *.h diff --git a/archive.c b/archive.c index f3c0059..cdfd982 100644 --- a/archive.c +++ b/archive.c @@ -15,14 +15,19 @@ */ #include <err.h> +#include <errno.h> #include <inttypes.h> #include <limits.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <sys/stat.h> #include <sysexits.h> +#include <time.h> #include <unistd.h> +#include "archive.h" #include "imap.h" #define ENV_PASSWORD "BUBGER_IMAP_PASSWORD" @@ -62,6 +67,9 @@ static enum Atom fetchNew(FILE *imap, struct List threads) { enum Atom tag = 0; if (!uids.len) goto done; + int error = mkdir("UID", 0775); + if (error && errno != EEXIST) err(EX_CANTCREAT, "UID"); + tag = atom("fetchNew"); fprintf(imap, "%s UID FETCH ", Atoms[tag]); for (size_t i = 0; i < uids.len; ++i) { @@ -74,6 +82,121 @@ done: return tag; } +static struct Address parseAddress(struct List list) { + if (list.len < 4) { + errx(EX_PROTOCOL, "missing address structure fields"); + } + struct Address addr = {0}; + if (list.ptr[0].type == String) { + // TODO: Decode UTF-8 in name. + addr.name = strdup(list.ptr[0].string); + if (!addr.name) err(EX_OSERR, "strdup"); + } + if (list.ptr[2].type == String) addr.mailbox = list.ptr[2].string; + if (list.ptr[3].type == String) addr.host = list.ptr[3].string; + return addr; +} + +static struct AddressList parseAddressList(struct List list) { + struct Address *addrs = calloc(list.len, sizeof(*addrs)); + if (!addrs) err(EX_OSERR, "calloc"); + for (size_t i = 0; i < list.len; ++i) { + if (list.ptr[i].type != List) { + errx(EX_PROTOCOL, "invalid address field"); + } + addrs[i] = parseAddress(list.ptr[i].list); + } + return (struct AddressList) { list.len, addrs }; +} + +static struct Envelope parseEnvelope(struct List list) { + enum { + Date, Subject, From, Sender, ReplyTo, + To, Cc, Bcc, InReplyTo, MessageID, + EnvelopeLen, + }; + if (list.len < EnvelopeLen) { + errx(EX_PROTOCOL, "missing envelope structure fields"); + } + struct Envelope envelope = {0}; + + if (list.ptr[Date].type != String) { + errx(EX_PROTOCOL, "invalid envelope date field"); + } + const char *date = list.ptr[Date].string; + date = strptime(date, "%a, %e %b %Y %H:%M:%S %z", &envelope.date); + if (!date) errx(EX_PROTOCOL, "invalid envelope date format"); + + if (list.ptr[Subject].type != String) { + errx(EX_PROTOCOL, "invalid envelope subject field"); + } + // TODO: Decode UTF-8 in subject. + envelope.subject = strdup(list.ptr[Subject].string); + if (!envelope.subject) err(EX_OSERR, "strdup"); + + for (size_t i = From; i <= Bcc; ++i) { + if (list.ptr[i].type == List) continue; + if (list.ptr[i].type == Atom && list.ptr[i].atom == AtomNil) { + list.ptr[i].type = List; + list.ptr[i].list = (struct List) {0}; + continue; + } + errx(EX_PROTOCOL, "invalid envelope address field"); + } + for (size_t i = From; i <= ReplyTo; ++i) { + if (!list.ptr[i].list.len || list.ptr[i].list.ptr[0].type != List) { + errx(EX_PROTOCOL, "invalid envelope address field"); + } + } + envelope.from = parseAddress(list.ptr[From].list.ptr[0].list); + envelope.sender = parseAddress(list.ptr[Sender].list.ptr[0].list); + envelope.replyTo = parseAddress(list.ptr[ReplyTo].list.ptr[0].list); + envelope.to = parseAddressList(list.ptr[To].list); + envelope.cc = parseAddressList(list.ptr[Cc].list); + envelope.bcc = parseAddressList(list.ptr[Bcc].list); + + if (list.ptr[InReplyTo].type == String) { + envelope.inReplyTo = list.ptr[InReplyTo].string; + } + if (list.ptr[MessageID].type != String) { + errx(EX_PROTOCOL, "invalid envelope message-id field"); + } + envelope.messageID = list.ptr[MessageID].string; + + return envelope; +} + +static void exportMessage(struct List items) { + static enum Atom AtomUID; + static enum Atom AtomEnvelope; + if (!AtomUID) AtomUID = atom("UID"); + if (!AtomEnvelope) AtomEnvelope = atom("ENVELOPE"); + + uint32_t uid = 0; + struct Envelope envelope = {0}; + for (size_t i = 0; i + 1 < items.len; i += 2) { + if (items.ptr[i].type != Atom) { + errx(EX_PROTOCOL, "invalid data item name"); + } + enum Atom name = items.ptr[i].atom; + if (name == AtomUID) { + if (items.ptr[i + 1].type != Number) { + errx(EX_PROTOCOL, "invalid UID data item value"); + } + uid = items.ptr[i + 1].number; + } else if (name == AtomEnvelope) { + if (items.ptr[i + 1].type != List) { + errx(EX_PROTOCOL, "invalid ENVELOPE data item value"); + } + envelope = parseEnvelope(items.ptr[i + 1].list); + } + } + if (!uid) errx(EX_PROTOCOL, "missing UID data item"); + if (!envelope.subject) errx(EX_PROTOCOL, "missing ENVELOPE data item"); + + envelopeFree(envelope); +} + static void checkValidity(uint32_t validity) { FILE *file = fopen("UIDVALIDITY", "r"); if (file) { @@ -197,6 +320,13 @@ int main(int argc, char *argv[]) { export = fetchNew(imap, resp.data); } + if (export && resp.resp == AtomFetch) { + if (!resp.data.len || resp.data.ptr[0].type != List) { + errx(EX_PROTOCOL, "invalid FETCH data"); + } + exportMessage(resp.data.ptr[0].list); + } + respFree(resp); } fclose(imap); diff --git a/archive.h b/archive.h new file mode 100644 index 0000000..9ec1f89 --- /dev/null +++ b/archive.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * 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 <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <time.h> + +struct Address { + char *name; + const char *mailbox; + const char *host; +}; + +struct AddressList { + size_t len; + struct Address *addrs; +}; + +struct Envelope { + struct tm date; + char *subject; + struct Address from, sender, replyTo; + struct AddressList to, cc, bcc; + const char *inReplyTo; + const char *messageID; +}; + +static inline void envelopeFree(struct Envelope envelope) { + free(envelope.subject); + free(envelope.from.name); + free(envelope.sender.name); + free(envelope.replyTo.name); + for (size_t i = 0; i < envelope.to.len; ++i) { + free(envelope.to.addrs[i].name); + } + for (size_t i = 0; i < envelope.cc.len; ++i) { + free(envelope.cc.addrs[i].name); + } + for (size_t i = 0; i < envelope.bcc.len; ++i) { + free(envelope.bcc.addrs[i].name); + } + free(envelope.to.addrs); + free(envelope.cc.addrs); + free(envelope.bcc.addrs); +} |