summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--archive.c27
-rw-r--r--imap.c115
-rw-r--r--imap.h13
3 files changed, 87 insertions, 68 deletions
diff --git a/archive.c b/archive.c
index 376e2fc..469c24c 100644
--- a/archive.c
+++ b/archive.c
@@ -151,11 +151,10 @@ int main(int argc, char *argv[]) {
 	struct List threads = {0};
 	struct Envelope *envelopes = NULL;
 
-	FILE *imapRead, *imap;
-	imapOpen(&imapRead, &imap, host, port);
+	struct IMAP imap = imapOpen(host, port);
 	for (
 		struct Resp resp;
-		resp = imapResp(imapRead), resp.resp != AtomBye;
+		resp = imapResp(&imap), resp.resp != AtomBye;
 		respFree(resp)
 	) {
 		if (resp.resp == AtomNo || resp.resp == AtomBad) {
@@ -165,7 +164,7 @@ int main(int argc, char *argv[]) {
 		switch (state) {
 			break; case Ready: {
 				fprintf(
-					imap, "%s LOGIN \"%s\" \"%s\"\r\n",
+					imap.w, "%s LOGIN \"%s\" \"%s\"\r\n",
 					Atoms[login], user, pass
 				);
 				state = Login;
@@ -173,14 +172,14 @@ int main(int argc, char *argv[]) {
 
 			break; case Login: {
 				if (resp.tag != login) break;
-				fprintf(imap, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox);
+				fprintf(imap.w, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox);
 				state = Examine;
 			}
 
 			break; case Examine: {
 				if (resp.tag == examine) {
 					fprintf(
-						imap, "%s UID THREAD %s UTF-8 %s\r\n",
+						imap.w, "%s UID THREAD %s UTF-8 %s\r\n",
 						Atoms[thread], algo, search
 					);
 					state = Thread;
@@ -206,7 +205,7 @@ int main(int argc, char *argv[]) {
 					uidNext = dataCheck(value, Number).number;
 					uint32_t prev = uidRead("UIDNEXT");
 					if (uidNext == prev) {
-						fprintf(imap, "ayy LOGOUT\r\n");
+						fprintf(imap.w, "ayy LOGOUT\r\n");
 						state = Logout;
 					} else {
 						exitStatus = EXIT_SUCCESS;
@@ -226,11 +225,11 @@ int main(int argc, char *argv[]) {
 				envelopes = calloc(threads.len, sizeof(*envelopes));
 				if (!envelopes) err(EX_OSERR, "calloc");
 
-				if (exportFetch(imap, export, threads)) {
+				if (exportFetch(imap.w, export, threads)) {
 					exportTags = 1;
 					state = Export;
 				} else {
-					concatFetch(imap, concat, threads);
+					concatFetch(imap.w, concat, threads);
 					state = Concat;
 				}
 			}
@@ -239,10 +238,10 @@ int main(int argc, char *argv[]) {
 				if (resp.resp == AtomFetch) {
 					if (!resp.data.len) errx(EX_PROTOCOL, "missing FETCH data");
 					struct List items = dataCheck(resp.data.ptr[0], List).list;
-					if (exportData(imap, export, items)) exportTags++;
+					if (exportData(imap.w, export, items)) exportTags++;
 				}
 				if (resp.tag != export || --exportTags) break;
-				concatFetch(imap, concat, threads);
+				concatFetch(imap.w, concat, threads);
 				state = Concat;
 			}
 
@@ -257,15 +256,15 @@ int main(int argc, char *argv[]) {
 				concatThreads(threads, envelopes);
 				concatIndex(threads, envelopes);
 				uidWrite("UIDNEXT", uidNext);
-				fprintf(imap, "ayy LOGOUT\r\n");
+				fprintf(imap.w, "ayy LOGOUT\r\n");
 				state = Logout;
 			}
 			
 			break; case Logout:;
 		}
 	}
-	fclose(imapRead);
-	fclose(imap);
+	fclose(imap.r);
+	fclose(imap.w);
 
 	return exitStatus;
 }
diff --git a/imap.c b/imap.c
index 748d51c..10864aa 100644
--- a/imap.c
+++ b/imap.c
@@ -33,6 +33,14 @@
 #include <sysexits.h>
 #include <tls.h>
 
+FILE *funopen(
+	const void *cookie,
+	int (*readfn)(void *, char *, int),
+	int (*writefn)(void *, const char *, int),
+	fpos_t (*seekfn)(void *, fpos_t, int),
+	int (*closefn)(void *)
+);
+
 #include "imap.h"
 
 const char *Atoms[AtomCap] = {
@@ -73,7 +81,7 @@ static int imapClose(void *_tls) {
 	return error;
 }
 
-void imapOpen(FILE **read, FILE **write, const char *host, const char *port) {
+struct IMAP imapOpen(const char *host, const char *port) {
 	struct tls *client = tls_client();
 	if (!client) errx(EX_SOFTWARE, "tls_client");
 
@@ -87,96 +95,99 @@ void imapOpen(FILE **read, FILE **write, const char *host, const char *port) {
 	error = tls_connect(client, host, port);
 	if (error) errx(EX_NOHOST, "tls_connect: %s", tls_error(client));
 
-	*read = funopen(client, imapRead, NULL, NULL, NULL);
-	*write = funopen(client, NULL, imapWrite, NULL, imapClose);
-	if (!*read || !*write) err(EX_SOFTWARE, "funopen");
+	struct IMAP imap = {
+		.r = funopen(client, imapRead, NULL, NULL, NULL),
+		.w = funopen(client, NULL, imapWrite, NULL, imapClose),
+	};
+	if (!imap.r || !imap.w) err(EX_OSERR, "funopen");
 
-	setlinebuf(*write);
+	setlinebuf(imap.w);
+	return imap;
 }
 
-static size_t cap;
-static char *buf;
-static char *ptr;
-
-static void imapLine(FILE *imap) {
-	ssize_t len = getline(&buf, &cap, imap);
+static void imapLine(struct IMAP *imap) {
+	ssize_t len = getline(&imap->buf, &imap->cap, imap->r);
 	if (len < 0) errx(EX_PROTOCOL, "unexpected eof");
-	if (len < 1 || buf[len - 1] != '\n') errx(EX_PROTOCOL, "missing LF");
-	if (len < 2 || buf[len - 2] != '\r') errx(EX_PROTOCOL, "missing CR");
-	buf[len - 2] = '\0';
-	ptr = buf;
+	if (len < 1 || imap->buf[len - 1] != '\n') errx(EX_PROTOCOL, "missing LF");
+	if (len < 2 || imap->buf[len - 2] != '\r') errx(EX_PROTOCOL, "missing CR");
+	imap->buf[len - 2] = '\0';
+	imap->ptr = imap->buf;
 }
 
-static struct Data parseAtom(void) {
-	size_t len = (*ptr == '.' ? 1 : strcspn(ptr, " .()[]{\""));
+static struct Data parseAtom(struct IMAP *imap) {
+	size_t len = (*imap->ptr == '.' ? 1 : strcspn(imap->ptr, " .()[]{\""));
 	struct Data data = {
 		.type = Atom,
-		.atom = atomn(ptr, len),
+		.atom = atomn(imap->ptr, len),
 	};
-	ptr += len;
+	imap->ptr += len;
 	return data;
 }
 
-static struct Data parseNumber(void) {
+static struct Data parseNumber(struct IMAP *imap) {
 	return (struct Data) {
 		.type = Number,
-		.number = strtoull(ptr, &ptr, 10),
+		.number = strtoull(imap->ptr, &imap->ptr, 10),
 	};
 }
 
-static struct Data parseQuoted(void) {
-	ptr++;
-	size_t len = strcspn(ptr, "\"");
-	if (ptr[len] != '"') errx(EX_PROTOCOL, "missing quoted string delimiter");
+static struct Data parseQuoted(struct IMAP *imap) {
+	imap->ptr++;
+	size_t len = strcspn(imap->ptr, "\"");
+	if (imap->ptr[len] != '"') {
+		errx(EX_PROTOCOL, "missing quoted string delimiter");
+	}
 	struct Data data = {
 		.type = String,
-		.string = strndup(ptr, len),
+		.string = strndup(imap->ptr, len),
 	};
 	if (!data.string) err(EX_OSERR, "strndup");
-	ptr += len + 1;
+	imap->ptr += len + 1;
 	return data;
 }
 
-static struct Data parseLiteral(FILE *imap) {
-	ptr++;
-	size_t len = strtoull(ptr, &ptr, 10);
-	if (*ptr != '}') errx(EX_PROTOCOL, "missing literal prefix delimiter");
+static struct Data parseLiteral(struct IMAP *imap) {
+	imap->ptr++;
+	size_t len = strtoull(imap->ptr, &imap->ptr, 10);
+	if (*imap->ptr != '}') {
+		errx(EX_PROTOCOL, "missing literal prefix delimiter");
+	}
 	struct Data data = {
 		.type = String,
 		.string = malloc(len + 1),
 	};
 	if (!data.string) err(EX_OSERR, "malloc");
-	size_t n = fread(data.string, len, 1, imap);
+	size_t n = fread(data.string, len, 1, imap->r);
 	if (!n) errx(EX_PROTOCOL, "truncated literal");
 	imapLine(imap);
 	data.string[len] = '\0';
 	return data;
 }
 
-static struct Data parseData(FILE *imap);
+static struct Data parseData(struct IMAP *imap);
 
-static struct Data parseList(FILE *imap, char close) {
-	if (*ptr) ptr++;
+static struct Data parseList(struct IMAP *imap, char close) {
+	if (*imap->ptr) imap->ptr++;
 	struct Data data = { .type = List };
-	while (*ptr != close) {
+	while (*imap->ptr != close) {
 		listPush(&data.list, parseData(imap));
 	}
-	if (*ptr) ptr++;
+	if (*imap->ptr) imap->ptr++;
 	return data;
 }
 
-static struct Data parseData(FILE *imap) {
-	if (*ptr == ' ') ptr++;
-	if (*ptr == '"') return parseQuoted();
-	if (*ptr == '{') return parseLiteral(imap);
-	if (*ptr == '(') return parseList(imap, ')');
-	if (*ptr == '[') return parseList(imap, ']');
-	if (*ptr >= '0' && *ptr <= '9') return parseNumber();
-	if (*ptr) return parseAtom();
+static struct Data parseData(struct IMAP *imap) {
+	if (*imap->ptr == ' ') imap->ptr++;
+	if (*imap->ptr == '"') return parseQuoted(imap);
+	if (*imap->ptr == '{') return parseLiteral(imap);
+	if (*imap->ptr == '(') return parseList(imap, ')');
+	if (*imap->ptr == '[') return parseList(imap, ']');
+	if (*imap->ptr >= '0' && *imap->ptr <= '9') return parseNumber(imap);
+	if (*imap->ptr) return parseAtom(imap);
 	errx(EX_PROTOCOL, "unexpected eof");
 }
 
-struct Resp imapResp(FILE *imap) {
+struct Resp imapResp(struct IMAP *imap) {
 	struct Data data;
 	struct Resp resp = {0};
 	imapLine(imap);
@@ -185,8 +196,8 @@ struct Resp imapResp(FILE *imap) {
 	if (data.type != Atom) errx(EX_PROTOCOL, "expected tag atom");
 	resp.tag = data.atom;
 	if (resp.tag == AtomContinue) {
-		if (*ptr == ' ') ptr++;
-		resp.text = ptr;
+		if (*imap->ptr == ' ') imap->ptr++;
+		resp.text = imap->ptr;
 		return resp;
 	}
 
@@ -205,13 +216,13 @@ struct Resp imapResp(FILE *imap) {
 		resp.resp == AtomPreauth ||
 		resp.resp == AtomBye
 	) {
-		if (*ptr == ' ') ptr++;
-		if (*ptr == '[') {
+		if (*imap->ptr == ' ') imap->ptr++;
+		if (*imap->ptr == '[') {
 			data = parseList(imap, ']');
 			resp.code = data.list;
 		}
-		if (*ptr == ' ') ptr++;
-		resp.text = ptr;
+		if (*imap->ptr == ' ') imap->ptr++;
+		resp.text = imap->ptr;
 	} else {
 		data = parseList(imap, '\0');
 		resp.data = data.list;
diff --git a/imap.h b/imap.h
index 0f034c0..b744bcc 100644
--- a/imap.h
+++ b/imap.h
@@ -168,7 +168,16 @@ static inline void respFree(struct Resp resp) {
 }
 
 extern bool imapVerbose;
-void imapOpen(FILE **read, FILE **write, const char *host, const char *port);
-struct Resp imapResp(FILE *imapRead);
+
+struct IMAP {
+	FILE *r;
+	FILE *w;
+	size_t cap;
+	char *buf;
+	char *ptr;
+};
+
+struct IMAP imapOpen(const char *host, const char *port);
+struct Resp imapResp(struct IMAP *imap);
 
 #endif /* IMAP_H */