/* 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(AtomOk, "OK") \ X(AtomNo, "NO") \ X(AtomBad, "BAD") \ X(AtomPreauth, "PREAUTH") \ X(AtomBye, "BYE") \ X(AtomAlert, "ALERT") \ X(AtomBadCharset, "BADCHARSET") \ X(AtomCapability, "CAPABILITY") \ X(AtomParse, "PARSE") \ X(AtomPermanentFlags, "PERMANENTFLAGS") \ X(AtomReadOnly, "READ-ONLY") \ X(AtomReadWrite, "READ-WRITE") \ X(AtomTryCreate, "TRYCREATE") \ X(AtomUIDNext, "UIDNEXT") \ X(AtomUIDValidity, "UIDVALIDITY") \ X(AtomUnseen, "UNSEEN") \ X(AtomList, "LIST") \ X(AtomLSub, "LSUB") \ X(AtomStatus, "STATUS") \ X(AtomSearch, "SEARCH") \ X(AtomFlags, "FLAGS") \ X(AtomExists, "EXISTS") \ X(AtomRecent, "RECENT") \ X(AtomExpunge, "EXPUNGE") \ X(AtomFetch, "FETCH") \ X(AtomThread, "THREAD") \ X(AtomUID, "UID") \ X(AtomEnvelope, "ENVELOPE") \ X(AtomBodyStructure, "BODYSTRUCTURE") \ X(AtomBody, "BODY") \ X(AtomHeaderFields, "HEADER.FIELDS") \ X(AtomText, "TEXT") \ X(AtomUntagged, "*") 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 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; FILE *imapOpen(const char *host, const char *port); struct Resp imapResp(FILE *imap); #endif /* IMAP_H */