/* 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 . */ #ifndef IMAP_H #define IMAP_H #include #include #include #include #include #include #include #define ENUM_ATOM \ X(AtomNil, "NIL") \ X(AtomUntagged, "*") \ X(AtomContinue, "+") \ X(AtomOk, "OK") \ X(AtomNo, "NO") \ X(AtomBad, "BAD") \ X(AtomPreauth, "PREAUTH") \ X(AtomBye, "BYE") \ X(AtomSearch, "SEARCH") \ X(AtomFetch, "FETCH") \ X(AtomEnvelope, "ENVELOPE") enum Atom { #define X(id, str) id, ENUM_ATOM #undef X AtomCap = 1024, }; extern const char *Atoms[AtomCap]; static inline enum Atom atomn(const char *str, size_t len) { enum Atom i; for (i = 0; i < AtomCap; ++i) { if (!Atoms[i]) break; if (strlen(Atoms[i]) != len) continue; if (!strncasecmp(Atoms[i], str, len)) return i; } if (i == AtomCap) errx(EX_SOFTWARE, "atom capacity exceeded"); Atoms[i] = strndup(str, len); if (!Atoms[i]) err(EX_OSERR, "strndup"); return i; } static inline enum Atom atom(const char *str) { return atomn(str, strlen(str)); } struct Data { enum Type { Atom, Number, String, List, } type; union { enum Atom atom; uint32_t number; char *string; struct List { size_t cap; size_t len; struct Data *ptr; } list; }; }; 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 struct Data dataTake(struct Data *from) { struct Data take = *from; from->type = Atom; from->atom = AtomNil; return take; } 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 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) { 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) listFree(data.list); } struct Resp { enum Atom tag; uint32_t number; enum Atom resp; struct List code; struct List data; const char *text; }; static inline void respFree(struct Resp resp) { listFree(resp.code); listFree(resp.data); } extern bool imapVerbose; void imapOpen(FILE **read, FILE **write, const char *host, const char *port); struct Resp imapResp(FILE *imapRead); #endif /* IMAP_H */