#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define DMID 14
#include "debug.h"
#include "lib.h"

#ifdef unix
#define strnicmp strncasecmp
#define stricmp strcmpnc
#endif

int domatch(char *text, char *p);
#ifdef SURGEMAIL
#define domatch filter_domatch
#define XTRUE			1
#define XFALSE			0
/*#define ABORT			-1 This is BAD!*/
#define NEGATE_CLASS		'^'

int domatch(char *text, char *p)
{
	register int	last;
	register int	matched;
	register int	reverse;

	for ( ; *p; text++, p++)
	{
		if (*text == '\0' && *p != '*')
			return XFALSE;
		switch (*p)
		{
		case '\\':
			p++;
		default:
			if (*text != *p)
				return XFALSE;
			continue;
		case '?':
			continue;
		case '*':
			while (*++p == '*')
				continue;
			if (*p == '\0')
				/* Trailing star matches everything. */
				return XTRUE;
			while (*text)
				if ((matched = domatch(text++, p)) != XFALSE)
					return matched;
			return XFALSE;
		case '[':
			reverse = ( p[1] == NEGATE_CLASS ? XTRUE : XFALSE );
			if (reverse)
				/* Inverted character class. */
				p++;
			matched = XFALSE;
			if (p[1] == ']' || p[1] == '-')
				if (*++p == *text)
					matched = XTRUE;
			for (last = *p; *++p && *p != ']'; last = *p)
				if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p)
					matched = XTRUE;
			if (matched == reverse)
				return XFALSE;
			continue;
		}
	}
	return *text == '\0';
}

#endif

#ifdef WIN32
#define snprintf _snprintf
#endif

#define abs(x) (((x)<0)?(-(x)):(x))

#include "filter.h"

struct attachmentfilter
{
	char *string;
	int accept;
	int nocase, rulenum;
	struct attachmentfilter *next;
};

static struct attachmentfilter *attach1;
struct filter *hfilters[256];
struct filter *bfilters[5000];

int filter_checkattach(char *name)
{
	char *lowname;
	struct attachmentfilter *tstr, *sstr;

	if(name==NULL)
		return 0;
	
	lowname=strdup(name);
	strlwr(lowname);

	for(tstr=attach1;tstr!=NULL;tstr=tstr->next)
	{
		if(tstr->accept)
			continue;
		if((tstr->nocase)&&(domatch(lowname, tstr->string)))
			break;
		if((!tstr->nocase)&&(domatch(name, tstr->string)))
			break;
	}
	for(sstr=attach1;sstr!=NULL;sstr=sstr->next)
	{
		if(!sstr->accept)
			continue;
		if((sstr->nocase)&&(domatch(lowname, sstr->string)))
			break;
		if((!sstr->nocase)&&(domatch(name, sstr->string)))
			break;
	}

	free(lowname);

	if((tstr!=NULL)&&(sstr==NULL))
		return tstr->rulenum;

	return 0;
}

