Index | Thread | Search

From:
Walter Alejandro Iglesias <wai@roquesor.com>
Subject:
Re: mail(1) patches (UPDATE)
To:
tech@openbsd.org
Date:
Mon, 5 Aug 2024 09:52:19 +0200

Download raw body.

Thread
  • Walter Alejandro Iglesias:

    mail(1) patches (UPDATE)

  • 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 <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <err.h>
    #include <errno.h>
    #include <stdint.h>
    #include <unistd.h>
    #include <ctype.h>
    #include <locale.h>
    
    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
    
    
    
  • Walter Alejandro Iglesias:

    mail(1) patches (UPDATE)