From 128491a7281f03023f7271bbac3cfb9c83ebe535 Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Thu, 9 Apr 2020 14:22:19 -0400 Subject: Send FETCH for uncached UIDs --- .gitignore | 3 +++ Makefile | 9 ++++++++- archive.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- imap.c | 13 +----------- imap.h | 33 +++++++++++++++++++----------- 5 files changed, 100 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 1edae8a..0af5f60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.o +UID/ +UIDVALIDITY bubger +tags diff --git a/Makefile b/Makefile index cd57ae9..81a107a 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,17 @@ LDLIBS = -ltls OBJS += archive.o OBJS += imap.o +dev: tags all + +all: bubger + bubger: ${OBJS} ${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@ ${OBJS}: imap.h +tags: *.c *.h + ctags -w *.c *.h + clean: - rm -f bubger ${OBJS} + rm -f bubger ${OBJS} tags diff --git a/archive.c b/archive.c index 4facd57..f3c0059 100644 --- a/archive.c +++ b/archive.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,53 @@ #define ENV_PASSWORD "BUBGER_IMAP_PASSWORD" +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 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]); + } + } +} + +static enum Atom fetchNew(FILE *imap, struct List threads) { + struct List uids = {0}; + flatten(&uids, threads); + for (size_t i = uids.len - 1; i < uids.len; --i) { + if (uids.ptr[i].type != Number) { + errx(EX_PROTOCOL, "invalid thread UID"); + } + uint32_t uid = uids.ptr[i].number; + int error = 0; + if (!error) error = access(uidPath(uid, "atom"), F_OK); + if (!error) error = access(uidPath(uid, "html"), F_OK); + if (!error) error = access(uidPath(uid, "mbox"), F_OK); + if (!error) { + uids.ptr[i] = uids.ptr[--uids.len]; + } + } + enum Atom tag = 0; + if (!uids.len) goto done; + + tag = atom("fetchNew"); + 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); + } + fprintf(imap, " (UID ENVELOPE)\r\n"); + +done: + listFree(uids); + return tag; +} + static void checkValidity(uint32_t validity) { FILE *file = fopen("UIDVALIDITY", "r"); if (file) { @@ -101,7 +149,9 @@ int main(int argc, char *argv[]) { } enum Atom login = 0; - enum Atom examine = atom("examine"); + enum Atom examine = atom("EXAMINE"); + enum Atom thread = atom("THREAD"); + enum Atom export = 0; FILE *imap = imapOpen(host, port); for (struct Resp resp; resp = imapResp(imap), resp.resp != AtomBye;) { @@ -132,6 +182,22 @@ int main(int argc, char *argv[]) { } checkValidity(resp.code.ptr[1].number); } + + 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); + } + export = fetchNew(imap, resp.data); + } + + respFree(resp); } fclose(imap); } diff --git a/imap.c b/imap.c index 620e282..7d623a5 100644 --- a/imap.c +++ b/imap.c @@ -147,18 +147,7 @@ static struct Data parseList(FILE *imap, char close) { if (*ptr) ptr++; struct Data data = { .type = List }; while (*ptr != close) { - if (data.list.len == data.list.cap) { - if (data.list.cap) { - data.list.cap *= 2; - } else { - data.list.cap = 4; - } - data.list.ptr = realloc( - data.list.ptr, sizeof(*data.list.ptr) * data.list.cap - ); - if (!data.list.ptr) err(EX_OSERR, "realloc"); - } - data.list.ptr[data.list.len++] = parseData(imap); + listPush(&data.list, parseData(imap)); } if (*ptr) ptr++; return data; diff --git a/imap.h b/imap.h index 0c9f6b9..e4ac7ea 100644 --- a/imap.h +++ b/imap.h @@ -96,14 +96,27 @@ struct Data { }; }; +static inline void listPush(struct List *list, struct Data data) { + if (list->len == list->cap) { + list->cap = (list->cap ? list->cap * 2 : 4); + list->ptr = realloc(list->ptr, sizeof(*list->ptr) * list->cap); + if (!list->ptr) err(EX_OSERR, "realloc"); + } + list->ptr[list->len++] = data; +} + +static inline void dataFree(struct Data data); + +static inline void listFree(struct List list) { + for (size_t i = 0; i < list.len; ++i) { + dataFree(list.ptr[i]); + } + free(list.ptr); +} + static inline void dataFree(struct Data data) { if (data.type == String) free(data.string); - if (data.type == List) { - for (size_t i = 0; i < data.list.len; ++i) { - dataFree(data.list.ptr[i]); - } - free(data.list.ptr); - } + if (data.type == List) listFree(data.list); } struct Resp { @@ -116,12 +129,8 @@ struct Resp { }; static inline void respFree(struct Resp resp) { - for (size_t i = 0; i < resp.code.len; ++i) { - dataFree(resp.code.ptr[i]); - } - for (size_t i = 0; i < resp.data.len; ++i) { - dataFree(resp.data.ptr[i]); - } + listFree(resp.code); + listFree(resp.data); } extern bool imapVerbose; -- cgit 1.4.1