Subject: Extensions to smap for MIME filtering.

From: Darren Reed <[email-masked]>
Date: Sat, 20 May 2000 16:13:42 +1000 (EST)

[To be removed from this list send the message "unsubscribe fwtk-users" in the
BODY of a mail message to [email-masked].]

Hi,
    I've just spent a few hours hacking on smap to do MIME filtering.
I will `erase' certain MIME content (replace it all with X's) or change
MIME types from one to another.  There are also a bunch of extensions to
do header stripping and provide protection from bounce attacks.  This is
based on the original 2.1 - I've not really followed what others have
done to avoid relaying.

    The extensions are all accessible through the config file as follows:

smap: localnet 192.168 10
smap: erase-mime */badmime
smap: convert-mime */badbad text/plain
smap: localdomains local.com domain.com

both erase-mime and convert-mime allow for wildcards in the MIME type
(only * and ?) and are case insensitive.  Hosts connecting from networks
which match "localnet" have "Received:" headers stripped.  parameters for
"convert-mime" *MUST* be given in pairs!

    BTW, I apologise in advance if this duplicates the work of others, I've
been paying very little attention to this list, except with the 'd' key.

Darren

/*
  * Copyright (c) 1993, 1997 Trusted Information Systems, Incorporated
  * All rights reserved.
  *
  * Redistribution and use are governed by the terms detailed in the
  * license document ("LICENSE") included with the toolkit.
  */

/*
  *	Author: Marcus J. Ranum, Trusted Information Systems, Inc.
  */
static	char	RcsId[] = "$Header: /home/rmurphy/fwtk/fwtk/smap/RCS/smap.c,v 1.22 1998/01/13 22:56:06 rmurphy Exp $";

#include	<sys/types.h>
#include	<sys/file.h>
#include	<sys/stat.h>
#include	<sys/wait.h>
#include	<stdio.h>
#include	<time.h>
#include	<ctype.h>
#include	<syslog.h>
#include	<netdb.h>
#include	<strings.h>
#include	<sys/socket.h>
#include	<sys/signal.h>
#include	<netinet/in.h>
#include	<errno.h>

#include	"firewall.h"

#define	MIME_CHECK

/*
	smap - sendmail wrapper.

	the object of this program is to allow us to present an SMTP service
	for people to talk to, which is unprivileged, and runs in a chrooted
	directory. a secondary requirement is that the code be as simple as
	possible, to permit manual review. this code, therefore, contains
	no comments other than this one - comments being an indication that
	code is too complex to be trusted.

	files created in spool directory are locked and given mode 644. when
	smap is done gathering them, they are unlocked and given mode 755.
	smapd checks the mode and locking status to ensure that it's not
	dealing with a partial file. unlocked files with mode 644 are by
	definition partial.

	mjr. 1993
*/

extern	char	*strtok();

extern	char	*optarg;

struct	towho	{
	char		*who;
	struct	towho	*nxt;
};

static	struct	towho	*recip = (struct towho *)0;
static	char		*ruser = (char *)0;
static	char		*rhost = (char *)0;
static	char		rladdr[512];
static	char		riaddr[512];
static	char		*rootdir = (char *)0;
static	int		runuid = -1;
static	int		rungid = -1;
static	char		*tempfile = (char *)0;
static	int		maxbytes = -1;
static	int		maxrecip = -1;
static	int		curbytes = 0;
static	int		currecip = 0;
static	int		timeout = PROXY_TIMEOUT;
static	int		livetim = 0;
static	int		msgsize = 0;

static	FILE	*smapopen();
static	int	crlfgets();
static  void	add_file_list();
static	void	smap_exit();
static	void	waitwaitwait();

struct file_list {
	struct file_list *next;
	char *tmpfile;
} *filelist = (struct file_list *) 0;

static	void
ringring()
{
	if(livetim == 0) {
		syslog(LLEV,"Network timeout signal after %d seconds",timeout);
		if(tempfile != (char *)0)
			unlink(tempfile);
		smap_exit(1);
	}
	livetim = 0;
	alarm(timeout);
}

main(ac,av)
int	ac;
char	*av[];
{
	Cfg		*cf;
	Cfg		*cfp;
	char		buf[BUFSIZ * 4];
	char		myhostname[512];
	char		*localhost = NULL;
	char		*p;
	int		x;
	int		opt;
	FILE		*vout = (FILE *)0;
	int		gotdata = 0;
	struct hostent	*hp;
	int		islocal = 0;

#ifndef	LOG_DAEMON
	openlog("smap",LOG_PID);
#else
	openlog("smap",LOG_PID|LOG_NDELAY,LFAC);
#endif

	if (ac > 1 && !strcmp(av[1], "-daemon")) {
		int sock, sockl;
		struct sockaddr_in sa;
		int pid;
	
		sa.sin_family = AF_INET;
		bzero((char *)&sa.sin_addr, sizeof(sa.sin_addr));
		sa.sin_port = htons(25);
		sock = socket(AF_INET, SOCK_STREAM, 0);
		if (sock < 0) {
			syslog(LLEV, "fwtksyserr: Failed to create socket: %m");
			exit(1);
		}
		if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) {
			syslog(LLEV,"fwtksyserr: Failed to bind port 25: %m");
			exit(1);
		}
		if (listen(sock, 5) < 0) {
			syslog(LLEV,"fwtksyserr: Failed to listen: %m");
			exit(1);
		}
		while (1) {
			int cstat;
			signal (SIGCHLD, waitwaitwait);
			sockl = accept (sock, (struct sockaddr *)0, (int *)0);
			if (sockl < 0) {
				if (errno = EINTR)
					continue;
				syslog(LLEV,"fwtksyserr: Accept failed: %m");
				exit(1);
			}

			pid = fork();
			if (pid < 0) {
				syslog(LLEV,"fwtksyserr: Fork failed: %m");
				exit(1);
			}
			if (pid == 0)
				break;	/* We are the child */
			close(sockl);
		}
		close(0);
		close(1);
		close(2);
		dup(sockl);
		dup(sockl);
		dup(sockl);
		close(sockl);
		close(sock);
	}

	if(peername(0,rladdr,riaddr,sizeof(riaddr))) {
		syslog(LLEV,"cannot get remote host");
		exit(1);
	} else
		syslog(LLEV,"connect host=%.512s/%.20s",rladdr,riaddr);

	if(gethostname(myhostname,sizeof(myhostname)))
		strcpy(myhostname,"amnesiac");
	else if ((hp = gethostbyname(myhostname)) != 0)
		strcpy(myhostname,hp->h_name);

	if((cfp = cfg_read("smap")) == (Cfg *)-1)
		exit(1);

	if((cf = cfg_get("groupid",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: groupid must have one parameter, line %d",cf->ln);
			exit(1);
		}
		if((rungid = mapgid(cf->argv[0])) == -1) {
			syslog(LLEV,"fwtkcfgerr: cannot map %.512s to gid",cf->argv[0]);
			exit(1);
		}
	}

	if((cf = cfg_get("userid",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: userid must have one parameter, line %d",cf->ln);
			exit(1);
		}
		if((runuid = mapuid(cf->argv[0])) == -1) {
			syslog(LLEV,"fwtkcfgerr: cannot map %.512s to uid",cf->argv[0]);
			exit(1);
		}
	}

	if((cf = cfg_get("directory",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: directory must have one parameter, line %d",cf->ln);
			exit(1);
		}
		rootdir = cf->argv[0];
	}

	if((cf = cfg_get("localnet",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: localnet must have one parameter, line %d",cf->ln);
			exit(1);
		}
		islocal = !strncmp(cf->argv[0], riaddr, strlen(cf->argv[0]));
	}

	if((cf = cfg_get("maxbytes",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: maxbytes must have one parameter, line %d",cf->ln);
			exit(1);
		}
		maxbytes = atoi(cf->argv[0]);
	}

	if((cf = cfg_get("maxrecip",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: maxrecip must have one parameter, line %d",cf->ln);
			exit(1);
		}
		maxrecip = atoi(cf->argv[0]);
	}

	if((cf = cfg_get("timeout",cfp)) != (Cfg *)0) {
		if(cf->argc != 1) {
			syslog(LLEV,"fwtkcfgerr: timeout must have one parameter, line %d",cf->ln);
			exit(1);
		}
		timeout = atoi(cf->argv[0]);
	}
	if (timeout > 0) {
		signal(SIGALRM,ringring);
		alarm(timeout);
	}

	if(rootdir != (char *)0) {
		chdir("/");
		if((chdir(rootdir) || chroot(rootdir))) {
			syslog(LLEV,"fwtksyserr: cannot chroot to %.512s: %m",rootdir);
			exit(1);
		}
		chdir("/");
	}

	if(rungid != -1 && setgid(rungid)) {
		syslog(LLEV,"fwtksyserr: cannot set gid to %d: %m",rungid);
		exit(1);
	}

	if(runuid != -1 && setuid(runuid)) {
		syslog(LLEV,"fwtksyserr: cannot set uid to %d: %m",runuid);
		exit(1);
	}

	printf("220 %s SMTP/smap Ready.\r\n",myhostname);
	fflush(stdout);

#ifdef	SO_KEEPALIVE
	opt = 1;
	(void)setsockopt(fileno(stdin),SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt));
#endif

	while(1) {
		if(crlfgets(buf,sizeof(buf),stdin) < 0) {
			syslog(LLEV,"peer dropped connection: %m");
			break;
		}

		if((p = strtok(buf," \r\t\n")) == (char *)0) {
			printf("500 Command unrecognized\r\n");
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"MAIL")) {
			char	*q;

			if((q = strtok((char *)0,"\r\n")) == (char *)0) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			if(strncasecmp(q,"From:",5)) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}
			/* block escape in names */
			if(strchr(q, '`')) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			q += 5;
			while(isspace(*q))
				q++;
			if(ruser != (char *)0)
				free(ruser);
			ruser = malloc(strlen(q) + 1);
			if(ruser == (char *)0) {
				printf("410 out of memory\r\n");
				fflush(stdout);
				syslog(LLEV,"fwtksyserr: out of memory: %m");
				unlink(tempfile);
				smap_exit(1);
			}
			strcpy(ruser,q);
			printf("250 %s... Sender Ok\r\n",ruser);
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"RCPT")) {
			struct	towho	*nr;
			char	*q;

			if(ruser == (char *)0) {
				printf("503 need MAIL From: first.\r\n");
				fflush(stdout);
				continue;
			}

			if((q = strtok((char *)0,"\r\n")) == (char *)0) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			if(strncasecmp(q,"To:",3)) {
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			q += 3;
			while(isspace(*q))
				q++;
			if(!islocal && !checkvalid(q,cfp)) {
				syslog(LLEV,"securityalert: rejecting recip %.512s",q);
				printf("501 Syntax error\r\n");
				fflush(stdout);
				continue;
			}

			if(maxrecip > 0 && currecip++ > maxrecip) {
				printf("550 too many recipients\r\n");
				fflush(stdout);
				syslog(LLEV,"exiting - too many recipients");
				unlink(tempfile);
				smap_exit(1);
			}

			nr = (struct towho *)malloc(sizeof(struct towho));
			if(nr == (struct towho *)0) {
				printf("410 out of memory\r\n");
				fflush(stdout);
				syslog(LLEV,"fwtksyserr: out of memory: %m");
				unlink(tempfile);
				smap_exit(1);
			}
			nr->who = malloc(strlen(q) + 1);
			if(nr->who == (char *)0) {
				printf("410 out of memory\r\n");
				fflush(stdout);
				syslog(LLEV,"fwtksyserr: out of memory: %m");
				unlink(tempfile);
				smap_exit(1);
			}
			strcpy(nr->who,q);
			nr->nxt = recip;
			recip = nr;
			printf("250 %s OK\r\n",q);
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"DATA")) {
			struct	towho	*tp;
			long		now;

			curbytes = 0;
			currecip = 0;
			if(recip == (struct towho *)0) {
				printf("503 need RCPT first.\r\n");
				fflush(stdout);
				continue;
			}
			if((vout = smapopen()) == (FILE *)0) {
				syslog(LLEV,"fwtksyserr: cannot open temp file %m");
				smap_exit(1);
			}

			printf("354 Enter mail, end with \".\" on a line by itself\r\n");
			fflush(stdout);

			fprintf(vout,"FROM %s\n",ruser);
			for(tp = recip; tp != (struct towho *)0; tp = tp->nxt)
				fprintf(vout,"RCPT %s\n",tp->who);
			fprintf(vout,"BODY\n");
			time(&now);
			if (!islocal)
				fprintf(vout,"Received: from %s(%s) by %s via smap (%s)\n\tid %s; %s\n",
					rladdr,riaddr,myhostname,
					FWTK_VERSION_MINOR,tempfile,
					arpadate((char *)0));

			msgsize = 0;
			gotdata = 0;
			switch(crunchbody(cfp,stdin,vout,islocal,islocal))  {
				case 1:
					printf("550 you could say goodbye\r\n");
					fflush(stdout);
					break;
				case 2:
					printf("552 Too much data\r\n");
					fflush(stdout);
					syslog(LLEV,"exiting too much data");
					unlink(tempfile);
					smap_exit(1);
				case 3:
					printf("450 System problem\r\n");
					fflush(stdout);
					continue;
				default:
					gotdata++;
					/* .... fall through... */
			}

			for(tp = recip; tp != (struct towho *)0; tp = tp->nxt)
				syslog(LLEV,"host=%.512s/%.20s bytes=%d from=%.100s to=%.100s",rladdr,riaddr,msgsize,ruser,tp->who);
			if(recip != (struct towho *)0) {
				currecip = 0;
				/* should really free them */
				recip = (struct towho *)0;
			}
			if(!gotdata) {
				unlink(tempfile);
				syslog(LLEV,"SMTP QUIT with no message %.512s/%.20s",rladdr,riaddr);
				smap_exit(1);
			}
			add_file_list(tempfile);
			chmod(tempfile,0700);
			printf("250 Mail accepted\r\n");
			fflush(stdout);
			syslog(LLEV,"exiting host=%.512s/%.20s bytes=%d",rladdr,riaddr,msgsize);
			vout = (FILE *)0;
			tempfile = (char *)0;
			continue;
		}

		if(!strcasecmp(p,"VRFY") || !strcasecmp(p,"EXPN")) {
			char	*q;

			if((q = strtok((char *)0," \t\r\n")) == (char *)0)
				printf("550 User Unknown\r\n");
			else
				printf("250 <%s>\r\n",q);
			syslog(LLEV,"%.100s %.100s (%.512s/%.20s)",p,q?q:"",rladdr,riaddr);
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"HELP")) {
			printf("214-Commands\r\n");
			printf("214-HELO    MAIL    RCPT    DATA    RSET\r\n");
			printf("214 NOOP    QUIT    HELP    VRFY    EXPN\r\n");
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"NOOP")) {
			printf("220 OK\r\n");
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"QUIT")) {
			printf("221 Closing connection\r\n");
			fflush(stdout);
			break;
		}

		if(!strcasecmp(p,"RSET")) {
			recip = (struct towho *)0;
			ruser = (char *)0;
			if(vout != (FILE *)0) {
				unlink(tempfile);
				fclose(vout);
				if((vout = smapopen()) == (FILE *)0) {
					syslog(LLEV,"fwtksyserr: cannot open temp file %m");
					smap_exit(1);
				}
			}
			printf("250 Reset state\r\n");
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"HELO")) {
			char	*q;

			if((q = strtok((char *)0,"\r\n")) == (char *)0) {
				printf("250 Charmed, I'm sure.\r\n");
				rhost = "??";
			} else {
				printf("250 (%s) pleased to meet you.\r\n",q);
				rhost = malloc(strlen(q) + 1);
				strcpy(rhost,q);
			}
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"VERB")) {
			printf("200 Verbose mode\r\n");
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"ONEX")) {
			printf("200 Only one transaction\r\n");
			fflush(stdout);
			continue;
		}

		if(!strcasecmp(p,"DEBUG")) {
			printf("200 Debug set -NOT!\r\n");
			fflush(stdout);
			continue;
		}

		printf("500 Command unrecognized\r\n");
		fflush(stdout);
	}
	smap_exit(0);
}

