summary refs log tree commit diff
path: root/export.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--export.c83
1 files changed, 73 insertions, 10 deletions
diff --git a/export.c b/export.c
index 21f5662..9aa3126 100644
--- a/export.c
+++ b/export.c
@@ -15,11 +15,14 @@
  */
 
 #include <err.h>
+#include <errno.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
+#include <sys/stat.h>
 #include <sysexits.h>
 #include <unistd.h>
 
@@ -115,8 +118,58 @@ static void exportAtom(
 	if (error) err(EX_IOERR, "%s", path);
 }
 
+static struct Attachment exportAttachment(
+	const struct Envelope *envelope, struct List section,
+	const struct BodyPart *part, struct Data body
+) {
+	struct Attachment attach = { "", "", "" };
+	strlcpy(
+		attach.path[0], pathSafe(envelope->messageID), sizeof(attach.path[0])
+	);
+	for (size_t i = 0; i < section.len; ++i) {
+		uint32_t num = dataCheck(section.ptr[i], Number).number;
+		char buf[32];
+		snprintf(buf, sizeof(buf), "%s%" PRIu32, (i ? "." : ""), num);
+		strlcat(attach.path[1], buf, sizeof(attach.path[1]));
+	}
+	struct List params = part->disposition.params;
+	for (size_t i = 0; i + 1 < params.len; i += 2) {
+		const char *key = dataCheck(params.ptr[i], String).string;
+		if (strcasecmp(key, "filename")) continue;
+		const char *value = dataCheck(params.ptr[i + 1], String).string;
+		strlcpy(attach.path[2], pathSafe(value), sizeof(attach.path[2]));
+	}
+	if (!attach.path[2][0]) {
+		const char *disposition = part->disposition.type;
+		if (!disposition) disposition = "INLINE";
+		strlcat(attach.path[2], pathSafe(disposition), sizeof(attach.path[2]));
+		strlcat(attach.path[2], ".", sizeof(attach.path[2]));
+		strlcat(attach.path[2], pathSafe(part->subtype), sizeof(attach.path[2]));
+	}
+
+	char path[PATH_MAX + 1] = "attachment";
+	for (int i = 0; i < 2; ++i) {
+		strlcat(path, "/", sizeof(path));
+		strlcat(path, attach.path[i], sizeof(path));
+		int error = mkdir(path, 0775);
+		if (error && errno != EEXIST) err(EX_CANTCREAT, "%s", path);
+	}
+	strlcat(path, "/", sizeof(path));
+	strlcat(path, attach.path[2], sizeof(path));
+
+	FILE *file = fopen(path, "w");
+	if (!file) err(EX_CANTCREAT, "%s", path);
+
+	int error = 0
+		|| decodeToFile(file, part, dataCheck(body, String).string)
+		|| fclose(file);
+	if (error) err(EX_IOERR, "%s", path);
+
+	return attach;
+}
+
 static int exportHTMLBody(
-	FILE *file, struct List *section,
+	FILE *file, const struct Envelope *envelope, struct List *section,
 	const struct BodyPart *part, struct Data body
 ) {
 	int error = 0;
@@ -124,36 +177,46 @@ static int exportHTMLBody(
 		for (size_t i = part->parts.len - 1; i < part->parts.len; --i) {
 			if (!isInline(&part->parts.ptr[i])) continue;
 			return exportHTMLBody(
-				file, section, &part->parts.ptr[i],
-				dataCheck(body, List).list.ptr[i]
+				file, envelope, section,
+				&part->parts.ptr[i], dataCheck(body, List).list.ptr[i]
 			);
 		}
 		return exportHTMLBody(
-			file, section, &part->parts.ptr[part->parts.len - 1],
+			file, envelope, section,
+			&part->parts.ptr[part->parts.len - 1],
 			dataCheck(body, List).list.ptr[part->parts.len - 1]
 		);
+
 	} else if (part->multipart) {
 		for (size_t i = 0; i < part->parts.len; ++i) {
 			struct Data num = { .type = Number, .number = 1 + i };
 			listPush(section, num);
 			error = exportHTMLBody(
-				file, section, &part->parts.ptr[i],
-				dataCheck(body, List).list.ptr[i]
+				file, envelope, section,
+				&part->parts.ptr[i], dataCheck(body, List).list.ptr[i]
 			);
 			if (error) return error;
 			section->len--;
 		}
-	} else if (part->message.envelope) {
+
+	} else if (part->message.structure) {
+		const struct BodyPart *structure = part->message.structure;
 		error = 0
 			|| htmlMessageOpen(file, part->message.envelope)
-			|| exportHTMLBody(file, section, part->message.structure, body)
+			|| exportHTMLBody(file, envelope, section, structure, body)
 			|| htmlMessageClose(file);
+
 	} else if (isInline(part)) {
 		char *content = decodeToString(part, dataCheck(body, String).string);
 		error = htmlInline(file, part, content);
 		free(content);
+
 	} else {
-		// TODO: Write out attachment.
+		// TODO: Open and close attachment lists.
+		struct Attachment attach = exportAttachment(
+			envelope, *section, part, body
+		);
+		error = htmlAttachment(file, part, &attach);
 	}
 	return error;
 }
@@ -170,7 +233,7 @@ static void exportHTML(
 	if (error) err(EX_IOERR, "%s", path);
 
 	struct List section = {0};
-	error = exportHTMLBody(file, &section, structure, body);
+	error = exportHTMLBody(file, envelope, &section, structure, body);
 	if (error) err(EX_IOERR, "%s", path);
 	listFree(section);