home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-02-02 | 38.6 KB | 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...
-