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);