diff f-demime/header_end.c @ 0:7e0d08176f32

f-demime starting code
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 06 May 2023 06:14:03 +0000
parents
children 1857d0d5a7bd
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/f-demime/header_end.c	Sat May 06 06:14:03 2023 +0000
@@ -0,0 +1,288 @@
+/*
+ * 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 = cont_type_buf;
+	char tokbuf[HDR_BUF_SIZE], attr[HDR_BUF_SIZE];
+	int rc;
+
+	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 = cont_te_buf;
+	char tokbuf[HDR_BUF_SIZE];
+
+	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;
+}