Index | Thread | Search

From:
Walter Alejandro Iglesias <wai@roquesor.com>
Subject:
Re: [patch] Adding In-Reply-To: support to mail(1)
To:
Theo de Raadt <deraadt@openbsd.org>
Cc:
Tim Chase <openbsd@tim.thechases.com>, tech@openbsd.org
Date:
Fri, 2 Feb 2024 21:07:51 +0100

Download raw body.

Thread
On Fri, Feb 02, 2024 at 07:57:43AM -0700, Theo de Raadt wrote:
> I would be happy to see improvements to mail(1), but past improvements have
> tried to do too much in one go.  The work must be in small incremental steps,
> each on judged on their own.  It is good that this is finally getting split
> up into pieces.  But I think the steps are still too large.
> 

If you read them carefully you'll notice that the modifications seem
larger than what they really are, the patches are noisy because I tried
to respect the way things are distributed in the code (adding variables
and functions in different files.)

Then you have the functions.  Except the function to generate the
message-id, which I just cut and pasted from smptd code, the patches
include little functions I wrote, I can also write more little programs
like the one below to let you test them and judge if they're acceptable
(needless to say I'm no C expert.)

I'll see if I can split things even more.


/* b64.c */

#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <locale.h>

static int newline_at_end = 0;

void		filecopy(FILE *, FILE *);
/* ---- mail(1) ptach extern.h ------- */
void		wrap_b64_subject(char **p);
void		wrap_ascii_subject(char **p);
void		base64(char **p);
unsigned	b64len(unsigned len);
/* ----------------------------------- */
void		usage(void);

int
main(int argc, char *argv[])
{
	FILE *fp;
	int option;

	while ((option = getopt(argc, argv, "h")) != -1)
		switch (option) {
		case 'h':
			usage();
			break;
		default:
			usage();
		}

	argc -= optind;
	argv += optind;

	if (argc > 0)
		while (argc-- > 0)
			if ((fp = fopen(*argv++, "r")) == NULL)
				warn("%s", *(argv - 1));
			else {
				filecopy(fp, stdout);
				fclose(fp);
			}
	else
		filecopy(stdin, stdout);

	return errno;
}

void
filecopy(FILE * ifp, FILE * ofp)
{
	char *p = NULL;
	size_t size = 0;
	size_t i = 0;
	int c;
	int ulen;

	while ((c = getc(ifp)) != EOF) {
		if (i == size) {
			p = realloc(p, size + 100);
			if (p == NULL)
				err(1, NULL);
			size += 100;
		}
		p[i++] = c;
	}

	if (i == size) {
		p = realloc(p, size + 1);
		if (p == NULL)
			err(1, NULL);
	}

	p[i] = '\0';

	setlocale(LC_CTYPE, "en_US.UTF-8");
	ulen = mbstowcs(NULL, p, 0);

	printf("Subject: ");

	if (ulen < i && ulen >= 0) {
		p = realloc(p, b64len(i) + 1);
		if (p == NULL)
			err(1, NULL);
		base64(&p);
		wrap_b64_subject(&p);
		printf("%s", p);
	} else {
		wrap_ascii_subject(&p);
		printf("%s", p);
	}
	free(p);
}

/* --------------------  mail(1) patch util.c ------------------ */
void
base64(char **p)
{
	size_t len, olen, i, j;
	uint32_t a, b, c, triple;
	char *enc = NULL;
	unsigned char *in = NULL;
	const char b64table[] =
	    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	int b64mod[] = { 0, 2, 1 };

	in = (unsigned char*)*p;

	i = 0;
	while (in[i] != '\0') {
		/* Replace control characters by spaces */
		if (iscntrl(in[i])) {
			/* ---- Remove from mail(1) patch ----- */
			if (in[i] == '\n' && in[i + 1] == '\0')
				newline_at_end = 1;
			/* ------------------------------------ */
			in[i] = ' ';
		}
		i++;
	}

	len = strlen(*p);

	enc = malloc(b64len(len));
	if (enc == NULL)
		err(1, NULL);
	olen = b64len(len);

	i = j = 0;
	while (in[i] != '\0') {
		a = i < len ? in[i++] : 0;
		b = i < len ? in[i++] : 0;
		c = i < len ? in[i++] : 0;

		triple = (a << 0x10) + (b << 0x08) + c;

		enc[j++] = b64table[(triple >> 3 * 6) & 0x3F];
		enc[j++] = b64table[(triple >> 2 * 6) & 0x3F];
		enc[j++] = b64table[(triple >> 1 * 6) & 0x3F];
		enc[j++] = b64table[(triple >> 0 * 6) & 0x3F];
	}

	i = 0;
	while (i < b64mod[len % 3])
		enc[olen - 1 - i++] = '=';

	if (olen == j) {
		enc = realloc(enc, olen + 1);
		if (enc == NULL)
			err(1, NULL);
	}
	enc[j] = '\0';

	*p = enc;

	/* Remove from mail(1) patch */
	free(in);
}

unsigned
b64len(unsigned len)
{
	return (len + 2) / 3 * 4;
}

void
wrap_b64_subject(char **p)
{
	char *s = NULL;
	char *r = NULL;
	char otag[] = "=?UTF-8?B?";
	char ctag[] = "?=";
	char mtag[] = "?=\n =?UTF-8?B?";
	size_t otag_len = 10;
	size_t ctag_len = 2;
	size_t mtag_len = 14;
	size_t i, n, size, tmp;
	int col = 0;
	int wrap = 72;

	/* Rest tags width to all lines */
	wrap -= otag_len + ctag_len + 1;
	/* ... and "Subject:" width to the first line */
	col += 8;

	/* Opening tag at the beginning of string */
	s = malloc(strlen(*p) + otag_len);
	if (s == NULL)
		err(1, NULL);
	size = strlen(*p) + otag_len;

	i = tmp = 0;
	while (tmp < otag_len)
		s[i++] = otag[tmp++];

	r = *p;

	n = 0;
	while (r[n] != '\0') {
		if (col % 4 == 0 && col >= wrap) {
			/* Closing tag / new line / space / opening tag  */
			s = realloc(s, size + mtag_len);
			if (s == NULL)
				err(1, NULL);
			size += mtag_len;

			tmp = 0;
			while (tmp < mtag_len)
				s[i++] = mtag[tmp++];

			col = 0;
		} else {
			s[i++] = r[n++];
			col++;
		}
	}

	/* Closing tag at the end of string */
	s = realloc(s, size + ctag_len);
	if (s == NULL)
		err(1, NULL);
	size += ctag_len;

	tmp = 0;
	while (tmp < ctag_len)
		s[i++] = ctag[tmp++];

	/* -- Remove from mail(1) patch -- */
	if (newline_at_end) {
		s = realloc(s, size + 1);
		if (s == NULL)
			err(1, NULL);
		size++;
		s[i++] = '\n';
	}
	/* ------------------------------- */

	if (i == size) {
		s = realloc(s, size + 1);
		if (s == NULL)
			err(1, NULL);
	}
	s[i] = '\0';

	*p = s;

	/* Remove from mail(1) patch */
	free(r);
}

void
wrap_ascii_subject(char **p)
{
	char *s = NULL;
	char *r = NULL;
	size_t i = 0;
	size_t n = 0;
	size_t size = 0;
	int col = 0;
	int wrap = 72;

	/* Rest Subject: width at first line */
	col += 9;

	s = malloc(strlen(*p));
	if (s == NULL)
		err(1, NULL);
	size = strlen(*p);

	r = *p;

	while (r[n] != '\0') {
		/* Skip control characters */
		if (iscntrl(r[n]) && r[n] != '\t' && r[n] != '\r' &&
		    r[n] != '\n')
			n++;
		/* Replace newlines by spaces */
		if (r[n] == '\n')
			if (r[n + 1] != '\0')
				r[n] = ' ';

		if (n != 0 && r[n] == ' ' &&
		    col + strcspn(&r[n + 1], " ") >= wrap) {
			s = realloc(s, size + 1);
			if (s == NULL)
				err(1, NULL);
			size++;
			s[i++] = '\n';
			col = 0;
		}
		s[i++] = r[n++];
		col++;
	}

	if (i == size) {
		s = realloc(s, size + 1);
		if (s == NULL)
			err(1, NULL);
	}
	s[i] = '\0';

	*p = s;

	/* Remove from mail(1) patch */
	free(r);
}

/* ------------------- end util.c mail(1) patch ------------------ */

void
usage(void)
{
	extern char *__progname;

	fprintf(stderr, "usage: %s [-h] [file ...]\n"
		"  -h	print this help\n", __progname);
	exit(1);
}


-- 
Walter