int filter_checkline(char *line, int body, char *ruledesc)
{
	int h, hit;
	unsigned int l, i, m, l2;
	struct filter *tmp, *cur;
	char *s, lowbuf[1024];
	unsigned char e[4];
	cur=NULL;
	hit=0;
	l=(int)strlen(line);
	if (strcmp(line,"t")==0) {
		hit=0;
	}
	if(body)
	{
		if (l>=3) for(i=0;i<(l-3);i++)
		{
			h=abs(((unsigned char)(line[i+0]&0xDF))+((unsigned char)(line[i+1]&0xDF)<<6)+((unsigned char)(line[i+2]&0xDF)<<8));
			h=abs(h)%5000;
			if(bfilters[h]==NULL)
				continue;
			s=&(line[i]);
			m=l-i;
			for(tmp=bfilters[h];tmp!=NULL;tmp=tmp->next)
			{
				if(m<tmp->len)
					continue;

				if(tmp->nc)
				{
					if(strnicmp(tmp->rule, s, tmp->len)!=0)
						continue;
				}
				else
				{
					if(strncmp(tmp->rule, s, tmp->len)!=0)
						continue;
				}
				if(hit<tmp->num)
				{
					hit=tmp->num;
					cur=tmp;
				}
			}
		}

		if(cur==NULL)
		{
			if(ruledesc!=NULL)
				strcpy(ruledesc, "");
			return 0;
		}

		if(ruledesc!=NULL)
			snprintf(ruledesc, 255, "%s body %s", cur->accept?"accept":"reject", cur->rule);

		return cur->accept?hit:-hit;
	}
	else
	{
		e[0]=line[0];
		e[1]=0;
		strlwr(e);
		if(hfilters[e[0]]==NULL)
			return 0;
		s=strchr(line, ':');
		if(s==NULL)
			return 0;
		s++;
		l2=(int)(s-line);
		while(*s==' ')
			s++;
		m=(int)strlen(s);
		for(tmp=hfilters[e[0]];tmp!=NULL;tmp=tmp->next)
		{
			if(strnicmp(line, tmp->part, l2)!=0)
				continue;
			if(strstr(tmp->rule, "$BLANK")||strstr(tmp->rule, "$blank"))
			{
				if((int)strlen(s)==0)
				{
					if(hit<tmp->num)
					{
						hit=tmp->num;
						cur=tmp;
					}
				}
			}
			if(m<tmp->len)
				continue;
			if(tmp->nc)
			{
				strcpy(lowbuf, s);
				strlwr(lowbuf);
				if(domatch(lowbuf, tmp->rule))
				{
					if(hit<tmp->num)
					{
						hit=tmp->num;
						cur=tmp;
					}
				}
			}
			else if(domatch(s, tmp->rule))
			{
				if(hit<tmp->num)
				{
					hit=tmp->num;
					cur=tmp;
				}
			}
		}

		if(cur==NULL)
		{
			if(ruledesc!=NULL)
				strcpy(ruledesc, "");
			return 0;
		}

		if(ruledesc!=NULL)
			snprintf(ruledesc, 255, "%s %s %s", cur->accept?"accept":"reject", cur->part, cur->rule);

		return cur->accept?hit:-hit;
	}
}

