scribo

Email-based phlog generator
git clone git://git.z3bra.org/scribo.git
Log | Files | Refs

commit 850be8fe6fee2527debe3351916e4b5627416b42
parent e96b2393492f00f4cb9143ac8eef814edd38e12c
Author: Willy Goiffon <dev@z3bra.org>
Date:   Tue,  8 Sep 2020 16:44:40 +0200

Add support for base64 transfer encoding

Diffstat:
Abase64.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abase64.h | 9+++++++++
Mmakefile | 2+-
Mscribo.c | 53++++++++++++++++++++++++++++++++++++++++-------------
4 files changed, 185 insertions(+), 14 deletions(-)

diff --git a/base64.c b/base64.c @@ -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); +} diff --git a/base64.h b/base64.h @@ -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 diff --git a/makefile b/makefile @@ -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: diff --git a/scribo.c b/scribo.c @@ -11,6 +11,7 @@ #include <sys/types.h> #include "arg.h" +#include "base64.h" #include "config.h" #include "rfc5322.h" @@ -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))) { @@ -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"); @@ -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);