xref: /plan9/sys/src/cmd/upas/scanmail/common.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
180ee5cbfSDavid du Colombier #include <u.h>
280ee5cbfSDavid du Colombier #include <libc.h>
380ee5cbfSDavid du Colombier #include <bio.h>
480ee5cbfSDavid du Colombier #include <regexp.h>
580ee5cbfSDavid du Colombier #include "spam.h"
680ee5cbfSDavid du Colombier 
780ee5cbfSDavid du Colombier enum {
880ee5cbfSDavid du Colombier 	Quanta	= 8192,
980ee5cbfSDavid du Colombier 	Minbody = 6000,
1080ee5cbfSDavid du Colombier 	HdrMax	= 15,
1180ee5cbfSDavid du Colombier };
1280ee5cbfSDavid du Colombier 
1380ee5cbfSDavid du Colombier typedef struct keyword Keyword;
1480ee5cbfSDavid du Colombier typedef struct word Word;
1580ee5cbfSDavid du Colombier 
1680ee5cbfSDavid du Colombier struct word{
1780ee5cbfSDavid du Colombier 	char	*string;
1880ee5cbfSDavid du Colombier 	int	n;
1980ee5cbfSDavid du Colombier };
2080ee5cbfSDavid du Colombier 
2180ee5cbfSDavid du Colombier struct	keyword{
2280ee5cbfSDavid du Colombier 	char	*string;
2380ee5cbfSDavid du Colombier 	int	value;
2480ee5cbfSDavid du Colombier };
2580ee5cbfSDavid du Colombier 
2680ee5cbfSDavid du Colombier Word	htmlcmds[] =
2780ee5cbfSDavid du Colombier {
2880ee5cbfSDavid du Colombier 	"html",		4,
2980ee5cbfSDavid du Colombier 	"!doctype html", 13,
3080ee5cbfSDavid du Colombier 	0,
3180ee5cbfSDavid du Colombier 
3280ee5cbfSDavid du Colombier };
3380ee5cbfSDavid du Colombier 
3480ee5cbfSDavid du Colombier Word	hrefs[] =
3580ee5cbfSDavid du Colombier {
3680ee5cbfSDavid du Colombier 	"a href=",	7,
3780ee5cbfSDavid du Colombier 	"a title=",	8,
38*9a747e4fSDavid du Colombier 	"a target=",	9,
3980ee5cbfSDavid du Colombier 	"base href=",	10,
4080ee5cbfSDavid du Colombier 	"img src=",	8,
4180ee5cbfSDavid du Colombier 	"img border=",	11,
4280ee5cbfSDavid du Colombier 	"form action=", 12,
4380ee5cbfSDavid du Colombier 	"!--",		3,
4480ee5cbfSDavid du Colombier 	0,
4580ee5cbfSDavid du Colombier 
4680ee5cbfSDavid du Colombier };
4780ee5cbfSDavid du Colombier 
4880ee5cbfSDavid du Colombier /*
4980ee5cbfSDavid du Colombier  *	RFC822 header keywords to look for for fractured header.
5080ee5cbfSDavid du Colombier  *	all lengths must be less than HdrMax defined above.
5180ee5cbfSDavid du Colombier  */
5280ee5cbfSDavid du Colombier Word	hdrwords[] =
5380ee5cbfSDavid du Colombier {
5480ee5cbfSDavid du Colombier 	"cc:",			3,
5580ee5cbfSDavid du Colombier 	"bcc:", 		4,
5680ee5cbfSDavid du Colombier 	"to:",			3,
5780ee5cbfSDavid du Colombier 	0,			0,
5880ee5cbfSDavid du Colombier 
5980ee5cbfSDavid du Colombier };
6080ee5cbfSDavid du Colombier 
6180ee5cbfSDavid du Colombier Keyword	keywords[] =
6280ee5cbfSDavid du Colombier {
6380ee5cbfSDavid du Colombier 	"header",	HoldHeader,
6480ee5cbfSDavid du Colombier 	"line",		SaveLine,
6580ee5cbfSDavid du Colombier 	"hold",		Hold,
6680ee5cbfSDavid du Colombier 	"dump",		Dump,
6780ee5cbfSDavid du Colombier 	"loff",		Lineoff,
6880ee5cbfSDavid du Colombier 	0,		Nactions,
6980ee5cbfSDavid du Colombier };
7080ee5cbfSDavid du Colombier 
7180ee5cbfSDavid du Colombier Patterns patterns[] = {
7280ee5cbfSDavid du Colombier [Dump]		{ "DUMP:", 0, 0 },
7380ee5cbfSDavid du Colombier [HoldHeader]	{ "HEADER:", 0, 0 },
7480ee5cbfSDavid du Colombier [Hold]		{ "HOLD:", 0, 0 },
7580ee5cbfSDavid du Colombier [SaveLine]	{ "LINE:", 0, 0 },
7680ee5cbfSDavid du Colombier [Lineoff]	{ "LINEOFF:", 0, 0 },
7780ee5cbfSDavid du Colombier [Nactions]	{ 0, 0, 0 },
7880ee5cbfSDavid du Colombier };
7980ee5cbfSDavid du Colombier 
8080ee5cbfSDavid du Colombier static char*	endofhdr(char*, char*);
8180ee5cbfSDavid du Colombier static	int	escape(char**);
8280ee5cbfSDavid du Colombier static	int	extract(char*);
8380ee5cbfSDavid du Colombier static	int	findkey(char*);
8480ee5cbfSDavid du Colombier static	int	hash(int);
8580ee5cbfSDavid du Colombier static	int	isword(Word*, char*, int);
8680ee5cbfSDavid du Colombier static	void	parsealt(Biobuf*, char*, Spat**);
8780ee5cbfSDavid du Colombier 
8880ee5cbfSDavid du Colombier /*
8980ee5cbfSDavid du Colombier  *	The canonicalizer: convert input to canonical representation
9080ee5cbfSDavid du Colombier  */
9180ee5cbfSDavid du Colombier char*
readmsg(Biobuf * bp,int * hsize,int * bufsize)9280ee5cbfSDavid du Colombier readmsg(Biobuf *bp, int *hsize, int *bufsize)
9380ee5cbfSDavid du Colombier {
9480ee5cbfSDavid du Colombier 	char *p, *buf;
9580ee5cbfSDavid du Colombier 	int n, offset, eoh, bsize, delta;
9680ee5cbfSDavid du Colombier 
9780ee5cbfSDavid du Colombier 	buf = 0;
9880ee5cbfSDavid du Colombier 	offset = 0;
9980ee5cbfSDavid du Colombier 	if(bufsize)
10080ee5cbfSDavid du Colombier 		*bufsize = 0;
10180ee5cbfSDavid du Colombier 	if(hsize)
10280ee5cbfSDavid du Colombier 		*hsize = 0;
10380ee5cbfSDavid du Colombier 	for(;;) {
10480ee5cbfSDavid du Colombier 		buf = Realloc(buf, offset+Quanta+1);
10580ee5cbfSDavid du Colombier 		n = Bread(bp, buf+offset, Quanta);
10680ee5cbfSDavid du Colombier 		if(n < 0){
10780ee5cbfSDavid du Colombier 			free(buf);
10880ee5cbfSDavid du Colombier 			return 0;
10980ee5cbfSDavid du Colombier 		}
11080ee5cbfSDavid du Colombier 		p = buf+offset;			/* start of this chunk */
11180ee5cbfSDavid du Colombier 		offset += n;			/* end of this chunk */
11280ee5cbfSDavid du Colombier 		buf[offset] = 0;
11380ee5cbfSDavid du Colombier 		if(n == 0){
11480ee5cbfSDavid du Colombier 			if(offset == 0)
11580ee5cbfSDavid du Colombier 				return 0;
11680ee5cbfSDavid du Colombier 			break;
11780ee5cbfSDavid du Colombier 		}
11880ee5cbfSDavid du Colombier 
11980ee5cbfSDavid du Colombier 		if(hsize == 0)			/* don't process header */
12080ee5cbfSDavid du Colombier 			break;
12180ee5cbfSDavid du Colombier 		if(p != buf && p[-1] == '\n')	/* check for EOH across buffer split */
12280ee5cbfSDavid du Colombier 			p--;
12380ee5cbfSDavid du Colombier 		p = endofhdr(p, buf+offset);
12480ee5cbfSDavid du Colombier 		if(p)
12580ee5cbfSDavid du Colombier 			break;
12680ee5cbfSDavid du Colombier 		if(offset >= Maxread)		/* gargantuan header - just punt*/
12780ee5cbfSDavid du Colombier 		{
12880ee5cbfSDavid du Colombier 			if(hsize)
12980ee5cbfSDavid du Colombier 				*hsize = offset;
13080ee5cbfSDavid du Colombier 			if(bufsize)
13180ee5cbfSDavid du Colombier 				*bufsize = offset;
13280ee5cbfSDavid du Colombier 			return buf;
13380ee5cbfSDavid du Colombier 		}
13480ee5cbfSDavid du Colombier 	}
13580ee5cbfSDavid du Colombier 	eoh = p-buf;				/* End of header */
13680ee5cbfSDavid du Colombier 	bsize = offset - eoh;			/* amount of body already read */
13780ee5cbfSDavid du Colombier 
13880ee5cbfSDavid du Colombier 		/* Read at least Minbody bytes of the body */
13980ee5cbfSDavid du Colombier 	if (bsize < Minbody){
14080ee5cbfSDavid du Colombier 		delta = Minbody-bsize;
14180ee5cbfSDavid du Colombier 		buf = Realloc(buf, offset+delta+1);
14280ee5cbfSDavid du Colombier 		n = Bread(bp, buf+offset, delta);
14380ee5cbfSDavid du Colombier 		if(n > 0) {
14480ee5cbfSDavid du Colombier 			offset += n;
14580ee5cbfSDavid du Colombier 			buf[offset] = 0;
14680ee5cbfSDavid du Colombier 		}
14780ee5cbfSDavid du Colombier 	}
14880ee5cbfSDavid du Colombier 	if(hsize)
14980ee5cbfSDavid du Colombier 		*hsize = eoh;
15080ee5cbfSDavid du Colombier 	if(bufsize)
15180ee5cbfSDavid du Colombier 		*bufsize = offset;
15280ee5cbfSDavid du Colombier 	return buf;
15380ee5cbfSDavid du Colombier }
15480ee5cbfSDavid du Colombier 
15580ee5cbfSDavid du Colombier static	int
isword(Word * wp,char * text,int len)15680ee5cbfSDavid du Colombier isword(Word *wp, char *text, int len)
15780ee5cbfSDavid du Colombier {
15880ee5cbfSDavid du Colombier 	for(;wp->string; wp++)
15980ee5cbfSDavid du Colombier 		if(len >= wp->n && strncmp(text, wp->string, wp->n) == 0)
16080ee5cbfSDavid du Colombier 			return 1;
16180ee5cbfSDavid du Colombier 	return 0;
16280ee5cbfSDavid du Colombier }
16380ee5cbfSDavid du Colombier 
16480ee5cbfSDavid du Colombier static char*
endofhdr(char * raw,char * end)16580ee5cbfSDavid du Colombier endofhdr(char *raw, char *end)
16680ee5cbfSDavid du Colombier {
16780ee5cbfSDavid du Colombier 	int i;
16880ee5cbfSDavid du Colombier 	char *p, *q;
16980ee5cbfSDavid du Colombier 	char buf[HdrMax];
17080ee5cbfSDavid du Colombier 
17180ee5cbfSDavid du Colombier 	/*
17280ee5cbfSDavid du Colombier  	 * can't use strchr to search for newlines because
17380ee5cbfSDavid du Colombier 	 * there may be embedded NULL's.
17480ee5cbfSDavid du Colombier 	 */
17580ee5cbfSDavid du Colombier 	for(p = raw; p < end; p++){
17680ee5cbfSDavid du Colombier 		if(*p != '\n' || p[1] != '\n')
17780ee5cbfSDavid du Colombier 			continue;
17880ee5cbfSDavid du Colombier 		p++;
17980ee5cbfSDavid du Colombier 		for(i = 0, q = p+1; i < sizeof(buf) && *q; q++){
18080ee5cbfSDavid du Colombier 			buf[i++] = tolower(*q);
18180ee5cbfSDavid du Colombier 			if(*q == ':' || *q == '\n')
18280ee5cbfSDavid du Colombier 				break;
18380ee5cbfSDavid du Colombier 		}
18480ee5cbfSDavid du Colombier 		if(!isword(hdrwords, buf, i))
18580ee5cbfSDavid du Colombier 			return p+1;
18680ee5cbfSDavid du Colombier 	}
18780ee5cbfSDavid du Colombier 	return 0;
18880ee5cbfSDavid du Colombier }
18980ee5cbfSDavid du Colombier 
19080ee5cbfSDavid du Colombier static	int
htmlmatch(Word * wp,char * text,char * end,int * n)19180ee5cbfSDavid du Colombier htmlmatch(Word *wp, char *text, char *end, int *n)
19280ee5cbfSDavid du Colombier {
19380ee5cbfSDavid du Colombier 	char *cp;
19480ee5cbfSDavid du Colombier 	int i, c, lastc;
19580ee5cbfSDavid du Colombier 	char buf[MaxHtml];
19680ee5cbfSDavid du Colombier 
19780ee5cbfSDavid du Colombier 	/*
19880ee5cbfSDavid du Colombier 	 * extract a string up to '>'
19980ee5cbfSDavid du Colombier 	 */
20080ee5cbfSDavid du Colombier 
20180ee5cbfSDavid du Colombier 	i = lastc = 0;
20280ee5cbfSDavid du Colombier 	cp = text;
20380ee5cbfSDavid du Colombier 	while (cp < end && i < sizeof(buf)-1){
20480ee5cbfSDavid du Colombier 		c = *cp++;
20580ee5cbfSDavid du Colombier 		if(c == '=')
20680ee5cbfSDavid du Colombier 			c = escape(&cp);
20780ee5cbfSDavid du Colombier 		switch(c){
20880ee5cbfSDavid du Colombier 		case 0:
20980ee5cbfSDavid du Colombier 		case '\r':
21080ee5cbfSDavid du Colombier 			continue;
21180ee5cbfSDavid du Colombier 		case '>':
21280ee5cbfSDavid du Colombier 			goto out;
21380ee5cbfSDavid du Colombier 		case '\n':
21480ee5cbfSDavid du Colombier 		case ' ':
21580ee5cbfSDavid du Colombier 		case '\t':
21680ee5cbfSDavid du Colombier 			if(lastc == ' ')
21780ee5cbfSDavid du Colombier 				continue;
21880ee5cbfSDavid du Colombier 			c = ' ';
21980ee5cbfSDavid du Colombier 			break;
22080ee5cbfSDavid du Colombier 		default:
22180ee5cbfSDavid du Colombier 			c = tolower(c);
22280ee5cbfSDavid du Colombier 			break;
22380ee5cbfSDavid du Colombier 		}
22480ee5cbfSDavid du Colombier 		buf[i++] = lastc = c;
22580ee5cbfSDavid du Colombier 	}
22680ee5cbfSDavid du Colombier out:
22780ee5cbfSDavid du Colombier 	buf[i] = 0;
22880ee5cbfSDavid du Colombier 	if(n)
22980ee5cbfSDavid du Colombier 		*n = cp-text;
23080ee5cbfSDavid du Colombier 	return isword(wp, buf, i);
23180ee5cbfSDavid du Colombier }
23280ee5cbfSDavid du Colombier 
23380ee5cbfSDavid du Colombier static int
escape(char ** msg)23480ee5cbfSDavid du Colombier escape(char **msg)
23580ee5cbfSDavid du Colombier {
23680ee5cbfSDavid du Colombier 	int c;
23780ee5cbfSDavid du Colombier 	char *p;
23880ee5cbfSDavid du Colombier 
23980ee5cbfSDavid du Colombier 	p = *msg;
24080ee5cbfSDavid du Colombier 	c = *p;
24180ee5cbfSDavid du Colombier 	if(c == '\n'){
24280ee5cbfSDavid du Colombier 		p++;
24380ee5cbfSDavid du Colombier 		c = *p++;
24480ee5cbfSDavid du Colombier 	} else
24580ee5cbfSDavid du Colombier 	if(c == '2'){
24680ee5cbfSDavid du Colombier 		c = tolower(p[1]);
24780ee5cbfSDavid du Colombier 		if(c == 'e'){
24880ee5cbfSDavid du Colombier 			p += 2;
24980ee5cbfSDavid du Colombier 			c = '.';
25080ee5cbfSDavid du Colombier 		}else
25180ee5cbfSDavid du Colombier 		if(c == 'f'){
25280ee5cbfSDavid du Colombier 			p += 2;
25380ee5cbfSDavid du Colombier 			c = '/';
25480ee5cbfSDavid du Colombier 		}else
25580ee5cbfSDavid du Colombier 		if(c == '0'){
25680ee5cbfSDavid du Colombier 			p += 2;
25780ee5cbfSDavid du Colombier 			c = ' ';
25880ee5cbfSDavid du Colombier 		}
25980ee5cbfSDavid du Colombier 		else c = '=';
26080ee5cbfSDavid du Colombier 	} else {
26180ee5cbfSDavid du Colombier 		if(c == '3' && tolower(p[1]) == 'd')
26280ee5cbfSDavid du Colombier 			p += 2;
26380ee5cbfSDavid du Colombier 		c = '=';
26480ee5cbfSDavid du Colombier 	}
26580ee5cbfSDavid du Colombier 	*msg = p;
26680ee5cbfSDavid du Colombier 	return c;
26780ee5cbfSDavid du Colombier }
26880ee5cbfSDavid du Colombier 
26980ee5cbfSDavid du Colombier static int
htmlchk(char ** msg,char * end)27080ee5cbfSDavid du Colombier htmlchk(char **msg, char *end)
27180ee5cbfSDavid du Colombier {
27280ee5cbfSDavid du Colombier 	int n;
27380ee5cbfSDavid du Colombier 	char *p;
27480ee5cbfSDavid du Colombier 
27580ee5cbfSDavid du Colombier 	static int ishtml;
27680ee5cbfSDavid du Colombier 
27780ee5cbfSDavid du Colombier 	p = *msg;
27880ee5cbfSDavid du Colombier 	if(ishtml == 0){
27980ee5cbfSDavid du Colombier 		ishtml = htmlmatch(htmlcmds, p, end, &n);
28080ee5cbfSDavid du Colombier 
28180ee5cbfSDavid du Colombier 		/* If not an HTML keyword, check if it's
28280ee5cbfSDavid du Colombier 		 * an HTML comment (<!comment>).  if so,
28380ee5cbfSDavid du Colombier 		 * skip over it; otherwise copy it in.
28480ee5cbfSDavid du Colombier 		 */
28580ee5cbfSDavid du Colombier 		if(ishtml == 0 && *p != '!')	/* not comment */
28680ee5cbfSDavid du Colombier 			return '<';		/* copy it */
28780ee5cbfSDavid du Colombier 
28880ee5cbfSDavid du Colombier 	} else if(htmlmatch(hrefs, p, end, &n))	/* if special HTML string  */
28980ee5cbfSDavid du Colombier 		return '<';			/* copy it */
29080ee5cbfSDavid du Colombier 
29180ee5cbfSDavid du Colombier 	/*
29280ee5cbfSDavid du Colombier 	 * this is an uninteresting HTML command; skip over it.
29380ee5cbfSDavid du Colombier 	 */
29480ee5cbfSDavid du Colombier 	p += n;
29580ee5cbfSDavid du Colombier 	*msg = p+1;
29680ee5cbfSDavid du Colombier 	return *p;
29780ee5cbfSDavid du Colombier }
29880ee5cbfSDavid du Colombier 
299*9a747e4fSDavid du Colombier /*
300*9a747e4fSDavid du Colombier  * decode a base 64 encode body
301*9a747e4fSDavid du Colombier  */
30280ee5cbfSDavid du Colombier void
conv64(char * msg,char * end,char * buf,int bufsize)303*9a747e4fSDavid du Colombier conv64(char *msg, char *end, char *buf, int bufsize)
304*9a747e4fSDavid du Colombier {
305*9a747e4fSDavid du Colombier 	int len, i;
306*9a747e4fSDavid du Colombier 	char *cp;
307*9a747e4fSDavid du Colombier 
308*9a747e4fSDavid du Colombier 	len = end - msg;
309*9a747e4fSDavid du Colombier 	i = (len*3)/4+1;	// room for max chars + null
310*9a747e4fSDavid du Colombier 	cp = Malloc(i);
311*9a747e4fSDavid du Colombier 	len = dec64((uchar*)cp, i, msg, len);
312*9a747e4fSDavid du Colombier 	convert(cp, cp+len, buf, bufsize, 1);
313*9a747e4fSDavid du Colombier 	free(cp);
314*9a747e4fSDavid du Colombier }
315*9a747e4fSDavid du Colombier 
316*9a747e4fSDavid du Colombier int
convert(char * msg,char * end,char * buf,int bufsize,int isbody)31780ee5cbfSDavid du Colombier convert(char *msg, char *end, char *buf, int bufsize, int isbody)
31880ee5cbfSDavid du Colombier {
31980ee5cbfSDavid du Colombier 
32080ee5cbfSDavid du Colombier 	char *p;
321*9a747e4fSDavid du Colombier 	int c, lastc, base64;
32280ee5cbfSDavid du Colombier 
32380ee5cbfSDavid du Colombier 	lastc = 0;
324*9a747e4fSDavid du Colombier 	base64 = 0;
32580ee5cbfSDavid du Colombier 	while(msg < end && bufsize > 0){
32680ee5cbfSDavid du Colombier 		c = *msg++;
32780ee5cbfSDavid du Colombier 
32880ee5cbfSDavid du Colombier 		/*
32980ee5cbfSDavid du Colombier 		 * In the body only, try to strip most HTML and
33080ee5cbfSDavid du Colombier 		 * replace certain MIME escape sequences with the character
33180ee5cbfSDavid du Colombier 		 */
33280ee5cbfSDavid du Colombier 		if(isbody) {
33380ee5cbfSDavid du Colombier 			do{
33480ee5cbfSDavid du Colombier 				p = msg;
33580ee5cbfSDavid du Colombier 				if(c == '<')
33680ee5cbfSDavid du Colombier 					c = htmlchk(&msg, end);
33780ee5cbfSDavid du Colombier 				if(c == '=')
33880ee5cbfSDavid du Colombier 					c = escape(&msg);
33980ee5cbfSDavid du Colombier 			} while(p != msg && p < end);
34080ee5cbfSDavid du Colombier 		}
34180ee5cbfSDavid du Colombier 		switch(c){
34280ee5cbfSDavid du Colombier 		case 0:
34380ee5cbfSDavid du Colombier 		case '\r':
34480ee5cbfSDavid du Colombier 			continue;
34580ee5cbfSDavid du Colombier 		case '\t':
34680ee5cbfSDavid du Colombier 		case ' ':
34780ee5cbfSDavid du Colombier 		case '\n':
34880ee5cbfSDavid du Colombier 			if(lastc == ' ')
34980ee5cbfSDavid du Colombier 				continue;
35080ee5cbfSDavid du Colombier 			c = ' ';
35180ee5cbfSDavid du Colombier 			break;
352*9a747e4fSDavid du Colombier 		case 'C':	/* check for MIME base 64 encoding in header */
353*9a747e4fSDavid du Colombier 		case 'c':
354*9a747e4fSDavid du Colombier 			if(isbody == 0)
355*9a747e4fSDavid du Colombier 			if(msg < end-32 && *msg == 'o' && msg[1] == 'n')
356*9a747e4fSDavid du Colombier 			if(cistrncmp(msg+2, "tent-transfer-encoding: base64", 30) == 0)
357*9a747e4fSDavid du Colombier 				base64 = 1;
358*9a747e4fSDavid du Colombier 			c = 'c';
359*9a747e4fSDavid du Colombier 			break;
36080ee5cbfSDavid du Colombier 		default:
36180ee5cbfSDavid du Colombier 			c = tolower(c);
36280ee5cbfSDavid du Colombier 			break;
36380ee5cbfSDavid du Colombier 		}
36480ee5cbfSDavid du Colombier 		*buf++ = c;
36580ee5cbfSDavid du Colombier 		lastc = c;
36680ee5cbfSDavid du Colombier 		bufsize--;
36780ee5cbfSDavid du Colombier 	}
36880ee5cbfSDavid du Colombier 	*buf = 0;
369*9a747e4fSDavid du Colombier 	return base64;
37080ee5cbfSDavid du Colombier }
37180ee5cbfSDavid du Colombier 
37280ee5cbfSDavid du Colombier /*
37380ee5cbfSDavid du Colombier  *	The pattern parser: build data structures from the pattern file
37480ee5cbfSDavid du Colombier  */
37580ee5cbfSDavid du Colombier 
37680ee5cbfSDavid du Colombier static int
hash(int c)37780ee5cbfSDavid du Colombier hash(int c)
37880ee5cbfSDavid du Colombier {
37980ee5cbfSDavid du Colombier 	return c & 127;
38080ee5cbfSDavid du Colombier }
38180ee5cbfSDavid du Colombier 
38280ee5cbfSDavid du Colombier static	int
findkey(char * val)38380ee5cbfSDavid du Colombier findkey(char *val)
38480ee5cbfSDavid du Colombier {
38580ee5cbfSDavid du Colombier 	Keyword *kp;
38680ee5cbfSDavid du Colombier 
38780ee5cbfSDavid du Colombier 	for(kp = keywords; kp->string; kp++)
38880ee5cbfSDavid du Colombier 		if(strcmp(val, kp->string) == 0)
38980ee5cbfSDavid du Colombier 				break;
39080ee5cbfSDavid du Colombier 	return kp->value;
39180ee5cbfSDavid du Colombier }
39280ee5cbfSDavid du Colombier 
39380ee5cbfSDavid du Colombier #define	whitespace(c)	((c) == ' ' || (c) == '\t')
39480ee5cbfSDavid du Colombier 
39580ee5cbfSDavid du Colombier void
parsepats(Biobuf * bp)39680ee5cbfSDavid du Colombier parsepats(Biobuf *bp)
39780ee5cbfSDavid du Colombier {
39880ee5cbfSDavid du Colombier 	Pattern *p, *new;
39980ee5cbfSDavid du Colombier 	char *cp, *qp;
40080ee5cbfSDavid du Colombier 	int type, action, n, h;
40180ee5cbfSDavid du Colombier 	Spat *spat;
40280ee5cbfSDavid du Colombier 
40380ee5cbfSDavid du Colombier 	for(;;){
40480ee5cbfSDavid du Colombier 		cp = Brdline(bp, '\n');
40580ee5cbfSDavid du Colombier 		if(cp == 0)
40680ee5cbfSDavid du Colombier 			break;
40780ee5cbfSDavid du Colombier 		cp[Blinelen(bp)-1] = 0;
40880ee5cbfSDavid du Colombier 		while(*cp == ' ' || *cp == '\t')
40980ee5cbfSDavid du Colombier 			cp++;
41080ee5cbfSDavid du Colombier 		if(*cp == '#' || *cp == 0)
41180ee5cbfSDavid du Colombier 			continue;
41280ee5cbfSDavid du Colombier 		type = regexp;
41380ee5cbfSDavid du Colombier 		if(*cp == '*'){
41480ee5cbfSDavid du Colombier 			type = string;
41580ee5cbfSDavid du Colombier 			cp++;
41680ee5cbfSDavid du Colombier 		}
41780ee5cbfSDavid du Colombier 		qp = strchr(cp, ':');
41880ee5cbfSDavid du Colombier 		if(qp == 0)
41980ee5cbfSDavid du Colombier 			continue;
42080ee5cbfSDavid du Colombier 		*qp = 0;
42180ee5cbfSDavid du Colombier 		if(debug)
42280ee5cbfSDavid du Colombier 			fprint(2, "action = %s\n", cp);
42380ee5cbfSDavid du Colombier 		action = findkey(cp);
42480ee5cbfSDavid du Colombier 		if(action >= Nactions)
42580ee5cbfSDavid du Colombier 			continue;
42680ee5cbfSDavid du Colombier 		cp = qp+1;
42780ee5cbfSDavid du Colombier 		n = extract(cp);
42880ee5cbfSDavid du Colombier 		if(n <= 0 || *cp == 0)
42980ee5cbfSDavid du Colombier 			continue;
43080ee5cbfSDavid du Colombier 
43180ee5cbfSDavid du Colombier 		qp = strstr(cp, "~~");
43280ee5cbfSDavid du Colombier 		if(qp){
43380ee5cbfSDavid du Colombier 			*qp = 0;
43480ee5cbfSDavid du Colombier 			n = strlen(cp);
43580ee5cbfSDavid du Colombier 		}
43680ee5cbfSDavid du Colombier 		if(debug)
43780ee5cbfSDavid du Colombier 			fprint(2, " Pattern: `%s'\n", cp);
43880ee5cbfSDavid du Colombier 
43980ee5cbfSDavid du Colombier 			/* Hook regexps into a chain */
44080ee5cbfSDavid du Colombier 		if(type == regexp) {
44180ee5cbfSDavid du Colombier 			new = Malloc(sizeof(Pattern));
44280ee5cbfSDavid du Colombier 			new->action = action;
44380ee5cbfSDavid du Colombier 			new->pat = regcomp(cp);
44480ee5cbfSDavid du Colombier 			if(new->pat == 0){
44580ee5cbfSDavid du Colombier 				free(new);
44680ee5cbfSDavid du Colombier 				continue;
44780ee5cbfSDavid du Colombier 			}
44880ee5cbfSDavid du Colombier 			new->type = regexp;
44980ee5cbfSDavid du Colombier 			new->alt = 0;
45080ee5cbfSDavid du Colombier 			new->next = 0;
45180ee5cbfSDavid du Colombier 
45280ee5cbfSDavid du Colombier 			if(qp)
45380ee5cbfSDavid du Colombier 				parsealt(bp, qp+2, &new->alt);
45480ee5cbfSDavid du Colombier 
45580ee5cbfSDavid du Colombier 			new->next = patterns[action].regexps;
45680ee5cbfSDavid du Colombier 			patterns[action].regexps = new;
45780ee5cbfSDavid du Colombier 			continue;
45880ee5cbfSDavid du Colombier 
45980ee5cbfSDavid du Colombier 		}
46080ee5cbfSDavid du Colombier 			/* not a Regexp - hook strings into Pattern hash chain */
46180ee5cbfSDavid du Colombier 		spat = Malloc(sizeof(*spat));
46280ee5cbfSDavid du Colombier 		spat->next = 0;
46380ee5cbfSDavid du Colombier 		spat->alt = 0;
46480ee5cbfSDavid du Colombier 		spat->len = n;
46580ee5cbfSDavid du Colombier 		spat->string = Malloc(n+1);
46680ee5cbfSDavid du Colombier 		spat->c1 = cp[1];
46780ee5cbfSDavid du Colombier 		strcpy(spat->string, cp);
46880ee5cbfSDavid du Colombier 
46980ee5cbfSDavid du Colombier 		if(qp)
47080ee5cbfSDavid du Colombier 			parsealt(bp, qp+2, &spat->alt);
47180ee5cbfSDavid du Colombier 
47280ee5cbfSDavid du Colombier 		p = patterns[action].strings;
47380ee5cbfSDavid du Colombier 		if(p == 0) {
47480ee5cbfSDavid du Colombier 			p = Malloc(sizeof(Pattern));
47580ee5cbfSDavid du Colombier 			memset(p, 0, sizeof(*p));
47680ee5cbfSDavid du Colombier 			p->action = action;
47780ee5cbfSDavid du Colombier 			p->type = string;
47880ee5cbfSDavid du Colombier 			patterns[action].strings = p;
47980ee5cbfSDavid du Colombier 		}
48080ee5cbfSDavid du Colombier 		h = hash(*spat->string);
48180ee5cbfSDavid du Colombier 		spat->next = p->spat[h];
48280ee5cbfSDavid du Colombier 		p->spat[h] = spat;
48380ee5cbfSDavid du Colombier 	}
48480ee5cbfSDavid du Colombier }
48580ee5cbfSDavid du Colombier 
48680ee5cbfSDavid du Colombier static void
parsealt(Biobuf * bp,char * cp,Spat ** head)48780ee5cbfSDavid du Colombier parsealt(Biobuf *bp, char *cp, Spat** head)
48880ee5cbfSDavid du Colombier {
48980ee5cbfSDavid du Colombier 	char *p;
49080ee5cbfSDavid du Colombier 	Spat *alt;
49180ee5cbfSDavid du Colombier 
49280ee5cbfSDavid du Colombier 	while(cp){
49380ee5cbfSDavid du Colombier 		if(*cp == 0){		/*escaped newline*/
49480ee5cbfSDavid du Colombier 			do{
49580ee5cbfSDavid du Colombier 				cp = Brdline(bp, '\n');
49680ee5cbfSDavid du Colombier 				if(cp == 0)
49780ee5cbfSDavid du Colombier 					return;
49880ee5cbfSDavid du Colombier 				cp[Blinelen(bp)-1] = 0;
49980ee5cbfSDavid du Colombier 			} while(extract(cp) <= 0 || *cp == 0);
50080ee5cbfSDavid du Colombier 		}
50180ee5cbfSDavid du Colombier 
50280ee5cbfSDavid du Colombier 		p = cp;
50380ee5cbfSDavid du Colombier 		cp = strstr(p, "~~");
50480ee5cbfSDavid du Colombier 		if(cp){
50580ee5cbfSDavid du Colombier 			*cp = 0;
50680ee5cbfSDavid du Colombier 			cp += 2;
50780ee5cbfSDavid du Colombier 		}
50880ee5cbfSDavid du Colombier 		if(strlen(p)){
50980ee5cbfSDavid du Colombier 			alt = Malloc(sizeof(*alt));
51080ee5cbfSDavid du Colombier 			alt->string = strdup(p);
51180ee5cbfSDavid du Colombier 			alt->next = *head;
51280ee5cbfSDavid du Colombier 			*head = alt;
51380ee5cbfSDavid du Colombier 		}
51480ee5cbfSDavid du Colombier 	}
51580ee5cbfSDavid du Colombier }
51680ee5cbfSDavid du Colombier 
51780ee5cbfSDavid du Colombier static int
extract(char * cp)51880ee5cbfSDavid du Colombier extract(char *cp)
51980ee5cbfSDavid du Colombier {
52080ee5cbfSDavid du Colombier 	int c;
52180ee5cbfSDavid du Colombier 	char *p, *q, *r;
52280ee5cbfSDavid du Colombier 
52380ee5cbfSDavid du Colombier 	p = q = r = cp;
52480ee5cbfSDavid du Colombier 	while(whitespace(*p))
52580ee5cbfSDavid du Colombier 		p++;
52680ee5cbfSDavid du Colombier 	while(c = *p++){
52780ee5cbfSDavid du Colombier 		if (c == '#')
52880ee5cbfSDavid du Colombier 			break;
52980ee5cbfSDavid du Colombier 		if(c == '"'){
53080ee5cbfSDavid du Colombier 			while(*p && *p != '"'){
53180ee5cbfSDavid du Colombier 				if(*p == '\\' && p[1] == '"')
53280ee5cbfSDavid du Colombier 					p++;
53380ee5cbfSDavid du Colombier 				if('A' <= *p && *p <= 'Z')
53480ee5cbfSDavid du Colombier 					*q++ = *p++ + ('a'-'A');
53580ee5cbfSDavid du Colombier 				else
53680ee5cbfSDavid du Colombier 					*q++ = *p++;
53780ee5cbfSDavid du Colombier 			}
53880ee5cbfSDavid du Colombier 			if(*p)
53980ee5cbfSDavid du Colombier 				p++;
54080ee5cbfSDavid du Colombier 			r = q;		/* never back up over a quoted string */
54180ee5cbfSDavid du Colombier 		} else {
54280ee5cbfSDavid du Colombier 			if('A' <= c && c <= 'Z')
54380ee5cbfSDavid du Colombier 				c += ('a'-'A');
54480ee5cbfSDavid du Colombier 			*q++ = c;
54580ee5cbfSDavid du Colombier 		}
54680ee5cbfSDavid du Colombier 	}
54780ee5cbfSDavid du Colombier 	while(q > r && whitespace(q[-1]))
54880ee5cbfSDavid du Colombier 		q--;
54980ee5cbfSDavid du Colombier 	*q = 0;
55080ee5cbfSDavid du Colombier 	return q-cp;
55180ee5cbfSDavid du Colombier }
55280ee5cbfSDavid du Colombier 
55380ee5cbfSDavid du Colombier /*
55480ee5cbfSDavid du Colombier  *	The matching engine: compare canonical input to pattern structures
55580ee5cbfSDavid du Colombier  */
55680ee5cbfSDavid du Colombier 
55780ee5cbfSDavid du Colombier static Spat*
isalt(char * message,Spat * alt)55880ee5cbfSDavid du Colombier isalt(char *message, Spat *alt)
55980ee5cbfSDavid du Colombier {
56080ee5cbfSDavid du Colombier 	while(alt) {
56180ee5cbfSDavid du Colombier 		if(*cmd)
56280ee5cbfSDavid du Colombier 		if(message != cmd && strstr(cmd, alt->string))
56380ee5cbfSDavid du Colombier 			break;
56480ee5cbfSDavid du Colombier 		if(message != header+1 && strstr(header+1, alt->string))
56580ee5cbfSDavid du Colombier 			break;
56680ee5cbfSDavid du Colombier 		if(strstr(message, alt->string))
56780ee5cbfSDavid du Colombier 			break;
56880ee5cbfSDavid du Colombier 		alt = alt->next;
56980ee5cbfSDavid du Colombier 	}
57080ee5cbfSDavid du Colombier 	return alt;
57180ee5cbfSDavid du Colombier }
57280ee5cbfSDavid du Colombier 
57380ee5cbfSDavid du Colombier int
matchpat(Pattern * p,char * message,Resub * m)57480ee5cbfSDavid du Colombier matchpat(Pattern *p, char *message, Resub *m)
57580ee5cbfSDavid du Colombier {
57680ee5cbfSDavid du Colombier 	Spat *spat;
57780ee5cbfSDavid du Colombier 	char *s;
57880ee5cbfSDavid du Colombier 	int c, c1;
57980ee5cbfSDavid du Colombier 
58080ee5cbfSDavid du Colombier 	if(p->type == string){
58180ee5cbfSDavid du Colombier 		c1 = *message;
58280ee5cbfSDavid du Colombier 		for(s=message; c=c1; s++){
58380ee5cbfSDavid du Colombier 			c1 = s[1];
58480ee5cbfSDavid du Colombier 			for(spat=p->spat[hash(c)]; spat; spat=spat->next){
58580ee5cbfSDavid du Colombier 				if(c1 == spat->c1)
58680ee5cbfSDavid du Colombier 				if(memcmp(s, spat->string, spat->len) == 0)
58780ee5cbfSDavid du Colombier 				if(!isalt(message, spat->alt)){
58880ee5cbfSDavid du Colombier 					m->sp = s;
58980ee5cbfSDavid du Colombier 					m->ep = s + spat->len;
59080ee5cbfSDavid du Colombier 					return 1;
59180ee5cbfSDavid du Colombier 				}
59280ee5cbfSDavid du Colombier 			}
59380ee5cbfSDavid du Colombier 		}
59480ee5cbfSDavid du Colombier 		return 0;
59580ee5cbfSDavid du Colombier 	}
59680ee5cbfSDavid du Colombier 	m->sp = m->ep = 0;
59780ee5cbfSDavid du Colombier 	if(regexec(p->pat, message, m, 1) == 0)
59880ee5cbfSDavid du Colombier 		return 0;
59980ee5cbfSDavid du Colombier 	if(isalt(message, p->alt))
60080ee5cbfSDavid du Colombier 		return 0;
60180ee5cbfSDavid du Colombier 	return 1;
60280ee5cbfSDavid du Colombier }
60380ee5cbfSDavid du Colombier 
60480ee5cbfSDavid du Colombier 
60580ee5cbfSDavid du Colombier void
xprint(int fd,char * type,Resub * m)60680ee5cbfSDavid du Colombier xprint(int fd, char *type, Resub *m)
60780ee5cbfSDavid du Colombier {
60880ee5cbfSDavid du Colombier 	char *p, *q;
60980ee5cbfSDavid du Colombier 	int i;
61080ee5cbfSDavid du Colombier 
61180ee5cbfSDavid du Colombier 	if(m->sp == 0 || m->ep == 0)
61280ee5cbfSDavid du Colombier 		return;
61380ee5cbfSDavid du Colombier 
61480ee5cbfSDavid du Colombier 		/* back up approx 30 characters to whitespace */
61580ee5cbfSDavid du Colombier 	for(p = m->sp, i = 0; *p && i < 30; i++, p--)
61680ee5cbfSDavid du Colombier 			;
61780ee5cbfSDavid du Colombier 	while(*p && *p != ' ')
61880ee5cbfSDavid du Colombier 		p--;
61980ee5cbfSDavid du Colombier 	p++;
62080ee5cbfSDavid du Colombier 
62180ee5cbfSDavid du Colombier 		/* grab about 30 more chars beyond the end of the match */
62280ee5cbfSDavid du Colombier 	for(q = m->ep, i = 0; *q && i < 30; i++, q++)
62380ee5cbfSDavid du Colombier 			;
62480ee5cbfSDavid du Colombier 	while(*q && *q != ' ')
62580ee5cbfSDavid du Colombier 		q++;
62680ee5cbfSDavid du Colombier 
62780ee5cbfSDavid du Colombier 	fprint(fd, "%s %.*s~%.*s~%.*s\n", type, (int)(m->sp-p), p, (int)(m->ep-m->sp), m->sp, (int)(q-m->ep), m->ep);
62880ee5cbfSDavid du Colombier }
629*9a747e4fSDavid du Colombier 
630*9a747e4fSDavid du Colombier enum {
631*9a747e4fSDavid du Colombier 	INVAL=	255
632*9a747e4fSDavid du Colombier };
633*9a747e4fSDavid du Colombier 
634*9a747e4fSDavid du Colombier static uchar t64d[256] = {
635*9a747e4fSDavid du Colombier /*00 */	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
636*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
637*9a747e4fSDavid du Colombier /*10*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
638*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
639*9a747e4fSDavid du Colombier /*20*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
640*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL,    62, INVAL, INVAL, INVAL,    63,
641*9a747e4fSDavid du Colombier /*30*/	   52,	  53,	 54,	55,    56,    57,    58,    59,
642*9a747e4fSDavid du Colombier 	   60,	  61, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
643*9a747e4fSDavid du Colombier /*40*/	INVAL,    0,      1,     2,     3,     4,     5,     6,
644*9a747e4fSDavid du Colombier 	    7,    8,      9,    10,    11,    12,    13,    14,
645*9a747e4fSDavid du Colombier /*50*/	   15,   16,     17,    18,    19,    20,    21,    22,
646*9a747e4fSDavid du Colombier 	   23,   24,     25, INVAL, INVAL, INVAL, INVAL, INVAL,
647*9a747e4fSDavid du Colombier /*60*/	INVAL,   26,     27,    28,    29,    30,    31,    32,
648*9a747e4fSDavid du Colombier 	   33,   34,     35,    36,    37,    38,    39,    40,
649*9a747e4fSDavid du Colombier /*70*/	   41,   42,     43,    44,    45,    46,    47,    48,
650*9a747e4fSDavid du Colombier 	   49,   50,     51, INVAL, INVAL, INVAL, INVAL, INVAL,
651*9a747e4fSDavid du Colombier /*80*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
652*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
653*9a747e4fSDavid du Colombier /*90*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
654*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
655*9a747e4fSDavid du Colombier /*A0*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
656*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
657*9a747e4fSDavid du Colombier /*B0*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
658*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
659*9a747e4fSDavid du Colombier /*C0*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
660*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
661*9a747e4fSDavid du Colombier /*D0*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
662*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
663*9a747e4fSDavid du Colombier /*E0*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
664*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
665*9a747e4fSDavid du Colombier /*F0*/	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
666*9a747e4fSDavid du Colombier 	INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL, INVAL,
667*9a747e4fSDavid du Colombier };
668