view f-demime/header_end.c @ 8:a92d0d59b669 default tip

f-demime: indicate X-backslash-escapes encoding in output
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 06 May 2023 17:00:23 +0000
parents 1857d0d5a7bd
children
line wrap: on
line source

/*
 * This module implements final processing of message and body part headers,
 * deciding what to do at the end of each header.
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "defs.h"

extern enum msg_state msg_state;
extern enum msg_hdr_state hdr_state;
extern unsigned mp_nest_level;
extern char mp_boundaries[MAX_MP_NESTING][MAX_MP_BOUNDARY+1];
extern int mp_is_digest[MAX_MP_NESTING];
extern char cont_type_buf[HDR_BUF_SIZE], cont_te_buf[HDR_BUF_SIZE];
extern int got_cont_type, got_cont_te;

static int
is_valid_tchar(ch)
{
	if (ch < '!' || ch > '~')
		return(0);
	switch (ch) {
	case '(':
	case ')':
	case '<':
	case '>':
	case '@':
	case ',':
	case ';':
	case ':':
	case '\\':
	case '"':
	case '/':
	case '[':
	case ']':
	case '?':
	case '=':
		return(0);
	default:
		return(1);
	}
}

static int
gettoken(cpp, tokbuf)
	char **cpp, *tokbuf;
{
	register char *cp, *dp;
	register int i;

	/* skip initial white space and comments */
	for (cp = *cpp; ; ) {
		if (isspace(*cp)) {
			cp++;
			continue;
		}
		if (*cp != '(')
			break;
		for (i = 0; ; ) {
			if (!*cp)
				break;
			if (*cp == '\\')
				cp++;
			else if (*cp == '(')
				i++;
			else if (*cp == ')')
				i--;
			if (*cp)
				cp++;
			if (!i)
				break;
		}
	}
	if (!*cp) {
		*cpp = cp;
		return(0);
	}
	if (*cp == '/' || *cp == ';' || *cp == '=') {
		i = *cp++;
		*cpp = cp;
		return(i);
	}
	if (*cp == '"') {
		cp++;
		for (dp = tokbuf; *cp; ) {
			if (*cp == '"') {
				cp++;
				break;
			}
			if (cp[0] == '\\' && cp[1])
				cp++;
			*dp++ = *cp++;
		}
		*dp = '\0';
		*cpp = cp;
		return(2);
	}
	if (!is_valid_tchar(*cp)) {
		*cpp = cp;
		return(-1);
	}
	for (dp = tokbuf; is_valid_tchar(*cp); )
		*dp++ = *cp++;
	*dp = '\0';
	*cpp = cp;
	return(1);
}

static int
parse_content_type(type, subtype, charset, boundary)
	char *type, *subtype, *charset, *boundary;
{
	char *ctstr;
	char tokbuf[HDR_BUF_SIZE], attr[HDR_BUF_SIZE];
	int rc;

	ctstr = index(cont_type_buf, ':') + 1;
	if (gettoken(&ctstr, type) != 1)
		return(-1);
	if (gettoken(&ctstr, tokbuf) != '/')
		return(-1);
	if (gettoken(&ctstr, subtype) != 1)
		return(-1);
	charset[0] = '\0';
	boundary[0] = '\0';
	for (;;) {
		rc = gettoken(&ctstr, tokbuf);
		if (!rc)
			return(0);
		if (rc != ';')
			return(-1);
		if (gettoken(&ctstr, attr) != 1)
			return(-1);
		if (gettoken(&ctstr, tokbuf) != '=')
			return(-1);
		rc = gettoken(&ctstr, tokbuf);
		if (rc != 1 && rc != 2)
			return(-1);
		if (!strcasecmp(attr, "charset"))
			strcpy(charset, tokbuf);
		else if (!strcasecmp(attr, "boundary"))
			strcpy(boundary, tokbuf);
	}
}

static int
parse_content_te(ctetoken)
	char *ctetoken;
{
	char *ctestr;
	char tokbuf[HDR_BUF_SIZE];

	ctestr = index(cont_te_buf, ':') + 1;
	if (gettoken(&ctestr, ctetoken) != 1)
		return(-1);
	if (gettoken(&ctestr, tokbuf) == 0)
		return(0);
	else
		return(-1);
}

