diff options
-rw-r--r-- | archive.c | 166 | ||||
-rw-r--r-- | archive.h | 5 | ||||
-rw-r--r-- | concat.c | 7 | ||||
-rw-r--r-- | export.c | 14 | ||||
-rw-r--r-- | imap.h | 11 |
5 files changed, 116 insertions, 87 deletions
diff --git a/archive.c b/archive.c index e741b4e..3b7d003 100644 --- a/archive.c +++ b/archive.c @@ -103,101 +103,119 @@ int main(int argc, char *argv[]) { if (!pass) errx(EX_CONFIG, ENV_PASSWORD " unset"); } - enum Atom login = 0; - enum Atom examine = atom("EXAMINE"); - enum Atom thread = atom("THREAD"); - enum Atom export = 0; - enum Atom concat = 0; + enum Atom AtomThread = atom("THREAD"); + + enum { + Ready, + Login, + Examine, + Thread, + Export, + Concat, + Logout, + } state = Ready; + + enum Atom login = atom("login"); + enum Atom examine = atom("examine"); + enum Atom thread = atom("thread"); + enum Atom export = atom("export"); + enum Atom concat = atom("concat"); uint32_t uidNext = 0; struct List threads = {0}; - FILE *imap = imapOpen(host, port); for (struct Resp resp; resp = imapResp(imap), resp.resp != AtomBye;) { if (resp.resp == AtomNo || resp.resp == AtomBad) { errx(EX_CONFIG, "%s %s", Atoms[resp.resp], resp.text); } - if (!login) { - login = atom("login"); - fprintf( - imap, "%s LOGIN \"%s\" \"%s\"\r\n", - Atoms[login], user, pass - ); - } + switch (state) { + break; case Ready: { + fprintf( + imap, "%s LOGIN \"%s\" \"%s\"\r\n", + Atoms[login], user, pass + ); + state = Login; + } - if (resp.tag == login) { - fprintf(imap, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox); - } + break; case Login: { + if (resp.tag != login) break; + fprintf(imap, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox); + state = Examine; + } - if ( - resp.resp == AtomOk && - resp.code.len > 1 && - resp.code.ptr[0].type == Atom - ) { - enum Atom code = resp.code.ptr[0].atom; - if (code == AtomUIDValidity || code == AtomUIDNext) { - if (resp.code.ptr[1].type != Number) { - errx(EX_PROTOCOL, "invalid %s", Atoms[code]); + break; case Examine: { + if (resp.resp == AtomOk && resp.code.len > 1) { + enum Atom code = dataCheck(resp.code.ptr[0], Atom).atom; + struct Data value = resp.code.ptr[1]; + if (code == AtomUIDValidity) { + uint32_t validity = dataCheck(value, Number).number; + uint32_t previous = uidRead("UIDVALIDITY"); + if (previous && validity != previous) { + errx( + EX_TEMPFAIL, + "UIDVALIDITY changed; fresh export required" + ); + } + if (!previous) uidWrite("UIDVALIDITY", validity); + } else if (code == AtomUIDNext) { + uidNext = dataCheck(value, Number).number; + uint32_t prev = uidRead("UIDNEXT"); + if (uidNext == prev) { + fprintf(imap, "ayy LOGOUT\r\n"); + state = Logout; + } + } } + + if (resp.tag != examine) break; + fprintf( + imap, "%s UID THREAD %s UTF-8 %s\r\n", + Atoms[thread], algo, search + ); + state = Thread; } - if (code == AtomUIDValidity) { - uint32_t validity = resp.code.ptr[1].number; - uint32_t previous = uidRead("UIDVALIDITY"); - if (previous && validity != previous) { - errx( - EX_TEMPFAIL, - "UIDVALIDITY changed; fresh export required" - ); + + break; case Thread: { + if (resp.resp != AtomThread) break; + if (!resp.data.len) { + errx(EX_TEMPFAIL, "no messages matching %s", search); } - if (!previous) uidWrite("UIDVALIDITY", validity); - } - if (code == AtomUIDNext) { - uidNext = resp.code.ptr[1].number; - uint32_t prev = uidRead("UIDNEXT"); - if (uidNext == prev) { - examine = 0; - fprintf(imap, "ayy LOGOUT\r\n"); + createDir("UID"); + createDir("message"); + threads = resp.data; + resp.data = (struct List) {0}; + if (exportFetch(imap, export, threads)) { + state = Export; + } else { + concatFetch(imap, concat, threads); + state = Concat; } } - } - if (resp.tag == examine) { - fprintf( - imap, "%s UID THREAD %s UTF-8 %s\r\n", - Atoms[thread], algo, search - ); - } - - if (resp.resp == thread) { - if (!resp.data.len) { - errx(EX_TEMPFAIL, "no messages matching %s", search); + break; case Export: { + if (resp.resp == AtomFetch) { + if (!resp.data.len) errx(EX_PROTOCOL, "missing FETCH data"); + exportData(dataCheck(resp.data.ptr[0], List).list); + } + if (resp.tag != export) break; + concatFetch(imap, concat, threads); + state = Concat; } - createDir("UID"); - createDir("message"); - threads = resp.data; - resp.data = (struct List) {0}; - export = exportFetch(imap, threads); - if (!export) concat = concatFetch(imap, threads); - } - if (export && resp.resp == AtomFetch) { - if (!resp.data.len || resp.data.ptr[0].type != List) { - errx(EX_PROTOCOL, "invalid FETCH data"); + break; case Concat: { + if (resp.resp == AtomFetch) { + if (!resp.data.len) errx(EX_PROTOCOL, "missing FETCH data"); + concatData(threads, dataCheck(resp.data.ptr[0], List).list); + } + if (resp.tag != concat) break; + uidWrite("UIDNEXT", uidNext); + fprintf(imap, "ayy LOGOUT\r\n"); + state = Logout; } - exportData(resp.data.ptr[0].list); + + break; case Logout:; } - - if (export && resp.tag == export) { - export = 0; - concat = concatFetch(imap, threads); - } - - if (concat && resp.tag == concat) { - uidWrite("UIDNEXT", uidNext); - fprintf(imap, "ayy LOGOUT\r\n"); - } - respFree(resp); } fclose(imap); diff --git a/archive.h b/archive.h index 872bf00..83f2e76 100644 --- a/archive.h +++ b/archive.h @@ -64,10 +64,11 @@ static inline void envelopeFree(struct Envelope envelope) { free(envelope.bcc.addrs); } -enum Atom exportFetch(FILE *imap, struct List threads); +bool exportFetch(FILE *imap, enum Atom tag, struct List threads); void exportData(struct List items); -enum Atom concatFetch(FILE *imap, struct List threads); +void concatFetch(FILE *imap, enum Atom tag, struct List threads); +void concatData(struct List threads, struct List items); #define TEMPLATE(...) #__VA_ARGS__ diff --git a/concat.c b/concat.c index 217662a..3417e8a 100644 --- a/concat.c +++ b/concat.c @@ -34,8 +34,7 @@ static uint32_t threadRoot(struct List thread) { return thread.ptr[0].number; } -enum Atom concatFetch(FILE *imap, struct List threads) { - enum Atom tag = atom("concatFetch"); +void concatFetch(FILE *imap, enum Atom tag, struct List threads) { fprintf(imap, "%s UID FETCH ", Atoms[tag]); for (size_t i = 0; i < threads.len; ++i) { if (threads.ptr[i].type != List) errx(EX_PROTOCOL, "invalid thread"); @@ -43,5 +42,7 @@ enum Atom concatFetch(FILE *imap, struct List threads) { fprintf(imap, "%s%" PRIu32, (i ? "," : ""), root); } fprintf(imap, " (UID ENVELOPE)\r\n"); - return tag; +} + +void concatData(struct List threads, struct List items) { } diff --git a/export.c b/export.c index 2ff6c5d..c8d742f 100644 --- a/export.c +++ b/export.c @@ -50,7 +50,7 @@ static void flatten(struct List *flat, struct List nested) { } } -enum Atom exportFetch(FILE *imap, struct List threads) { +bool exportFetch(FILE *imap, enum Atom tag, struct List threads) { struct List uids = {0}; flatten(&uids, threads); for (size_t i = uids.len - 1; i < uids.len; --i) { @@ -66,10 +66,11 @@ enum Atom exportFetch(FILE *imap, struct List threads) { uids.ptr[i] = uids.ptr[--uids.len]; } } - enum Atom tag = 0; - if (!uids.len) goto done; + if (!uids.len) { + listFree(uids); + return false; + } - tag = atom("exportFetch"); fprintf(imap, "%s UID FETCH ", Atoms[tag]); for (size_t i = 0; i < uids.len; ++i) { fprintf(imap, "%s%" PRIu32, (i ? "," : ""), uids.ptr[i].number); @@ -81,10 +82,7 @@ enum Atom exportFetch(FILE *imap, struct List threads) { "BODY[HEADER.FIELDS (" MBOX_HEADERS ")] BODY[TEXT]" ")\r\n" ); - -done: - listFree(uids); - return tag; + return true; } static struct Address parseAddress(struct List list) { diff --git a/imap.h b/imap.h index f9b3106..a7b4b64 100644 --- a/imap.h +++ b/imap.h @@ -99,6 +99,17 @@ struct Data { }; }; +static inline struct Data dataCheck(struct Data data, enum Type type) { + const char *Types[] = { "atom", "number", "string", "list" }; + if (data.type != type) { + errx( + EX_PROTOCOL, "expected %s, found %s", + Types[type], Types[data.type] + ); + } + return data; +} + static inline void listPush(struct List *list, struct Data data) { if (list->len == list->cap) { list->cap = (list->cap ? list->cap * 2 : 4); |