static	FILE	*
smapopen()
{
	FILE	*ret;
	int	fd;
	char	xuf[512];

	strcpy(xuf,"xmaXXXXXX");
	/* system V locking will have problems if mkstemp does
	not open for random access as well as write */
	if((fd = mkstemp(xuf)) < 0)
		return((FILE *)0);
	chmod(xuf,0600);
	if((ret = fdopen(fd,"w")) == (FILE *)0)
		return((FILE *)0);
	lock_fd(fd);
	if((tempfile = malloc(strlen(xuf) + 1)) == (char *)0) {
		syslog(LLEV,"fwtksyserr: out of memory: %m");
		unlink(xuf);
		smap_exit(1);
	}
	strcpy(tempfile,xuf);
	return(ret);
}

crunchbody(cfp,in,out,local,strip)
Cfg	*cfp;
FILE	*in;
FILE	*out;
int	local;
int	strip;
{
	char	buffer[BUFSIZ], *buf = NULL;
	int	x;
	int	len;
	int	drop = 0;
	int	inhdr = 1;
	int	skip;
	int	domime = 0;

	while(1) {
		x = crlfgets(buffer, sizeof(buffer), in);
		if (x < 0)
			return(1);
		if (x == 1)
			break;
		if (drop)
			continue;
		buf = buffer;
		len = strlen(buf);
		if (!inhdr) {
#ifdef	MIME_CHECK
			if (domime && (checkmime(cfp, &buf, len) == -1))
				drop = 1;
#endif
		} else if (inhdr) {
#ifdef	MIME_CHECK
			domime = mimehdr(buf, len);
#endif
			if (len == 0)
				inhdr = 0;

			if (strip) {
				if (local) {
					if (skip && (*buf == '\t'))
						continue;
					skip = !strncmp("Received:", buf, 9);
					if (skip)
						continue;
				}
			}
		}
		if (maxbytes > 0) {
			if ((curbytes += len) > maxbytes) {
				syslog(LLEV,"message larger than limit of %d bytes - discarding", maxbytes);
				drop = 1;
			}
		}
		if(buf[0] == '.' && buf[1] == '.')
			x = fprintf(out,"%s\n",&buf[1]);
		else
			x = fprintf(out,"%s\n",buf);

		if (buf != buffer)
			free(buf);

		if (x == -1 || ferror(out)) {
			fclose(out);
			syslog(LLEV,"fwtksyserr: cannot fprintf: %m");
			return(3);
		}
	}
	if (fflush(out) || ferror(out)) {
		syslog(LLEV,"fwtksyserr: cannot fprintf: %m");
		return(3);
	}
	msgsize = filesize(fileno(out));
	lockun_fd(fileno(out));
	if (fclose(out)) {
		syslog(LLEV,"fwtksyserr: cannot fclose: %m");
		return (1);
	}
	if (drop)
		return (2);
	return(0);
}

