Re: [exim] DKIM signing

Top Page
Delete this message
Reply to this message
Author: Gedalya
Date:  
To: exim-users
Subject: Re: [exim] DKIM signing
Here is my simple line folding filter.

Going with getc() is a lot simpler but this way seems faster and it was fun to play around with. And it seems to work.

This will break long lines exactly at the specified length without regard to whitespace. In the header, a tab (\t) is added after added newlines.

This would work for e.g. attachments formatted as one very long line of base64. It could cause problems in other cases.

compile: gcc -o email-line-fold email-line-fold.c

usage:

remote_smtp:
  transport_filter = '${if > {$max_received_linelength}{998}{/usr/local/bin/email-line-fold}{}}' 998

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <error.h>

#define BUFSIZE 4096

int main(int argc, char *argv[])
{
    char *endptr, *str;
    long linemax = 0;
    char buf[BUFSIZE];
    char *bufi;
    char *bufend;
    char *nl;
    ssize_t bytes_read = 0;
    ssize_t bytes_written = 0;
    size_t bytes_remaining = 0;
    size_t bytestowrite = 0;
    int header_done = 0;
    int length = 0;
    int lastlength = 0;


    if (argc < 2) {
        fprintf(stderr, "Usage: %s linemax\n", argv[0]);
        exit(EXIT_FAILURE);
    }


    str = argv[1];
    errno = 0;
    linemax = strtol(str, &endptr, 0);
    if (endptr == str) {
        fprintf(stderr, "Usage: %s linemax\n", argv[0]);
        fprintf(stderr, "linemax must be a numerical value\n");
        exit(EXIT_FAILURE);
    }
    if (errno != 0) {
        perror("strtol");
        exit(EXIT_FAILURE);
    }
    if (linemax < 2) {
        fprintf(stderr, "linemax must be at least 2\n");
        exit(EXIT_FAILURE);
    }


    bufi = &buf[0];
    while (1) {
        if (! bytes_remaining) {
            if (feof(stdin)) break;
            bytes_read = fread(buf, 1, BUFSIZE, stdin);
            if (ferror (stdin))
                error(EXIT_FAILURE, errno, "Error while reading");
            if (! bytes_read) break;
            bytes_remaining = bytes_read;
            bufi = &buf[0];
            bufend = &buf[0] + bytes_read - 1;
        }
        if (! header_done && nl == bufend && buf[0] == '\n')
            header_done = 1;
        while (bytes_remaining) {
            nl = memchr(bufi, '\n', bytes_remaining);
            if (nl == NULL)
                length = bytes_remaining;
            else
                length = nl - bufi + 1;
            if (! header_done && nl != NULL && nl < bufend && nl[1] == '\n')
                header_done = 1;
            bytes_remaining -= length;
            while (length) {
                int addnl = 0;
                if (lastlength + length > linemax) {
                    bytestowrite = linemax - lastlength;
                    if (nl == NULL || lastlength + length > (linemax + 1))
                        addnl = 1;
                    lastlength = 0;
                } else {
                    bytestowrite = length;
                    if (bufi[length-1] != '\n')
                        lastlength += length;
                    else
                        lastlength = 0;
                }
                bytes_written = fwrite(bufi, 1, bytestowrite, stdout);
                if (ferror (stdout))
                    error(EXIT_FAILURE, errno, "Error while writing");
                if (addnl)
                    if (! header_done) {
                        if (fputs("\n\t", stdout) == EOF)
                            error(EXIT_FAILURE, errno, "Error while writing");
                        lastlength++;
                    }
                    else {
                        if (putchar('\n') == EOF)
                            error(EXIT_FAILURE, errno, "Error while writing");
                    }
                length -= bytestowrite;
                bufi += bytestowrite;
            }
        }
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}