summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-04-13 17:19:13 -0400
committerJune McEnroe <june@causal.agency>2020-04-13 17:19:13 -0400
commit8d72b2e967c36c2117a9ad9ffd46d42515996403 (patch)
treec8b8ce1ac6aac2bd4fbd90f6c19d66aee61c69ef
parentUse two FILEs for IMAP (diff)
downloadbubger-8d72b2e967c36c2117a9ad9ffd46d42515996403.tar.gz
bubger-8d72b2e967c36c2117a9ad9ffd46d42515996403.zip
Fetch multipart body parts
-rw-r--r--export.c114
1 files changed, 81 insertions, 33 deletions
diff --git a/export.c b/export.c
index 54178f6..52f6f88 100644
--- a/export.c
+++ b/export.c
@@ -101,54 +101,102 @@ static void exportAtom(
 	if (error) err(EX_IOERR, "%s", path);
 }
 
+static void exportFetchParts(
+	FILE *imap, struct List *parts, const struct BodyPart *structure
+) {
+	if (structure->multipart) {
+		for (size_t i = 0; i < structure->parts.len; ++i) {
+			struct Data part = { .type = Number, .number = 1 + i };
+			listPush(parts, part);
+			exportFetchParts(imap, parts, &structure->parts.ptr[i]);
+			parts->len--;
+		}
+	} else if (structure->message.structure) {
+		exportFetchParts(imap, parts, structure->message.structure);
+	} else {
+		fprintf(imap, " BODY[");
+		for (size_t i = 0; i < parts->len; ++i) {
+			fprintf(imap, "%s%" PRIu32, (i ? "." : ""), parts->ptr[i].number);
+		}
+		fprintf(imap, "]");
+	}
+}
+
 bool exportData(FILE *imap, enum Atom tag, struct List items) {
 	uint32_t uid = 0;
 	struct Envelope envelope = {0};
 	struct BodyPart structure = {0};
 	const char *header = NULL;
-	const char *body = NULL;
+	const char *text = NULL;
 
 	for (size_t i = 0; i + 1 < items.len; i += 2) {
-		enum Atom name;
-		if (items.ptr[i].type == Atom) {
-			name = items.ptr[i].atom;
-		} else if (
-			items.ptr[i].type == List &&
-			items.ptr[i].list.len &&
-			items.ptr[i].list.ptr[0].type == Atom
-		) {
-			name = items.ptr[i].list.ptr[0].atom;
-		} else {
-			errx(EX_PROTOCOL, "invalid data item name");
+		enum Atom name = dataCheck(items.ptr[i], Atom).atom;
+		struct Data data = items.ptr[i + 1];
+		if (name == AtomUID) {
+			uid = dataCheck(data, Number).number;
+		} else if (name == AtomEnvelope) {
+			parseEnvelope(&envelope, dataCheck(data, List).list);
+		} else if (name == AtomBodyStructure) {
+			parseBodyPart(&structure, dataCheck(data, List).list);
 		}
+		if (name != AtomBody) continue;
 
-		struct Data data = items.ptr[i + 1];
-		switch (name) {
-			break; case AtomBody:
-				i--;
-			break; case AtomUID:
-				uid = dataCheck(data, Number).number;
-			break; case AtomEnvelope:
-				parseEnvelope(&envelope, dataCheck(data, List).list);
-			break; case AtomBodyStructure:
-				parseBodyPart(&structure, dataCheck(data, List).list);
-			break; case AtomHeaderFields:
+		struct List section = dataCheck(data, List).list;
+		if (!section.len) {
+			errx(EX_PROTOCOL, "missing body data item section");
+		}
+		if (i + 2 >= items.len) {
+			errx(EX_PROTOCOL, "missing body data item value");
+		}
+		data = items.ptr[++i + 1];
+
+		if (section.ptr[0].type == Atom) {
+			name = section.ptr[0].atom;
+			if (name == AtomHeaderFields) {
 				header = dataCheck(data, String).string;
-			break; case AtomText:
-				body = dataCheck(data, String).string;
-			break; default:;
+			} else if (name == AtomText) {
+				text = dataCheck(data, String).string;
+			}
+			continue;
 		}
+
+		// TODO: Build a structure of body data parallel to structure.
 	}
 
-	if (!uid) errx(EX_PROTOCOL, "missing UID data item");
-	if (!envelope.subject) errx(EX_PROTOCOL, "missing ENVELOPE data item");
-	if (!header) errx(EX_PROTOCOL, "missing BODY[HEADER.FIELDS] data item");
-	if (!body) errx(EX_PROTOCOL, "missing BODY[TEXT] data item");
+	if (!uid) {
+		errx(EX_PROTOCOL, "missing UID data item");
+	}
+	if (!structure.subtype) {
+		errx(EX_PROTOCOL, "missing BODYSTRUCTURE data item");
+	}
 
-	exportMbox(uid, &envelope, header, body);
-	exportAtom(uid, &envelope, &structure, body);
+	bool fetch = false;
+	if (envelope.subject) {
+		if (!header) {
+			errx(EX_PROTOCOL, "missing BODY[HEADER.FIELDS] data item");
+		}
+		if (!text) {
+			errx(EX_PROTOCOL, "missing BODY[TEXT] data item");
+		}
+		exportMbox(uid, &envelope, header, text);
+		exportAtom(uid, &envelope, &structure, text);
+
+		if (structure.multipart) {
+			fetch = true;
+			fprintf(
+				imap, "%s UID FETCH %" PRIu32 " (UID BODYSTRUCTURE",
+				Atoms[tag], uid
+			);
+			struct List parts = {0};
+			exportFetchParts(imap, &parts, &structure);
+			listFree(parts);
+			fprintf(imap, ")\r\n");
+		}
+	} else {
+		// TODO: Correlate body parts to body data.
+	}
 
 	envelopeFree(envelope);
 	bodyPartFree(structure);
-	return false;
+	return fetch;
 }