home *** CD-ROM | disk | FTP | other *** search
- From: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
- Newsgroups: comp.sources.misc
- Subject: v43i057: procmail - mail processing package v3.03, Part02/10
- Date: 5 Jul 1994 20:49:35 -0500
- Organization: Sterling Software
- Sender: kent@sparky.sterling.com
- Approved: kent@sparky.sterling.com
- Message-ID: <2vd2jf$i4o@sparky.sterling.com>
- X-Md4-Signature: d1e1793edcda5575ee218801e2f88a43
-
- Submitted-by: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
- Posting-number: Volume 43, Issue 57
- Archive-name: procmail/part02
- Environment: sendmail, ZMailer, smail, MMDF, mailsurr, UNIX, POSIX
- Supersedes: procmail: Volume 38, Issue 19-31
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # Contents: procmail-3.03/src/goodies.c procmail-3.03/src/procmail.c
- # Wrapped by kent@sparky on Tue Jul 5 20:42:12 1994
- PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 2 (of 10)."'
- if test -f 'procmail-3.03/src/goodies.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'procmail-3.03/src/goodies.c'\"
- else
- echo shar: Extracting \"'procmail-3.03/src/goodies.c'\" \(11872 characters\)
- sed "s/^X//" >'procmail-3.03/src/goodies.c' <<'END_OF_FILE'
- X/************************************************************************
- X * Collection of library-worthy routines *
- X * *
- X * Copyright (c) 1990-1994, S.R. van den Berg, The Netherlands *
- X * #include "../README" *
- X ************************************************************************/
- X#ifdef RCS
- Xstatic /*const*/char rcsid[]=
- X "$Id: goodies.c,v 1.34 1994/06/28 16:56:17 berg Exp $";
- X#endif
- X#include "procmail.h"
- X#include "sublib.h"
- X#include "robust.h"
- X#include "shell.h"
- X#include "misc.h"
- X#include "pipes.h"
- X#include "common.h"
- X#include "cstdio.h"
- X#include "goodies.h"
- X
- Xlong Stdfilled;
- Xconst char test[]="test";
- Xconst char*Tmnate,*All_args;
- X
- Xstatic const char*evalenv P((void)) /* expects the variable name in buf2 */
- X{ int j;
- X return (unsigned)(j=(*buf2)-'0')>9?getenv(buf2):
- X !j?argv0:
- X j<=crestarg?restargv[j-1]:(const char*)0;
- X}
- X
- X#define NOTHING_YET (-1) /* readparse understands a very complete */
- X#define SKIPPING_SPACE 0 /* subset of the standard /bin/sh syntax */
- X#define NORMAL_TEXT 1 /* that includes single-, double- and back- */
- X#define DOUBLE_QUOTED 2 /* quotes, backslashes and $subtitutions */
- X#define SINGLE_QUOTED 3
- X
- X#define fgetc() (*fpgetc)() /* some compilers previously choked on it */
- X
- X/* sarg==0 : normal parsing, split up arguments like in /bin/sh
- X * sarg==1 : environment assignment parsing, parse up till first whitespace
- X * sarg==2 : normal parsing, split up arguments by existing whitespace
- X */
- Xvoid readparse(p,fpgetc,sarg)register char*p;int(*const fpgetc)();
- X const int sarg;
- X{ static i,skipbracelev,bracegot;int got,bracelev,qbracelev;char*startb;
- X static char*skipback;static const char*oldstartb;
- X bracelev=qbracelev=0;All_args=0;
- X for(got=NOTHING_YET;;) /* buf2 is used as scratch space */
- Xloop:
- X { i=fgetc();
- X if(buf+linebuf-3<p) /* doesn't catch everything, just a hint */
- X { nlog("Exceeded LINEBUF\n");p=buf+linebuf-3;
- X goto ready;
- X }
- Xnewchar:
- X switch(i)
- X { case EOF: /* check sarg too to prevent warnings in the recipe- */
- X if(sarg<2&&got>NORMAL_TEXT) /* condition expansion code */
- Xearly_eof: nlog(unexpeof);
- Xready: if(got!=SKIPPING_SPACE||sarg) /* not terminated yet or sarg==2 ? */
- X *p++='\0';
- X Tmnate=p;
- X return;
- X case '\\':
- X if(got==SINGLE_QUOTED)
- X break;
- X switch(i=fgetc())
- X { case EOF:
- X goto early_eof; /* can't quote EOF */
- X case '\n':
- X continue; /* concatenate lines */
- X case '#':
- X if(got>SKIPPING_SPACE) /* escaped comment at start of word? */
- X goto noesc; /* apparently not, literally */
- X case ' ':case '\t':case '\'':
- X if(got==DOUBLE_QUOTED)
- X goto noesc;
- X case '"':case '\\':case '$':case '`':
- X goto nodelim;
- X case '}':
- X if(got<=NORMAL_TEXT&&bracelev||
- X got==DOUBLE_QUOTED&&bracelev>qbracelev)
- X goto nodelim;
- X }
- X if(got>NORMAL_TEXT)
- Xnoesc: *p++='\\'; /* nothing to escape, just echo both */
- X break;
- X case '`':
- X if(got==SINGLE_QUOTED)
- X goto nodelim;
- X for(startb=p;;) /* mark your position */
- X { switch(i=fgetc()) /* copy till next backquote */
- X { case '"':
- X if(got!=DOUBLE_QUOTED) /* missing closing backquote? */
- X break;
- Xforcebquote: case EOF:case '`':
- X if(skiprc)
- X *(p=startb)='\0';
- X else
- X { int osh=sh;
- X *p='\0';
- X if(!(sh=!!strpbrk(startb,shellmetas)))
- X { const char*save=sgetcp,*sAll_args;
- X sgetcp=p=tstrdup(startb);sAll_args=All_args;
- X readparse(startb,sgetc,0);All_args=sAll_args;
- X#ifndef GOT_bin_test
- X if(!strcmp(test,startb))
- X strcpy(startb,p),sh=1; /* oops, `test' found */
- X#endif
- X free(p);sgetcp=save; /* chopped up */
- X } /* drop source buffer, read from program */
- X startb=fromprog(
- X p=startb,startb,(size_t)(buf-startb+linebuf-3));
- X sh=osh; /* restore sh */
- X }
- X if(got!=DOUBLE_QUOTED)
- X { i=0;startb=p;
- X goto simplsplit; /* split it up */
- X }
- X if(i=='"'||got<=SKIPPING_SPACE) /* missing closing ` ? */
- X got=NORMAL_TEXT;
- X p=startb;
- X goto loop;
- X case '\\':
- X switch(i=fgetc())
- X { case EOF:nlog(unexpeof);
- X goto forcebquote;
- X case '\n':
- X continue;
- X case '"':
- X if(got!=DOUBLE_QUOTED)
- X break;
- X case '\\':case '$':case '`':
- X goto escaped;
- X }
- X *p++='\\';
- X }
- Xescaped: *p++=i;
- X }
- X case '"':
- X switch(got)
- X { case DOUBLE_QUOTED:
- X if(qbracelev<bracelev) /* still inside a ${...}? */
- X case SINGLE_QUOTED:
- X goto nodelim; /* nonsense */
- X got=NORMAL_TEXT;
- X continue; /* closing " */
- X }
- X qbracelev=bracelev;got=DOUBLE_QUOTED;
- X continue; /* opening " */
- X case '\'':
- X switch(got)
- X { case DOUBLE_QUOTED:
- X goto nodelim;
- X case SINGLE_QUOTED:got=NORMAL_TEXT;
- X continue; /* closing ' */
- X }
- X got=SINGLE_QUOTED;
- X continue; /* opening ' */
- X case '}':
- X if(got<=NORMAL_TEXT&&bracelev||
- X got==DOUBLE_QUOTED&&bracelev>qbracelev)
- X { bracelev--;
- X if(skipback&&bracelev==skipbracelev)
- X { skiprc--;p=skipback;skipback=0;startb=(char*)oldstartb;
- X got=bracegot;
- X goto closebrace;
- X }
- X continue;
- X }
- X goto nodelim;
- X case '#':
- X if(got>SKIPPING_SPACE) /* comment at start of word? */
- X break;
- X while((i=fgetc())!=EOF&&i!='\n'); /* skip till EOL */
- X goto ready;
- X case '$':
- X if(got==SINGLE_QUOTED)
- X break;
- X startb=buf2;
- X switch(i=fgetc())
- X { case EOF:*p++='$';
- X goto ready;
- X case '@':
- X if(got!=DOUBLE_QUOTED)
- X goto normchar;
- X if(!skiprc) /* don't do it while skipping (braces) */
- X All_args=p;
- X continue;
- X case '{': /* ${name} */
- X while(EOF!=(i=fgetc())&&alphanum(i))
- X *startb++=i;
- X *startb='\0';startb=(char*)evalenv();
- X if(numeric(*buf2)&&buf2[1])
- X goto badsub;
- X switch(i)
- X { default:
- X goto badsub;
- X case ':':
- X switch(i=fgetc())
- X { default:
- Xbadsub: nlog("Bad substitution of");logqnl(buf2);
- X continue;
- X case '-':
- X if(startb&&*startb)
- X goto noalt;
- X goto doalt;
- X case '+':
- X if(startb&&*startb)
- X goto doalt;
- X startb=0;
- X }
- X case '+':
- X if(startb)
- X goto doalt;
- X goto noalt;
- X case '-':
- X if(startb)
- Xnoalt: if(!skiprc)
- X { skiprc++;skipback=p;skipbracelev=bracelev;
- X oldstartb=startb;bracegot=got;
- X }
- Xdoalt: bracelev++;
- X continue;
- X case '}':
- Xclosebrace: if(!startb)
- X startb="";
- X }
- X goto ibreak; /* $$ =pid */
- X case '$':ultstr(0,(unsigned long)thepid,p);
- X goto ieofstr;
- X case '?':ltstr(0,(long)lexitcode,p);
- X goto ieofstr;
- X case '#':ultstr(0,(unsigned long)crestarg,p);
- X goto ieofstr;
- X case '=':ltstr(0,lastscore,p);
- Xieofstr: i='\0';
- X goto eofstr;
- X case '_':startb=incnamed?incnamed->ename:"";
- X goto ibreak;
- X case '-':startb=(char*)tgetenv(lastfolder); /* $- =$LASTFOLDER */
- Xibreak: i='\0';break;
- X default:
- X if(numeric(i)) /* $n positional argument */
- X { *startb++=i;i='\0';
- X goto finsb;
- X }
- X if(alphanum(i)) /* $name */
- X { do *startb++=i;
- X while(EOF!=(i=fgetc())&&alphanum(i));
- X if(i==EOF)
- X i='\0';
- Xfinsb: *startb='\0';
- X if(!(startb=(char*)evalenv()))
- X startb="";
- X break;
- X }
- Xnormchar: *p++='$';
- X goto newchar; /* not a substitution */
- X }
- X if(got!=DOUBLE_QUOTED)
- Xsimplsplit: { if(sarg)
- X goto copyit;
- X for(;;startb++) /* simply split it up in arguments */
- X { switch(*startb)
- X { case ' ':case '\t':case '\n':
- X if(got<=SKIPPING_SPACE)
- X continue;
- X *p++='\0';got=SKIPPING_SPACE;
- X continue;
- X case '\0':
- X goto eeofstr;
- X }
- X *p++= *startb;got=NORMAL_TEXT;
- X }
- X }
- X else
- Xcopyit: { strcpy(p,startb); /* simply copy it */
- Xeofstr: if(got<=SKIPPING_SPACE) /* can only occur if sarg!=0 */
- X got=NORMAL_TEXT;
- X p=strchr(p,'\0');
- X }
- Xeeofstr: if(i) /* already read next character? */
- X goto newchar;
- X continue;
- X case ' ':case '\t':
- X switch(got)
- X { case NORMAL_TEXT:
- X if(sarg==1)
- X goto ready; /* already fetched a single argument */
- X got=SKIPPING_SPACE;*p++=sarg?' ':'\0'; /* space or \0 sep. */
- X case NOTHING_YET:case SKIPPING_SPACE:
- X continue; /* skip space */
- X }
- X case '\n':
- X if(got<=NORMAL_TEXT)
- X goto ready; /* EOL means we're ready */
- X }
- Xnodelim:
- X *p++=i; /* ah, a normal character */
- X if(got<=SKIPPING_SPACE) /* should we bother to change mode? */
- X got=NORMAL_TEXT;
- X }
- X}
- X
- Xvoid ltstr(minwidth,val,dest)const int minwidth;const long val;char*dest;
- X{ if(val<0)
- X { *dest=' ';ultstr(minwidth-1,-val,dest+1);
- X while(*++dest==' '); /* look for the first non-space */
- X dest[-1]='-'; /* replace it with a minus */
- X }
- X else
- X ultstr(minwidth,val,dest); /* business as usual */
- X}
- X
- Xdouble stod(str,ptr)const char*str;const char**const ptr;
- X{ int sign,any;unsigned i;char*chp;double acc,fracc;
- X fracc=1;acc=any=sign=0;
- X switch(*(chp=skpspace(str))) /* the sign */
- X { case '-':sign=1;
- X case '+':chp++;
- X }
- X while((i=(unsigned)*chp++-'0')<=9) /* before the decimal point */
- X acc=acc*10+i,any=1;
- X switch(i)
- X { case (unsigned)'.'-'0':case (unsigned)','-'0':
- X while(fracc/=10,(i=(unsigned)*chp++-'0')<=9) /* the fractional part */
- X acc+=fracc*i,any=1;
- X }
- X if(ptr)
- X *ptr=any?chp-1:str;
- X return sign?-acc:acc;
- X}
- X
- Xstatic struct dynstring*myenv;
- Xstatic char**lastenv;
- X /* smart putenv, the way it was supposed to be */
- Xconst char*sputenv(a)const char*const a;
- X{ static alloced;size_t eq,i;int remove;const char*split;char**preenv;
- X struct dynstring*curr,**last;
- X yell("Assigning",a);remove=0;
- X if(!(split=strchr(a,'='))) /* assignment or removal? */
- X remove=1,split=strchr(a,'\0');
- X eq=split-a; /* is it */
- X for(curr= *(last= &myenv);curr;curr= *(last= &curr->enext)) /* one I made */
- X if(!strncmp(a,curr->ename,eq)&&((char*)curr->ename)[eq]=='=')
- X { split=curr->ename;*last=curr->enext;free(curr); /* earlier? */
- X for(preenv=environ;*preenv!=split;preenv++);
- X goto wipenv;
- X }
- X for(preenv=environ;*preenv;preenv++) /* is it in the standard */
- X if(!strncmp(a,*preenv,eq)&&(*preenv)[eq]=='=') /* environment? */
- Xwipenv:
- X { while(*preenv=preenv[1]) /* wipe this entry out of the environment */
- X preenv++;
- X break;
- X }
- X i=(preenv-environ+2)*sizeof*environ;
- X if(alloced) /* have we ever alloced the environ array before? */
- X environ=realloc(environ,i);
- X else
- X alloced=1,environ=tmemmove(malloc(i),environ,i-sizeof*environ);
- X if(!remove) /* if not remove, then add it to both environments */
- X { for(preenv=environ;*preenv;preenv++);
- X preenv[1]=0;*(lastenv=preenv)=(char*)(split=newdynstring(&myenv,a));
- X return split+eq+1;
- X }
- X return "";
- X}
- X /* between calling primeStdout() and retStdout() */
- Xvoid primeStdout P((void)) /* *no* environment changes are allowed! */
- X{ char*p;
- X if((p=strchr(buf,'\0'))[-1]!='=') /* does it end in an '='? */
- X *p='=',p[1]='\0'; /* make sure it does */
- X sputenv(buf);Stdout=(char*)myenv;
- X Stdfilled=ioffsetof(struct dynstring,ename[0])+strlen(myenv->ename);
- X}
- X
- Xvoid retStdout(newmyenv)char*const newmyenv; /* see note on primeStdout() */
- X{ if(newmyenv[Stdfilled-1]=='\n') /* strip one trailing newline */
- X Stdfilled--;
- X newmyenv[Stdfilled]='\0';*lastenv=(myenv=(struct dynstring*)newmyenv)->ename;
- X Stdout=0;
- X}
- X
- Xvoid postStdout P((void)) /* throw it into the keyword parser */
- X{ const char*p;size_t i;
- X p= *lastenv;tmemmove(buf,p,i=strchr(p,'=')-p);buf[i]='\0';asenv(p+i+1);
- X}
- END_OF_FILE
- if test 11872 -ne `wc -c <'procmail-3.03/src/goodies.c'`; then
- echo shar: \"'procmail-3.03/src/goodies.c'\" unpacked with wrong size!
- fi
- # end of 'procmail-3.03/src/goodies.c'
- fi
- if test -f 'procmail-3.03/src/procmail.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'procmail-3.03/src/procmail.c'\"
- else
- echo shar: Extracting \"'procmail-3.03/src/procmail.c'\" \(38724 characters\)
- sed "s/^X//" >'procmail-3.03/src/procmail.c' <<'END_OF_FILE'
- X/************************************************************************
- X * procmail - The autonomous mail processor *
- X * *
- X * It has been designed to be able to be run suid root and (in *
- X * case your mail spool area is *not* world writable) sgid *
- X * mail (or daemon), without creating security holes. *
- X * *
- X * Seems to be perfect. *
- X * *
- X * Copyright (c) 1990-1994, S.R. van den Berg, The Netherlands *
- X * #include "../README" *
- X ************************************************************************/
- X#ifdef RCS
- Xstatic /*const*/char rcsid[]=
- X "$Id: procmail.c,v 1.86 1994/06/30 13:52:34 berg Exp $";
- X#endif
- X#include "../patchlevel.h"
- X#include "procmail.h"
- X#include "acommon.h"
- X#include "sublib.h"
- X#include "robust.h"
- X#include "shell.h"
- X#include "misc.h"
- X#include "pipes.h"
- X#include "common.h"
- X#include "cstdio.h"
- X#include "exopen.h"
- X#include "regexp.h"
- X#include "mcommon.h"
- X#include "goodies.h"
- X#include "locking.h"
- X#include "mailfold.h"
- X
- Xstatic const char orgmail[]="ORGMAIL",*const nullp,From_[]=FROM,
- X exflags[]=RECFLAGS,drcfile[]="Rcfile:",systm_mbox[]=SYSTEM_MBOX,
- X pmusage[]=PM_USAGE,*etcrc=ETCRC,misrecpt[]="Missing recipient\n",
- X extrns[]="Extraneous ",ignrd[]=" ignored\n",conflicting[]="Conflicting ",
- X pardir[]=chPARDIR,suppressed[]=" suppressed\n",
- X insufprivs[]="Insufficient privileges\n";
- Xchar*buf,*buf2,*loclock,*tolock;
- Xconst char shell[]="SHELL",lockfile[]="LOCKFILE",newline[]="\n",binsh[]=BinSh,
- X unexpeof[]="Unexpected EOL\n",*const*gargv,*const*restargv= &nullp,*sgetcp,
- X *rcfile=PROCMAILRC,dirsep[]=DIRSEP,devnull[]=DevNull,lgname[]="LOGNAME",
- X executing[]="Executing",oquote[]=" \"",cquote[]="\"\n",procmailn[]="procmail",
- X whilstwfor[]=" whilst waiting for ",home[]="HOME",maildir[]="MAILDIR",
- X host[]="HOST",*defdeflock,*argv0="",errwwriting[]="Error while writing to",
- X slogstr[]="%s \"%s\"";
- Xchar*Stdout;
- Xint retval=EX_CANTCREAT,retvl2=EX_OK,sh,pwait,lcking,rcstate,rc= -1,ignwerr,
- X lexitcode=EX_OK,asgnlastf,accspooldir,crestarg,skiprc,savstdout;
- Xsize_t linebuf=mx(DEFlinebuf+XTRAlinebuf,STRLEN(systm_mbox)<<1);
- Xvolatile int nextexit; /* if termination is imminent */
- Xpid_t thepid;
- Xlong filled,lastscore; /* the length of the mail, and the last score */
- Xchar*themail,*thebody; /* the head and body of the mail */
- Xuid_t uid;
- Xgid_t gid,sgid;
- X
- X#if 0
- X#define wipetcrc() (etcrc&&(etcrc=0,closerc(),1))
- X#else
- Xstatic int wipetcrc P((void)) /* stupid function to avoid a compiler bug */
- X{ if(etcrc)
- X { etcrc=0;closerc();
- X return 1;
- X }
- X return 0;
- X}
- X#endif
- X
- Xmain(argc,argv)const char*const argv[];
- X{ register char*chp,*chp2;register i;int suppmunreadable,mailfilter;
- X#if 0 /* enable this if you want to trace procmail */
- X kill(getpid(),SIGSTOP);/*raise(SIGSTOP);*/
- X#endif
- X newid();
- X ;{ int presenviron,Deliverymode,override;char*fromwhom=0;
- X const char*idhint=0;gid_t egid=getegid();
- X Deliverymode=mailfilter=override=0;
- X openlog(procmailn,LOG_PID,LOG_MAIL); /* for the syslogd */
- X if(argc) /* sanity check, any argument at all? */
- X { Deliverymode=strncmp(lastdirsep(argv0=argv[0]),procmailn,
- X STRLEN(procmailn));
- X for(presenviron=argc=0;(chp2=(char*)argv[++argc])&&*chp2=='-';)
- X for(;;) /* processing options */
- X { switch(*++chp2)
- X { case VERSIONOPT:elog(VERSION);
- X return EX_OK;
- X case HELPOPT1:case HELPOPT2:elog(pmusage);elog(PM_HELP);
- X elog(PM_QREFERENCE);
- X return EX_USAGE;
- X case PRESERVOPT:presenviron=1;
- X continue;
- X case MAILFILTOPT:mailfilter=1;
- X continue;
- X case OVERRIDEOPT:override=1;
- X continue;
- X case TEMPFAILOPT:retval=EX_TEMPFAIL;
- X continue;
- X case FROMWHOPT:case ALTFROMWHOPT:
- X if(*++chp2)
- X fromwhom=chp2;
- X else if(chp2=(char*)argv[argc+1])
- X argc++,fromwhom=chp2;
- X else
- X nlog("Missing name\n");
- X break;
- X case ARGUMENTOPT:
- X { static const char*argv1[]={"",0};
- X if(*++chp2)
- X goto setarg;
- X else if(chp2=(char*)argv[argc+1])
- X { argc++;
- Xsetarg: *argv1=chp2;restargv=argv1;crestarg=1;
- X }
- X else
- X nlog("Missing argument\n");
- X break;
- X }
- X case DELIVEROPT:
- X if(!*(chp= ++chp2)&&!(chp=(char*)argv[++argc]))
- X { nlog(misrecpt);
- X break;
- X }
- X else
- X { Deliverymode=1;
- X goto last_option;
- X }
- X case '-':
- X if(!*chp2)
- X { argc++;
- X goto last_option;
- X }
- X default:nlog("Unrecognised options:");logqnl(chp2);
- X elog(pmusage);elog("Processing continued\n");
- X case '\0':;
- X }
- X break;
- X }
- X }
- X if(Deliverymode&&!(chp=chp2))
- X nlog(misrecpt),Deliverymode=0;
- Xlast_option:
- X if(Deliverymode&&presenviron)
- X { presenviron=0; /* -d disables -p */
- X goto conflopt;
- X }
- X if(mailfilter)
- X { if(Deliverymode) /* -d supersedes -m */
- X { mailfilter=0;
- X goto conflopt;
- X }
- X if(crestarg) /* -m will supersede -a */
- Xconflopt: nlog(conflicting),elog("options\n"),elog(pmusage);
- X }
- X if(!Deliverymode)
- X idhint=getenv(lgname);
- X if(!presenviron) /* drop the environment */
- X { const char**emax=(const char**)environ,*const*ep,*const*kp;
- X static const char*const keepenv[]=KEEPENV;
- X for(kp=keepenv;*kp;kp++) /* preserve a happy few */
- X for(i=strlen(*kp),ep=emax;chp2=(char*)*ep;ep++)
- X if(!strncmp(*kp,chp2,(size_t)i)&&(chp2[i]=='='||chp2[i-1]=='_'))
- X { *emax++=chp2;
- X break;
- X }
- X *emax=0; /* drop the rest */
- X }
- X#ifdef LD_ENV_FIX
- X ;{ const char**emax=(const char**)environ,**ep;
- X static const char ld_[]="LD_";
- X for(ep=emax;*emax;emax++); /* find the end of the environment */
- X while(*ep)
- X if(!strncmp(ld_,*ep++,STRLEN(ld_))) /* it starts with LD_ */
- X *--ep= *--emax,*emax=0; /* copy from the end */
- X }
- X#endif /* LD_ENV_FIX */
- X ;{ const struct passwd*pass,*passinvk;struct passwd spassinvk;int privs;
- X uid_t euid=geteuid();
- X spassinvk.pw_name=spassinvk.pw_dir=spassinvk.pw_shell=0;
- X passinvk=savepass(&spassinvk,uid=getuid());privs=1;gid=getgid();
- X ;{ static const char*const trusted_ids[]=TRUSTED_IDS;
- X if(*trusted_ids&&uid!=euid)
- X { struct group*grp;const char*const*kp;
- X if(passinvk) /* check out the invoker's uid */
- X for(chp2=passinvk->pw_name,kp=trusted_ids;*kp;)
- X if(!strcmp(chp2,*kp++)) /* is it amongst the privileged? */
- X goto privileged;
- X if(grp=getgrgid(gid)) /* check out the invoker's gid */
- X for(chp2=grp->gr_name,kp=trusted_ids;*kp;)
- X if(!strcmp(chp2,*kp++)) /* is it among the privileged? */
- X goto privileged;
- X privs=0;
- X }
- X }
- Xprivileged: /* move stdout out of the way */
- X endgrent();doumask(INIT_UMASK);savstdout=rdup(STDOUT);
- X fclose(stdout);rclose(STDOUT); /* just to make sure */
- X if(0>opena(devnull))
- X { writeerr(devnull);syslog(LOG_EMERG,slogstr,errwwriting,devnull);
- X return EX_OSFILE; /* couldn't open stdout */
- X }
- X#ifdef console
- X opnlog(console);
- X#endif
- X setbuf(stdin,(char*)0);buf=malloc(linebuf);buf2=malloc(linebuf);
- X#ifdef SIGXCPU
- X signal(SIGXCPU,SIG_IGN);signal(SIGXFSZ,SIG_IGN);
- X#endif
- X#ifdef SIGLOST
- X signal(SIGLOST,SIG_IGN);
- X#endif
- X#if DEFverbose
- X verboff();verbon();
- X#else
- X verbon();verboff();
- X#endif
- X signal(SIGPIPE,SIG_IGN);qsignal(SIGTERM,srequeue);
- X qsignal(SIGINT,sbounce);qsignal(SIGHUP,sbounce);
- X qsignal(SIGQUIT,slose);signal(SIGALRM,(void(*)())ftimeout);
- X ultstr(0,(unsigned long)uid,buf);
- X chp2=!passinvk||!*passinvk->pw_name?buf:passinvk->pw_name;
- X filled=0;
- X ;{ const char*fwhom;size_t lfr,linv;int tstamp;
- X tstamp=fromwhom&&*fromwhom==REFRESH_TIME&&!fromwhom[1];fwhom=chp2;
- X if(fromwhom&&!tstamp)
- X { if(!privs&&!strcmp(fromwhom,fwhom))
- X privs=1; /* if -f user is the same as the invoker, allow it */
- X fwhom=fromwhom;
- X }
- X thebody=themail=
- X malloc(2*linebuf+(lfr=strlen(fwhom))+(linv=strlen(chp2)));
- X if(Deliverymode||fromwhom) /* need to peek for a leading From_ ? */
- X { char*rstart;int r;static const char Fakefield[]=FAKE_FIELD;
- X ;{ time_t t; /* the current time */
- X t=time((time_t*)0);strcat(strcpy(buf2," "),ctime(&t));
- X }
- X lfr+=STRLEN(From_)+(r=strlen(buf2));
- X if(tstamp)
- X tstamp=r; /* save time stamp length */
- X if(privs) /* privileged user? */
- X linv=0; /* yes, so no need to insert >From_ */
- X else
- X linv+=STRLEN(Fakefield)+r;
- X while(1==(r=rread(STDIN,themail,1))&&*themail=='\n');
- X i=0;rstart=themail; /* skip garbage */
- X if(r>0&&STRLEN(From_)<=(i=rread( /* is it a From_ line? */
- X STDIN,rstart+1,(int)(linebuf-2-1))+1)&&eqFrom_(themail))
- X { rstart[i]='\0';
- X if(!(rstart=strchr(rstart,'\n')))
- X { do /* drop long From_ line */
- X { if((i=rread(STDIN,themail,(int)(linebuf-2)))<=0)
- X break;
- X themail[i]='\0'; /* terminate it for strchr */
- X }
- X while(!(rstart=strchr(themail,'\n')));
- X i=rstart?i-(++rstart-themail):0;
- X goto no_from;
- X }
- X ;{ size_t tfrl;
- X i-=tfrl= ++rstart-themail; /* demarcate From_ line */
- X if(Deliverymode&&override&&!privs)
- X { if(verbose) /* discard the bogus From_ */
- X nlog(insufprivs);
- X goto no_from;
- X }
- X if(tstamp)
- X lfr=findtstamp(themail+STRLEN(From_),rstart)
- X -themail+tstamp;
- X else if(!fromwhom) /* leave the From_ line alone */
- X if(linv) /* fake alert? */
- X lfr=tfrl; /* yes, so separate From_ from the rest */
- X else
- X lfr=0,i+=tfrl; /* no, tack it onto the rest */
- X }
- X }
- X else
- Xno_from: { tstamp=0; /* no existing From_, so nothing to stamp */
- X if(fromwhom&&override&&!privs)
- X { if(verbose)
- X nlog(insufprivs);
- X fromwhom=0; /* ignore the bogus -f */
- X }
- X if(!fromwhom) /* no -f ? */
- X linv=0; /* then it can't be a fake */
- X }
- X filled=lfr+linv+i; /* From_ + >From_ + rest */
- X if(lfr||linv) /* move read text beyond our From_ line */
- X { r= *rstart;tmemmove(themail+lfr+linv,rstart,i);
- X rstart=themail+lfr; /* skip the From_ line, if any */
- X if(!linv) /* no fake alert */
- X { rstart[-tstamp]='\0'; /* where do we append */
- X if(!tstamp) /* no timestamp, so generate it all */
- X strcat(strcpy(themail,From_),fwhom); /* From user */
- X }
- X else
- X { if(lfr) /* did we skip a From_ line? */
- X if(tstamp) /* copy the timestamp over the tail */
- X strcpy(rstart-tstamp,buf2);
- X else if(fromwhom) /* whole new From_? */
- X strcat(strcat(strcpy(themail,From_),fwhom),buf2);
- X strcat(strcpy(rstart,Fakefield),chp2); /* fake alert */
- X } /* overwrite the trailing \0 again */
- X strcat(themail,buf2);themail[lfr+linv]=r;
- X }
- X }
- X }
- X readmail(0,0L); /* read in the mail completely */
- X if(Deliverymode)
- X do /* chp should point to the first recipient */
- X { chp2=chp;
- X#ifndef NO_USER_TO_LOWERCASE_HACK
- X for(;*chp;chp++) /* kludge recipient into lowercase */
- X if((unsigned)*chp-'A'<='Z'-'A')
- X *chp+='a'-'A'; /* getpwnam might be case sensitive */
- X#endif
- X if(argv[++argc]) /* more than one recipient */
- X if(pidchild=sfork())
- X { if(forkerr(pidchild,procmailn)||waitfor(pidchild)!=EX_OK)
- X retvl2=retval;
- X pidchild=0; /* loop for the next recipient */
- X }
- X else
- X { newid();
- X while(argv[++argc]); /* skip till end of command line */
- X }
- X }
- X while(chp=(char*)argv[argc]);
- X gargv=argv+argc;suppmunreadable=verbose; /* save it for nextrcfile() */
- X if(Deliverymode)
- X { if(!(pass=getpwnam(chp2))) /* chp2 should point to the recipient */
- X { static const char unkuser[]="Unknown user";
- X nlog(unkuser);logqnl(chp2);
- X syslog(LOG_ERR,slogstr,unkuser,chp2);
- X return EX_NOUSER;
- X }
- X if(enoughprivs(passinvk,euid,egid,pass->pw_uid,pass->pw_gid))
- X goto Setuser;
- X nlog(insufprivs);
- X }
- X else
- X { suppmunreadable=nextrcfile();
- X if(presenviron) /* preserving the environment? */
- X etcrc=0; /* don't read etcrc then */
- X if(suppmunreadable) /* command-line rcfile? */
- X etcrc=0,scomsat=DEFcomsat; /* forget etcrc and comsat */
- X if(mailfilter)
- X { if(!suppmunreadable)
- X { nlog("Missing rcfile\n");
- X return EX_NOINPUT;
- X }
- X#ifdef ETCRCS
- X ;{ static const char etcrcs[]=ETCRCS;
- X if(!strncmp(etcrcs,rcfile,STRLEN(etcrcs)))
- X { struct stat stbuf; /* path starts with /etc/procmailrcs/ */
- X /*
- X * although the filename starts with /etc/procmailrcs/
- X * we will now check if it does not contain any backward
- X * references which would allow it to escape the secure
- X * tree; look for /../ sequences
- X */
- X for(chp=(char*)rcfile+STRLEN(etcrcs)-1;
- X chp; /* any devious embedded /../? */
- X chp=strpbrk(chp,dirsep))
- X if(!strncmp(pardir,++chp,STRLEN(pardir))&&
- X (chp+=STRLEN(pardir),strchr(dirsep,*chp)))
- X goto nospecial; /* yes, security violation */
- X /*
- X * so far, so good, now verify the credentials down to the
- X * last bit
- X */
- X if(presenviron|| /* -p is dangerous */
- X suppmunreadable!=2|| /* so are variable assignments */
- X lstat(rcfile,&stbuf)|| /* it seems to exist */
- X !enoughprivs(passinvk,euid,egid,stbuf.st_uid,
- X stbuf.st_gid)|| /* can we do this at all? */
- X S_ISDIR(stbuf.st_mode)|| /* no directories! */
- X !savepass(&spassinvk,stbuf.st_uid)) /* user exists? */
- Xnospecial: { static const char densppr[]=
- X "Denying special privileges for";
- X nlog(densppr);logqnl(rcfile);
- X syslog(LOG_ALERT,slogstr,densppr,rcfile);
- X }
- X else
- X passinvk= &spassinvk; /* no security violation */
- X } /* accept new identity */
- X }
- X#endif
- X }
- X }
- X if(idhint&&(pass=getpwnam(idhint))&&
- X passinvk&&passinvk->pw_uid==pass->pw_uid||
- X (pass=passinvk))
- X /*
- X * set preferred uid to the intended recipient
- X */
- XSetuser: { gid=pass->pw_gid;uid=pass->pw_uid;
- X setdef(lgname,chp= *pass->pw_name?pass->pw_name:buf);
- X setdef(home,pass->pw_dir);
- X if(euid==ROOT_uid)
- X initgroups(chp,gid);
- X endgrent();setdef(shell,*pass->pw_shell?pass->pw_shell:binsh);
- X }
- X else /* user could not be found, set reasonable defaults */
- X /*
- X * to prevent security holes, drop any privileges now
- X */
- X { setdef(lgname,buf);setdef(home,RootDir);setdef(shell,binsh);
- X setids();
- X }
- X endpwent();
- X if(passinvk)
- X { free(spassinvk.pw_name);free(spassinvk.pw_dir);
- X free(spassinvk.pw_shell);
- X }
- X }
- X setdef(orgmail,systm_mbox);setdef(maildir,DEFmaildir);
- X if(!presenviron||!mailfilter)
- X { setdef(host,hostname());sputenv(lastfolder);sputenv(exitcode);
- X initdefenv();
- X ;{ const char*const*kp;static const char*const prestenv[]=PRESTENV;
- X for(kp=prestenv;*kp;) /* preset some environment variables */
- X { strcpy((char*)(sgetcp=buf2),*kp++);readparse(buf,sgetc,2);
- X sputenv(buf);
- X }
- X }
- X } /* set fdefault and find out the name of our system lockfile */
- X sgetcp=fdefault;readparse(buf,sgetc,2);fdefault=tstrdup(buf);
- X strcpy(chp2=strchr(strcpy(buf,chp=(char*)getenv(orgmail)),'\0'),lockext);
- X defdeflock=tstrdup(buf);*chp2='\0';buf[i=lastdirsep(chp)-chp]='\0';
- X ;{ struct stat stbuf; /* strip off the basename */
- X sgid=gid; /* presumed innocent */
- X /*
- X * do we need sgidness to access the mail-spool directory/files?
- X */
- X if(!stat(buf,&stbuf))
- X { accspooldir=!!(stbuf.st_mode&(S_IWGRP|S_IWOTH))<<1|
- X uid==stbuf.st_uid;
- X if((uid!=stbuf.st_uid&&
- X stbuf.st_gid==egid||
- X (rcstate=rc_NOSGID,0))&&
- X (stbuf.st_mode&(S_IWGRP|S_IXGRP|S_IWOTH))==(S_IWGRP|S_IXGRP))
- X { doumask(INIT_UMASK&~S_IRWXG); /* make it group-writable */
- X goto keepgid;
- X }
- X else if(stbuf.st_mode&S_ISGID)
- Xkeepgid: sgid=stbuf.st_gid; /* keep the gid from the parent directory */
- X }
- X /*
- X * check if the default-mailbox-lockfile is owned by the
- X * recipient, if not, mark it for further investigation, it
- X * might need to be removed
- X */
- X for(;;)
- X { ;{ int mboxstat;
- X static const char renbogus[]="Renamed bogus \"%s\" into \"%s\"",
- X renfbogus[]="Couldn't rename bogus \"%s\" into \"%s\"";
- X ;{ int goodlock;
- X if(!(goodlock=lstat(defdeflock,&stbuf)||stbuf.st_uid==uid))
- X ultoan((unsigned long)stbuf.st_ino, /* i-node numbered */
- X strchr(strcpy(buf+i,BOGUSprefix),'\0'));
- X /*
- X * check if the original/default mailbox of the recipient
- X * exists, if it does, perform some security checks on it
- X * (check if it's a regular file, check if it's owned by
- X * the recipient), if something is wrong try and move the
- X * bogus mailbox out of the way, create the
- X * original/default mailbox file, and chown it to
- X * the recipient
- X */
- X if(lstat(chp,&stbuf)) /* stat the mailbox */
- X { mboxstat= -(errno==EACCES);
- X goto boglock;
- X } /* lockfile unrightful owner */
- X else
- X { mboxstat=1;
- X if(!(stbuf.st_mode&S_IWGRP))
- Xboglock: if(!goodlock) /* try & rename bogus lockfile */
- X if(rename(defdeflock,buf)) /* out of the way */
- X syslog(LOG_EMERG,renfbogus,defdeflock,buf);
- X else
- X syslog(LOG_ALERT,renbogus,defdeflock,buf);
- X }
- X }
- X if(mboxstat>0||mboxstat<0&&(setids(),!lstat(chp,&stbuf)))
- X if(!(stbuf.st_mode&S_IWUSR)|| /* recipient can write? */
- X S_ISLNK(stbuf.st_mode)|| /* no symbolic links */
- X (S_ISDIR(stbuf.st_mode)? /* directories, yes, hardlinks */
- X !(stbuf.st_mode&S_IXUSR):stbuf.st_nlink!=1)) /* no */
- X goto bogusbox; /* can't deliver to this contraption */
- X else if(stbuf.st_uid!=uid) /* recipient not owner */
- Xbogusbox: { ultoan((unsigned long)stbuf.st_ino, /* i-node numbered */
- X strchr(strcpy(buf+i,BOGUSprefix),'\0')); /* bogus */
- X nlog("Renaming bogus mailbox \"");elog(chp);
- X elog("\" into");logqnl(buf);
- X if(rename(chp,buf)) /* try and move it out of the way */
- X { syslog(LOG_EMERG,renfbogus,chp,buf);
- X goto fishy; /* rename failed, something's fishy here */
- X }
- X else
- X syslog(LOG_ALERT,renbogus,chp,buf);
- X } /* SysV type autoforwarding? */
- X else if(Deliverymode&&stbuf.st_mode&(S_ISGID|S_ISUID))
- X { nlog("Autoforwarding mailbox found\n");
- X return EX_NOUSER;
- X }
- X else
- X { if(!(stbuf.st_mode&OVERRIDE_MASK)&&stbuf.st_mode&cumask)
- X { static const char enfperm[]=
- X "Enforcing stricter permissions on";
- X nlog(enfperm);logqnl(chp);
- X syslog(LOG_NOTICE,slogstr,enfperm,chp);setids();
- X chmod(chp,stbuf.st_mode&=~cumask);
- X }
- X break; /* everything is just fine */
- X }
- X }
- X if(!(accspooldir&1)) /* recipient does not own the spool dir */
- X { if(!xcreat(chp,NORMperm,(time_t*)0,doCHOWN|doCHECK)) /* create */
- X break; /* mailbox... yes we could, fine, proceed */
- X if(!lstat(chp,&stbuf)) /* anything in the way? */
- X continue; /* check if it could be valid */
- X }
- X setids(); /* try some magic */
- X if(!xcreat(chp,NORMperm,(time_t*)0,doCHECK)) /* try again */
- X break;
- X if(lstat(chp,&stbuf)) /* nothing in the way? */
- Xfishy: { nlog("Couldn't create");logqnl(chp);sputenv(orgmail);
- X if(!strcmp(chp,fdefault)) /* DEFAULT the same? */
- X fdefault=""; /* so panic */
- X break;
- X }
- X } /* bad news, be conservative */
- X doumask(INIT_UMASK);
- X }
- X while(chp=(char*)argv[argc]) /* interpret command line specs first */
- X /*
- X * really change the uid now, since it would not be safe to
- X * evaluate the extra command line arguments otherwise
- X */
- X { setids();argc++;
- X if(!asenvcpy(chp)&&mailfilter)
- X { gargv= &nullp;
- X for(restargv=argv+argc;restargv[crestarg];crestarg++);
- X break;
- X }
- X resettmout();
- X }
- X }
- X ;{ int succeed,lastcond,prevcond;struct dyna_long ifstack;
- X ifstack.filled=ifstack.tspace=0;ifstack.offs=0;
- X if(etcrc) /* do we start with an /etc/procmailrc file first? */
- X { if(0<=bopen(etcrc))
- X { yell(drcfile,etcrc);
- X#if !DEFverbose
- X if(rcstate!=rc_NORMAL)
- X verbose=0; /* no peeking in /etc/procmailrc */
- X#endif
- X goto startrc;
- X }
- X etcrc=0; /* no such file */
- X }
- X do /* main rcfile interpreter loop */
- X { resettmout();
- X if(rc<0) /* open new rc file */
- X { struct stat stbuf;
- X /*
- X * if we happen to be still running as root, and the rcfile
- X * is mounted on a secure NFS-partition, we might not be able
- X * to access it, so check if we can stat it or don't need any
- X * sgid privileges, if yes, drop all privs and set uid to
- X * the recipient beforehand
- X */
- X goto findrc;
- X do
- X { if(suppmunreadable) /* should we supress this message? */
- Xfake_rc: readerr(buf);
- X if(!nextrcfile()) /* not available? try the next */
- X { skiprc=0;
- X goto nomore_rc;
- X }
- X suppmunreadable=0;
- Xfindrc: i=0; /* should we keep the current directory? */
- X if(strchr(dirsep,*rcfile)|| /* absolute path? */
- X (mailfilter||*rcfile==chCURDIR&&strchr(dirsep,rcfile[1]))&&
- X (i=1)) /* mailfilter or ./ pfx */
- X *buf='\0'; /* do not put anything in front then */
- X else
- X cat(tgetenv(home),MCDIRSEP); /* prepend $HOME directory */
- X if(stat(strcat(buf,rcfile),&stbuf)? /* accessible? */
- X rcstate==rc_NOSGID:stbuf.st_mode&S_IRUSR) /* owner-readable? */
- X setids(); /* then transmogrify */
- X }
- X while(0>bopen(buf)); /* try opening the rcfile */
- X if(i) /* opened rcfile in the current directory? */
- X { if(!didchd)
- X { didchd=1;*(chp=strcpy(buf2,maildir)+STRLEN(maildir))='=';
- X *++chp=chCURDIR;*++chp='\0';sputenv(buf2);
- X }
- X }
- X else
- X /*
- X * OK, so now we have opened an absolute rcfile, but for security
- X * reasons we only accept it if it is owned by the recipient or
- X * root and is not world writable, and the directory it is in is
- X * not world writable or has the sticky bit set
- X */
- X { i= *(chp=lastdirsep(buf));
- X if(lstat(buf,&stbuf)||
- X ((stbuf.st_uid!=uid&&stbuf.st_uid!=ROOT_uid||
- X stbuf.st_mode&S_IWOTH)&&
- X strcmp(devnull,buf)&& /* /dev/null is a special case */
- X (*chp='\0',stat(buf,&stbuf)||
- X (stbuf.st_mode&(S_IWOTH|S_IXOTH))==(S_IWOTH|S_IXOTH)&&
- X !(stbuf.st_mode&S_ISVTX))))
- X { static const char susprcf[]="Suspicious rcfile";
- X *chp=i;closerc();nlog(susprcf);logqnl(buf);
- X syslog(LOG_ALERT,slogstr,susprcf,buf);
- X goto fake_rc;
- X }
- X *chp=i;
- X }
- X /*
- X * set uid back to recipient in any case, since we might just
- X * have opened his/her .procmailrc (don't remove these, since
- X * the rcfile might have been created after the first stat)
- X */
- X yell(drcfile,buf);setids();firstchd();
- Xstartrc: succeed=lastcond=prevcond=0;
- X }
- X unlock(&loclock); /* unlock any local lockfile */
- X goto commint;
- X do
- X { for(;;) /* skip the rest of the line */
- X { switch(getb())
- X { default:
- X continue;
- X case '\n':case EOF:;
- X }
- X break;
- X }
- Xcommint: do skipspace(); /* skip whitespace */
- X while(testb('\n'));
- X }
- X while(testb('#')); /* no comment :-) */
- X if(testb(':')) /* check for a recipe */
- X { int locknext;long tobesent;char*startchar;
- X static char flags[maxindex(exflags)];
- X ;{ int nrcond,scored;double score;
- X score=scored=0;
- X readparse(buf,getb,0);
- X ;{ char*chp3;
- X nrcond=strtol(buf,&chp3,10);chp=chp3;
- X }
- X if(chp==buf) /* no number parsed */
- X nrcond= -1;
- X if(tolock) /* clear temporary buffer for lockfile name */
- X free(tolock);
- X for(i=maxindex(flags);flags[i]=0,i--;); /* clear the flags */
- X for(tolock=0,locknext=0;;)
- X { chp=skpspace(chp);
- X switch(i= *chp++)
- X { default:
- X if(!(chp2=strchr(exflags,i))) /* a valid flag? */
- X { chp--;
- X break;
- X }
- X flags[chp2-exflags]=1; /* set the flag */
- X case '\0':
- X if(chp!=Tmnate) /* if not the real end, skip */
- X continue;
- X break;
- X case ':':locknext=1; /* yep, local lockfile specified */
- X if(*chp||++chp!=Tmnate)
- X tolock=tstrdup(chp),chp=strchr(chp,'\0')+1;
- X }
- X concatenate(chp);skipped(chp); /* display leftovers */
- X break;
- X }
- X if(flags[ERROR_DO]&&flags[ELSE_DO])
- X nlog(conflicting),elog("else-if-flag"),elog(suppressed);
- X if(flags[ERROR_DO]&&flags[ALSO_N_IF_SUCC])
- X { nlog(conflicting);elog("also-if-succeeded-flag");
- X elog(suppressed);
- X }
- X if(nrcond<0) /* assume appropriate default nr of conditions */
- X nrcond=!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC]&&
- X !flags[ELSE_DO]&&!flags[ERROR_DO];
- X startchar=themail;tobesent=thebody-themail;
- X if(flags[BODY_GREP]) /* what needs to be egrepped? */
- X if(flags[HEAD_GREP])
- X tobesent=filled;
- X else
- X { startchar=thebody;tobesent=filled-tobesent;
- X goto noconcat;
- X }
- X if(!skiprc)
- X concon(' ');
- Xnoconcat: i=flags[ERROR_DO]?prevcond&&!succeed:
- X flags[ELSE_DO]?!prevcond:
- X flags[ALSO_N_IF_SUCC]?lastcond&&succeed:
- X flags[ALSO_NEXT_RECIPE]?lastcond:1; /* init test value */
- X if(skiprc)
- X i=0;
- X while(skipspace(),nrcond--,testb('*')||nrcond>=0)
- X { skipspace();getlline(buf2); /* any conditions (left) */
- X for(chp=strchr(buf2,'\0');--chp>=buf2;)
- X { switch(*chp) /* strip off whitespace at the end */
- X { case ' ':case '\t':*chp='\0';
- X continue;
- X }
- X break;
- X }
- X if(i) /* check out all conditions */
- X { int negate,scoreany;double weight,xponent,lscore;
- X negate=scoreany=0;lscore=score;
- X for(chp=buf2+1;;strcpy(buf2,buf))
- Xcopydone: { switch(*(sgetcp=buf2))
- X { case '0':case '1':case '2':case '3':case '4':
- X case '5':case '6':case '7':case '8':case '9':
- X case '-':case '+':case '.':case ',':
- X { char*chp3;double w;
- X w=stod(buf2,(const char**)&chp3);chp2=chp3;
- X if(chp2>buf2&&*(chp2=skpspace(chp2))=='^')
- X { double x;
- X x=stod(chp2+1,(const char**)&chp3);
- X if(chp3>chp2+1)
- X { if(score>=MAX32)
- X goto skiptrue;
- X chp2=skpspace(chp3);xponent=x;weight=w;
- X scored=scoreany=1;
- X goto copyrest;
- X }
- X }
- X }
- X default:chp--; /* no special character, backup */
- X case '\\':
- X { int or_nocase; /* case-distinction override */
- X static const struct {const char*regkey,*regsubst;}
- X *regsp,regs[]=
- X { {FROMDkey,FROMDsubstitute},
- X {TOkey,TOsubstitute},
- X {FROMMkey,FROMMsubstitute},
- X {0,0}
- X };
- X squeeze(chp);or_nocase=0;
- X goto jinregs;
- X do /* find special keyword in regexp */
- X if((chp2=strstr(chp,regsp->regkey))&&
- X (chp2==buf2||chp2[-1]!='\\')) /* escaped? */
- X { size_t lregs,lregk; /* no, so */
- X lregk=strlen(regsp->regkey); /* insert it */
- X tmemmove(
- X chp2+(lregs=strlen(regsp->regsubst)),
- X chp2+lregk,strlen(chp2)-lregk+1);
- X tmemmove(chp2,regsp->regsubst,lregs);
- X if(regsp==regs) /* daemon regexp? */
- X or_nocase=1; /* no case sensitivity! */
- Xjinregs: regsp=regs; /* start over and look again */
- X }
- X else
- X regsp++; /* next keyword */
- X while(regsp->regkey);
- X ;{ int igncase;
- X igncase=or_nocase||!flags[DISTINGUISH_CASE];
- X if(scoreany)
- X { struct eps*re;long rest;
- X re=bregcomp(chp,igncase);
- X chp=startchar;rest=tobesent;
- X if(negate)
- X { if(weight&&!bregexec(re,
- X (const uchar*)chp,(const uchar*)chp,
- X (size_t)rest,igncase))
- X score+=weight;
- X }
- X else
- X { double oweight=weight*weight;
- X while(weight!=0&&
- X MIN32<score&&
- X score<MAX32&&
- X (chp2=
- X bregexec(re,(const uchar*)startchar,
- X (const uchar*)chp,(size_t)rest,
- X igncase)))
- X { score+=weight;weight*=xponent;
- X if(chp>=chp2) /* break off empty */
- X { if(0<xponent&&xponent<1)
- X score+=weight/(1-xponent);
- X else if(xponent>=1&&weight!=0)
- X score+=weight<0?MIN32:MAX32;
- X break; /* matches early */
- X }
- X ;{ double nweight;
- X if((nweight=weight*weight)<oweight
- X &&oweight<1)
- X break;
- X oweight=nweight;
- X }
- X rest-=chp2-chp;chp=chp2;
- X }
- X }
- X free(re);
- X }
- X else /* egrep for it */
- X i=!!egrepin(chp,startchar,tobesent,
- X !igncase)^negate;
- X }
- X break;
- X }
- X case '$':*buf2='"';squeeze(chp);
- X readparse(buf,sgetc,2);strcpy(buf2,skpspace(buf));
- X goto copydone;
- X case '!':negate^=1;chp2=skpspace(chp);
- Xcopyrest: strcpy(buf,chp2);
- X continue;
- X case '?':pwait=2;metaparse(chp);inittmout(buf);
- X ignwerr=1;pipin(buf,startchar,tobesent);
- X if(scoreany&&lexitcode>=0)
- X { int j=lexitcode;
- X if(negate)
- X while(--j>=0&&
- X (score+=weight)<MAX32&&
- X score>MIN32)
- X weight*=xponent;
- X else
- X score+=j?xponent:weight;
- X }
- X else if(!!lexitcode^negate)
- X i=0;
- X strcpy(buf2,buf);
- X break;
- X case '>':case '<':readparse(buf,sgetc,2);
- X { long pivot;
- X ;{ char*chp3;
- X pivot=strtol(buf+1,&chp3,10);chp=chp3;
- X }
- X skipped(skpspace(chp));strcpy(buf2,buf);
- X if(scoreany)
- X { double f;
- X if((*buf=='<')^negate)
- X if(filled)
- X f=(double)pivot/filled;
- X else if(pivot>0)
- X goto plusinfty;
- X else
- X goto mininfty;
- X else if(pivot)
- X f=(double)filled/pivot;
- X else
- X goto plusinfty;
- X score+=weight*tpow(f,xponent);
- X }
- X else if(!((*buf=='<'?
- X filled<pivot:
- X filled>pivot)^
- X negate))
- X i=0;
- X }
- X }
- X break;
- X }
- X if(score>MAX32) /* chop off at plus infinity */
- Xplusinfty: score=MAX32;
- X if(score<=MIN32) /* chop off at minus infinity */
- Xmininfty: score=MIN32,i=0;
- X if(verbose) /* not entirely correct, but it will do */
- X { if(scoreany)
- X { charNUM(num,long);
- X nlog("Score: ");ltstr(7,(long)(score-lscore),num);
- X elog(num);elog(" ");
- X ;{ long iscore=score;
- X ltstr(7,iscore,num);
- X if(!iscore&&score>0)
- X num[7-2]='+'; /* show +0 for (0,1) */
- X }
- X elog(num);
- X }
- X else
- X nlog(i?"M":"No m"),elog("atch on");
- X if(negate)
- X elog(" !");
- X logqnl(buf2);
- X }
- Xskiptrue:; }
- X }
- X if(!(lastscore=score)&&score>0) /* save it for $= */
- X lastscore=1; /* round up +0 to 1 */
- X if(scored&&i&&score<=0)
- X i=0; /* it was not a success */
- X }
- X if(!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC])
- X lastcond=i; /* save the outcome for posterity */
- X if(!prevcond||!flags[ELSE_DO])
- X prevcond=i; /* same here, for `else if' like constructs */
- X startchar=themail;tobesent=filled;
- X if(flags[PASS_HEAD]) /* body, header or both? */
- X { if(!flags[PASS_BODY])
- X tobesent=thebody-themail;
- X }
- X else if(flags[PASS_BODY])
- X tobesent-=(startchar=thebody)-themail;
- X chp=strchr(strcpy(buf,sendmail),'\0');succeed=sh=0;
- X pwait=flags[WAIT_EXIT]|flags[WAIT_EXIT_QUIET]<<1;
- X ignwerr=flags[IGNORE_WRITERR];Stdout=0;skipspace();
- X if(i)
- X zombiecollect(),concon('\n');
- Xprogrm: if(testb('!')) /* forward the mail */
- X { if(!i)
- X skiprc++;
- X readparse(chp+1,getb,0);
- X if(i)
- X { if(startchar==themail)
- X { startchar[filled]='\0'; /* just in case */
- X if(eqFrom_(startchar)) /* leave off any leading From_ */
- X do
- X while(i= *startchar++,--tobesent&&i!='\n');
- X while(*startchar=='>'&&eqFrom_(startchar+1));
- X } /* it confuses some mailers */
- X goto forward;
- X }
- X skiprc--;
- X }
- X else if(testb('|')) /* pipe the mail */
- X { getlline(buf2); /* get the command to start */
- X if(i)
- X { metaparse(buf2);
- X if(!sh&&buf+1==Tmnate) /* just a pipe symbol? */
- X { *buf='|';*(char*)(Tmnate++)='\0';
- X goto tostdout;
- X } /* fake it */
- Xforward: if(locknext)
- X { if(!tolock) /* an explicit lockfile specified already */
- X { *buf2='\0'; /* find the implicit lockfile ('>>name') */
- X for(chp=buf;i= *chp++;)
- X if(i=='>'&&*chp=='>')
- X { chp=skpspace(chp+1);
- X tmemmove(buf2,chp,i=strcspn(chp,EOFName));
- X buf2[i]='\0';
- X if(sh) /* expand any environment variables */
- X { chp=tstrdup(buf);sgetcp=buf2;
- X readparse(buf,sgetc,0);strcpy(buf2,buf);
- X strcpy(buf,chp);free(chp);
- X }
- X break;
- X }
- X if(!*buf2)
- X { nlog("Couldn't determine implicit lockfile from");
- X logqnl(buf);
- X }
- X }
- X lcllock();
- X if(!pwait) /* try and protect the user from his */
- X pwait=2; /* blissful ignorance :-) */
- X }
- X inittmout(buf);asgnlastf=1;
- X if(flags[FILTER])
- X { if(startchar==themail&&tobesent!=filled) /* if only 'h' */
- X { if(!pipthrough(buf,startchar,tobesent))
- X readmail(1,tobesent),succeed=!pipw;
- X }
- X else if(!pipthrough(buf,startchar,tobesent))
- X filled=startchar-themail,readmail(0,0L),succeed=!pipw;
- X }
- X else if(Stdout) /* capturing stdout again? */
- X { if(!pipthrough(buf,startchar,tobesent))
- X succeed=1,postStdout(); /* only parse if no errors */
- X }
- X else if(!pipin(buf,startchar,tobesent)&& /* regular program */
- X (succeed=1,!flags[CONTINUE]))
- X goto frmailed;
- X }
- X }
- X else if(testb(EOF))
- X nlog("Incomplete recipe\n");
- X else /* dump the mail into a mailbox file or directory */
- X { int ofiltflag;
- X if(ofiltflag=flags[FILTER])
- X flags[FILTER]=0,nlog(extrns),elog("filter-flag"),elog(ignrd);
- X if(chp=gobenv(buf)) /* can it be an environment name? */
- X { if(skipspace())
- X chp++; /* keep pace with argument breaks */
- X if(testb('=')) /* is it really an assignment? */
- X { int c;
- X *chp++='=';*chp='\0';
- X if(skipspace())
- X chp++;
- X ungetb(c=getb());
- X switch(c)
- X { case '!':case '|': /* ok, it's a pipe */
- X if(i)
- X primeStdout();
- X goto progrm;
- X }
- X }
- X } /* find the end, start of a nesting recipe? */
- X else if((chp=strchr(buf,'\0'))==buf&&
- X testb('{')&&
- X (*chp++='{',*chp='\0',testb(' ')||
- X testb('\t')||
- X testb('\n')))
- X { if(locknext)
- X nlog(extrns),elog("locallockfile"),elog(ignrd);
- X app_val(&ifstack,(off_t)prevcond); /* push prevcond */
- X app_val(&ifstack,(off_t)lastcond); /* push lastcond */
- X if(!i) /* no match? */
- X skiprc++; /* increase the skipping level */
- X else
- X { if(locknext)
- X { lcllock();
- X if(!pwait) /* try and protect the user from his */
- X pwait=2; /* blissful ignorance :-) */
- X }
- X inittmout(procmailn);
- X if(flags[CONTINUE])
- X { yell("Forking",procmailn);onguard();
- X if(!(pidchild=sfork())) /* clone yourself */
- X { if(loclock) /* lockfiles are not inherited */
- X free(loclock),loclock=0;
- X if(globlock)
- X free(globlock),globlock=0; /* clear up the */
- X newid();offguard();duprcs(); /* identity crisis */
- X }
- X else
- X { offguard();
- X if(forkerr(pidchild,procmailn))
- X succeed=0; /* Tsk, tsk, no cloning today */
- X else
- X { int excode;
- X succeed=1; /* wait for our significant other? */
- X if(pwait&&(excode=waitfor(pidchild))!=EX_OK)
- X { if(!(pwait&2)||verbose) /* do we report it? */
- X progerr(procmailn,excode);
- X succeed=0;
- X }
- X pidchild=0;skiprc++; /* skip over the braces */
- X }
- X }
- X }
- X }
- X continue;
- X }
- X if(!i) /* no match? */
- X skiprc++; /* temporarily disable subprograms */
- X readparse(chp,getb,0);
- X if(i)
- X { if(ofiltflag) /* protect those who use bogus filter-flags */
- X startchar=themail,tobesent=filled; /* save whole message */
- Xtostdout: strcpy(buf2,buf);
- X if(locknext)
- X lcllock(); /* write to a file or directory */
- X inittmout(buf); /* to break messed-up kernel locks */
- X if(dump(deliver(buf,strchr(buf,'\0')+1),startchar,tobesent)
- X &&!ignwerr)
- X writeerr(buf);
- X else if(succeed=1,!flags[CONTINUE])
- Xfrmailed: { if(ifstack.offs)
- X free(ifstack.offs);
- X goto mailed;
- X }
- X }
- X else
- X skiprc--; /* reenable subprograms */
- X }
- X }
- X else if(testb('}')) /* end block */
- X { if(ifstack.filled) /* restore lastcond from stack */
- X { lastcond=ifstack.offs[--ifstack.filled];
- X prevcond=ifstack.offs[--ifstack.filled]; /* prevcond as well */
- X }
- X else
- X nlog("Closing brace unexpected\n"); /* stack empty */
- X if(skiprc) /* just skipping */
- X skiprc--; /* decrease level */
- X }
- X else /* then it must be an assignment */
- X { if(!(chp=gobenv(buf)))
- X { if(!*buf) /* skip a word first */
- X getbl(buf); /* then a line */
- X skipped(buf); /* display leftovers */
- X continue;
- X }
- X skipspace();
- X if(testb('=')) /* removal or assignment? */
- X *chp='=',readparse(++chp,getb,1);
- X else
- X *++chp='\0'; /* throw in a second terminator */
- X if(!skiprc)
- X chp2=(char*)sputenv(buf),chp[-1]='\0',asenv(chp2);
- X }
- X } /* main interpreter loop */
- X while(rc<0||!testb(EOF)||poprc()||wipetcrc());
- Xnomore_rc:
- X if(ifstack.offs)
- X free(ifstack.offs);
- X }
- X ;{ int succeed;
- X concon('\n');succeed=0;
- X if(*(chp=(char*)fdefault)) /* DEFAULT set? */
- X { setuid(uid);firstchd();
- X if(strcmp(chp,devnull)&&strcmp(chp,"|")) /* neither /dev/null nor | */
- X { cat(chp,lockext);
- X if(!globlock||strcmp(buf,globlock)) /* already locked? */
- X lockit(buf,&loclock); /* implicit lock */
- X }
- X if(dump(deliver(chp,(char*)0),themail,filled)) /* default */
- X writeerr(buf);
- X else
- X succeed=1;
- X }
- X if(!succeed&&*(chp=(char*)tgetenv(orgmail))) /* if all else failed */
- X if(dump(deliver(chp,(char*)0),themail,filled)) /* don't panic */
- X writeerr(buf); /* try the last resort */
- X else
- X succeed=1;
- X if(succeed) /* should we panic now? */
- Xmailed: retval=EX_OK; /* we're home free, mail delivered */
- X }
- X unlock(&loclock);Terminate();
- X}
- X
- XeqFrom_(a)const char*const a;
- X{ return !strncmp(a,From_,STRLEN(From_));
- X}
- END_OF_FILE
- if test 38724 -ne `wc -c <'procmail-3.03/src/procmail.c'`; then
- echo shar: \"'procmail-3.03/src/procmail.c'\" unpacked with wrong size!
- fi
- # end of 'procmail-3.03/src/procmail.c'
- fi
- echo shar: End of archive 2 \(of 10\).
- cp /dev/null ark2isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 10 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
-