#ifdef RCS
static char rcsid[]=
"$Id: misc.c,v 1.117 2001/06/26 08:46:48 guenther Exp $";
#endif
#include "procmail.h"
#include "acommon.h"
#include "sublib.h"
#include "robust.h"
#include "misc.h"
#include "pipes.h"
#include "common.h"
#include "cstdio.h"
#include "exopen.h"
#include "regexp.h"
#include "mcommon.h"
#include "goodies.h"
#include "locking.h"
#include "comsat.h"
#include "mailfold.h"
#include "lastdirsep.h"
#include "memblk.h"
#include "authenticate.h"
#include "variables.h"
#include "shell.h"
void elog(newt)const char*const newt;
{ int lnew;size_t i;static size_t lold,lmax;static char*old;
if(lcking&lck_LOGGING)
lold=lmax=0;
i=lold+(lnew=strlen(newt));
if(lnew&&
(lmax>=i||
(MAXlogbuf>=i&&
!nextexit)))
{ if(i>lmax)
{ char*p;size_t newmax=lmax*2;
if(i>newmax)
newmax=i;
if(MINlogbuf>newmax)
newmax=MINlogbuf;
lcking|=lck_LOGGING;
if(p=lmax?frealloc(old,newmax):fmalloc(newmax))
lmax=newmax,old=p;
lcking&=~lck_LOGGING;
if(!p)
goto flush;
}
tmemmove(old+lold,newt,lnew);
lnew=0;lold=i;
if(old[i-1]!='\n')
return;
}
flush:
#ifndef O_CREAT
lseek(STDERR,(off_t)0,SEEK_END);
#endif
if(lold)
{ rwrite(STDERR,old,lold);
lold=0;
}
if(lnew)
rwrite(STDERR,newt,lnew);
}
void ignoreterm P((void))
{ signal(SIGTERM,SIG_IGN);signal(SIGHUP,SIG_IGN);signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
}
void shutdesc P((void))
{ rclose(savstdout);closelog();closerc();
}
void checkroot(c,Xid)const int c;const unsigned long Xid;
{ uid_t eff;
if((eff=geteuid())!=ROOT_uid&&getuid()!=ROOT_uid)
return;
syslog(LOG_CRIT,"set%cid(%lu) failed with ruid/euid = %lu/%lu",c,Xid,
(unsigned long)getuid(),(unsigned long)eff);
nlog(insufprivs);
exit(EX_NOPERM);
}
void setids P((void))
{ if(privileged)
{ if(setRgid(gid)&&
setgid(gid))
checkroot('g',(unsigned long)gid);
setruid(uid);
if(setuid(uid))
checkroot('u',(unsigned long)uid);
setegid(gid);
privileged=0;
#if !DEFverbose
verbose=0;
#endif
}
}
void writeerr(line)const char*const line;
{ nlog(errwwriting);logqnl(line);
}
int forkerr(pid,a)const pid_t pid;const char*const a;
{ if(pid==-1)
{ nlog("Failed forking");logqnl(a);
return 1;
}
return 0;
}
void progerr(line,xitcode,okay)const char*const line;int xitcode,okay;
{ charNUM(num,thepid);
nlog(okay?"Non-zero exitcode (":"Program failure (");
ltstr(0,(long)xitcode,num);elog(num);elog(okay?") from":") of");
logqnl(line);
}
void chderr(dir)const char*const dir;
{ nlog("Couldn't chdir to");logqnl(dir);
}
void readerr(file)const char*const file;
{ nlog("Couldn't read");logqnl(file);
}
int buildpath(name,path,file)const char*name,*const path,*const file;
{ static const char toolong[]=" path too long",
notabsolute[]=" is not an absolute path";
sgetcp=path;
if(readparse(buf,sgetc,2,0))
{ syslog(LOG_CRIT,"%s%s for LINEBUF for uid \"%lu\"\n",name,toolong,
(unsigned long)uid);
bad: nlog(name);elog(toolong);elog(newline);
return 1;
}
if(!strchr(dirsep,buf[0]))
{ nlog(name);elog(notabsolute);elog(newline);
syslog(LOG_CRIT,"%s%s for uid \"%lu\"\n",name,notabsolute,
(unsigned long)uid);
return 1;
}
if(file)
{ char*chp=strchr(buf,'\0');
if(chp-buf+strlen(file)+2>linebuf)
{ name="full rcfile";
goto bad;
}
*chp++= *MCDIRSEP_;
strcpy(chp,file);
}
return 0;
}
void verboff P((void))
{ verbose=0;
#ifdef SIGUSR1
qsignal(SIGUSR1,verboff);
#endif
}
void verbon P((void))
{ verbose=1;
#ifdef SIGUSR2
qsignal(SIGUSR2,verbon);
#endif
}
void yell(a,b)const char*const a,*const b;
{ if(verbose)
nlog(a),logqnl(b);
}
static time_t oldtime;
void newid P((void))
{ thepid=getpid();oldtime=0;
}
void zombiecollect P((void))
{ while(waitpid((pid_t)-1,(int*)0,WNOHANG)>0);
}
void nlog(a)const char*const a;
{ time_t newtime;
static const char colnsp[]=": ";
elog(procmailn);elog(colnsp);
if(verbose&&!nextexit&&oldtime!=(newtime=time((time_t*)0)))
{ charNUM(num,thepid);
elog("[");oldtime=newtime;ultstr(0,(unsigned long)thepid,num);elog(num);
elog("] ");elog(ctime(&oldtime));elog(procmailn);elog(colnsp);
}
elog(a);
}
void logqnl(a)const char*const a;
{ elog(oquote);elog(a);elog(cquote);
}
void skipped(x)const char*const x;
{ if(*x)
nlog("Skipped"),logqnl(x);
}
int nextrcfile P((void))
{ const char*p;int rval=2;
while(p= *gargv)
{ gargv++;
if(!strchr(p,'='))
{ if(strlen(p)>linebuf-1)
{ nlog("Excessively long rcfile path skipped\n");
continue;
}
rcfile=p;
return rval;
}
rval=1;
}
return 0;
}
char*tstrdup(a)const char*const a;
{ int i;
i=strlen(a)+1;
return tmemmove(malloc(i),a,i);
}
char*cstr(a,b)char*const a;const char*const b;
{ if(a)
free(a);
return tstrdup(b);
}
void onguard P((void))
{ lcking|=lck_DELAYSIG;
}
void offguard P((void))
{ lcking&=~lck_DELAYSIG;
if(nextexit==1)
elog(newline),Terminate();
}
static void sterminate P((void))
{ static const char*const msg[]={"memory","fork",
"a file descriptor","a kernel-lock"};
ignoreterm();
if(pidchild>0)
kill(pidchild,SIGTERM);
if(!nextexit)
{ nextexit=1;nlog("Terminating prematurely");
if(!(lcking&lck_DELAYSIG))
{ register unsigned i,j;
if(i=(lcking&~lck__NOMSG)>>1)
{ elog(whilstwfor);
for(j=0;!((i>>=1)&1);j++);
elog(msg[j]);
}
elog(newline);Terminate();
}
}
}
int fakedelivery;
void Terminate P((void))
{ ignoreterm();
if(getpid()==thepid)
{ const char*lstfolder;
if(retval!=EXIT_SUCCESS)
{ lasttell= -1;
lstfolder=fakedelivery?"**Lost**":
retval==EX_TEMPFAIL?"**Requeued**":"**Bounced**";
sendcomsat(lstfolder);
}
else
{ lstfolder=tgetenv(lastfolder);
sendcomsat(0);
}
logabstract(lstfolder);
if(!nextexit)
{ shutdesc();
exectrap(traps);
fdunlock();
}
nextexit=2;unlock(&loclock);unlock(&globlock);
}
elog(empty);
_exit(retvl2!=EXIT_SUCCESS?retvl2:
fakedelivery==2?EXIT_SUCCESS:
retval);
}
void suspend P((void))
{ ssleep((unsigned)suspendv);
}
static void srequeue P((void))
{ retval=EX_TEMPFAIL;sterminate();
}
static void slose P((void))
{ fakedelivery=2;sterminate();
}
static void sbounce P((void))
{ retval=EX_CANTCREAT;sterminate();
}
void setupsigs P((void))
{ qsignal(SIGTERM,srequeue);qsignal(SIGINT,sbounce);
qsignal(SIGHUP,sbounce);qsignal(SIGQUIT,slose);
signal(SIGALRM,(void(*)())ftimeout);
}
static void squeeze(target)char*target;
{ int state;char*src;
for(state=0,src=target;;target++,src++)
{ switch(*target= *src)
{ case '\n':
if(state==1)
target-=2;
state=2;
continue;
case '\\':state=1;
continue;
case ' ':case '\t':
if(state==2)
{ target--;
continue;
}
default:state=0;
continue;
case '\0':;
}
break;
}
}
char*egrepin(expr,source,len,casesens)char*expr;const char*source;
const long len;int casesens;
{ if(*expr)
{ source=(const char*)bregexec((struct eps*)(expr=(char*)
bregcomp(expr,!casesens)),(const uchar*)source,(const uchar*)source,
len>0?(size_t)len:(size_t)0,!casesens);
free(expr);
}
return (char*)source;
}
int enoughprivs(passinvk,euid,egid,uid,gid)const auth_identity*const passinvk;
const uid_t euid,uid;const gid_t egid,gid;
{ return euid==ROOT_uid||
passinvk&&auth_whatuid(passinvk)==uid||
euid==uid&&egid==gid;
}
const char*newdynstring(adrp,chp)struct dynstring**const adrp;
const char*const chp;
{ struct dynstring*curr;size_t len;
curr=malloc(ioffsetof(struct dynstring,ename[0])+(len=strlen(chp)+1));
tmemmove(curr->ename,chp,len);curr->enext= *adrp;*adrp=curr;
return curr->ename;
}
void*app_val_(sp,size)struct dyna_array*const sp;int size;
{ if(sp->filled==sp->tspace)
{ size_t len=(sp->tspace+=4)*size;
sp->vals=sp->vals?realloc(sp->vals,len):malloc(len);
}
return &sp->vals[size*sp->filled++];
}
int conditions(flags,prevcond,lastsucc,lastcond,skipping,nrcond)char flags[];
const int prevcond,lastsucc,lastcond,skipping;int nrcond;
{ char*chp,*chp2,*startchar;double score;int scored,i,skippedempty;
long tobesent;static const char suppressed[]=" suppressed\n";
score=scored=0;
if(nrcond<0)
nrcond=!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC]&&!flags[ELSE_DO]&&
!flags[ERROR_DO];
startchar=themail.p;tobesent=thebody-themail.p;
if(flags[BODY_GREP])
if(flags[HEAD_GREP])
tobesent=filled;
else
{ startchar=thebody;tobesent=filled-tobesent;
goto noconcat;
}
if(!skipping)
concon(' ');
noconcat:
i=!skipping;
if(flags[ERROR_DO])
{ i&=prevcond&&!lastsucc;
if(flags[ELSE_DO])
nlog(conflicting),elog("else-if-flag"),elog(suppressed),
flags[ELSE_DO]=0;
if(flags[ALSO_N_IF_SUCC])
nlog(conflicting),elog("also-if-succeeded-flag"),elog(suppressed),
flags[ALSO_N_IF_SUCC]=0;
}
if(flags[ELSE_DO])
i&=!prevcond;
if(flags[ALSO_N_IF_SUCC])
i&=lastcond&&lastsucc;
if(flags[ALSO_NEXT_RECIPE])
i=i&&lastcond;
for(skippedempty=0;;)
{ skipspace();--nrcond;
if(!testB('*'))
if(nrcond<0)
{ if(testB('#'))
{ skipline();
continue;
}
if(testB('\n'))
{ skippedempty=1;
continue;
}
if(skippedempty&&testB(':'))
{ nlog("Missing action\n");i=2;
goto ret;
}
break;
}
skipspace();
if(getlline(buf2,buf2+linebuf))
i=0;
if(i)
{ int negate,scoreany;double weight,xponent,lscore;
char*lstartchar=startchar;long ltobesent=tobesent,sizecheck=filled;
for(chp=strchr(buf2,'\0');--chp>=buf2;)
{ switch(*chp)
{ case ' ':case '\t':*chp='\0';
continue;
}
break;
}
negate=scoreany=0;lscore=score;
for(chp=buf2+1;;strcpy(buf2,buf))
copydone: { switch(*(sgetcp=buf2))
{ case '0':case '1':case '2':case '3':case '4':case '5':case '6':
case '7':case '8':case '9':case '-':case '+':case '.':case ',':
{ char*chp3;double w;
w=strtod(buf2,&chp3);chp2=chp3;
if(chp2>buf2&&*(chp2=skpspace(chp2))=='^')
{ double x;
x=strtod(chp2+1,&chp3);
if(chp3>chp2+1)
{ if(score>=MAX32)
goto skiptrue;
xponent=x;weight=w;scored=scoreany=1;
chp2=skpspace(chp3);
goto copyrest;
}
}
chp--;
goto normalregexp;
}
default:chp--;
{ if(alphanum(*(chp2=chp))==1)
{ char*chp3;
while(alphanum(*++chp2));
if(!strncmp(chp3=skpspace(chp2),"??",2))
{ *chp2='\0';lstartchar=themail.p;
if(!chp[1])
{ ltobesent=thebody-themail.p;
switch(*chp)
{ case 'B':lstartchar=thebody;
ltobesent=filled-ltobesent;
goto partition;
case 'H':
goto docon;
}
}
else if(!strcmp("HB",chp)||
!strcmp("BH",chp))
{ ltobesent=filled;
docon: concon(' ');
goto partition;
}
ltobesent=strlen(lstartchar=(char*)tgetenv(chp));
partition: chp2=skpspace(chp3+2);chp++;sizecheck=ltobesent;
goto copyrest;
}
}
}
case '\\':
normalregexp: { int or_nocase;
static const struct {const char*regkey,*regsubst;}
*regsp,regs[]=
{ {FROMDkey,FROMDsubstitute},
{TO_key,TO_substitute},
{TOkey,TOsubstitute},
{FROMMkey,FROMMsubstitute},
{0,0}
};
squeeze(chp);or_nocase=0;
goto jinregs;
do
if((chp2=strstr(chp,regsp->regkey))&&
(chp2==buf2||chp2[-1]!='\\'))
{ size_t lregs,lregk;
lregk=strlen(regsp->regkey);
tmemmove(chp2+(lregs=strlen(regsp->regsubst)),
chp2+lregk,strlen(chp2)-lregk+1);
tmemmove(chp2,regsp->regsubst,lregs);
if(regsp==regs)
or_nocase=1;
jinregs: regsp=regs;
}
else
regsp++;
while(regsp->regkey);
;{ int igncase;
igncase=or_nocase||!flags[DISTINGUISH_CASE];
if(scoreany)
{ struct eps*re;
re=bregcomp(chp,igncase);chp=lstartchar;
if(negate)
{ if(weight&&!bregexec(re,(const uchar*)chp,
(const uchar*)chp,(size_t)ltobesent,igncase))
score+=weight;
}
else
{ double oweight=weight*weight;
while(weight!=0&&
MIN32<score&&
score<MAX32&&
ltobesent>=0&&
(chp2=bregexec(re,(const uchar*)lstartchar,
(const uchar*)chp,(size_t)ltobesent,
igncase)))
{ score+=weight;weight*=xponent;
if(chp>=chp2)
{ if(0<xponent&&xponent<1)
score+=weight/(1-xponent);
else if(xponent>=1&&weight!=0)
score+=weight<0?MIN32:MAX32;
break;
}
;{ volatile double nweight=weight*weight;
if(nweight<oweight&&oweight<1)
break;
oweight=nweight;
}
ltobesent-=chp2-chp;chp=chp2;
}
}
free(re);
}
else
i=!!egrepin(chp,lstartchar,ltobesent,!igncase)^negate;
}
break;
}
case '$':*buf2='"';squeeze(chp);
if(readparse(buf,sgetc,2,0)&&(i=0,1))
break;
strcpy(buf2,skpspace(buf));
goto copydone;
case '!':negate^=1;chp2=skpspace(chp);
copyrest: strcpy(buf,chp2);
continue;
case '?':pwait=2;metaparse(chp);inittmout(buf);ignwerr=1;
pipin(buf,lstartchar,ltobesent,0);
resettmout();
if(scoreany&&lexitcode>=0)
{ int j=lexitcode;
if(negate)
while(--j>=0&&(score+=weight)<MAX32&&score>MIN32)
weight*=xponent;
else
score+=j?xponent:weight;
}
else if(!!lexitcode^negate)
i=0;
strcpy(buf2,buf);
break;
case '>':case '<':
{ long pivot;
if(readparse(buf,sgetc,2,0)&&(i=0,1))
break;
;{ char*chp3;
pivot=strtol(buf+1,&chp3,10);chp=chp3;
}
skipped(skpspace(chp));strcpy(buf2,buf);
if(scoreany)
{ double f;
if((*buf=='<')^negate)
if(sizecheck)
f=(double)pivot/sizecheck;
else if(pivot>0)
goto plusinfty;
else
goto mininfty;
else if(pivot)
f=(double)sizecheck/pivot;
else
goto plusinfty;
score+=weight*tpow(f,xponent);
}
else if(!((*buf=='<'?sizecheck<pivot:sizecheck>pivot)^
negate))
i=0;
}
}
break;
}
if(score>MAX32)
plusinfty: score=MAX32;
if(score<=MIN32)
mininfty: score=MIN32,i=0;
if(verbose)
{ if(scoreany)
{ charNUM(num,long);
nlog("Score: ");ltstr(7,(long)(score-lscore),num);
elog(num);elog(" ");
;{ long iscore=score;
ltstr(7,iscore,num);
if(!iscore&&score>0)
num[7-2]='+';
}
elog(num);
}
else
nlog(i?"M":"No m"),elog("atch on");
if(negate)
elog(" !");
logqnl(buf2);
}
skiptrue:;
}
}
if(!(lastscore=score)&&score>0)
lastscore=1;
if(scored&&i&&score<=0)
i=0;
ret:
return i;
}