summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--imap.c107
-rw-r--r--imap.h13
-rw-r--r--imbox.c33
3 files changed, 82 insertions, 71 deletions
diff --git a/imap.c b/imap.c
index b4aa6be..77381f7 100644
--- a/imap.c
+++ b/imap.c
@@ -75,7 +75,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");
 
@@ -89,96 +89,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);
@@ -187,8 +190,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;
 	}
 
@@ -207,13 +210,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 c2b7af1..0af96ff 100644
--- a/imap.h
+++ b/imap.h
@@ -163,7 +163,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 */
diff --git a/imbox.c b/imbox.c
index 08b7181..e58663e 100644
--- a/imbox.c
+++ b/imbox.c
@@ -185,11 +185,10 @@ int main(int argc, char *argv[]) {
 	enum Atom login = 0;
 	enum Atom examine = atom("examine");
 
-	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) {
@@ -199,33 +198,33 @@ int main(int argc, char *argv[]) {
 		if (!login) {
 			login = atom("login");
 			fprintf(
-				imap, "%s LOGIN \"%s\" \"%s\"\r\n",
+				imap.w, "%s LOGIN \"%s\" \"%s\"\r\n",
 				Atoms[login], user, pass
 			);
 		}
 
 		if (resp.tag == login) {
-			fprintf(imap, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox);
+			fprintf(imap.w, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox);
 		}
 
 		if (resp.tag == examine) {
-			fprintf(imap, "%s SEARCH CHARSET UTF-8", Atoms[AtomSearch]);
-			if (subject) fprintf(imap, " SUBJECT \"%s\"", subject);
-			if (from) fprintf(imap, " FROM \"%s\"", from);
-			if (to) fprintf(imap, " TO \"%s\"", to);
-			if (cc) fprintf(imap, " CC \"%s\"", cc);
-			fprintf(imap, "\r\n");
+			fprintf(imap.w, "%s SEARCH CHARSET UTF-8", Atoms[AtomSearch]);
+			if (subject) fprintf(imap.w, " SUBJECT \"%s\"", subject);
+			if (from) fprintf(imap.w, " FROM \"%s\"", from);
+			if (to) fprintf(imap.w, " TO \"%s\"", to);
+			if (cc) fprintf(imap.w, " CC \"%s\"", cc);
+			fprintf(imap.w, "\r\n");
 		}
 
 		if (resp.resp == AtomSearch) {
 			if (!resp.data.len) errx(EX_TEMPFAIL, "no matching messages");
-			fprintf(imap, "%s FETCH ", Atoms[AtomFetch]);
+			fprintf(imap.w, "%s FETCH ", Atoms[AtomFetch]);
 			for (size_t i = 0; i < resp.data.len; ++i) {
 				uint32_t num = dataCheck(resp.data.ptr[i], Number).number;
-				fprintf(imap, "%s%" PRIu32, (i ? "," : ""), num);
+				fprintf(imap.w, "%s%" PRIu32, (i ? "," : ""), num);
 			}
 			fprintf(
-				imap,
+				imap.w,
 				" (BODY[HEADER.FIELDS (" FETCH_HEADERS ")] BODY[TEXT])\r\n"
 			);
 		}
@@ -254,9 +253,9 @@ int main(int argc, char *argv[]) {
 		}
 
 		if (resp.tag == AtomFetch) {
-			fprintf(imap, "ayy LOGOUT\r\n");
+			fprintf(imap.w, "ayy LOGOUT\r\n");
 		}
 	}
-	fclose(imapRead);
-	fclose(imap);
+	fclose(imap.r);
+	fclose(imap.w);
 }