about summary refs log tree commit diff
path: root/export.c
diff options
context:
space:
mode:
Diffstat (limited to 'export.c')
-rw-r--r--export.c124
1 files changed, 68 insertions, 56 deletions
diff --git a/export.c b/export.c
index 9aa3126..873870c 100644
--- a/export.c
+++ b/export.c
@@ -29,15 +29,28 @@
 #include "archive.h"
 #include "imap.h"
 
+static const char *exportPath(uint32_t uid, const char *type) {
+	char str[32];
+	snprintf(str, sizeof(str), "%" PRIu32, uid);
+	struct Variable vars[] = {
+		{ "uid", str },
+		{ "type", type },
+		{0},
+	};
+	static char buf[PATH_MAX + 1];
+	templateBuffer(buf, sizeof(buf), PATH_UID, vars, escapePath);
+	return buf;
+}
+
 bool exportFetch(FILE *imap, enum Atom tag, struct List threads) {
 	struct List uids = {0};
 	listFlatten(&uids, threads);
 	for (size_t i = uids.len - 1; i < uids.len; --i) {
 		uint32_t uid = dataCheck(uids.ptr[i], Number).number;
 		int error = 0
-			|| access(pathUID(uid, "atom"), F_OK)
-			|| access(pathUID(uid, "html"), F_OK)
-			|| access(pathUID(uid, "mbox"), F_OK);
+			|| access(exportPath(uid, "atom"), F_OK)
+			|| access(exportPath(uid, "html"), F_OK)
+			|| access(exportPath(uid, "mbox"), F_OK);
 		if (!error) uids.ptr[i] = uids.ptr[--uids.len];
 	}
 	if (!uids.len) {
@@ -61,7 +74,7 @@ static void exportMbox(
 	uint32_t uid, const struct Envelope *envelope,
 	const char *header, const char *body
 ) {
-	const char *path = pathUID(uid, "mbox");
+	const char *path = exportPath(uid, "mbox");
 	FILE *file = fopen(path, "w");
 	if (!file) err(EX_CANTCREAT, "%s", path);
 	int error = 0
@@ -71,10 +84,17 @@ static void exportMbox(
 		|| fclose(file);
 	if (error) err(EX_IOERR, "%s", path);
 
-	const char *msg = pathMessage(envelope->messageID, "mbox");
-	unlink(msg);
-	error = link(path, msg);
-	if (error) err(EX_CANTCREAT, "%s", msg);
+	char buf[PATH_MAX + 1];
+	struct Variable vars[] = {
+		{ "messageID", envelope->messageID },
+		{ "type", "mbox" },
+		{0},
+	};
+	templateBuffer(buf, sizeof(buf), PATH_MESSAGE, vars, escapePath);
+
+	unlink(buf);
+	error = link(path, buf);
+	if (error) err(EX_CANTCREAT, "%s", buf);
 }
 
 static bool isInline(const struct BodyPart *part) {
@@ -87,7 +107,7 @@ static void exportAtom(
 	uint32_t uid, const struct Envelope *envelope,
 	const struct BodyPart *structure, struct Data body
 ) {
-	const char *path = pathUID(uid, "atom");
+	const char *path = exportPath(uid, "atom");
 	FILE *file = fopen(path, "w");
 	if (!file) err(EX_CANTCREAT, "%s", path);
 
@@ -118,61 +138,55 @@ static void exportAtom(
 	if (error) err(EX_IOERR, "%s", path);
 }
 
-static struct Attachment exportAttachment(
-	const struct Envelope *envelope, struct List section,
+static int exportHTMLAttachment(
+	FILE *file, 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 buf[256] = "";
+	for (size_t i = 0; i < section->len; ++i) {
+		snprintf(
+			&buf[strlen(buf)], sizeof(buf) - strlen(buf), "%s%" PRIu32,
+			(i ? "." : ""), dataCheck(section->ptr[i], Number).number
+		);
 	}
-
-	char path[PATH_MAX + 1] = "attachment";
-	for (int i = 0; i < 2; ++i) {
-		strlcat(path, "/", sizeof(path));
-		strlcat(path, attach.path[i], sizeof(path));
+	const char *name = paramGet(part->disposition.params, "filename");
+	const char *disposition = part->disposition.type;
+	if (!disposition) disposition = "INLINE";
+
+	char path[PATH_MAX + 1];
+	struct Variable vars[] = {
+		{ "messageID", envelope->messageID },
+		{ "section", buf },
+		{ "name", (name ? name : "") },
+		{ "disposition", (name ? "" : disposition) },
+		{ ".", (name ? "" : ".") },
+		{ "subtype", (name ? "" : part->subtype) },
+		{0},
+	};
+	templateBuffer(path, sizeof(path), PATH_ATTACHMENT, vars, escapePath);
+
+	for (char *ch = path; (ch = strchr(ch, '/')); ++ch) {
+		*ch = '\0';
 		int error = mkdir(path, 0775);
 		if (error && errno != EEXIST) err(EX_CANTCREAT, "%s", path);
+		*ch = '/';
 	}
-	strlcat(path, "/", sizeof(path));
-	strlcat(path, attach.path[2], sizeof(path));
 
-	FILE *file = fopen(path, "w");
+	FILE *attachment = fopen(path, "w");
 	if (!file) err(EX_CANTCREAT, "%s", path);
 
 	int error = 0
-		|| decodeToFile(file, part, dataCheck(body, String).string)
-		|| fclose(file);
+		|| decodeToFile(attachment, part, dataCheck(body, String).string)
+		|| fclose(attachment);
 	if (error) err(EX_IOERR, "%s", path);
 
-	return attach;
+	return htmlAttachment(file, part, vars);
 }
 
 static int exportHTMLBody(
 	FILE *file, const struct Envelope *envelope, struct List *section,
 	const struct BodyPart *part, struct Data body
 ) {
-	int error = 0;
 	if (bodyPartType(part, "multipart", "alternative")) {
 		for (size_t i = part->parts.len - 1; i < part->parts.len; --i) {
 			if (!isInline(&part->parts.ptr[i])) continue;
@@ -191,41 +205,39 @@ static int exportHTMLBody(
 		for (size_t i = 0; i < part->parts.len; ++i) {
 			struct Data num = { .type = Number, .number = 1 + i };
 			listPush(section, num);
-			error = exportHTMLBody(
+			int error = exportHTMLBody(
 				file, envelope, section,
 				&part->parts.ptr[i], dataCheck(body, List).list.ptr[i]
 			);
 			if (error) return error;
 			section->len--;
 		}
+		return 0;
 
 	} else if (part->message.structure) {
 		const struct BodyPart *structure = part->message.structure;
-		error = 0
+		int error = 0
 			|| htmlMessageOpen(file, part->message.envelope)
 			|| exportHTMLBody(file, envelope, section, structure, body)
 			|| htmlMessageClose(file);
+		return error;
 
 	} else if (isInline(part)) {
 		char *content = decodeToString(part, dataCheck(body, String).string);
-		error = htmlInline(file, part, content);
+		int error = htmlInline(file, part, content);
 		free(content);
+		return error;
 
 	} else {
-		// TODO: Open and close attachment lists.
-		struct Attachment attach = exportAttachment(
-			envelope, *section, part, body
-		);
-		error = htmlAttachment(file, part, &attach);
+		return exportHTMLAttachment(file, envelope, section, part, body);
 	}
-	return error;
 }
 
 static void exportHTML(
 	uint32_t uid, const struct Envelope *envelope,
 	const struct BodyPart *structure, struct Data body
 ) {
-	const char *path = pathUID(uid, "html");
+	const char *path = exportPath(uid, "html");
 	FILE *file = fopen(path, "w");
 	if (!file) err(EX_CANTCREAT, "%s", path);