static int	
crlfgets(buf,siz,fp)
char	*buf;
int	siz;
FILE	*fp;
{
	int	x;
	char	*s = buf;
	int	endline = 0;

	if(ferror(fp) || feof(fp))
		return(-1);

	while(--siz) {
		if((x = fgetc(fp)) == EOF) {
			if(s == buf)
				return(-1);
			break;
		}
		if(x == '\n') {
			/* back up over carriage returns */
			if(s > buf && *(s - 1) == '\r') {
				s--;
				if (buf[0] == '.' && s == buf+1)
					endline = 1;
			}
			break;
		}
		*s++ = x;
	}
	*s = '\0';
	livetim = 1;
	/* SunOS sendmail violates end-of-message spec and sends ".\0".
	* You can be correct or you can work ;-)
	*/
	if ((buf[0] == '.') && !endline) {
		if ((buf[1] == '\n') || (buf[1] == '\0')) {
			syslog(LLEV, "Caught pathological End of Message? buf[1] is %d", (int) buf[1]);
			endline = 1;
		}
	}
	return (endline);
}

filesize(fd)
int	fd;
{
	struct	stat	sbuf;

	if(fstat(fd,&sbuf))
		return(-1);
	return(sbuf.st_size);
}

usage()
{
	printf("usage:\n");
	return(1);
}

