home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
misc
/
volume28
/
procmail
/
part03
< prev
next >
Wrap
Text File
|
1992-02-02
|
40KB
|
1,178 lines
Newsgroups: comp.sources.misc
From: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
Subject: v28i003: procmail - mail processing program v2.61, Part03/05
Message-ID: <1992Feb2.030752.24162@sparky.imd.sterling.com>
X-Md4-Signature: 54e82e612217da10cf0e59137f438823
Date: Sun, 2 Feb 1992 03:07:52 GMT
Approved: kent@sparky.imd.sterling.com
Submitted-by: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg)
Posting-number: Volume 28, Issue 3
Archive-name: procmail/part03
Environment: UNIX, sendmail, smail, MMDF
Supersedes: procmail: Volume 25, Issue 01-04
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 3 (of 5)."
# Contents: procmail/examples/advanced procmail/goodies.c
# procmail/nonint.c procmail/regexp.c
# Wrapped by berg@tabaqui on Fri Jan 31 14:16:34 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'procmail/examples/advanced' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/examples/advanced'\"
else
echo shar: Extracting \"'procmail/examples/advanced'\" \(7701 characters\)
sed "s/^X//" >'procmail/examples/advanced' <<'END_OF_FILE'
XDiscusses:
X 1. Crossmounted mailboxes
X 2. Procmail as an integrated local mail delivery agent
X 2a.Special directions for sites with smail
X 3. Security considerations (when installing procmail suid root)
X 4. How to generate autoreplies
X 5. Some exorbitant examples of rcfile formats
X 6. Some advanced examples of the use of the 'A' flag
X
X ---
X
X1. Crossmounted mailboxes
X ----------------------
X
XFor users that have crossmounted mailboxes (i.e. you can access the very
Xsame mail from a whole bunch of different workstations), but on machines
Xwith differing architectures (i.e. you need different executables), and they
Xhave to explicitly use (i.e. the system administrator did not arrange,
Xfor example, /usr/local/bin/procmail to have exactly the right contents
Xdepending on from which machine it is called) two executables of procmail,
XI have the following suggestion to use as a .forward file (examples are for
Xsparc and sun3 architectures):
X
X"|IFS=' ';if /usr/bin/sparc;then exec /home/berg/bin.sun4/procmail;else exec /home/berg/bin.sun3/procmail;fi #YOUR_LOGIN_NAME"
X
Xor alternatively:
X
X"|IFS=' ';exec /home/berg/bin.`/usr/bin/arch`/procmail #YOUR_LOGIN_NAME"
X
XPlease note, in the .forward file there can NOT be any newlines between
Xthe doublequotes, i.e. the former example *has* to be typed in as one long
Xline.
X
X ---
X
X2. Procmail as an integrated local mail delivery agent
X ---------------------------------------------------
X
XCompletely integrating procmail in the mail delivery means that mail is
Xdelivered as normal, unless a .procmailrc file is present in the home
Xdirectory of the recipient. This will be completely independent of the
Xfact if a .forward file is present. This will not break anything, it
Xjust makes the use of procmail easier because people are not required to
Xstart up procmail from within their .forward files. Creation of a .procmailrc
Xfile will suffice.
X
XIn order to do this, the following line should take the place of the standard
XMlocal rule in your sendmail.cf (this way sendmail will start up procmail with
Xroot priv, procmail will immediately setuid itself to the recipient's uid):
X
XMlocal, P=/usr/local/bin/procmail, F=lsSDFMuhP, S=10, R=20, A=procmail -d $u
X
XIf your sendmail does not allow starting programs with root privs (the
X'S' flag), you can instead make procmail suid root. This will not create
Xa security hole, procmail will normally setuid immediately to the real
Xuid (effectively losing root privs), or will immediately setuid to the
Xrecipient's uid (and be completely loyal to the recipient's absent or present
X.procmailrc file). Actually installing procmail suid root is a slightly more
Xflexible approach (not at all more dangerous).
X
XIf using the suid root version of procmail, you only need to insert the
Xfollowing: line in your sendmail.cf:
X
XMlocal, P=/usr/local/bin/procmail, F=lsDFMuhP, S=10, R=20, A=procmail -d $u
X
XSo, to summarise, if you install procmail not-suid-root you should use the
Xfirst rule (with the 'S' flag), and if you install it suid-root you should
Xuse the second rule (without the 'S' flag). If you install procmail
Xnot-suid-root you cannot use the second rule, since procmail will not be
Xable to change uid to the recipient, and therefore it cannot read/write
Xthe recipient's files (including any .procmailrc). The alternative would
Xbe that procmail already has the recipient's uid upon startup, this is not
Xpossible in sendmail without changing some configuration options.
X
XIn addition to needing root priviliges upon startup, on some systems procmail
Xneeds to be sgid to daemon or mail. One way to check is by looking at the
Xcurrent mail delivery agent (usually /bin/mail) and to mimic its permissions,
Xowner and group.
X
X ---
X
X2a.Special directions for sites with smail
X ---------------------------------------
X
XReplace any existing "local"-entry in the /usr/lib/smail/transports file
X(create one, if need be) with the following two lines:
X
Xlocal: return_path, local, from, driver=pipe;
X cmd="/usr/local/bin/procmail -d $($user$)"
X
XFor any ideas on suid/sgid modes which *might* be needed, see the previous
Xparagraph (2).
X
X ---
X
X3. Security considerations (when installing procmail suid root)
X -------------------------------------------------------------
X
XIf in EXPLICIT DELIVERY mode (typically when called from within sendmail)
Xprocmail will ALWAYS change UID and gid to the RECIPIENT's defaults as soon as
Xit starts reading the recipient's $HOME/.procmailrc file.
X
XIf NOT in explicit delivery mode (typically when called from within the
Xrecipient's $HOME/.forward file) procmail will ALWAYS change UID and gid to
Xthe real uid and gid of the INVOKER (effectively losing any suid or sgid
Xpriviliges).
X
XThese two precautions should effectively eliminate any security holes because
Xprocmail will always have the uid of the person whose commands it is executing.
X
XTo summarise, procmail will only behave better if made suid/sgid something,
Xit should never make things worse.
X
X ---
X
X4. How to generate autoreplies
X ---------------------------
X
XUsing a recipe like the following, you can generate autoreplies to mail
Xreceived by you:
X
X: 2 h c
X!^From +(postmaster|Mailer)
X!^From +your_own_login_name
X| formail -r | (cat; echo "Mail received.") | $SENDMAIL -t
X
XAs you can see, I made sure that neither bouncing mail (from postmaster or the
Xmailer-daemon), nor mail coming from yourself will be autoreplied. If this
Xprecaution would not be taken, disaster could result ("ringing" mail).
XThe abovementioned recipe should be inserted before all other recipes in
Xyour rcfile, however, it is advisable to put it *after* any recipes that
Xprocess mailinglist subscriptions; it generally is not a good idea to
Xgenerate autoreplies to mailinglists.
X
X ---
X
X5. Some exorbitant examples of rcfile formats
X ------------------------------------------
X
X# Now follows an example of what you can do in a procmailrc file
XHELLO=oneword
XHELLO="two words"
XHELLO='two words' HELLO = one\
Xword
XHELLO=two\ words
XHELLO=two\ `echo words`
XHELLO= # empty
XHELLO # This will wipe "HELLO" from the environment
XHELLO = "three words"\ yes
XHELLO = "$HELLO `cat somefile` " # Trailing blanks
XHELLO = "wheeee`date`${HELLO} this works too" HELLO = 'But so does this!'
X
X# As you can see, every trick in the book of /bin/sh programming can be used
X# (and more).
X
XLOCALLOCKFILE = llf
X
X ::$LOCALLOCKFILE
Xgrep for this
X |$HELLO # calls up a program named "But" with 3 arguments
X
X:: "test ing" # lockfilename with a space in it
Xgrep for this
X |$HELLO
X
X:
Xor for this
X|"$HELLO" # tries to call up a program named "But so does this!"
X
X:
Xand this
X|$HELLO \
Xthere # action lines can be continued
X
X ---
X
X6. Some advanced examples of the use of the 'A' flag
X -------------------------------------------------
X
X:c # Specify the 'c' otherwise we never arrive at the next recipe
X^From Myfriend
Xevery_message_from_my_friend # Mailbox for everything he/she writes
X
X:Ac # Note the 'c' again
X! my_other_friend # Forward everything Myfriend writes to my_other_friend
X
X:1Ac
X^Subject:.*jokes
X! my_third_friend # Forward everything Myfriend writes about jokes
X # to my_third_friend
X
X:2A
X^Subject:.*parties
X!beach
X! my_third_friend # Forward everything Myfriend writes about parties,
X # except beach parties, to my_third_friend
X
X:A # Provide a mail sink, in order to fake procmail into
X/dev/null # believing that the mail was absorbed/delivered,
X # even if the mail was about beach parties :-).
X # This is not the best solution though, better would be to
X # rearrange these last five recipes so that the current
X # number one or two is last, the current number five can be
X # omitted then.
X
X ---
END_OF_FILE
if test 7701 -ne `wc -c <'procmail/examples/advanced'`; then
echo shar: \"'procmail/examples/advanced'\" unpacked with wrong size!
fi
# end of 'procmail/examples/advanced'
fi
if test -f 'procmail/goodies.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/goodies.c'\"
else
echo shar: Extracting \"'procmail/goodies.c'\" \(9098 characters\)
sed "s/^X//" >'procmail/goodies.c' <<'END_OF_FILE'
X/************************************************************************
X * Collection of library-worthy routines *
X * *
X * Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands *
X * The sources can be freely copied for non-commercial use. *
X * #include "README" *
X * *
X ************************************************************************/
X#ifdef RCS
Xstatic char rcsid[]="$Id: goodies.c,v 2.13 1992/01/31 11:32:45 berg Rel $";
X#endif
X#include "config.h"
X#include "procmail.h"
X#include "shell.h"
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/* 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 single spaces
X */
Xreadparse(p,fgetc,sarg)register char*p;int(*const fgetc)();const int sarg;
X{ static i;int got;char*startb;
X for(got=NOTHING_YET;;) /* buf2 is used as scratch space */
X {
Xloop:
X i=fgetc();
X if(buf+linebuf-3<p) /* doesn't catch everything, just a hint */
X { log("Exceeded LINEBUF\n");p=buf+linebuf-3;goto ready;
X }
Xnewchar:
X switch(i)
X { case EOF:
X if(got>NORMAL_TEXT)
Xearly_eof: log(unexpeof);
Xready: if(got!=SKIPPING_SPACE||sarg) /* not terminated yet or sarg==2 ? */
X *p++='\0';
X *p=TMNATE;return;
X case '\\':
X if(got==SINGLE_QUOTED)
X break;
X switch(i=fgetc())
X { case EOF:goto early_eof; /* can't quote EOF */
X case '\n':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 '`':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 switch(i=fgetc())
X { case EOF:log(unexpeof);goto forcebquote;
X case '\n':continue;
X case '"':
X if(got!=DOUBLE_QUOTED)
X break;
X case '\\':case '$':case '`':goto escaped;
X }
X *p++='\\';break;
X case '"':
X if(got!=DOUBLE_QUOTED) /* missing closing backquote? */
X break;
Xforcebquote: case EOF:case '`':
X { int osh=sh;
X *p='\0';
X if(!(sh=!!strpbrk(startb,tgetenv(shellmetas))))
X { const char*save=sgetcp;
X sgetcp=p=tstrdup(startb);readparse(startb,sgetc,0);
X free(p);sgetcp=save; /* chopped up */
X } /* drop source buffer, read from program */
X startb=fromprog(p=startb,startb);sh=osh; /* restore sh */
X if(!sarg&&got!=DOUBLE_QUOTED)
X { i=0;startb=p;goto simplsplit; /* split it up */
X }
X if(i=='"'||got<=SKIPPING_SPACE) /* missing closing ` ? */
X got=NORMAL_TEXT; /* or sarg!=0 ? */
X p=startb;goto loop;
X }
X case '\n':i=';'; /* newlines separate commands */
X }
Xescaped: *p++=i;
X }
X case '"':
X switch(got)
X { case DOUBLE_QUOTED:got=NORMAL_TEXT;continue; /* closing " */
X case SINGLE_QUOTED:goto nodelim;
X }
X got=DOUBLE_QUOTED;continue; /* opening " */
X case '\'':
X switch(got)
X { case DOUBLE_QUOTED:goto nodelim;
X case SINGLE_QUOTED:got=NORMAL_TEXT;continue;} /* closing ' */
X got=SINGLE_QUOTED;continue; /* opening ' */
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 if(EOF==(i=fgetc()))
X { *p++='$';goto ready;
X }
X startb=buf2;
X if(i=='{') /* ${name} */
X { while(EOF!=(i=fgetc())&&alphanum(i))
X *startb++=i;
X *startb='\0';
X if(i!='}')
X { log("Bad substitution of");logqnl(buf2);continue;
X }
X i='\0';
X }
X else if(alphanum(i)) /* $name */
X { do *startb++=i;
X while(EOF!=(i=fgetc())&&alphanum(i));
X if(i==EOF)
X i='\0';
X *startb='\0';
X }
X else if(i=='$') /* $$ =pid */
X { ultstr(0,(unsigned long)thepid,p);goto ieofstr;
X }
X else if(i=='-') /* $- =lastfolder */
X { strcpy(p,lastfolder);
Xieofstr: i='\0';goto eofstr;
X }
X else
X { *p++='$';goto newchar; /* not a substitution */
X }
X startb=(char*)tgetenv(buf2);
X if(!sarg&&got!=DOUBLE_QUOTED)
Xsimplsplit: 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;continue;
X case '\0':goto eeofstr;
X }
X *p++= *startb;got=NORMAL_TEXT;
X }
X else
X { 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: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
Xultstr(minwidth,val,dest)unsigned long val;char*dest;
X{ int i;unsigned long j;
X j=val;i=0; /* a beauty, isn't it :-) */
X do i++; /* determine needed width */
X while(j/=10);
X while(--minwidth>=i) /* fill up any excess width */
X *dest++=' ';
X *(dest+=i)='\0';
X do *--dest='0'+val%10; /* display value backwards */
X while(val/=10);
X}
X
Xsputenv(a)char*a; /* smart putenv, the way it was supposed to be */
X{ static struct lienv{struct lienv*next;char name[255];}*myenv;
X static alloced;int i,remove;char*split,**preenv;struct lienv*curr,**last;
X yell("Assigning",a);remove=0;a=tstrdup(a); /* make working copy */
X if(!(split=strchr(a,'='))) /* assignment or removal? */
X { remove=1;i=strlen(a);*(split=i+(a=realloc(a,i+2)))='=';
X split[1]='\0';
X }
X i= ++split-a;
X for(curr= *(last= &myenv);curr;curr= *(last= &curr->next))
X if(!strncmp(a,curr->name,i)) /* is it one I created earlier? */
X { split=curr->name;*last=curr->next;free(curr);
X for(preenv=environ;*preenv!=split;preenv++);
X goto wipenv;
X }
X for(preenv=environ;*preenv;preenv++)
X if(!strncmp(a,*preenv,i)) /* is it in the standard environment? */
X {
Xwipenv: 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 }
X if(!remove) /* if not remove, then add it to both environments */
X { for(preenv=environ;*preenv;preenv++);
X curr=malloc(ioffsetof(struct lienv,name)+strlen(a)+1);
X strcpy(*preenv=curr->name,a);free(a);preenv[1]=0;curr->next=myenv;
X myenv=curr;
X }
X}
X /* strtol replacement which lacks range checking */
X#ifdef NOstrtol
Xlong strtol(start,ptr,base)const char*start,**const ptr;
X{ long result;const char*str=start;unsigned i;int sign,found;
X if(base>=36||base<(sign=found=result=0))
X goto fault;
X for(;;str++) /* skip leading whitespace */
X { switch(*str)
X { case '\t':case '\n':case '\v':case '\f':case '\r':case ' ':continue;
X }
X break;
X }
X switch(*str) /* any signs? */
X { case '-':sign=1;
X case '+':str++;
X }
X if(*str=='0') /* leading zero(s)? */
X { start++;
X if((i= *++str)=='x'||i=='X') /* leading 0x or 0X? */
X if(!base||base==16)
X { base=16;str++; /* hexadecimal all right */
X }
X else
X goto fault;
X else if(!base)
X base=8; /* then it is octal */
X }
X else if(!base)
X base=10; /* or else decimal */
X goto jumpin;
X do
X { found=1;result=result*base+i;++str; /* start converting */
Xjumpin:
X if((i= *str-'0')<10);
X else if(i-'A'+'0'<26)
X i-='A'-10-'0';
X else if(i-'a'+'0'<26)
X i-='a'-10-'0';
X else
X break; /* not of this world */
X }
X while(i<base); /* still of this world */
Xfault:
X if(ptr)
X *ptr=found?str:start; /* how far did we get */
X return sign?-result:result;
X}
X#endif
END_OF_FILE
if test 9098 -ne `wc -c <'procmail/goodies.c'`; then
echo shar: \"'procmail/goodies.c'\" unpacked with wrong size!
fi
# end of 'procmail/goodies.c'
fi
if test -f 'procmail/nonint.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/nonint.c'\"
else
echo shar: Extracting \"'procmail/nonint.c'\" \(7358 characters\)
sed "s/^X//" >'procmail/nonint.c' <<'END_OF_FILE'
X/************************************************************************
X * Collection of routines that don't return int *
X * *
X * Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands *
X * The sources can be freely copied for non-commercial use. *
X * #include "README" *
X * *
X ************************************************************************/
X#ifdef RCS
Xstatic char rcsid[]="$Id: nonint.c,v 2.16 1992/01/31 12:35:17 berg Rel $";
X#endif
X#include "config.h"
X#include "procmail.h"
X
X#define nomemretry noresretry
X#define noforkretry noresretry
X
Xvoid*tmalloc(len)const size_t len; /* this malloc can survive a temporary */
X{ void*p;int i; /* "out of swap space" condition */
X if(p=malloc(len))
X return p;
X if(p=malloc(1))
X free(p); /* works on some systems with latent free */
X for(lcking=2,i=nomemretry;i<0||i--;)
X { suspend(); /* problems? don't panic, wait a few secs till */
X if(p=malloc(len)) /* some other process has paniced (and died 8-) */
X { lcking=0;return p;
X }
X }
X nomemerr();
X}
X
Xvoid*trealloc(old,len)void*const old;const size_t len;
X{ void*p;int i;
X if(p=realloc(old,len))
X return p; /* for comment see tmalloc above */
X if(p=malloc(1))
X free(p);
X for(lcking=2,i=nomemretry;i<0||i--;)
X { suspend();
X if(p=realloc(old,len))
X { lcking=0;return p;
X }
X }
X nomemerr();
X}
X /* line buffered to keep concurrent entries untangled */
Xlog(new)const char*const new;
X{ int lnew,i;static lold;static char*old;char*p;
X if(lnew=strlen(new)) /* anything? */
X {
X#ifndef O_CREAT
X lseek(STDERR,0L,SEEK_END); /* locking should be done actually */
X#endif
X if(nextexit)
X goto direct; /* careful, in terminate code */
X i=lold+lnew;
X if(p=lold?realloc(old,i):malloc(i)) /* unshelled malloc */
X { memmove((old=p)+lold,new,(size_t)lnew); /* append */
X if(p[(lold=i)-1]=='\n') /* EOL? */
X { rwrite(STDERR,p,i);lold=0;free(p); /* flush the line(s) */
X }
X }
X else /* no memory, force flush */
X { if(lold)
X { rwrite(STDERR,old,i);lold=0;free(old);
X }
Xdirect: rwrite(STDERR,new,lnew);
X }
X }
X}
X
X#include "shell.h"
X
Xpid_t sfork() /* this fork can survive a temporary */
X{ pid_t i;int r; /* "process table full" condition */
X r=noforkretry;
X while((i=fork())==-1)
X { lcking=3;
X if(!(r<0||r--))
X break;
X suspend();
X }
X lcking=0;return i;
X}
X
Xvoid srequeue()
X{ retval=EX_TEMPFAIL;sterminate();
X}
X
Xvoid slose()
X{ fakedelivery=2;sterminate();
X}
X
Xvoid sbounce()
X{ retval=EX_CANTCREAT;sterminate();
X}
X
Xextern char*lastexec,*backblock; /* see retint.c for comment */
Xextern long backlen;
Xextern pid_t pidfilt,pidchild;
Xextern pbackfd[2];
X
Xvoid stermchild()
X{ if(pidfilt>0) /* don't kill what is not ours, we might be root */
X kill(pidfilt,SIGTERM);
X log("Rescue of unfiltered data ");
X if(dump(PWRB,backblock,backlen)) /* pump back the data via the backpipe */
X log("failed\n");
X else
X log("succeeded\n");
X exit(EX_UNAVAILABLE);
X}
X
Xvoid ftimeout()
X{ alarm(0);alrmtime=0;
X if(pidchild>0&&!kill(pidchild,SIGTERM)) /* careful, killing again */
X { log("Timeout, terminating");logqnl(lastexec);
X }
X signal(SIGALRM,ftimeout);
X}
X
Xlong dump(s,source,len)const int s;const char*source;long len;
X{ int i;
X if(s>=0)
X { fdlock(s);lastdump=len;mboxseparator(s); /* prepend optional separator */
X#ifndef O_CREAT
X lseek(s,0L,SEEK_END);
X#endif
X if(len&&tofile) /* if it is a file, trick NFS into an */
X { --len;rwrite(s,source++,1);sleep(1); /* a_time<m_time */
X }
X while(i=rwrite(s,source,BLKSIZ<len?BLKSIZ:(int)len))
X { if(i<0)
X { i=0;goto writefin;
X }
X len-=i;source+=i;
X }
X if(!len&&(lastdump<2||!(source[-1]=='\n'&&source[-2]=='\n')))
X lastdump++,rwrite(s,newline,1); /* message always ends with a */
X mboxseparator(s); /* newline and an optional custom separator */
Xwritefin:
X fdunlock();rclose(s);return ignwerr?(ignwerr=0):len-i;
X }
X return len?len:-1; /* return an error even if nothing was to be sent */
X}
X
Xlong pipin(line,source,len)char*const line;char*source;long len;
X{ int poutfd[2];
X rpipe(poutfd);
X if(!(pidchild=sfork())) /* spawn program */
X { rclose(PWRO);rclose(rc);getstdin(PRDO);callnewprog(line);
X }
X rclose(PRDO);
X if(forkerr(pidchild,line))
X return 1;
X if(len=dump(PWRO,source,len)) /* dump mail in the pipe */
X writeerr(line); /* pipe was shut in our face, get mad */
X if(pwait&&waitfor(pidchild)!=EX_OK) /* optionally check the exitcode */
X { progerr(line);len=1;
X }
X pidchild=0;
X if(!sh)
X concatenate(line);
X lastfolder=cstr(lastfolder,line);return len;
X}
X
Xchar*readdyn(bf,filled)char*bf;long*const filled;
X{ int i;long oldsize;
X oldsize= *filled;goto jumpin;
X do
X { *filled+=i; /* change listed buffer size */
Xjumpin:
X#ifdef SMALLHEAP
X if((size_t)*filled>=(size_t)(*filled+BLKSIZ))
X lcking=2,nomemerr();
X#endif
X bf=realloc(bf,*filled+BLKSIZ); /* dynamically adjust the buffer size */
Xjumpback:;
X }
X while(0<(i=rread(STDIN,bf+*filled,BLKSIZ))); /* read mail */
X if(pidchild>0)
X { pidchild=0;getstdin(PRDB); /* filter ready, get backpipe */
X if(1==rread(STDIN,buf,1)) /* backup pipe closed? */
X { bf=realloc(bf,(*filled=oldsize+1)+BLKSIZ);bf[oldsize]= *buf;
X goto jumpback; /* filter goofed, rescue data */
X }
X }
X pidchild=0; /* child must be gone by now */
X if(!*filled)
X return realloc(bf,1); /* +1 for housekeeping purposes */
X return realloc(bf,*filled+1); /* minimise the buffer space */
X}
X
Xchar*fromprog(name,dest)char*const name;char*dest;
X{ int pinfd[2];long nls;
X rpipe(pinfd);inittmout(name);
X if(!(pidchild=sfork())) /* spawn program */
X { rclose(STDIN);opena(devnull);rclose(PRDI);rclose(rc);rclose(STDOUT);
X rdup(PWRI);rclose(PWRI);callnewprog(name);
X }
X rclose(PWRI);nls=0;
X if(!forkerr(pidchild,name))
X { while(0<rread(PRDI,dest,1)) /* read its lips */
X if(*dest=='\n') /* careful with newlines */
X nls++; /* trailing newlines should be discarded */
X else
X { if(nls)
X for(dest[nls]= *dest;*dest++='\n',--nls;); /* fill them in */
X dest++;
X }
X waitfor(pidchild);
X }
X pidchild=0;rclose(PRDI);*dest='\0';return dest;
X}
X
Xchar*cat(a,b)const char*const a,*const b;
X{ return strcat(strcpy(buf,a),b);
X}
X
Xchar*tstrdup(a)const char*const a;
X{ int i;
X i=strlen(a)+1;return tmemmove(malloc(i),a,i);
X}
X
Xconst char*tgetenv(a)const char*const a;
X{ const char*b;
X return(b=getenv(a))?b:"";
X}
X
Xchar*cstr(a,b)const char*const a,*const b; /* dynamic buffer management */
X{ if(a)
X free(a);
X return tstrdup(b);
X}
X
Xlong renvint(i,env)const long i;const char*const env;
X{ const char*p;long t;
X t=strtol(env,&p,10);return p==env?i:t; /* parse it like a decimal nr */
X}
X
Xchar*egrepin(expr,source,len,casesens)const char*expr,*source;
X const long len;
X{ source=bregexec(expr=bregcomp(expr,!casesens),
X source,len>0?(size_t)len:(size_t)0,!casesens);
X free(expr);return(char*)source;
X}
X
Xchar*lastdirsep(filename)const char*filename; /* finds the next character */
X{ const char*p; /* following the last DIRSEP */
X while(p=strpbrk(filename,dirsep))
X filename=p+1;
X return(char*)filename;
X}
END_OF_FILE
if test 7358 -ne `wc -c <'procmail/nonint.c'`; then
echo shar: \"'procmail/nonint.c'\" unpacked with wrong size!
fi
# end of 'procmail/nonint.c'
fi
if test -f 'procmail/regexp.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'procmail/regexp.c'\"
else
echo shar: Extracting \"'procmail/regexp.c'\" \(10740 characters\)
sed "s/^X//" >'procmail/regexp.c' <<'END_OF_FILE'
X/************************************************************************
X * Custom regular expression library, *fully* egrep compatible *
X * *
X * Seems to be relatively bug free. *
X * *
X * Copyright (c) 1991-1992, S.R. van den Berg, The Netherlands *
X * The sources can be freely copied for non-commercial use. *
X * #include "README" *
X * *
X ************************************************************************/
X#ifdef RCS
Xstatic char rcsid[]="$Id: regexp.c,v 2.12 1992/01/21 17:27:04 berg Rel $";
X#endif
X#include "config.h"
X#include "procmail.h"
X#include "shell.h"
X
X#define R_BEG_GROUP '('
X#define R_OR '|'
X#define R_END_GROUP ')'
X#define R_0_OR_MORE '*'
X#define R_0_OR_1 '?'
X#define R_1_OR_MORE '+'
X#define R_DOT '.'
X#define R_SOL '^'
X#define R_EOL '$'
X#define R_BEG_CLASS '['
X#define R_NOT_CLASS '^'
X#define R_RANGE '-'
X#define R_END_CLASS ']'
X#define R_ESCAPE '\\'
X
X#define BITS_P_CHAR 8
X#define OPB (1<<BITS_P_CHAR)
X#define OPC_EPS OPB
X#define OPC_CLASS (OPB+1)
X#define OPC_DOT (OPB+2)
X#define OPC_FIN (OPB+3)
X
X#define bit_type unsigned
X#define bit_bits (sizeof(bit_type)*8)
X#define bit_index(which) ((unsigned)(which)/bit_bits)
X#define bit_mask(which) ((unsigned)1<<(unsigned)(which)%bit_bits)
X#define bit_toggle(name,which) (name[bit_index(which)]^=bit_mask(which))
X#define bit_test(name,which) (!!(name[bit_index(which)]&bit_mask(which)))
X#define bit_set(name,which,value) \
X (value?(name[bit_index(which)]|=bit_mask(which)):\
X (name[bit_index(which)]&=~bit_mask(which)))
X#define bit_field(name,size) bit_type name[((size)+bit_bits-1)/bit_bits]
X
X#define SZ(x) (sizeof(struct x))
X#define Ceps (struct eps*)
X#define epso(to,add) (Ceps((char*)(to)+(add)))
X#define ii (aleps.topc)
X#define jj (aleps.ua.jjua)
X#define jjp (aleps.ua.tnext)
X
X/* the spawn and stack members are reused in the normal opcodes as pc fields */
Xstatic struct eps{unsigned opc;struct eps*stack,*spawn,*next;}*r;
Xstatic struct{unsigned topc;union{struct eps*tnext;int jjua;}ua;}aleps;
Xstatic uchar*p;
Xstatic ignore_case;
X
Xstruct chclass {unsigned opc_;struct eps*stack_,*spawn_,*next_;
X bit_field(c,OPB);};
X
Xstatic puteps(spot,to,aswell)struct eps*const spot; /* epsilon transition */
X const struct eps*const to,*const aswell;
X{ spot->opc=OPC_EPS;spot->next=to!=spot?Ceps to:Ceps aswell;
X spot->spawn=aswell!=spot?Ceps aswell:Ceps to;spot->stack=0;
X}
X
Xstatic putneps(spot,to)struct eps*const spot;const struct eps*const to;
X{ puteps(spot,to,spot+1);
X}
X
X#define rAc (((struct chclass*)r)->c)
X
Xstatic bseti(i,j)unsigned i;const int j;
X{ bit_set(rAc,i,j); /* mark 'i' as being in the class */
X if(ignore_case) /* mark the other case too */
X { if(i-'A'<26) /* uppercase */
X i+='a'-'A';
X else if(i-'a'<26) /* lowercase */
X i-='a'-'A';
X else return; /* no case */
X bit_set(rAc,i,j);
X }
X}
X
Xstatic por();
X
Xstatic psimp(e)struct eps const*const e;
X{ switch(*p)
X { case R_BEG_GROUP:++p;por(e);return; /* not so simple after all */
X case R_BEG_CLASS: /* a simple class */
X jj=R_NOT_CLASS==*++p;
X if(e)
X { r->opc=OPC_CLASS;r->next=Ceps e;r->spawn=r->stack=0;
X ii=maxindex(rAc);
X do rAc[ii]=jj?~0:0; /* preset the bit field */
X while(ii--);
X }
X if(jj) /* skip the 'not' modifier */
X { ++p;
X if(e)
X bit_toggle(rAc,'\n');
X }
X if(*p==R_END_CLASS) /* right at the start, cannot mean the end */
X { ++p;
X if(e)
X { ii=R_END_CLASS;bit_toggle(rAc,R_END_CLASS);
X }
X }
X else if(*p==R_RANGE) /* take it literally */
X { ++p;
X if(e)
X { ii=R_RANGE;bit_toggle(rAc,R_RANGE);
X }
X }
X for(;;++p)
X { switch(*p)
X { case R_END_CLASS:++p;
X case '\0':r=epso(r,SZ(chclass));return;
X case R_RANGE:
X switch(*++p)
X { default:
X if(e)
X while(++ii<*p) /* mark all in the range */
X bseti(ii,!jj);
X break;
X case '\0':case R_END_CLASS:--p; /* literally */
X }
X }
X if(e)
X bseti(ii= *p,!jj); /* a normal character, mark it */
X }
X case '\0':return;
X case R_DOT: /* matches everything but a newline */
X if(e)
X { r->opc=OPC_DOT;goto fine;
X }
X goto fine2;
X case R_EOL:case R_SOL: /* match a newline (in effect) */
X if(e)
X { r->opc='\n';goto fine;
X }
X goto fine2;
X case R_ESCAPE: /* quote something */
X if(!*++p) /* nothing to quote */
X --p;
X }
X if(e) /* a regular character */
X { r->opc=ignore_case&&(unsigned)*p-'A'<26?*p+'a'-'A':*p;
Xfine:
X r->next=Ceps e;r->spawn=r->stack=0;
X }
Xfine2:
X ++p;++r;
X}
X
X#define EOS(x) (jjp?jjp:(x))
X
Xstatic pnorm(e)struct eps const*const e;
X{ void*pold;struct eps*rold;
X for(;;)
X { pold=p;rold=r;psimp(Ceps 0);ii= *p; /* skip it first */
X jjp=p[1]==R_OR||p[1]==R_END_GROUP||!p[1]?Ceps e:Ceps 0;
X if(e)
X { p=pold;pold=r;
X }
X switch(ii) /* check for any of the postfix operators */
X { case R_0_OR_MORE:++r;
X if(e) /* first an epsilon, then the rest */
X { putneps(rold,EOS(r));r=rold+1;psimp(rold);
X }
X goto incagoon;
X case R_1_OR_MORE: /* first the rest */
X if(e) /* and then an epsilon */
X { puteps(r,rold,EOS(r+1));r=rold;psimp(Ceps pold);
X }
X ++r;goto incagoon;
X case R_0_OR_1:++r;
X if(e) /* first an epsilon, then the rest */
X { putneps(rold,r=EOS(r));pold=r;r=rold+1;psimp(Ceps pold);
X }
Xincagoon: switch(*++p) /* at the end of this group already? */
X { case R_OR:case R_END_GROUP:case '\0':return;
X }
X continue; /* regular end of the group */
X case R_OR:case R_END_GROUP:case '\0':
X if(e)
X { r=rold;psimp(e);
X }
X return;
X }
X if(e) /* no fancy postfix operators, plain vanilla */
X { r=rold;psimp(Ceps pold);
X }
X }
X}
X
Xstatic por(e)struct eps const*const e;
X{ uchar*pold;struct eps*rold;
X for(;;)
X for(pold=p;;)
X { rold=r;
X switch(*p)
X { default:pnorm(Ceps 0);r=rold;continue; /* still in this group */
X case '\0':case R_END_GROUP: /* found the end of the group */
X if(p==pold) /* empty 'or' group */
X { if(e)
X puteps(r,e,e); /* misused epsilon as branch, */
X ++r; /* let the optimiser (fillout()) take it out */
X }
X else
X { p=pold;pnorm(e); /* normal last group */
X }
X if(*p)
X ++p;
X return;
X case R_OR:++r;
X if(p==pold) /* empty 'or' group */
X { if(e)
X putneps(rold,e); /* special epsilon */
X }
X else
X { p=pold;pnorm(e); /* normal 'or' group, first an */
X if(e) /* epsilon, then the rest */
X putneps(rold,r);
X }
X ++p;
X }
X break;
X }
X}
X
Xstatic findandrep(old,new)register struct eps**const old;
X struct eps*const new;
X{ register struct eps*i,*t= *old; /* save the value */
X for(i=r;;) /* change all pointers from *old to new */
X { if(i->next==t)
X i->next=new;
X if(i->spawn==t)
X i->spawn=new;
X switch(i->opc)
X { case OPC_FIN:*old=t;return; /* last one, restore value, ready */
X case OPC_CLASS:i=epso(i,SZ(chclass));break;
X default:++i;
X }
X }
X}
X
X#define drs(m) (*(struct eps**)((char*)*stack+(ioffsetof(struct eps,m)^ofs)))
X
Xstatic cstack(stack,ofs)struct eps**const stack;
X{ if(drs(next)->stack==Ceps p)
X { findandrep(*stack,drs(next));*stack=drs(spawn);return 1;
X }
X return 0;
X}
X /* break up any closed epsilon circles, otherwise they can't be executed */
Xstatic fillout(stack)struct eps**const stack;
X{ if((*stack)->opc!=OPC_EPS||(*stack)->stack)
X return 0;
X (*stack)->stack=Ceps p; /* mark this one as used */
X#define RECURS(nxt) \
X do\
X if(cstack(stack,ioffsetof(struct eps,nxt)^ioffsetof(struct eps,next)))\
X return 1;\
X while(fillout(&(*stack)->nxt))
X RECURS(next);RECURS(spawn);return 0; /* recurse */
X}
X
Xvoid*bregcomp(a,ign_case)char const*a;
X{ struct eps*st;size_t i; /* first a trial run, determine memory needed */
X p=(uchar*)a;ignore_case=ign_case;r=Ceps&aleps+1;por(Ceps 0);
X st=malloc((i=(char*)r-(char*)&aleps)+ioffsetof(struct eps,stack)+sizeof r);
X putneps(st,r=st+1);p=(uchar*)a;por(Ceps((char*)st+i)); /* really compile */
X r->opc=OPC_FIN;r->stack=0; /* tack on the end */
X for(r=st;;) /* simplify the compiled code (i.e. */
X switch(st->opc) /* take out cyclic epsilon references) */
X { case OPC_FIN:return r; /* finished */
X case OPC_CLASS:st=epso(st,SZ(chclass));break; /* skip */
X case OPC_EPS:p=(uchar*)st;fillout(&st); /* check tree */
X default:++st; /* skip too */
X }
X}
X
X#define XOR1 \
X (ioffsetof(struct eps,spawn)^ioffsetof(struct eps,stack))
X#define PC(this,t) (*(struct eps**)((char*)(this)+(t)))
X
Xchar*bregexec(code,text,len,ign_case)struct eps*code;const uchar*text;
X size_t len;
X{ register struct eps*reg,*t,*stack,*other,*this;unsigned i,th1,ot1;
X if(code[1].opc==OPC_EPS)
X ++code; /* two epsilons at the start would be superfluous */
X (this=code)->stack=0;th1=ioffsetof(struct eps,spawn);
X ot1=ioffsetof(struct eps,stack);--text;++len;
X i='\n';goto setups; /* make sure any beginning-of-line-hooks catch */
X do
X { i= *++text; /* get the next real-text character */
Xlastrun: /* switch this & other pc-stack */
X th1^=XOR1;ot1^=XOR1;this=other;
Xsetups:
X reg=(other=stack=code)->next;goto nostack;
X do /* pop next entry off this pc-stack */
X { reg=(t=this)->next;this=PC(t,th1);PC(t,th1)=0;goto nostack;
X do /* pop next entry off the work-stack */
X { stack=(t=stack)->stack;t->stack=0;reg=t->spawn;
Xnostack: switch(reg->opc-OPB) /* push spawned branch on the work-stack */
X { default:
X if(ign_case&&i-'A'<26)
X i+='a'-'A'; /* transmogrify it to lowercase */
X if(i==reg->opc) /* regular character match */
X goto yep;
X break;
X case OPC_EPS-OPB:reg->stack=stack;reg=(stack=reg)->next;
X goto nostack;
X case OPC_FIN-OPB: /* hurray! complete regexp match */
X return(char*)text; /* return one past the match */
X case OPC_CLASS-OPB:
X if(bit_test(((struct chclass*)reg)->c,i))
X goto yep; /* character in class */
X break;
X case OPC_DOT-OPB: /* dot-wildcard */
X if(i!='\n')
Xyep: if(!PC(reg,ot1)) /* state not yet pushed */
X { PC(reg,ot1)=other;other=reg; /* push location onto */
X } /* other pc-stack */
X }
X }
X while(stack); /* the work-stack is not empty */
X }
X while(this!=code); /* this pc-stack is not empty */
X }
X while(--len); /* still text to search */
X if(ign_case!=2) /* out of text */
X { ign_case=2;len=1;++text;goto lastrun; /* check if we just matched */
X }
X return 0; /* no match */
X}
END_OF_FILE
if test 10740 -ne `wc -c <'procmail/regexp.c'`; then
echo shar: \"'procmail/regexp.c'\" unpacked with wrong size!
fi
# end of 'procmail/regexp.c'
fi
echo shar: End of archive 3 \(of 5\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 5 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Sincerely, berg@messua.informatik.rwth-aachen.de
Stephen R. van den Berg (AKA BuGless). berg@physik.tu-muenchen.de
He did a quarter of the work in *half* the time!
exit 0 # Just in case...