From: Walter Alejandro Iglesias Subject: Re: mail(1) patches (UPDATE) To: tech@openbsd.org Date: Mon, 5 Aug 2024 09:52:19 +0200 For those interested, know that I'll keep correcting, updating and uploading my patches on the same link: https://en.roquesor.com/Downloads/mail_patches.tar.gz Yesterday I debugged the function I wrote to encode headers in base64. An update of the program I wrote to test it is pasted below. Stressing it with large and UTF-8-dense files, ktrace (and valgring under Linux) showed me zero errors, zero memory leaks. /* * Program to test the base64 headers encode_word function and wrap_header * functions included in id.diff */ #include #include #include #include #include #include #include #include #include static int newline_at_end = 0; void encode_word(char **p); static void filecopy(FILE *, FILE *); void usage(void); void base64(char **p); unsigned b64len(unsigned len); void wrap_header(char **p, size_t name_len); 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; while ((c = getc(ifp)) != EOF) { if (i == size) { p = realloc(p, size + 100); if (p == NULL) err(1, NULL); size += 100; } /* Strip control characters */ if (!iscntrl(c) || c == '\t' || c == '\n') p[i++] = c; } if (i == size) { p = realloc(p, size + 1); if (p == NULL) err(1, NULL); } p[i] = '\0'; encode_word(&p); wrap_header(&p, 0); printf("%s", p); free(p); } void encode_word(char **p) { char *s = NULL; char *r = NULL; char *word = NULL; size_t i = 0; size_t n = 0; size_t size = 0; size_t word_len = 0; char otag[] = "=?UTF-8?B?"; char ctag[] = "?="; size_t tags_len = strlen(otag) + strlen(ctag); int utf8word = 0; setlocale(LC_CTYPE, "en_US.UTF-8"); size = strlen(*p); s = malloc(size); if (s == NULL) err(1, NULL); r = *p; while (r[n] != '\0') { /* We are at the first character of a word */ if ((n == 0 || (isspace(r[n - 1]) || r[n - 1] == '\n')) && !isspace(r[n]) && r[n] != '\n') { /* Load the size of the word in a variable */ word_len = strcspn(&r[n], "\t\n "); /* * If we are not at the first character of the * string count one more character[1]. */ if (n > 0 && utf8word) word_len++; word = malloc(word_len + 1); if (word == NULL) err(1, NULL); /* * (1) If the pervious word was encoded we need * to include the previous space or tab in the * encoded word. */ if (n > 0 && utf8word) { if (r[n - 1] == '\n') r[n - 1] = ' '; snprintf(word, word_len + 1, "%s", &r[n - 1]); } else snprintf(word, word_len + 1, "%s", &r[n]); /* Check if the word contains UTF-8 characters */ if (word_len > mbstowcs(NULL, word, 0) ) { s = realloc(s, size + tags_len + (b64len(word_len) - word_len) + 1); if (s == NULL) err(1, NULL); size += tags_len + (b64len(word_len) - word_len) + 1; /* Encode the word */ base64(&word); /* Append the encoded word to the string */ snprintf(&s[i], i + b64len(word_len) + tags_len + 1, "%s%s%s", otag, word, ctag); i += b64len(word_len) + tags_len; n += word_len; /* * If we included the last space or tab in * the encoded word shift the count to * prevent the last caracter of the word * be loaded twice in the destination * string. */ if (n > 0 && utf8word) n--; utf8word = 1; } else utf8word = 0; free(word); } s[i++] = r[n++]; } if (i == size) { s = realloc(s, size + 1); if (s == NULL) err(1, NULL); } s[i] = '\0'; /* Remove this free() from mail patch */ free(r); *p = s; } 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_header(char **p, size_t name_len) { char *s = NULL; char *r = NULL; size_t i = 0; size_t n = 0; size_t size = 0; int col = name_len; /* let room for header name */ int wrap = 72; size = strlen(*p); s = malloc(size); if (s == NULL) err(1, NULL); r = *p; while (r[n] != '\0') { /* Replace newlines by spaces */ if (r[n] == '\n' && r[n + 1] != '\0') r[n] = ' '; if (n != 0 && isspace(r[n]) && col + strcspn(&r[n + 1], "\t\n ") >= wrap) { s = realloc(s, size + 1); if (s == NULL) err(1, NULL); size++; if (r[n + 1] != '\0') s[i++] = '\n'; while (isspace(r[n + 1])) n++; col = 0; if (r[n] == '\t') col += 7; } s[i++] = r[n++]; if (r[n] == '\t') col += 7; col++; } if (i == size) { s = realloc(s, size + 1); if (s == NULL) err(1, NULL); } s[i] = '\0'; /* Remove this free from mail patch */ free(r); *p = s; } void usage(void) { extern char *__progname; fprintf(stderr, "Usage: %s [-bhlnp] [-w width] [file ...]\n" " -h print this help\n", __progname); exit(1); } -- Walter