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;
}
-- 
## List details at https://lists.exim.org/mailman/listinfo/exim-users
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/

Reply via email to