static void
handle_multipart(cont_subtype, boundary_attr)
	char *cont_subtype, *boundary_attr;
{
	if (!boundary_attr[0]) {
		puts("X-Fdemime-Error: multipart without boundary attr");
		putchar('\n');
		msg_state = MSG_STATE_BODY_PASS;
		return;
	}
	if (index(boundary_attr, '\n')) {
	      puts("X-Fdemime-Error: multipart boundary attr contains newline");
		putchar('\n');
		msg_state = MSG_STATE_BODY_PASS;
		return;
	}
	if (strlen(boundary_attr) > MAX_MP_BOUNDARY) {
		puts("X-Fdemime-Error: multipart boundary attr is too long");
		putchar('\n');
		msg_state = MSG_STATE_BODY_PASS;
		return;
	}
	if (mp_nest_level >= MAX_MP_NESTING) {
		puts("X-Fdemime-Error: multipart nesting is too deep");
		putchar('\n');
		msg_state = MSG_STATE_BODY_PASS;
		return;
	}
	putchar('\n');
	strcpy(mp_boundaries[mp_nest_level], boundary_attr);
	mp_is_digest[mp_nest_level] = !strcasecmp(cont_subtype, "digest");
	mp_nest_level++;
	msg_state = MSG_STATE_BODY_PASS;
}

void
process_header_end()
{
	char cont_type[HDR_BUF_SIZE], cont_subtype[HDR_BUF_SIZE];
	char charset_attr[HDR_BUF_SIZE], boundary_attr[HDR_BUF_SIZE];
	char content_te[HDR_BUF_SIZE];
	int in_digest, rc;

	if (hdr_state == HDR_STATE_ERROR) {
		if (got_cont_type)
			fputs(cont_type_buf, stdout);
		if (got_cont_te)
			fputs(cont_te_buf, stdout);
		putchar('\n');
		msg_state = MSG_STATE_BODY_PASS;
		return;
	}
	if (mp_nest_level)
		in_digest = mp_is_digest[mp_nest_level-1];
	else
		in_digest = 0;
	if (got_cont_type) {
		fputs(cont_type_buf, stdout);
		rc = parse_content_type(cont_type, cont_subtype, charset_attr,
					boundary_attr);
		if (rc < 0) {
			puts("X-Fdemime-Error: unable to parse Content-Type");
			if (got_cont_te)
				fputs(cont_te_buf, stdout);
			putchar('\n');
			msg_state = MSG_STATE_BODY_PASS;
			return;
		}
	} else {
		if (in_digest) {
			strcpy(cont_type, "message");
			strcpy(cont_subtype, "rfc822");
		} else {
			strcpy(cont_type, "text");
			strcpy(cont_subtype, "plain");
		}
		charset_attr[0] = '\0';
		boundary_attr[0] = '\0';
	}
	if (!strcasecmp(cont_type, "multipart")) {
		if (got_cont_te)
			fputs(cont_te_buf, stdout);
		handle_multipart(cont_subtype, boundary_attr);
		return;
	}
	if (!strcasecmp(cont_type, "message")) {
		if (got_cont_te)
			fputs(cont_te_buf, stdout);
		putchar('\n');
		msg_state = MSG_STATE_BODY_PASS;
		return;
	}
	if (got_cont_te) {
		rc = parse_content_te(content_te);
		if (rc < 0) {
			fputs(cont_te_buf, stdout);
			puts(
		"X-Fdemime-Error: unable to parse Content-Transfer-Encoding");
			putchar('\n');
			msg_state = MSG_STATE_BODY_PASS;
			return;
		}
	} else
		content_te[0] = '\0';
	if (!strcasecmp(content_te, "base64")) {
		if (!strcasecmp(cont_type, "text")) {
			if (!strcasecmp(cont_subtype, "plain"))
				init_base64_text_plain(charset_attr);
			else
				init_base64_text_other();
		} else
			init_base64_nontext();
		return;
	}
	if (!strcasecmp(content_te, "quoted-printable") &&
	    !strcasecmp(cont_type, "text") &&
	    !strcasecmp(cont_subtype, "plain")) {
		init_qp_text_plain(charset_attr);
		return;
	}
	if (got_cont_te)
		fputs(cont_te_buf, stdout);
	putchar('\n');
	msg_state = MSG_STATE_BODY_PASS;
}