aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorC. McEnroe <june@causal.agency>2020-05-04 17:46:58 -0400
committerC. McEnroe <june@causal.agency>2020-05-04 17:48:18 -0400
commit75f6594d4e9287c83de171b7a41ed9f1eb045ae6 (patch)
treec4e3e3d2ae1e66fc5e80106c0419997393b7d8a2
parentUse a real IMAP parser (diff)
downloadnotemap-75f6594d4e9287c83de171b7a41ed9f1eb045ae6.tar.gz
notemap-75f6594d4e9287c83de171b7a41ed9f1eb045ae6.zip
Update IMAP parser
-rw-r--r--imap.c26
-rw-r--r--imap.h93
-rw-r--r--notemap.c18
3 files changed, 77 insertions, 60 deletions
diff --git a/imap.c b/imap.c
index 6894319..7984e0e 100644
--- a/imap.c
+++ b/imap.c
@@ -60,10 +60,11 @@ static int imapClose(void *_tls) {
struct tls *tls = _tls;
int error = tls_close(tls);
if (error) errx(EX_IOERR, "tls_close: %s", tls_error(tls));
+ tls_free(tls);
return error;
}
-FILE *imapOpen(const char *host, const char *port) {
+void imapOpen(FILE **read, FILE **write, const char *host, const char *port) {
struct tls *client = tls_client();
if (!client) errx(EX_SOFTWARE, "tls_client");
@@ -77,11 +78,11 @@ FILE *imapOpen(const char *host, const char *port) {
error = tls_connect(client, host, port);
if (error) errx(EX_NOHOST, "tls_connect: %s", tls_error(client));
- FILE *imap = funopen(client, imapRead, imapWrite, NULL, imapClose);
- if (!imap) err(EX_SOFTWARE, "funopen");
+ *read = funopen(client, imapRead, NULL, NULL, NULL);
+ *write = funopen(client, NULL, imapWrite, NULL, imapClose);
+ if (!*read || !*write) err(EX_SOFTWARE, "funopen");
- setlinebuf(imap);
- return imap;
+ setlinebuf(*write);
}
static size_t cap;
@@ -98,7 +99,7 @@ static void imapLine(FILE *imap) {
}
static struct Data parseAtom(void) {
- size_t len = strcspn(ptr, " ()[]{\"");
+ size_t len = (*ptr == '.' ? 1 : strcspn(ptr, " .()[]{\""));
struct Data data = {
.type = Atom,
.atom = atomn(ptr, len),
@@ -149,18 +150,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 04cf5a0..8b6b717 100644
--- a/imap.h
+++ b/imap.h
@@ -14,6 +14,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#ifndef IMAP_H
+#define IMAP_H
+
#include <err.h>
#include <stdbool.h>
#include <stdint.h>
@@ -24,33 +27,16 @@
#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(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(AtomUntagged, "*") \
- X(AtomContinue, "+")
+ X(AtomEnvelope, "ENVELOPE")
enum Atom {
#define X(id, str) id,
@@ -97,16 +83,57 @@ struct Data {
};
};
-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]);
+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]);
}
- free(data.list.ptr);
}
}
+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;
@@ -117,14 +144,12 @@ 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;
-FILE *imapOpen(const char *host, const char *port);
-struct Resp imapResp(FILE *imap);
+void imapOpen(FILE **read, FILE **write, const char *host, const char *port);
+struct Resp imapResp(FILE *imapRead);
+
+#endif /* IMAP_H */
diff --git a/notemap.c b/notemap.c
index dfe9872..99a1418 100644
--- a/notemap.c
+++ b/notemap.c
@@ -283,9 +283,14 @@ int main(int argc, char *argv[]) {
enum Atom next = atom("next");
enum Atom create = atom("create");
enum Atom replace = atom("replace");
- enum Atom envelope = atom("ENVELOPE");
- FILE *imap = imapOpen(host, port);
- for (struct Resp resp; resp = imapResp(imap), resp.resp != AtomBye;) {
+
+ FILE *imapRead, *imap;
+ imapOpen(&imapRead, &imap, host, port);
+ for (
+ struct Resp resp;
+ resp = imapResp(imapRead), resp.resp != AtomBye;
+ respFree(resp)
+ ) {
if (resp.resp == AtomNo || resp.resp == AtomBad) {
errx(EX_CONFIG, "%s: %s", Atoms[resp.resp], resp.text);
}
@@ -335,8 +340,6 @@ next:
"%s SEARCH HEADER X-Universally-Unique-Identifier \"%s\"\r\n",
Atoms[AtomSearch], uuid
);
-
- respFree(resp);
continue;
}
@@ -368,7 +371,7 @@ next:
resp.data.ptr[0].type == List &&
resp.data.ptr[0].list.len > 1 &&
resp.data.ptr[0].list.ptr[0].type == Atom &&
- resp.data.ptr[0].list.ptr[0].atom == envelope &&
+ resp.data.ptr[0].list.ptr[0].atom == AtomEnvelope &&
resp.data.ptr[0].list.ptr[1].type == List
) {
struct List envelope = resp.data.ptr[0].list.ptr[1].list;
@@ -414,9 +417,8 @@ next:
Atoms[next], seq
);
}
-
- respFree(resp);
}
+ fclose(imapRead);
fclose(imap);
int ret = EX_OK;