static void
add_file_list (tmpf)
char *tmpf;
{
	struct file_list *fl = (struct file_list *) malloc(sizeof(struct file_list));
	char *p = (char *)malloc(strlen(tmpf) + 1);

	if (p == (char *)0 || fl == (struct file_list *)0)
		smap_exit(1);

	strcpy(p, tmpf);
	fl->next = filelist;
	fl->tmpfile = p;
	filelist = fl;

	/* use link to make it visible to smapd keeping original as well */
	p[0] = 's';
	if (link(tmpf, p) && (errno != EEXIST)) {
		syslog(LLEV, "link from %.100s to %.100s failed, %m", tmpf, p);
		p[0] = 'x';
		smap_exit(1);
	}
	p[0] = 'x';
}

static void
smap_exit (code)
int code;
{
	struct file_list *fl;

	while (filelist != (struct file_list *)0) {
		fl = filelist->next;
		unlink(filelist->tmpfile);

		free(filelist->tmpfile);
		filelist = fl;
	}
	exit(code);
}

static void
waitwaitwait()
{
	int cstat;

#ifdef SYSV
	while (waitpid(-1, &cstat, WNOHANG) > 0)
#else
	while (wait3(&cstat, WNOHANG, (struct rusage *)0) > 0)
#endif
		;
}

