tAdd support for base64 transfer encoding - scribo - Email-based phlog generator
(HTM) git clone git://git.z3bra.org/scribo.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit 850be8fe6fee2527debe3351916e4b5627416b42
(DIR) parent e96b2393492f00f4cb9143ac8eef814edd38e12c
(HTM) Author: Willy Goiffon <dev@z3bra.org>
Date: Tue, 8 Sep 2020 16:44:40 +0200
Add support for base64 transfer encoding
Diffstat:
A base64.c | 135 +++++++++++++++++++++++++++++++
A base64.h | 9 +++++++++
M makefile | 2 +-
M scribo.c | 53 +++++++++++++++++++++++--------
4 files changed, 185 insertions(+), 14 deletions(-)
---
(DIR) diff --git a/base64.c b/base64.c
t@@ -0,0 +1,135 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "base64.h"
+
+#define BASE64_FOLD 76
+
+int base64_index(const char *, char);
+
+const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * Return the index of a base64 char in the table
+ */
+int
+base64_index(const char *base64, char ch)
+{
+ uint8_t idx = 0;
+
+ for (idx = 0; idx < 64; idx++)
+ if (base64[idx] == ch)
+ return idx;
+
+ return ch == '=' ? 0 : -1;
+}
+
+/*
+ * Encode the given message in base64, allocate memory to store the encoded
+ * message, and return the size allocated.
+ */
+size_t
+base64_encode(char **buf, const unsigned char *msg, size_t len)
+{
+ size_t i, j;
+ uint64_t b64;
+ size_t size;
+
+ /* calculate size needed for the base64 buffer */
+ size = (len / 3) * 4 + (len % 3 ? 4 : 0);
+
+ *buf = malloc(size);
+ memset(*buf, 0, size);
+
+ for (i = j = 0; i < len; i+=3) {
+ /* concatenate 3 bytes into one 24 bits quantum, or 0 */
+ b64 = 0;
+ b64 |= (msg[i]<<16);
+ b64 |= ((i+1 < len ? msg[i+1] : 0) << 8);
+ b64 |= i+2 < len ? msg[i+2] : 0;
+
+ /* extract 4 base64 values from it */
+ (*buf)[j++] = base64_table[63 & (b64>>18)];
+ (*buf)[j++] = base64_table[63 & (b64>>12)];
+ (*buf)[j++] = i+1 < len ? base64_table[63 & (b64>>6)] : '=';
+ (*buf)[j++] = i+2 < len ? base64_table[63 & b64] : '=';
+ }
+
+ return size;
+}
+
+/*
+ * Allocate size to decode a base64 message, decode it in the buffer and
+ * return the allocated size.
+ */
+size_t
+base64_decode(char **buf, const unsigned char *msg, size_t len)
+{
+ uint64_t b64;
+ size_t size, i, j;
+
+ size = (len / 4) * 3;
+ size -= msg[len - 1] == '=' ? 1 : 0;
+ size -= msg[len - 2] == '=' ? 1 : 0;
+
+ *buf = malloc(size);
+ if (*buf == NULL)
+ return 0;
+ memset(*buf, 0, size);
+
+ for (i = j = 0; i < len; i+=4) {
+ b64 = 0;
+ b64 |= (base64_index(base64_table, msg[i])<<18);
+ b64 |= (base64_index(base64_table, msg[i+1])<<12);
+ b64 |= i + 2 < len ? (base64_index(base64_table, msg[i+2])<<6) : 0;
+ b64 |= i + 3 < len ? (base64_index(base64_table, msg[i+3])) : 0;
+
+ if (j < size)
+ (*buf)[j++] = 255 & (b64>>16);
+ if (j < size)
+ (*buf)[j++] = 255 & (b64>>8);
+ if (j < size)
+ (*buf)[j++] = 255 & (b64);
+ }
+
+ return size;
+}
+
+/*
+ * Write a base64 encoded message to the given file pointer, folded at the
+ * given width (defaults to BASE64_FOLD if specified width is 0).
+ */
+size_t
+base64_fold(FILE *fp, char *base64, size_t len, size_t fold)
+{
+ size_t i;
+
+ fold = fold > 0 ? fold : BASE64_FOLD;
+
+ for (i = 0; i < len; i += BASE64_FOLD) {
+ fwrite(base64+i, 1, i+BASE64_FOLD>len?len-i:BASE64_FOLD, fp);
+ fwrite("\n", 1, 1, fp);
+ }
+
+ return fold;
+}
+
+/*
+ * Unfold a base64 encoded message in-place.
+ */
+size_t
+base64_unfold(char *base64, size_t len)
+{
+ size_t i;
+ char *p;
+
+ p = base64;
+ for (i = 0, p = base64; i < len && base64[i] != '\0'; i++)
+ *(p++) = base64[i] == '\n' ? base64[++i] : base64[i];
+
+ return strnlen(base64, len);
+}
(DIR) diff --git a/base64.h b/base64.h
t@@ -0,0 +1,9 @@
+#ifndef BASE64_H__
+#define BASE64_H__
+
+size_t base64_encode(char **buf, const unsigned char *msg, size_t len);
+size_t base64_decode(char **buf, const unsigned char *msg, size_t len);
+size_t base64_fold(FILE *out, char *msg, size_t len, size_t fold);
+size_t base64_unfold(char *msg, size_t len);
+
+#endif
(DIR) diff --git a/makefile b/makefile
t@@ -2,7 +2,7 @@ include config.mk
.SUFFIXES: .h .def.h
-scribo: scribo.o rfc5322.o strlcpy.o strlcat.o
+scribo: scribo.o base64.o rfc5322.o strlcpy.o strlcat.o
scribo.o: config.h
.def.h.h:
(DIR) diff --git a/scribo.c b/scribo.c
t@@ -11,6 +11,7 @@
#include <sys/types.h>
#include "arg.h"
+#include "base64.h"
#include "config.h"
#include "rfc5322.h"
t@@ -160,13 +161,6 @@ verifyheaders(struct headers *head)
return -1;
}
- /* ensure message body is unaltered */
- encoding = header(head, "Content-Transfer-Encoding");
- if (encoding && strncmp(encoding, "8bit", 4)) {
- fprintf(stderr, "Content-Transfer-Encoding: %s is not supported\n", encoding);
- return -1;
- }
-
/* verify sender's address */
addr = rfc5322_addr(header(head, "From"));
if (strncmp(addr, author, strlen(author))) {
t@@ -178,19 +172,50 @@ verifyheaders(struct headers *head)
}
int
+write_8bit(FILE *in, FILE *out)
+{
+ ssize_t len;
+ char buf[BUFSIZ];
+
+ while ((len = fread(buf, 1, sizeof(buf), in)))
+ fwrite(buf, 1, len, out);
+
+ return 0;
+}
+
+int
+write_b64(FILE *in, FILE *out)
+{
+ size_t bufsiz;
+ ssize_t len;
+ char *msg, *b64;
+
+ while ((len = getline(&b64, &bufsiz, in)) > 0);
+
+ len = base64_unfold(b64, bufsiz);
+ len = base64_decode(&msg, (unsigned char *)b64, len);
+
+ free(b64);
+
+ fwrite(msg, 1, len, out);
+
+ free(msg);
+
+ return 0;
+}
+
+int
writeentry(FILE *in, char *dir, struct headers *head)
{
FILE *out;
struct tm tm = {.tm_isdst = -1};
char stamp[BUFSIZ];
- char *subject, *date;
+ char *subject, *date, *transfer;
char entry[PATH_MAX];
- ssize_t len;
- char buf[BUFSIZ];
-
subject = header(head, "Subject");
date = header(head, "Date");
+ transfer = header(head, "Content-Transfer-Encoding");
snprintf(entry, sizeof(entry), "%s/%s.txt", dir, sanitize(subject));
out = fopen(entry, "w");
t@@ -205,8 +230,10 @@ writeentry(FILE *in, char *dir, struct headers *head)
fprintf(out, titlefmt, subject);
- while ((len = fread(buf, 1, sizeof(buf), in)))
- fwrite(buf, 1, len, out);
+ if (!transfer || !strncmp(transfer, "8bit", 4))
+ write_8bit(in, out);
+ else if (!strncmp(transfer, "base64", 6))
+ write_b64(in, out);
fprintf(out, "\n%s\n", stamp);