about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-04-12 21:56:44 -0400
committerJune McEnroe <june@causal.agency>2020-04-12 21:56:44 -0400
commit27ce93d8778ccf68c8b436454213e6f6fe826a38 (patch)
treeddb766133be35390ad3cce7d6e25c608afe74de1
parentRefactor exportData some (diff)
downloadbubger-27ce93d8778ccf68c8b436454213e6f6fe826a38.tar.gz
bubger-27ce93d8778ccf68c8b436454213e6f6fe826a38.zip
Concatenate HTML threads
-rw-r--r--archive.h5
-rw-r--r--concat.c67
-rw-r--r--html.c59
3 files changed, 113 insertions, 18 deletions
diff --git a/archive.h b/archive.h
index addd94d..541c03c 100644
--- a/archive.h
+++ b/archive.h
@@ -184,6 +184,11 @@ int mboxBody(FILE *file, const char *body);
 
 int htmlMessageHead(FILE *file, const struct Envelope *envelope);
 int htmlMessageTail(FILE *file);
+int htmlThreadHead(FILE *file, const struct Envelope *envelope);
+int htmlThreadHeader(FILE *file, const struct Envelope *envelope);
+int htmlThreadOpen(FILE *file);
+int htmlThreadClose(FILE *file);
+int htmlThreadTail(FILE *file);
 
 int atomEntryHead(FILE *file, const struct Envelope *envelope);
 int atomEntryTail(FILE *file);
diff --git a/concat.c b/concat.c
index a2b767a..6d0e507 100644
--- a/concat.c
+++ b/concat.c
@@ -77,6 +77,27 @@ static int concatFile(FILE *dst, const char *path) {
 	return 0;
 }
 
