From: Walter Alejandro Iglesias Subject: Re: [patch] Adding In-Reply-To: support to mail(1) To: Theo de Raadt Cc: Tim Chase , tech@openbsd.org Date: Fri, 2 Feb 2024 21:07:51 +0100 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 #include #include #include #include #include #include #include #include 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