int filter_read(char *fname, char *reason)
{
	int i, rct;
	char *c, *d, newd[256], bf[256];
	FILE *fin;
	struct filter *tmp;
	struct attachmentfilter *afil;
	unsigned char e[4];

	fin=fopen(fname, "r");
	if(fin==NULL) {
		strcpy(reason,strerror(errno));
		return 0;
	}
	rct=0;
	while(!feof(fin))
	{
		if(fgets(bf, 255, fin)==NULL)
			continue;
		if(bf[(int)strlen(bf)-1]=='\n')
			bf[(int)strlen(bf)-1]=0;
		c=strchr(bf, ' ');
		if(c==NULL)
			c=strchr(bf, '\t');
		if(c==NULL)
			continue;
		*c=0;

		if((stricmp(bf, "accept")!=0)&&(stricmp(bf, "reject")!=0)
			&&(stricmp(bf, "reject_case")!=0)&&(stricmp(bf, "accept_case")!=0)
			&&(stricmp(bf, "reject_nocase")!=0)&&(stricmp(bf, "accept_nocase")!=0)
			)
			continue;

		c++;
		while((*c==' ')||(*c=='\t'))
			c++;
		d=strchr(c, ' ');
		if(d==NULL)
			continue;
		*d=0;
		d++;
		if((int)strlen(c)==0)
			continue;
		if((int)strlen(d)<3)
			continue;
		rct++;
		if(stricmp(c, "attachment")==0)
		{
			afil=calloc(1,sizeof(struct attachmentfilter));

			if(strnicmp(bf, "accept", 6)==0)
				afil->accept=1;
			else
				afil->accept=0;

			if(stricmp(bf+6, "_case")==0)
				afil->nocase=0;
			else
				afil->nocase=1;
			afil->rulenum=rct;

			if(afil->nocase)
				strlwr(d);

			if(d[0]!='*')
				strcpy(newd, "*");
			else
				strcpy(newd, "");
			strcat(newd, d);
			if(d[(int)strlen(d)-1]!='*')
				strcat(newd, "*");

			strlwr(newd);
			afil->string=strdup(newd);
			afil->next=attach1;
			attach1=afil;
		}
		else if(stricmp(c, "body")==0)
		{
			if(stricmp(bf, "accept")==0)
				continue;
			if(stricmp(bf, "accept_nocase")==0)
				continue;
			tmp=calloc(1, sizeof(struct filter));

			if(stricmp(bf, "reject_case")==0)
				tmp->nc=0;
			else
				tmp->nc=1;

			if(tmp==NULL)
			{
				rct--;
				continue;
			}
			tmp->rule=strdup(d);
			tmp->len=(int)strlen(d);
			tmp->num=rct;
			tmp->part=NULL;
			tmp->accept=0;
			i=((unsigned char)(d[0]&0xDF))+((unsigned char)(d[1]&0xDF)<<6)+((unsigned char)(d[2]&0xDF)<<8);
			i=abs(i)%5000;
			tmp->next=bfilters[i];
			bfilters[i]=tmp;
		}
		else
		{
			tmp=malloc(sizeof(struct filter));
			
			tmp->nc=0;
			if(stricmp(bf, "reject_nocase")==0)
				tmp->nc=1;
			if(stricmp(bf, "accept_nocase")==0)
				tmp->nc=1;

			if(strncmp(bf, "reject", 6)==0)
				tmp->accept=0;
			else
				tmp->accept=1;
			strcpy(newd, c);
			if(c[(int)strlen(c)-1]!=':')
				strcat(newd, ":");
			tmp->part=strdup(newd);
			tmp->num=rct;
			if(d[0]!='*')
				strcpy(newd, "*");
			else
				strcpy(newd, "");
			strcat(newd, d);
			if(d[(int)strlen(d)-1]!='*')
				strcat(newd, "*");
			
			if(tmp->nc)
				strlwr(newd);

			tmp->rule=strdup(newd);
			tmp->len=(int)strlen(newd)-2;
			e[0]=c[0];
			e[1]=0;
			strlwr(e);
			tmp->next=hfilters[e[0]];
			hfilters[e[0]]=tmp;
		}
	}
	fclose(fin);

	if(rct>0)
		return 1;
	strcpy(reason,"No rules found");

	return 0;
}

void filter_free()
{
	struct filter *tmp;
	struct attachmentfilter *afil;
	int i;

	for(i=0;i<5000;i++)
	{
		tmp=bfilters[i];
		while(tmp!=NULL)
		{
			tmp=bfilters[i]->next;
			if(bfilters[i]->rule)
				free(bfilters[i]->rule);
			free(bfilters[i]);
			bfilters[i]=tmp;
		}
	}
	for(i=0;i<256;i++)
	{
		tmp=hfilters[i];
		while(tmp!=NULL)
		{
			tmp=hfilters[i]->next;
			if(hfilters[i]->rule)
				free(hfilters[i]->rule);
			free(hfilters[i]);
			hfilters[i]=tmp;
		}
	}

	while(attach1!=NULL)
	{
		if(attach1->string!=NULL)
			free(attach1->string);
		afil=attach1->next;
		free(attach1);
		attach1=afil;
	}
}

int filter_init(char *fname, int reload, char *reason)
{
	int i, dospam;

	if(!reload)
	{
		for(i=0;i<5000;i++)
			bfilters[i]=NULL;
		for(i=0;i<256;i++)
			hfilters[i]=NULL;
		attach1=NULL;
	}
	else
		filter_free();

	dospam=0;

	if(fname!=NULL)
		dospam=filter_read(fname,reason);

	return dospam;
}
