diff options
author | June McEnroe <june@causal.agency> | 2020-04-09 18:55:16 -0400 |
---|---|---|
committer | June McEnroe <june@causal.agency> | 2020-04-09 18:55:16 -0400 |
commit | ef48616f2b63973830a07199c6bbe3b9a7ea6cc8 (patch) | |
tree | 7d92c7dc3560d75469e72e33f1f69dfc2089dcf1 | |
parent | Trim angle brackets from message IDs (diff) | |
download | bubger-ef48616f2b63973830a07199c6bbe3b9a7ea6cc8.tar.gz bubger-ef48616f2b63973830a07199c6bbe3b9a7ea6cc8.zip |
Render basic HTML envelopes with templating
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | archive.c | 18 | ||||
-rw-r--r-- | archive.h | 17 | ||||
-rw-r--r-- | html.c | 69 | ||||
-rw-r--r-- | template.c | 57 |
5 files changed, 159 insertions, 4 deletions
diff --git a/Makefile b/Makefile index 20b440f..74b0de4 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,10 @@ CFLAGS += -std=c11 -Wall -Wextra -Wpedantic LDLIBS = -ltls OBJS += archive.o +OBJS += html.o OBJS += imap.o OBJS += mbox.o +OBJS += template.o dev: tags all diff --git a/archive.c b/archive.c index 889f253..fd8ef53 100644 --- a/archive.c +++ b/archive.c @@ -240,16 +240,26 @@ static void exportMessage(struct List items) { if (!header) errx(EX_PROTOCOL, "missing BODY[HEADER.FIELDS] data item"); if (!body) errx(EX_PROTOCOL, "missing BODY[TEXT] data item"); - const char *path = uidPath(uid, "mbox"); - FILE *file = fopen(path, "w"); - if (!file) err(EX_CANTCREAT, "%s", path); + const char *path; + FILE *file; + int error; - int error = mboxFrom(file) + path = uidPath(uid, "mbox"); + file = fopen(path, "w"); + if (!file) err(EX_CANTCREAT, "%s", path); + error = mboxFrom(file) || mboxHeader(file, header) || mboxBody(file, body) || fclose(file); if (error) err(EX_IOERR, "%s", path); + path = uidPath(uid, "html"); + file = fopen(path, "w"); + if (!file) err(EX_CANTCREAT, "%s", path); + error = htmlEnvelope(file, &envelope) + || fclose(file); + if (error) err(EX_IOERR, "%s", path); + envelopeFree(envelope); } diff --git a/archive.h b/archive.h index d804021..8d96878 100644 --- a/archive.h +++ b/archive.h @@ -14,6 +14,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +#include <stdio.h> #include <stdlib.h> #include <time.h> @@ -56,6 +57,20 @@ static inline void envelopeFree(struct Envelope envelope) { free(envelope.bcc.addrs); } +#define TEMPLATE(...) #__VA_ARGS__ + +struct Variable { + const char *name; + const char *value; +}; + +typedef int EscapeFn(FILE *file, const char *str); + +int templateRender( + FILE *file, const char *template, + const struct Variable *vars, EscapeFn *escape +); + #define MBOX_HEADERS \ "Date Subject From Sender Reply-To To Cc Bcc " \ "Message-Id In-Reply-To References " \ @@ -64,3 +79,5 @@ static inline void envelopeFree(struct Envelope envelope) { int mboxFrom(FILE *file); int mboxHeader(FILE *file, char *header); int mboxBody(FILE *file, char *body); + +int htmlEnvelope(FILE *file, const struct Envelope *envelope); diff --git a/html.c b/html.c new file mode 100644 index 0000000..4903c5b --- /dev/null +++ b/html.c @@ -0,0 +1,69 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <time.h> + +#include "archive.h" + +static int htmlEscape(FILE *file, const char *str) { + while (*str) { + int n = 0; + switch (*str) { + break; case '"': n = fprintf(file, """); str++; + break; case '&': n = fprintf(file, "&"); str++; + break; case '<': n = fprintf(file, "<"); str++; + break; case '>': n = fprintf(file, ">"); str++; + } + if (n < 0) return n; + size_t len = strcspn(str, "\"&<>"); + if (len) { + size_t n = fwrite(str, len, 1, file); + if (!n) return -1; + } + str += len; + } + return 0; +} + +static const char *Summary = TEMPLATE( + <details id="[messageID]"> + <summary> + <h1><a href="#[messageID]">[subject]</a></h1> + <address> + <a href="mailto:[from.mailbox]@[from.host]">[from.name]</a> + </address> + </summary> +); + +int htmlEnvelope(FILE *file, const struct Envelope *envelope) { + const char *fromName = envelope->from.name; + if (!fromName) fromName = envelope->from.mailbox; + + struct Variable vars[] = { + { "subject", envelope->subject }, + { "from.name", fromName }, + { "from.mailbox", envelope->from.mailbox }, + { "from.host", envelope->from.host }, + { "messageID", envelope->messageID }, + {0}, + }; + return templateRender(file, Summary, vars, htmlEscape); +} diff --git a/template.c b/template.c new file mode 100644 index 0000000..3caa8b2 --- /dev/null +++ b/template.c @@ -0,0 +1,57 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <err.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include "archive.h" + +int templateRender( + FILE *file, const char *template, + const struct Variable *vars, EscapeFn *escape +) { + for (bool subst = false; *template; subst ^= true) { + size_t len = strcspn(template, "[]"); + if (subst) { + const char *value = NULL; + for (const struct Variable *var = vars; var->name; ++var) { + if (strlen(var->name) != len) continue; + if (strncmp(var->name, template, len)) continue; + value = var->value; + break; + } + if (!value) { + errx( + EX_SOFTWARE, "no value for template variable %.*s", + (int)len, template + ); + } + int error = escape(file, value); + if (error) return error; + } else { + size_t n = fwrite(template, len, 1, file); + if (!n) return -1; + } + template += len; + if (*template) template++; + } + return 0; +} |