Viewing file:
recipenode.C (29.33 KB) -rw-rw-rw-Select action/file-type:

(
+) |

(
+) |

(
+) |
Code (
+) |
Session (
+) |

(
+) |
SDB (
+) |

(
+) |

(
+) |

(
+) |

(
+) |

(
+) |
/*
** 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);
}