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;
error] [client 172.21.1.6] Use '--' to > separate paths from revisions, like this:, referer: > http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ > [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] 'git <command> > [<revision>...] -- [<file>...]', referer: > http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ > [Tue Jun 10 17:45:32 2014] [error] [client 172.21.1.6] Premature end > of script headers: cgit, referer: > http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ The cache will kick in, so if you search for the same string again, it'll show an empty range, so you have to change the bogus strings each time. This is because we just pass the arguments straight to Git's revision parsing machinery which die()s if it cannot parse an argument, printing the above to stderr and exiting. The patch below makes it a bit friendlier by just ignoring unhandled arguments, but I can't see an easy way to report errors when we can't parse revision arguments without losing the flexibility of supporting all of the revision specifiers supported by Git. Reported-by: Konstantin Ryabitsev <mricon@kernel.org> 2014-06-28git: update for git 2.0Christian Hesse prefixcmp() and suffixcmp() have been remove, functionality is now provided by starts_with() and ends_with(). Retrurn values have been changed, so instead of just renaming we have to fix logic. Everything else looks just fine. 2014-04-17remove trailing whitespaces from source filesChristian Hesse 2014-04-12git: update to 1.9.2Christian Hesse Everything works just bumping the version in Makefile and commit hash in submodule. No code changes required. 2014-04-05Fix cgit_parse_url when a repo url is contained in another repo urlJulian Maurice For example, if I have two repos (remove-suffix is enabled): /foo /foo/bar http://cgit/foo/bar/ is interpreted as "repository 'foo', command 'bar'" instead of "repository 'foo/bar'" 2014-03-20Makefile: use more reliable git tarball mirrorJason A. Donenfeld 2014-03-20git: update to 1.9.1Christian Hesse