summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-05-04 17:46:58 -0400
committerJune 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;