about summary refs log tree commit diff
path: root/archive.c
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-04-10 14:20:35 -0400
committerJune McEnroe <june@causal.agency>2020-04-10 14:20:35 -0400
commit4d541ff929d0c320c382d6ebd6569c0c7faf8304 (patch)
tree0d15cc5675aedd31ac6d9c2b219ed6b0bb11ef7e /archive.c
parentFix UIDNEXT check and write (diff)
downloadbubger-4d541ff929d0c320c382d6ebd6569c0c7faf8304.tar.gz
bubger-4d541ff929d0c320c382d6ebd6569c0c7faf8304.zip
Refactor main loop state machine
Diffstat (limited to 'archive.c')
-rw-r--r--archive.c166
1 files changed, 92 insertions, 74 deletions
diff --git a/archive.c b/archive.c
index e741b4e..3b7d003 100644
--- a/archive.c
+++ b/archive.c
@@ -103,101 +103,119 @@ int main(int argc, char *argv[]) {
 		if (!pass) errx(EX_CONFIG, ENV_PASSWORD " unset");
 	}
 
-	enum Atom login = 0;
-	enum Atom examine = atom("EXAMINE");
-	enum Atom thread = atom("THREAD");
-	enum Atom export = 0;
-	enum Atom concat = 0;
+	enum Atom AtomThread = atom("THREAD");
+
+	enum {
+		Ready,
+		Login,
+		Examine,
+		Thread,
+		Export,
+		Concat,
+		Logout,
+	} state = Ready;
+
+	enum Atom login = atom("login");
+	enum Atom examine = atom("examine");
+	enum Atom thread = atom("thread");
+	enum Atom export = atom("export");
+	enum Atom concat = atom("concat");
 
 	uint32_t uidNext = 0;
 	struct List threads = {0};
-
 	FILE *imap = imapOpen(host, port);
 	for (struct Resp resp; resp = imapResp(imap), resp.resp != AtomBye;) {
 		if (resp.resp == AtomNo || resp.resp == AtomBad) {
 			errx(EX_CONFIG, "%s %s", Atoms[resp.resp], resp.text);
 		}
 
-		if (!login) {
-			login = atom("login");
-			fprintf(
-				imap, "%s LOGIN \"%s\" \"%s\"\r\n",
-				Atoms[login], user, pass
-			);
-		}
+		switch (state) {
+			break; case Ready: {
+				fprintf(
+					imap, "%s LOGIN \"%s\" \"%s\"\r\n",
+					Atoms[login], user, pass
+				);
+				state = Login;
+			}
 
-		if (resp.tag == login) {
-			fprintf(imap, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox);
-		}
+			break; case Login: {
+				if (resp.tag != login) break;
+				fprintf(imap, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox);
+				state = Examine;
+			}
 
-		if (
-			resp.resp == AtomOk &&
-			resp.code.len > 1 &&
-			resp.code.ptr[0].type == Atom
-		) {
-			enum Atom code = resp.code.ptr[0].atom;
-			if (code == AtomUIDValidity || code == AtomUIDNext) {
-				if (resp.code.ptr[1].type != Number) {
-					errx(EX_PROTOCOL, "invalid %s", Atoms[code]);
+			break; case Examine: {
+				if (resp.resp == AtomOk && resp.code.len > 1) {
+					enum Atom code = dataCheck(resp.code.ptr[0], Atom).atom;
+					struct Data value = resp.code.ptr[1];
+					if (code == AtomUIDValidity) {
+						uint32_t validity = dataCheck(value, Number).number;
+						uint32_t previous = uidRead("UIDVALIDITY");
+						if (previous && validity != previous) {
+							errx(
+								EX_TEMPFAIL,
+								"UIDVALIDITY changed; fresh export required"
+							);
+						}
+						if (!previous) uidWrite("UIDVALIDITY", validity);
+					} else if (code == AtomUIDNext) {
+						uidNext = dataCheck(value, Number).number;
+						uint32_t prev = uidRead("UIDNEXT");
+						if (uidNext == prev) {
+							fprintf(imap, "ayy LOGOUT\r\n");
+							state = Logout;
+						}
+					}
 				}
+
+				if (resp.tag != examine) break;
+				fprintf(
+					imap, "%s UID THREAD %s UTF-8 %s\r\n",
+					Atoms[thread], algo, search
+				);
+				state = Thread;
 			}
-			if (code == AtomUIDValidity) {
-				uint32_t validity = resp.code.ptr[1].number;
-				uint32_t previous = uidRead("UIDVALIDITY");
-				if (previous && validity != previous) {
-					errx(
-						EX_TEMPFAIL,
-						"UIDVALIDITY changed; fresh export required"
-					);
+
+			break; case Thread: {
+				if (resp.resp != AtomThread) break;
+				if (!resp.data.len) {
+					errx(EX_TEMPFAIL, "no messages matching %s", search);
 				}
-				if (!previous) uidWrite("UIDVALIDITY", validity);
-			}
-			if (code == AtomUIDNext) {
-				uidNext = resp.code.ptr[1].number;
-				uint32_t prev = uidRead("UIDNEXT");
-				if (uidNext == prev) {
-					examine = 0;
-					fprintf(imap, "ayy LOGOUT\r\n");
+				createDir("UID");
+				createDir("message");
+				threads = resp.data;
+				resp.data = (struct List) {0};
+				if (exportFetch(imap, export, threads)) {
+					state = Export;
+				} else {
+					concatFetch(imap, concat, threads);
+					state = Concat;
 				}
 			}
-		}
 
-		if (resp.tag == examine) {
-			fprintf(
-				imap, "%s UID THREAD %s UTF-8 %s\r\n",
-				Atoms[thread], algo, search
-			);
-		}
-
-		if (resp.resp == thread) {
-			if (!resp.data.len) {
-				errx(EX_TEMPFAIL, "no messages matching %s", search);
+			break; case Export: {
+				if (resp.resp == AtomFetch) {
+					if (!resp.data.len) errx(EX_PROTOCOL, "missing FETCH data");
+					exportData(dataCheck(resp.data.ptr[0], List).list);
+				}
+				if (resp.tag != export) break;
+				concatFetch(imap, concat, threads);
+				state = Concat;
 			}
-			createDir("UID");
-			createDir("message");
-			threads = resp.data;
-			resp.data = (struct List) {0};
-			export = exportFetch(imap, threads);
-			if (!export) concat = concatFetch(imap, threads);
-		}
 
-		if (export && resp.resp == AtomFetch) {
-			if (!resp.data.len || resp.data.ptr[0].type != List) {
-				errx(EX_PROTOCOL, "invalid FETCH data");
+			break; case Concat: {
+				if (resp.resp == AtomFetch) {
+					if (!resp.data.len) errx(EX_PROTOCOL, "missing FETCH data");
+					concatData(threads, dataCheck(resp.data.ptr[0], List).list);
+				}
+				if (resp.tag != concat) break;
+				uidWrite("UIDNEXT", uidNext);
+				fprintf(imap, "ayy LOGOUT\r\n");
+				state = Logout;
 			}
-			exportData(resp.data.ptr[0].list);
+			
+			break; case Logout:;
 		}
-
-		if (export && resp.tag == export) {
-			export = 0;
-			concat = concatFetch(imap, threads);
-		}
-
-		if (concat && resp.tag == concat) {
-			uidWrite("UIDNEXT", uidNext);
-			fprintf(imap, "ayy LOGOUT\r\n");
-		}
-
 		respFree(resp);
 	}
 	fclose(imap);