/* kludge for whitehouse.gov anti-spoofing bogosity */

#ifndef SYSV
extern	char	*index();
extern	char	*rindex();
#endif
extern	char	*strpbrk();

char	*bad = "550 Recipient must be in form of: user, [email-masked], or [email-masked]\r\n";

checkvalid(r,cfp)
char	*r;
Cfg	*cfp;
{
	Cfg	*cf;
	char	*atp;
	char	*jxp;
	char	*chop;
	char	*chsavp;
	int	x;

	if((chop = malloc((x = strlen(r)) + 1)) == (char *)0) {
		unlink(tempfile);
		syslog(LLEV,"fwtksyserr: of memory: %m");
		exit(1);
	}
	chsavp = chop;
	strcpy(chop,r);

	if(r[0] == '<') {
		if(chop[x - 1] == '>')
			chop[x - 1] = '\0';
		chop++;
	}

	if((atp = rindex(chop,'@')) != (char *)0) {
		atp++;

		if((cf = cfg_get("localdomains",cfp)) != (Cfg *)0) {
			int i;

			for (i = 0; i < cf->argc; i++)
				if (!strcasecmp(atp, cf->argv[i]))
					break;
			if (i == cf->argc)
				goto bomb;
		}

		/* now make sure there are no other routing chars */
		atp--;
		*atp = '\0';
		if((jxp = strpbrk(chop,"%@:[]!")) != (char *)0) {
			goto bomb;
		}
	}
	if((jxp = strpbrk(chop,"%@:[]!")) != (char *)0)
		goto bomb;

	free(chsavp);
	return(1);
bomb:
	printf(bad);
	free(chsavp);
	return(0);
}

