diff options
Diffstat (limited to 'maildrop/recipenode.C')
| -rw-r--r-- | maildrop/recipenode.C | 1671 | 
1 files changed, 1671 insertions, 0 deletions
| diff --git a/maildrop/recipenode.C b/maildrop/recipenode.C new file mode 100644 index 0000000..c69c017 --- /dev/null +++ b/maildrop/recipenode.C @@ -0,0 +1,1671 @@ +/* +** Copyright 1998 - 2009 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"recipenode.h" +#include	"recipe.h" +#include	"varlist.h" +#include	"message.h" +#include	"funcs.h" +#include	"mio.h" +#include	"search.h" +#include	"dotlock.h" +#include	"maildrop.h" +#include	"log.h" +#include	"config.h" +#include	"xconfig.h" +#include	"lexer.h" +#include	"filelock.h" +#include	"rfc822.h" +#include	"mytime.h" +#include	<ctype.h> +#include	<stdlib.h> +#include	<string.h> +#include	<sysexits.h> + + +extern int xfilter(const char *, int); + +static void parse_backslash(const Buffer &, Buffer &); + +////////////////////////////////////////////////////////////////////////// +// +// Utility class to save and restore the status of the embedded_mode flag. +// +// Embedded mode can be temporarily disabled by including something from +// ETCDIR/maildroprcs. +// + +class MaildropSaveEM { +	int save_mode; +public: +	MaildropSaveEM() { save_mode=maildrop.embedded_mode; } +	~MaildropSaveEM() { maildrop.embedded_mode=save_mode; } +} ; + +////////////////////////////////////////////////////////////////////////// + +RecipeNode::RecipeNode(RecipeNodeType t) : prevNode(0), nextNode(0), +	parentNode(0), prevSibling(0), nextSibling(0), +	firstChild(0), lastChild(0), linenum(0), nodeType(t) +{ +} + +void	RecipeNode::AppendSibling(RecipeNode *chld) +{ +	chld->parentNode=this; +	chld->nextSibling=0; +	if ( (chld->prevSibling=lastChild) != 0) +		lastChild->nextSibling=chld; +	else +		firstChild=chld; +	lastChild=chld; +} + +void	RecipeNode::Evaluate(Recipe &r, Buffer &b) +{ +RecipeNode	*c; + +	b.reset(); +	switch (nodeType)	{ +	case statementlist: + +		for (c=firstChild; c; c=c->nextSibling) +		{ +			b.reset(); +			c->Evaluate(r, b); +		} +		break; +	case assignment: +		if (!firstChild || !firstChild->nextSibling || +			firstChild->nodeType != qstring) +				throw "Internal error in assignment."; +		firstChild->nextSibling->Evaluate(r, b); +		if (VerboseLevel() > 3) +		{ +		Buffer	s; + +			s=firstChild->str; +			s += "=\""; +			s += b; +			s += "\""; +			s += '\0'; +			r.errmsg(*this, s); +		} +		SetVar(firstChild->str, b); +		break; +	case regexpr: +		EvaluateRegExp(r, b, (Buffer *)NULL); +		break; +	case qstring: +	case sqstring: +	case btstring: +		EvaluateString(r, b); +		break; +	case add: +	case subtract: +	case multiply: +	case divide: +	case lessthan: +	case lessthanoreq: +	case greaterthan: +	case greaterthanoreq: +	case equal: +	case notequal: +	case bitwiseor: +	case bitwiseand: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in evaluate binary."; +		{ +		double a1, a2; +		Buffer ba1, ba2; +		Buffer debug; + +			firstChild->Evaluate(r, ba1); +			firstChild->nextSibling->Evaluate(r, ba2); + +			if (VerboseLevel() > 5) +			{ +				debug="Operation on: "; +				debug += ba1; +				debug += " and "; +				debug += ba2; +			} + +			ba1 += '\0'; +			ba2 += '\0'; +			maildrop.sigfpe=0; +			a1=atof( (const char *)ba1 ); +			a2=atof( (const char *)ba2 ); +			switch (nodeType)	{ +			case add: +				a1 += a2; +				if (VerboseLevel() > 5) +					debug += " - add"; +				break; +			case subtract: +				a1 -= a2; +				if (VerboseLevel() > 5) +					debug += " - subtract"; +				break; +			case multiply: +				if (VerboseLevel() > 5) +					debug += " - multiply"; +				a1 *= a2; +				break; +			case divide: +				if (VerboseLevel() > 5) +					debug += " - divide"; +				a1 /= a2; +				break; +			case lessthan: +				a1=a1 < a2; +				if (VerboseLevel() > 5) +					debug += " - less than"; +				break; +			case lessthanoreq: +				a1=a1 <= a2; +				if (VerboseLevel() > 5) +					debug += " - less than or equal"; +				break; +			case greaterthan: +				a1=a1 > a2; +				if (VerboseLevel() > 5) +					debug += " - greater than"; +				break; +			case greaterthanoreq: +				a1=a1 >= a2; +				if (VerboseLevel() > 5) +					debug += " - greater than or equal"; +				break; +			case equal: +				a1= a1 == a2; +				if (VerboseLevel() > 5) +					debug += " - equal"; +				break; +			case notequal: +				a1= a1 != a2; +				if (VerboseLevel() > 5) +					debug += " - not equal"; +				break; +			case bitwiseor: +				a1= (unsigned long)a1 | (unsigned long)a2; +				if (VerboseLevel() > 5) +					debug += " - bitwise or"; +				break; +			case bitwiseand: +				a1= (unsigned long)a1 & (unsigned long)a2; +				if (VerboseLevel() > 5) +					debug += " - bitwise and"; +				break; +			default: +				break; +			} +			if (maildrop.sigfpe) +			{ +				r.errmsg(*this, "Numerical exception.\n"); +				a1=0; +			} +			b.append(a1); +			if (VerboseLevel() > 5) +			{ +				debug += ", result is "; +				debug += b; +				debug += '\0'; +				r.errmsg(*this, debug); +			} +		} +		break; +	case strlessthan: +	case strlessthanoreq: +	case strgreaterthan: +	case strgreaterthanoreq: +	case strequal: +	case strnotequal: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in evaluate binary."; +		{ +		Buffer ba1, ba2; +		Buffer debug; + +			firstChild->Evaluate(r, ba1); +			firstChild->nextSibling->Evaluate(r, ba2); + +			if (VerboseLevel() > 5) +			{ +				debug="Operation on: "; +				debug += ba1; +				debug += " and "; +				debug += ba2; +			} + +			ba1 += '\0'; +			ba2 += '\0'; + +		int	n=strcmp( (const char *)ba1, (const char *)ba2); + +			switch (nodeType)	{ +			case strlessthan: +				n= n < 0; +				if (VerboseLevel() > 5) +					debug += " - string less than"; +				break; +			case strlessthanoreq: +				n= n <= 0; +				if (VerboseLevel() > 5) +					debug += " - string less than or equal"; +				break; +			case strgreaterthan: +				n= n > 0; +				if (VerboseLevel() > 5) +					debug += " - string greater than"; +				break; +			case strgreaterthanoreq: +				n= n >= 0; +				if (VerboseLevel() > 5) +					debug += " - string grater than or equal"; +				break; +			case strequal: +				n= n == 0; +				if (VerboseLevel() > 5) +					debug += " - string equal"; +				break; +			case strnotequal: +				n= n != 0; +				if (VerboseLevel() > 5) +					debug += " - string not equal"; +				break; +			default: +				break; +			} +			b.append((unsigned long)n); +			if (VerboseLevel() > 5) +			{ +				debug += ", result is "; +				debug += b; +				debug += '\0'; +				r.errmsg(*this, debug); +			} +			break; +		} +	case logicalnot: +		if (!firstChild) +			throw "Internal error in evaluate unary."; +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation on: "; +			debug += b; +			debug += " - logical not."; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		{ +		int n= !boolean(b); + +			b.reset(); +			b.append( (unsigned long)n ); +		} +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation: logical not="; +			debug += b; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		break; +	case bitwisenot: +		if (!firstChild) +			throw "Internal error in evaluate unary."; +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation on: "; +			debug += b; +			debug += " - bitwise not."; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		maildrop.sigfpe=0; +		{ +			b += '\0'; + +		long n=atol( (const char *) b); + +			n= ~n; +			b.reset(); +			if (n < 0) +			{ +				b += '-'; +				n= -n; +			} +			b.append( (unsigned long)n ); +		} +		if (maildrop.sigfpe) +		{ +			r.errmsg(*this, "Numerical exception.\n"); +		} +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation: bitwise not="; +			debug += b; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		break; +	case logicalor: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in evaluate binary."; +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation: logical or, left hand side="; +			debug += b; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		if (!boolean(b)) +		{ +			if (VerboseLevel() > 5) +			{ +			Buffer	debug; + +				debug="Operation: logical or, evaluating right hand size."; +				debug += '\0'; +				r.errmsg(*this, debug); +			} +			firstChild->nextSibling->Evaluate(r, b); +		} +		if (VerboseLevel() > 5) +		{ +		Buffer debug; + +			debug="Operation: logical or, result="; +			debug += b; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		break; +	case logicaland: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in evaluate binary."; +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation: logical and, left hand side="; +			debug += b; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		if (boolean(b)) +		{ +			if (VerboseLevel() > 5) +			{ +			Buffer	debug; + +				debug="Operation: logical and, evaluating right hand size."; +				debug += '\0'; +				r.errmsg(*this, debug); +			} +			firstChild->nextSibling->Evaluate(r, b); +		} +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation: logical and, result="; +			debug += b; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		break; +	case concat: +		{ +		Buffer	bb; + +			for (c=firstChild; c; c=c->nextSibling) +			{ +				c->Evaluate(r, bb); +				b += bb; +			} +		} +		break; +	case strlength: +		if (!firstChild) +			throw "Internal error in evaluate unary."; +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation on: "; +			debug += b; +			debug += " - strlength."; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		{ +		unsigned long	n=b.Length(); + +			b.reset(); +			b.append(n); +		} +		if (VerboseLevel() > 5) +		{ +		Buffer	debug; + +			debug="Operation: strlength="; +			debug += b; +			debug += '\0'; +			r.errmsg(*this, debug); +		} +		break; +	case	strsubstr: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in evaluate unary."; +		{ +		Buffer	bb, cc; + +			firstChild->Evaluate(r, bb); +			firstChild->nextSibling->Evaluate(r, cc); +			cc += '\0'; + +		long n=atol( (const char *) cc); + +			if (VerboseLevel() > 5) +			{ +			Buffer	debug; + +				debug="Operation on: "; +				debug += bb; +				debug += " - strsubstr "; +				debug.append( (unsigned long) n); +				debug += '\0'; +				r.errmsg(*this, debug); +			} +	 +		long l=bb.Length(); + +			if (n < 0 || n > l)	n=l; +			b.append( (const char *)bb + n, l-n); +			if (firstChild->nextSibling->nextSibling) +			{ +				firstChild->nextSibling->nextSibling-> +							Evaluate(r, cc); +				cc += '\0'; +				n=atol( (const char *)cc); +				if (n < b.Length()) +					b.Length(n); + +				if (VerboseLevel() > 5) +				{ +				Buffer	debug; + +					debug="Operation on: "; +					debug += bb; +					debug += " - strsubstr chop "; +					debug.append( (unsigned long)n); +					debug += '\0'; +					r.errmsg(*this, debug); +				} +			} +			if (VerboseLevel() > 5) +			{ +			Buffer	debug; + +				debug="Operation: "; +				debug += " strsubstr="; +				debug.append(b); +				debug += '\0'; +				r.errmsg(*this, debug); +			} +		} +		break; +	case strregexp: +		EvaluateStrRegExp(r, b, (Buffer *)NULL); +		break; +	case whileloop: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in while loop."; + +		for (;;) +		{ +			if (VerboseLevel() > 3) +			{ +			Buffer	buf; + +				buf="Evaluating WHILE condition."; +				r.errmsg(*this, buf); +			} +			firstChild->Evaluate(r,b); +			if (VerboseLevel() > 3) +			{ +			Buffer	buf; + +				buf="While condition evaluated, result="; +				buf += b; +				buf += '\0'; +				r.errmsg(*this, buf); +			} +			if (! boolean(b))	break; + +			firstChild->nextSibling->Evaluate(r, b); +		} +		break; +	case ifelse: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in while loop."; +		if (VerboseLevel() > 3) +		{ +		Buffer	buf; + +			buf="Evaluating IF condition."; +			buf += '\0'; +			r.errmsg(*this, buf); +		} +		firstChild->Evaluate(r,b); + +		if (VerboseLevel() > 3) +		{ +		Buffer	buf; + +			buf="IF evaluated, result="; +			buf += b; +			buf += '\0'; +			r.errmsg(*this, buf); +		} +		if (boolean(b)) +			firstChild->nextSibling->Evaluate(r, b); +		else if (firstChild->nextSibling->nextSibling) +			firstChild->nextSibling->nextSibling->Evaluate(r, b); +		break; +	case deliver: +		if (!firstChild) +			throw "Internal error in delivery statement."; +		firstChild->Evaluate(r,b); +		b += '\0'; +		if (delivery(b) < 0) +			throw "Unable to deliver to mailbox."; +		b="EXITCODE"; +		throw ( GetVar(b)->Int("0") ); +	case delivercc: +		if (!firstChild) +			throw "Internal error in delivery statement."; +		firstChild->Evaluate(r,b); +		b += '\0'; +		if (delivery(b) < 0) +			throw "Unable to deliver to mailbox."; +		b = "0"; +		break; +	case xfilter: +		if (!firstChild) +			throw "Internal error in xfilter statement."; +		firstChild->Evaluate(r,b); +		b += '\0'; +		if (VerboseLevel() > 0) +			merr << "maildrop: Filtering through xfilter " << +				(const char *)b << "\n"; +		if (filter(b) < 0) +			throw "Unable to filter message."; +		b = "0"; +		break; +	case exception: +		if (!firstChild) +			throw "Internal error in delivery statement."; +		try +		{ +			if (VerboseLevel() > 3) +			{ +			Buffer	buf; + +				buf="Trapping exceptions."; +				buf += '\0'; +				r.errmsg(*this, buf); +			} +			firstChild->Evaluate(r, b); +			b=""; +			if (VerboseLevel() > 3) +			{ +			Buffer	buf; + +				buf="Exception trapping removed."; +				buf += '\0'; +				r.errmsg(*this, buf); +			} +		} +		catch (const char *p) +		{ +			b=p; +			if (VerboseLevel() > 3) +			{ +			Buffer	buf; + +				buf="Trapped exception."; +				buf += '\0'; +				r.errmsg(*this, buf); +			} +		} +#if NEED_NONCONST_EXCEPTIONS +		catch (char *p) +		{ +			if (VerboseLevel() > 3) +			{ +			Buffer	buf; + +				buf="Trapped exceptions."; +				buf += '\0'; +				r.errmsg(*this, buf); +			} +			b=p; +		} +#endif +		catch (int rc) +		{ +			if (rc == 0) +				throw rc; +			rc=0; +			b=""; +			if (VerboseLevel() > 3) +			{ +			Buffer	buf; + +				buf="Trapped exception."; +				buf += '\0'; +				r.errmsg(*this, buf); +			} +		} +		break; +	case echo: +		if (!firstChild) +			throw "Internal error in echo statement."; +		firstChild->Evaluate(r, b); +		{ +		Buffer	s; + +			parse_backslash(b, s); +			mout << s; +		} +		break; +	case dotlock: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in dotlock statement."; +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 3) +		{ +		Buffer	s; + +			s="Creating dotlock "; +			s += b; +			s += '\0'; +			r.errmsg(*this, s); +		} +		b += '\0'; +		{ +		DotLock	d; + +			d.Lock(b); +			firstChild->nextSibling->Evaluate(r, b); +			d.Unlock(); +		} +		break; +	case flock: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in flock statement."; +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 3) +		{ +		Buffer	s; + +			s="Creating flock "; +			s += b; +			s += '\0'; +			r.errmsg(*this, s); +		} +		b += '\0'; +		{ +		FileLock	filelock; + +			filelock.Lock(b); +			firstChild->nextSibling->Evaluate(r, b); +		} +		break; +	case logfile: +		if (!firstChild) +			throw "Internal error in logfile statement."; +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 3) +		{ +		Buffer	s; + +			s="Opening logfile "; +			s += b; +			s += '\0'; +			r.errmsg(*this, s); +		} +		b += '\0'; +		maildrop.logfile.Close(); +		if (maildrop.logfile.Open(b, O_CREAT | O_WRONLY | O_APPEND, +			0600) < 0) +			throw "Unable to create log file."; +		break; +	case log: +		if (!firstChild) +			throw "Internal error in logfile statement."; +		firstChild->Evaluate(r, b); +		{ +		Buffer	s; + +			parse_backslash(b, s); +			log_line(s); +		} +		break; +	case importtoken: +		if (!firstChild) +			throw "Internal error in import statement."; +		firstChild->Evaluate(r, b); +		{ +		Buffer	s; + +			if (VerboseLevel() > 3) +			{ +				s="import "; +				s += " \""; +				s += b; +				s += "\""; +				s += '\0'; +				r.errmsg(*this, s); +			} + +			s=b; + +			s += '\0'; + +		const char *name=s; +		const char *val=getenv(name); + +			if (!val)	val=""; +			s=val; + +			SetVar(b, s); +		} +		break; +	case include: +		if (!firstChild) +			throw "Internal error in logfile statement."; +		if (maildrop.includelevel >= 20) +		{ +			r.errmsg(*this, "Too many included files."); +			throw EX_TEMPFAIL; +		} + +		firstChild->Evaluate(r, b); +		if (VerboseLevel() > 3) +		{ +		Buffer	s; + +			s="Opening include file "; +			s += b; +			s += '\0'; +			r.errmsg(*this, s); +		} +		b += '\0'; +		{ +		Recipe	r; +		Lexer	in; +		MaildropSaveEM save_embedded_mode; +		static const char embedded_mode_directory[]=ETCDIR "/maildroprcs/"; + +			if (strncmp( (const char *)b, embedded_mode_directory, +				sizeof(embedded_mode_directory)-1) == 0 && +				strchr( (const char *)b, '.') == 0) +			{ +				maildrop.embedded_mode=0; +				maildrop.reset_vars(); +			} +			if (in.Open( (const char *)b ) < 0) +				throw "Unable to open include file."; +			if (r.ParseRecipe(in) < 0) +				throw EX_TEMPFAIL; + +			try +			{ +				++maildrop.includelevel; +				r.ExecuteRecipe(); +				--maildrop.includelevel; +			} +			catch (...) +			{ +				--maildrop.includelevel; +				throw; +			} +		} +		break; +	case exit: +		b="EXITCODE"; +		::exit ( GetVar(b)->Int("0") ); +	case foreach: +		if (!firstChild || !firstChild->nextSibling || +			( firstChild->nodeType != regexpr && +				firstChild->nodeType != strregexp)) +			throw "Internal error in foreach statement."; +		{ +		Buffer	foreachbuf; +		Buffer	varname; +		Buffer	varvalue; + +			if (firstChild->nodeType == regexpr) +				firstChild->EvaluateRegExp(r, b, &foreachbuf); +			else +				firstChild->EvaluateStrRegExp(r,b,&foreachbuf); + +		const char *p=foreachbuf; +		int l=foreachbuf.Length(); + +			while (l) +			{ +				varvalue.reset(); + +			int	i; + +				for (i=0; i<l && p[i]; i++) +					; +				varvalue.append(p, i); +				p += i; +				l -= i; +				if (l)	{ p++; l--; } +				varname="MATCH"; +				SetVar(varname, varvalue); +				firstChild->nextSibling->Evaluate(r, b); +			} +		} +		break; +	case getaddr: +		if (!firstChild) +			throw "Internal error in getaddr statement."; +		firstChild->Evaluate(r, b); +		rfc822getaddr(b); +		break; +	case escape: +		if (!firstChild) +			throw "Internal error in escape statement."; +		firstChild->Evaluate(r, b); +		SpecialEscape(b); +		break; +	case lookup: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in lookup statement."; +		{ +		Buffer	expr, file, opts; + +			firstChild->Evaluate(r, expr); +			firstChild->nextSibling->Evaluate(r, file); +			if ( firstChild->nextSibling->nextSibling ) +				firstChild->nextSibling->nextSibling +					->Evaluate(r, opts); +			if (dolookup(expr, file, opts)) +				b="1"; +			else +				b="0"; +		} +		break; +	case to_lower: +		if (!firstChild) +			throw "Internal error in tolower statement."; +		firstChild->Evaluate(r, b); +		{ +		Buffer	bb; +		const char *p=b; +		int	l=b.Length(); + +			while (l) +			{ +				--l; +				bb.push( tolower( *p++ )); +			} +			b=bb; +		} +		break; +	case to_upper: +		if (!firstChild) +			throw "Internal error in toupper statement."; +		firstChild->Evaluate(r, b); +		{ +		Buffer	bb; +		const char *p=b; +		int	l=b.Length(); + +			while (l) +			{ +				--l; +				bb.push( toupper( *p++ )); +			} +			b=bb; +		} +		break; +	case hasaddr: +		if (!firstChild) +			throw "Internal error in toupper statement."; +		firstChild->Evaluate(r, b); +		if (rfc822hasaddr(b)) +		{ +			b.reset(); +			b.push('1'); +		} +		else +		{ +			b.reset(); +			b.push('0'); +		} +		break; +#ifdef	DbObj +	case gdbmopen: +		if (!firstChild) +			throw "Internal error in evaluate gdbmopen."; +		firstChild->Evaluate(r, b); +		b += '\0'; +		{ +		const char *filename=b; +		const char *openmode="R"; +		Buffer	bb; + +			if (firstChild->nextSibling) +			{ +				firstChild->nextSibling->Evaluate(r, bb); +				bb += '\0'; +				openmode=bb; +			} +			if (maildrop.embedded_mode) +				switch ( *openmode )	{ +				case '\0': +				case 'r': +				case 'R': +					break; +				default: +					throw "Open gdbm file for write in embedded mode not allowed."; +				} + +		int	n=r.gdbm_file.Open(filename, openmode); +			b.reset(); +			if (n < 0) { b += '-'; n= -n; } +			b.append( (unsigned long)n ); +		} +		break; +	case gdbmclose: +		r.gdbm_file.Close(); +		break; +	case gdbmfetch: +		if (!firstChild) +			throw "Internal error in evaluate gdbmfetch."; +		firstChild->Evaluate(r, b); +		{ +		size_t	result_size; +		Buffer	optbuf; + +			if (firstChild->nextSibling) +				firstChild->nextSibling->Evaluate(r, optbuf); + +			optbuf += '\0'; + +		char	*result=r.gdbm_file.Fetch( +				(const char *)b, b.Length(), result_size, +				(const char *)optbuf); + +			b.reset(); +			if (result) +			{ +				b.append(result, result_size); +				free(result); +			} +			else if (firstChild->nextSibling && +				firstChild->nextSibling->nextSibling) +				firstChild->nextSibling->nextSibling +							->Evaluate(r, b); +		} +		break; +	case gdbmstore: +		if (!firstChild || !firstChild->nextSibling) +			throw "Internal error in evaluate gdbmstore."; +		{ +		Buffer	key, val; + +			firstChild->Evaluate(r, key); +			firstChild->nextSibling->Evaluate(r, val); + +		int	n=r.gdbm_file.Store(key, key.Length(), +						val, val.Length(), "R"); + +			b.reset(); +			if (n < 0) { b += '-'; n= -n; } +			b.append( (unsigned long)n ); +		} +		break; +#else +	case gdbmopen: +	case gdbmclose: +	case gdbmfetch: +	case gdbmstore: +		b.reset(); +		b.push('0'); +		break; +#endif +	case timetoken: +		b.reset(); +		{ +		time_t	t; + +			time(&t); +			b.append( (unsigned long)t ); +		} +		break; +	case unset: +		if (!firstChild) +			throw "Internal error in unset statement."; +		firstChild->Evaluate(r, b); +		{ +		Buffer	s; + +			if (VerboseLevel() > 3) +			{ +				s="unset "; +				s += b; +				s += '\0'; +				r.errmsg(*this, s); +			} + +			UnsetVar(b); +		} +		break; +	} +} + +void RecipeNode::EvaluateRegExp(Recipe &r, Buffer &b, Buffer *foreachp) +{ +Search	c; +Buffer	buf1, buf2; + +	ParseRegExp(str, buf1, buf2); + +	dollarexpand(r, buf1); +	buf1 += '\0'; +	buf2 += '\0'; +	if (c.find( *maildrop.msgptr, maildrop.msginfo, buf1, buf2, foreachp)) +	{ +		c.score=0; +		r.errmsg(*this, "Syntax error in /pattern/.\n"); +	} +	else if (VerboseLevel() > 3) +	{ +	Buffer	buf; + +		buf="Search of "; +		buf += (const char *)buf1; +		buf += " = "; +		buf.append(c.score); +		buf += '\0'; +		r.errmsg(*this, buf); +	} +	b.append(c.score); +} + +void RecipeNode::EvaluateStrRegExp(Recipe &r, Buffer &b, Buffer *foreachp) +{ +	if (!firstChild || !firstChild->nextSibling || +		firstChild->nextSibling->nodeType != regexpr) +		throw "Internal error in evaluate =~."; + +Search	c; +Buffer	buf1, buf2; + +	firstChild->Evaluate(r,str); +	ParseRegExp( firstChild->nextSibling->str, buf1, buf2); + +	dollarexpand(r, buf1); +	buf1 += '\0'; +	buf2 += '\0'; +	str += '\0'; +	if (c.find( str, buf1, buf2, foreachp)) +	{ +		c.score=0; +		r.errmsg(*this, "Syntax error in /pattern/.\n"); +	} +	else if (VerboseLevel() > 3) +	{ +	Buffer	buf; + +		buf="Search of "; +		buf += (const char *)buf1; +		buf += " = "; +		buf.append(c.score); +		buf += '\0'; +		r.errmsg(*this, buf); +	} +	b.append(c.score); +} + +// Break down /foo/:bar into foo and bar. + +void	RecipeNode::ParseRegExp(const Buffer &str, Buffer &buf1, Buffer &buf2) +{ +	buf2=str; +	buf2 += '\0'; + +const	char *p; + +	p=buf2; + +	if (*p != '/')	throw "Internal error."; +	++p; +	buf1=p; + +int	l=buf1.Length(), i; + +	buf1 += '\0'; +	buf2.reset(); +	p=buf1; +	for (i=0; i<l; i++, p++) +	{ +		if (*p == '/') +		{ +			if (p[1] == ':') +				buf2=p+2; +			break; +		} +		if (*p == '\\' && i+1 < l) +		{ +			++p; +			++i; +		} +	} +	buf1.Length(i); +} + +void	RecipeNode::EvaluateString(Recipe &r, Buffer &b) +{ +Buffer	buf; + +	switch (nodeType)	{ +	case qstring: +		buf=str; +		dollarexpand(r, buf); +		b += buf; +		break; +	case sqstring: +		b += str; +		break; +	case btstring: +		buf=str; +		buf += '\0'; +		if (VerboseLevel() > 0) +			merr << "maildrop: Filtering through `" << +				(const char *)buf << "`\n"; +		try +		{ +		int	rc=::xfilter(buf, 1); +		int	l=0; + +			if (rc < 0) +			{ +				maildrop.savemsgptr->Init(); +				break; +			} +			maildrop.savemsgptr->RewindIgnore(); + +			// Strip leading/trailing spaces.  Newlines are +			// replaced by spaces. + +			while ((rc=maildrop.savemsgptr->get_c()) >= 0 +				&& isspace(rc)) +					;	// Skip leading space +			while (rc >= 0) +			{ +				if (rc == '\r' || rc == '\n')	rc=' '; +				b.push(rc); +				if (!isspace(rc))	l=b.Length(); +				rc=maildrop.savemsgptr->get_c(); +			} +			maildrop.savemsgptr->Init(); +			b.Length(l); +		} +		catch (...) +		{ +			maildrop.savemsgptr->Init(); +			throw; +		} +		break; +	default: +		break; +	} +} + +void	RecipeNode::dollarexpand(Recipe &r, Buffer &b) +{ +int	i, l; +const char *p=b; + +	for (i=0, l=b.Length(); i<l; ) +	{ +		if (p[i] == '\\' && i+1 < l) +			++i; +		else +		{ +			if (p[i] == '$') +			{ +				i=dollarexpand(r, b, i); +				l=b.Length(); +				p=b; +				continue; +			} +		} +		++i; +	} +} + +void RecipeNode::SpecialEscape(Buffer &b) +{ +int	i, l; +const char *p=b; +Buffer	s; + +	for (i=0, l=b.Length(); i<l; i++) +	{ +		switch (p[i])	{ +		case '|': +		case '!': +		case '$': +		case '(': +		case ')': +		case '[': +		case ']': +		case '\\': +		case '+': +		case '*': +		case '?': +		case '.': + +		case '&': +		case ';': +		case '`': +		case '\'': +		case '-': +		case '~': +		case '<': +		case '>': +		case '^': +		case '{': +		case '}': +		case '"': +			s.push('\\'); +			break; +		} +		s.push(p[i]); +	} +	b=s; +} + +int	RecipeNode::dollarexpand(Recipe &r, Buffer &b, int index) +{ +int	l=b.Length(); + +	if (index+1 >= l)	return (index+1); + +const	char *p=b; +Buffer	varname; +int	j; + +	if (p[index+1] == '{') +	{ +		j=index+2; +		for (;;) +		{ +			if (j >= l || isspace(p[j])) +			{ +				varname="Terminating } is missing.\n"; +				varname += '\0'; +				r.errmsg(*this, varname); +				return (index+1); +			} +			if (p[j] == '}') +				break; +			varname += p[j]; +			++j; +		} +		++j; +	} +	else +	{ +		if (!isalnum(p[index+1]) && p[index+1] != '_') +			return (index+1); + +		for (j=index+1; j<l; j++) +		{ +			if (!isalnum(p[j]) && p[j] != '_')	break; +			varname += p[j]; +		} +	} + +Buffer	newstr; + +	newstr.append(p, index); + +const Buffer *bb=GetVar(varname); + +	if (bb)	newstr += *bb; + +int	newindex=newstr.Length(); + +	newstr.append(p + j, l-j); +	b=newstr; +	return (newindex); +} + +// Is arbitrary string true, or false? +// A false is either an empty string, or a number 0. + +int RecipeNode::boolean(const Buffer &b) +{ +const char *p=b; +int l=b.Length(); + +	while (l && isspace(*p))	p++, l--; +	if (l == 0)	return (0); + +	if (*p != '0' && *p != '.')	return (1);	// Can't be zero. + +	if (*p == '0') +	{ +		++p; +		--l; +		if (l && *p == '.')	p++,l--; +		else +		{ +			while (l && isspace(*p))	p++, l--; +			if (l)	return (1);	// Some text after 0 +			return (0); +		} +	} +	++p, --l; +	if (!l)	return (1);	// Period by itself is ok. +	while (l && *p == '0')	p++, l--;	// Zeroes after . +	while (l && isspace(*p))	p++, l--; +	if (l)	return (1);	// Some text after 0 +	return (0); +} + +static void parse_backslash(const Buffer &in, Buffer &s) +{ +const char *p=in; +int l=in.Length(); +int append_newline=1; + +	while (l) +	{ +	int	c; + +		if (*p != '\\' || l == 1) +		{ +			s.push(*p); +			p++; +			l--; +			continue; +		} +		++p; +		--l; +		if (*p >= '0' && *p <= '7') +		{ +			c=0; +			do +			{ +				c=c * 8 + (*p++ - '0'); +				--l; +			} while (l && *p >= '0' && *p <= '7'); +			s.push(c); +			continue; +		} +		c=backslash_char(*p); +		++p; +		--l; +		if (l == 0 && c == 'c') +		{ +			append_newline=0; +			break; +		} +		s.push( c ); +	} +	if (append_newline) +	{ +#if	CRLF_TERM +		s.push('\r'); +#endif +		s.push('\n'); +	} +} + +void RecipeNode::rfc822getaddr(Buffer &buf) +{ +	buf += '\0'; + +	struct	rfc822t	*p=rfc822t_alloc_new(buf, NULL, NULL); + +	if (!p)	outofmem(); + +	struct	rfc822a	*a=rfc822a_alloc(p); + +	if (!a) outofmem(); + +	try +	{ +		int n; +		Buffer newbuf; + +		for (n=0; n<a->naddrs; n++) +			if (a->addrs[n].tokens) +			{ +				char *p=rfc822_display_addr_tobuf(a, n, +								  NULL); + +				if (p) +					try { +					        newbuf += p; +						newbuf += "\n"; +					} catch (...) +					{ +						free(p); +						throw; +					} + +				free(p); +			} + +		buf=newbuf; +	} +	catch (...) +	{ +		rfc822a_free(a); +		rfc822t_free(p); +		throw; +	} +	rfc822a_free(a); +	rfc822t_free(p); +} + +int RecipeNode::rfc822hasaddr(Buffer &buf) +{ +Buffer	lower_addr; +Buffer	next_line; +const char *p; +int	l; +Buffer	header; + +	for (p=buf, l=buf.Length(); l; --l) +		lower_addr.push(tolower(*p++)); +	lower_addr += '\0'; + +	if (VerboseLevel() > 5) +		merr << "maildrop: hasaddr('" << +			(const char *)lower_addr << "')\n"; + +	maildrop.msgptr->Rewind(); +	if (maildrop.msgptr->appendline(next_line))	return (0); +	while ( *(p=next_line) != '\n') +	{ +	int	c; + +		for (l=0; l<7; l++) +		{ +			c=tolower(p[l]); +			if (c != "resent-"[l]) +				break; +		} +		if (l == 7)	p += 7; +		else	l=0; + +		c=tolower (*p); + +		if (((c == 't' && tolower(p[1]) == 'o') +				|| +		    (c == 'c' && tolower(p[1]) == 'c')) +			&& p[2] == ':') +			; +		else +		{ +			next_line.reset(); +			if (maildrop.msgptr->appendline(next_line)) +				return (0); +			continue; +		} +		next_line += '\0'; +		header=(const char *)next_line+3+l; +		for (;;) +		{ +			next_line.reset(); +			if (maildrop.msgptr->appendline(next_line)) +				return ( rfc822hasaddr(lower_addr, header)); +			if (*(p=next_line) == '\n')	break; +			if ( !isspace(*p))	break; +			header += ' '; +			header += next_line; +		} +		if (rfc822hasaddr(lower_addr, header))	return (1); +	} +	return (0); +} + +int RecipeNode::rfc822hasaddr(const char *addr, Buffer &header) +{ +	header += '\0'; + +	if (VerboseLevel() > 5) +		merr << "maildrop: hasaddr: rfc822 parsing: " +			<< (const char *)header << "\n"; + +	struct	rfc822t	*p=rfc822t_alloc_new(header, NULL, NULL); + +	if (!p)	outofmem(); + +	struct	rfc822a	*a=rfc822a_alloc(p); + +	if (!a)	outofmem(); + +int	i; +Buffer	rfc822buf; +int	found=0; + +	for (i=0; i<a->naddrs; i++) +	{ +		char *p=rfc822_getaddr(a, i); + +		if (!p) +			continue; + +		char *q; + +		for (q=p; *q; q++) +			*q=tolower(*q); + +		rfc822buf=p; +		free(p); +		rfc822buf.push('\0'); +		if (VerboseLevel() > 5) +			merr << "maildrop: hasaddr: rfc822 parsed: " +				<< (const char *)rfc822buf << "\n"; +		if (strcmp(addr, rfc822buf) == 0) +		{ +			found=1; +			break; +		} +	} +	rfc822a_free(a); +	rfc822t_free(p); +	return (found); +} + +int RecipeNode::dolookup(Buffer &strng, Buffer &filename, Buffer &opts) +{ +static Buffer	errbuf; +Buffer	real_opts; + +	filename += '\0'; + +	strng += '\0'; +	opts += '\0'; +	if (strchr (opts, 'D'))	real_opts.push('D');	// Only allow this opt +	real_opts += '\0'; + +Mio	fp; + +	if (fp.Open((const char *)filename, O_RDONLY) < 0) +	{ +		errbuf="Unable to open "; +		errbuf += (const char *)filename; +		errbuf += ".\n"; +		errbuf += '\0'; +		throw (const char *)errbuf; +	} + +	for (;;) +	{ +	int	c; +	const	char *p; + +		errbuf.reset(); +		while ((c=fp.get()) >= 0 && c != '\r' && c != '\n') +			errbuf.push(c); +		if (c < 0 && errbuf.Length() == 0)	break; +		errbuf.push(0); + +		p=errbuf; +		while (*p && isspace(*p))	p++; +		if (!*p || *p == '#')	continue; + +	Search	srch; + +		if (srch.find( strng, p, real_opts, (Buffer *)NULL)) +		{ +			opts=p;		// Convenient buffer + +			errbuf="Bad pattern in "; +			errbuf += (const char *)filename; +			errbuf += ": "; +			errbuf += opts; +			errbuf += '\n'; +			errbuf += '\0'; +			throw (const char *)errbuf; +		} +		if (srch.score) +			return (1); +	} +	return (0); +} | 