+static int concatHTML(FILE *file, struct List thread) {
+	static char path[PATH_MAX];
+	int error;
+	for (size_t i = 0; i < thread.len; ++i) {
+		error = htmlThreadOpen(file);
+		if (error) return error;
+		if (thread.ptr[i].type == List) {
+			error = concatHTML(file, thread.ptr[i].list);
+		} else {
+			uint32_t uid = dataCheck(thread.ptr[i], Number).number;
+			error = concatFile(file, pathUID(path, uid, "html"));
+		}
+		if (error) return error;
+	}
+	for (size_t i = 0; i < thread.len; ++i) {
+		error = htmlThreadClose(file);
+		if (error) return error;
+	}
+	return 0;
+}
+
 void concatData(struct List threads, struct List items) {
 	uint32_t uid = 0;
 	struct Envelope envelope = {0};
@@ -99,43 +120,55 @@ void concatData(struct List threads, struct List items) {
 	listFlatten(&flat, thread);
 
 	int error;
-	struct stat file;
+	FILE *file;
+	struct stat status;
 	char dst[PATH_MAX];
 	char src[PATH_MAX];
 
-	pathThread(dst, envelope.messageID, "mbox");
-	error = stat(dst, &file);
-	if (error || file.st_mtime < uidNewest(flat, "mbox")) {
-		FILE *mbox = fopen(dst, "w");
-		if (!mbox) err(EX_CANTCREAT, "%s", dst);
+	error = stat(pathThread(dst, envelope.messageID, "mbox"), &status);
+	if (error || status.st_mtime < uidNewest(flat, "mbox")) {
+		file = fopen(dst, "w");
+		if (!file) err(EX_CANTCREAT, "%s", dst);
 
 		for (size_t i = 0; i < flat.len; ++i) {
 			uint32_t uid = dataCheck(flat.ptr[i], Number).number;
-			error = concatFile(mbox, pathUID(src, uid, "mbox"));
+			error = concatFile(file, pathUID(src, uid, "mbox"));
 			if (error) err(EX_IOERR, "%s", dst);
 		}
 
-		error = fclose(mbox);
+		error = fclose(file);
 		if (error) err(EX_IOERR, "%s", dst);
 	}
 
-	pathThread(dst, envelope.messageID, "atom");
-	error = stat(dst, &file);
-	if (error || file.st_mtime < uidNewest(flat, "atom")) {
-		FILE *atom = fopen(dst, "w");
-		if (!atom) err(EX_CANTCREAT, "%s", dst);
+	error = stat(pathThread(dst, envelope.messageID, "atom"), &status);
+	if (error || status.st_mtime < uidNewest(flat, "atom")) {
+		FILE *file = fopen(dst, "w");
+		if (!file) err(EX_CANTCREAT, "%s", dst);
 
-		error = atomFeedHead(atom, &envelope);
+		error = atomFeedHead(file, &envelope);
 		if (error) err(EX_IOERR, "%s", dst);
 
 		for (size_t i = 0; i < flat.len; ++i) {
 			uint32_t uid = dataCheck(flat.ptr[i], Number).number;
-			error = concatFile(atom, pathUID(src, uid, "atom"));
+			error = concatFile(file, pathUID(src, uid, "atom"));
 			if (error) err(EX_IOERR, "%s", dst);
 		}
 
-		error = atomFeedTail(atom)
-			|| fclose(atom);
+		error = atomFeedTail(file) || fclose(file);
+		if (error) err(EX_IOERR, "%s", dst);
+	}
+
+	error = stat(pathThread(dst, envelope.messageID, "html"), &status);
+	if (error || status.st_mtime < uidNewest(flat, "html")) {
+		FILE *file = fopen(dst, "w");
+		if (!file) err(EX_CANTCREAT, "%s", dst);
+
+		error = 0
+			|| htmlThreadHead(file, &envelope) // TODO: Include -h file.
+			|| htmlThreadHeader(file, &envelope)
+			|| concatHTML(file, thread)
+			|| htmlThreadTail(file)
+			|| fclose(file);
 		if (error) err(EX_IOERR, "%s", dst);
 	}
 
diff --git a/html.c b/html.c
index de64ed8..e92ab0a 100644
--- a/html.c
+++ b/html.c
@@ -79,6 +79,7 @@ int htmlMessageHead(FILE *file, const struct Envelope *envelope) {
 		{ "re", (strncmp(envelope->subject, "Re: ", 4) ? "Re: " : "") },
 		{ "subject", envelope->subject },
 		{ "messageID", envelope->messageID },
+		{ "pathID", pathMangle(envelope->messageID) },
 		{0},
 	};
 	char *fragment = templateURL("#[messageID]", urlVars);
@@ -86,7 +87,7 @@ int htmlMessageHead(FILE *file, const struct Envelope *envelope) {
 		"mailto:[mailbox]@[host]?subject=[re][subject]&In-Reply-To=[messageID]",
 		urlVars
 	);
-	char *mbox = templateURL("../message/[messageID].mbox", urlVars);
+	char *mbox = templateURL("../message/[pathID].mbox", urlVars);
 
 	char date[256];
 	char utc[sizeof("0000-00-00T00:00:00Z")];
@@ -127,3 +128,59 @@ int htmlMessageTail(FILE *file) {
 	int n = fprintf(file, "</details>\n");
 	return (n < 0 ? n : 0);
 }
+
+int htmlThreadHead(FILE *file, const struct Envelope *envelope) {
+	struct Variable urlVars[] = {
+		{ "pathID", pathMangle(envelope->messageID) },
+		{0},
+	};
+	char *path = templateURL("[pathID]", urlVars);
+
+	struct Variable vars[] = {
+		{ "subject", envelope->subject },
+		{ "path", path },
+		{0},
+	};
+	const char *Head = TEMPLATE(
+		<!DOCTYPE html>
+		<meta charset="utf-8">
+		<title>[subject]</title>
+		<link rel="alternate" type="application/atom+xml" href="[path].atom">
+		<link rel="alternate" type="application/mbox" href="[path].mbox">
+		<style>
+		address { display: inline; }
+		section.thread section.thread:not(:first-child) {
+			border-left: 1px solid gray;
+			padding-left: 2ch;
+		}
+		</style>
+	);
+	int error = templateRender(file, Head, vars, escapeXML);
+	free(path);
+	return error;
+}
+
+int htmlThreadHeader(FILE *file, const struct Envelope *envelope) {
+	struct Variable vars[] = {
+		{ "subject", envelope->subject },
+		{0},
+	};
+	const char *Header = TEMPLATE(
+		<h1>[subject]</h1>
+	);
+	return templateRender(file, Header, vars, escapeXML);
+}
+
+int htmlThreadOpen(FILE *file) {
+	int n = fprintf(file, TEMPLATE(<section class="thread">));
+	return (n < 0 ? n : 0);
+}
+
+int htmlThreadClose(FILE *file) {
+	int n = fprintf(file, TEMPLATE(</section>));
+	return (n < 0 ? n : 0);
+}
+
+int htmlThreadTail(FILE *file) {
+	return 0;
+}