#ifdef	MIME_CHECK
#define	CONTENT_TYPE_LEN	30

static char *boundary = NULL;
static int boundary_len = 0;
static int erase_mime = 0;
static int mime_next = 0;

static int parse_boundary(buf, len)
char *buf;
int len;
{
	char *s, *t;

	for (s = buf; (len > 0) && (isspace(*s)); s++)
		;
	if (len < 12)
		return 1;

	if (strncasecmp(s, "boundary", 8))
		return 1;

	s += 8;
	if (*s == '=')
		s++, len--;
	else
		return 1;

	if (*s == '"')
		s++, len--;
	else
		return 1;

	t = strchr(s, '"');
	if (t == NULL)
		return 1;

	boundary_len = t - s;
	boundary = malloc(boundary_len + 2);
	if (boundary != NULL) {
		strncpy(boundary, s, boundary_len);
		boundary[boundary_len + 1] = '\0';
	}
	return 0;
}

int matches(mask, name)
u_char *mask, *name;
{
	u_char m;
	u_char c;

	for (;; mask++, name++) {
		m = islower(*mask) ? *mask : tolower(*mask);
		c = islower(*mask) ? *mask : tolower(*name);
		if (c == '\0')
			break;
		if (m != '?' && m != c || c == '*')
			break;
	}
	if (m == '*') {
		for ( ; *mask == '*'; mask++);
			if (*mask == '\0')
				return(0);
		for (; *name && matches(mask, name); name++);
			return(*name ? 0 : 1);
	} else
		return ((m == '\0' && c == '\0') ? 0 : 1);
}

int mimehdr(buf, len)
char *buf;
int len;
{
	static int boundary_next = 0;
	char *s, *t;

	if (boundary)
		return 1;

	if (boundary_next) {
		(void) parse_boundary(buf, len);
		boundary_next = 0;
		if (boundary)
			return 1;
		return 0;
	}

	if (len < CONTENT_TYPE_LEN)
		return 0;

	if (strncasecmp(buf, "Content-type: multipart/mixed;",
			CONTENT_TYPE_LEN))
		return 0;
	buf += CONTENT_TYPE_LEN;
	len -= CONTENT_TYPE_LEN;
	boundary_next = parse_boundary(buf, len);
	if (boundary)
		return 1;
	return 0;
}

int checkmime(cfp, bufp, len)
char **bufp;
int len;
{
	char c, *s, *t, *buf, *v;
	int i, j;
	Cfg *cf;

	buf = *bufp;

	if (!mime_next) {
		if (len >= (boundary_len + 2) && !strncmp(buf, "--", 2) &&
		    !strncmp(buf + 2, boundary, boundary_len)) {
			mime_next = 1;
			erase_mime = 0;
			return 0;
		}
	}

	if (erase_mime && !mime_next) {
		if (erase_mime == 1) {
			if (!len)
				erase_mime = 2;
		} else if (erase_mime == 2) {
			for (s = buf, i = len; i; s++, i--)
				*s = 'X';
		}
		return 0;
	}

	if (len < 18) {
		mime_next = 0;
		return 0;
	}
	if (!mime_next)
		return 0;

	if (strncasecmp("Content-Type:", buf, 13)) {
		mime_next = 0;
		return 0;
	}

	s = buf + 13;
	i = len - 13;
	while (isspace(*s) && (i > 0)) {
		s++;
		i--;
	}

	if (i > 4) {
		for (t = s; (i > 0) && !isspace(*t); t++, i--)
			;
		if (i > 0) {
			c = *t;
			*t = '\0';
		} else
			c = '\0';

		if((cf = cfg_get("erase-mime", cfp)) != (Cfg *)0) {
			if (cf->argc < 1) {
				syslog(LLEV,"fwtkcfgerr: erase-mime must have at least one parameter, line %d",cf->ln);
				exit(1);
			}

			for (j = 0; j < cf->argc; j++)
				if (!matches(cf->argv[j], s)) {
					syslog(LLEV, "erasing MIME type %s", s);
					erase_mime = 1;
					break;
				}
		}

		if((cf = cfg_get("convert-mime", cfp)) != (Cfg *)0) {
			if (cf->argc & 1) {
				syslog(LLEV,"fwtkcfgerr: convert-mime must have parameters in pairs, line %d",cf->ln);
				exit(1);
			}

			for (j = 0; (j + 1) < cf->argc; j += 2)
				if (!matches(cf->argv[j], s)) {
					syslog(LLEV, "MIME type %s -> %s",
					       s, cf->argv[j + 1]);
					v = malloc(strlen(cf->argv[j + 1]) +
						   len + 1);
					if (!v)	/* FORCE DROP */
						return -1;
					strncpy(v, buf, s - buf);
					strcat(v, cf->argv[j + 1]);
					if (c)
						*t = c;
					strcat(v, t);
					if (c)
						*t = '\0';
					buf = *bufp = v;
					break;
				}
		}

		if (i > 0)
			*t = c;
	}
	return 0;
}
#endif