Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!hobbes.physics.uiowa.edu!ns-mx!uunet!sparky!kent From: berg@messua.informatik.rwth-aachen.de (Stephen R. van den Berg) Newsgroups: comp.sources.misc Subject: v20i050: procmail - mail processing program v2.02, Part02/03 Message-ID: <1991Jun17.043123.1899@sparky.IMD.Sterling.COM> Date: 17 Jun 91 04:31:23 GMT References: Sender: kent@sparky.IMD.Sterling.COM (Kent Landfield) Organization: Sterling Software, IMD Lines: 1138 Approved: kent@sparky.imd.sterling.com X-Md4-Signature: 5542db79dfa88d913086900e268b3f17 Submitted-by: Stephen R. van den Berg Posting-number: Volume 20, Issue 50 Archive-name: procmail/part02 Environment: UNIX, sendmail Supersedes: procmail: Volume 17, Issue 31-32 ---- Cut Here and feed the following to sh ---- #!/bin/sh # This is part 02 of procmail # ============= procmail/README ============== if test ! -d 'procmail'; then echo 'x - creating directory procmail' mkdir 'procmail' fi if test -f 'procmail/README' -a X"$1" != X"-c"; then echo 'x - skipping procmail/README (File already exists)' else echo 'x - extracting procmail/README (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/README' && XSome legal stuff: X XUse this software package at your own risk. The programmer can not Xbe held liable for any incurred damages due to the use of this software. X XYou are encouraged to distribute this package freely. This package is Xhowever not to be sold (minor transfer costs excepted) or included in Xany commercially sold software package (if you want to do this anyway, Xcontact me (address below), and we'll work something out). X XIf you distribute it, please leave the package in tact. If you have some Ximportant changes that might be usefull to the rest of the world, contact Xme instead. X X-------------------------- SYSTEM REQUIREMENTS ------------------------------- X XA ".forward" file (or equivalent) mechanism for forwarding mail, *grep or Xequivalent. X XThe most important system calls that need to be supported (among others): Xfork(),read(),write(),dup(),wait(),pipe(),getpwent() X XFor a more complete list of all library references see "includes.h" X X------------------------------ DESCRIPTION ----------------------------------- X XThe procmail mail processing program. (v2.02 1991/06/12) X XCan be used to create mail-servers, mailing lists, sort your incoming mail Xinto separate folders/files (real convenient when subscribing to one or more Xmailing lists), preprocess your mail, or selectively forward certain incoming Xmail automatically to someone. X XFor installation instructions see the INSTALL file. X X---------------------- X XAlthough I can't guarantee that the procmail program will perform as Xrequired, I must say that I made the utmost effort to make procmail as Xrobust as any program can be (every conceivable system error is caught *and* Xhandled). X Xprocmail was designed to deliver the mail under the worst conditions X(file system full, out of swap space, process table full, file table full, Xmissing support files, unavailable executables; it all doesn't matter). XShould (in the unlikely event) procmail be unable to deliver your mail Xsomewhere, the mail will bounce back to the sender. X XFor a more extensive list of features see the FEATURES file. X XHowever, as with any program, bugs can not be completely ruled out. XI tested the program extensively, and believe it should be relatively Xbug free (no known bug at the time). Should, however, anyone find any Xbugs (highly unlikely :-), I would be pleased (well, sort of :-) to hear Xabout it. Please send me the patches or bug report. XI'll look at them and will try to fix it in a future release. X(BTW, if you should find any spelling or grammar errors in these files, Xit's not priority one, but if you were sending me mail anyway, don't hesitate Xto point them out to me; I like correct English just as much as you do). X XPlease note that this program essentially is supposed to be static, that Xmeans no extra features (honouring the *NIX spirit) are supposed to be Xadded (though any usefull suggestions will be appreciated and evaluated if Xtime permits). X XCheers, X Stephen R. van den Berg at RWTH-Aachen, Germany. X XInternet E-mail: berg@messua.informaik.rwth-aachen.de X berg@physik.tu-muenchen.de X XOr: P.O.Box 21074 X 6369 ZG Simpelveld X The Netherlands X X---------------------- X XP.S. I don't mind if you feed the program files through your favourite C X beautifier first, so any patches need not necessarily be from the X original sources; I apply these patches by hand anyway. SHAR_EOF chmod 0644 procmail/README || echo 'restore of procmail/README failed' Wc_c="`wc -c < 'procmail/README'`" test 3358 -eq "$Wc_c" || echo 'procmail/README: original size 3358, current size' "$Wc_c" fi # ============= procmail/STYLE ============== if test -f 'procmail/STYLE' -a X"$1" != X"-c"; then echo 'x - skipping procmail/STYLE (File already exists)' else echo 'x - extracting procmail/STYLE (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/STYLE' && XDon't complain about the formatting of my C code. I know it's unconventional, Xbut it's my standard format. If you don't like it, feed it through your Xfavourite C beautifier. X XYes, I do have my reasons for formatting it this way: X1. consistent, functional indenting X2. I got used to it X3. the C compiler doesn't mind at all X4. I get more program text on my screen when editing, hence I don't have to X scroll as often to see other parts of the program while working, (you won't X believe this, I know) and therefore it helps to get a better overview of X the program X5. I hate it when I have to scroll back the screen those 5 lines just to see X the top of the loop (and then back down to look at the end again, etc.) X XAnd, now don't start flaming me about "bad practice", I don't consider this Xformatting method bad practice (it's properly indented). X XI consider "bad practice" to be unportable or non-ANSI code, which my code is Xcertainly not. SHAR_EOF chmod 0644 procmail/STYLE || echo 'restore of procmail/STYLE failed' Wc_c="`wc -c < 'procmail/STYLE'`" test 951 -eq "$Wc_c" || echo 'procmail/STYLE: original size 951, current size' "$Wc_c" fi # ============= procmail/common.c ============== if test -f 'procmail/common.c' -a X"$1" != X"-c"; then echo 'x - skipping procmail/common.c (File already exists)' else echo 'x - extracting procmail/common.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/common.c' && X/************************************************************************ X * A some common routines for procmail and formail * X * * X * Copyright (c) 1990-1991, S.R.van den Berg, The Netherlands * X * The sources can be freely copied for non-commercial use. * X * #include "README" * X * * X * #include "STYLE" * X * * X ************************************************************************/ X#ifdef RCS Xstatic char rcsid[]="$Id: common.c,v 2.0 1991/06/10 14:35:35 berg Rel $"; X#endif X#include "includes.h" X Xvoid*tmalloc(); Xextern const char binsh[]; X X#ifdef NOmemmove Xvoid*memmove(To,From,count)void*To,*From;register size_t count;{ X#ifdef NObcopy X register char*to=To,*from=From;/*void*old;*/ /* silly compromise, throw */ X /*old=to;*/count++;--to;--from; /* away space to be syntactically correct */ X if(to<=from){ X goto jiasc; X do{ X *++to= *++from; /* copy from above */ Xjiasc:;} X while(--count);} X else{ X to+=count;from+=count; X goto jidesc; X do{ X *--to= *--from; /* copy from below */ Xjidesc:;} X while(--count);} X return To/*old*/;} X#else X bcopy(From,To,count);return To;} X#endif X#endif X X#include "shell.h" X Xshexec(argv)const char *const*argv;{int i;const char**newargv,**p; X execvp(*argv,argv); /* if this one fails, we retry it as a shell script */ X for(p=(const char**)argv,i=1;i++,*p++;); /* count the arguments */ X newargv=malloc(i*sizeof*p); X for(*(p=newargv)=binsh;*++p= *++argv;); X execve(*newargv,newargv,environ); /* no shell script? -> trouble */ X log("Failed to execute");logqnl(*argv);exit(EX_UNAVAILABLE);} SHAR_EOF chmod 0644 procmail/common.c || echo 'restore of procmail/common.c failed' Wc_c="`wc -c < 'procmail/common.c'`" test 1611 -eq "$Wc_c" || echo 'procmail/common.c: original size 1611, current size' "$Wc_c" fi # ============= procmail/exopen.c ============== if test -f 'procmail/exopen.c' -a X"$1" != X"-c"; then echo 'x - skipping procmail/exopen.c (File already exists)' else echo 'x - extracting procmail/exopen.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/exopen.c' && X/************************************************************************ X * Collection of NFS secure exclusive open routines * X * * X * Copyright (c) 1990-1991, S.R.van den Berg, The Netherlands * X * The sources can be freely copied for non-commercial use. * X * #include "README" * X * * X * #include "STYLE" * X * * X ************************************************************************/ X#ifdef RCS Xstatic char rcsid[]="$Id: exopen.c,v 2.0 1991/06/10 14:35:35 berg Rel $"; X#endif X#include "config.h" X#include "includes.h" X#include "exopen.h" X Xconst char*hostname(); Xextern pid_t thepid; X Xconst char*hostname(){static char name[HOSTNAMElen+1]; X#ifdef NOuname X gethostname(name,HOSTNAMElen+1); X#else X struct utsname names; X uname(&names);strncpy(name,names.nodename,HOSTNAMElen); X#endif X name[HOSTNAMElen]='\0';return name;} X Xultoan(val,dest)unsigned long val;char*dest;{register i; /* convert to */ X do{ /* a number within the set [0-9A-Za-z-_] */ X i=val&0x3f; X *dest++=i+(i<10?'0':i<10+26?'A'-10:i<10+26+26?'a'-10-26: X i==10+26+26?'-'-10-26-26:'_'-10-26-27);} X while(val>>=6); X *dest='\0';} X Xunique(full,p,mode)const char*const full;char*const p;const mode_t mode;{ X unsigned long retry=3;int i; /* create unique file name */ X do{ X ultoan(SERIALmask&(retry<<16)+(unsigned long)thepid,p+1); X *p='_';strcat(p,hostname());} X while(0>(i=ropen(full,O_WRONLY|O_CREAT|O_EXCL|O_SYNC,mode))&&errno==EEXIST X &&retry--); /* casually check if it already exists (highly unlikely) */ X if(i<0){ X writeerr(full);return 0;} X rclose(i);return 1;} X /* rename MUST fail if already existent */ Xmyrename(old,new)const char*const old,*const new;{int i,serrno; X i=link(old,new);serrno=errno;unlink(old);errno=serrno;return i;} SHAR_EOF chmod 0644 procmail/exopen.c || echo 'restore of procmail/exopen.c failed' Wc_c="`wc -c < 'procmail/exopen.c'`" test 1788 -eq "$Wc_c" || echo 'procmail/exopen.c: original size 1788, current size' "$Wc_c" fi # ============= procmail/Manifest ============== if test -f 'procmail/Manifest' -a X"$1" != X"-c"; then echo 'x - skipping procmail/Manifest (File already exists)' else echo 'x - extracting procmail/Manifest (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/Manifest' && XMakefile We all know what that is. XREADME Important, read it. XINSTALL A description of what has to be done to install procmail. XHISTORY Recent and ancient changes (or bugs) documented. XFEATURES A summary of all the things procmail is particularly good at. XSTYLE A short explanation of why the source looks so "awkward" Xlockfile.c main program for lockfile Xformail.c main program for formail XManifest You guessed it. Xprocmail.c main program for procmail. Xnonint.c Collection of routines that don't return ints. Xretint.c Collection of routines that return ints. Xgoodies.c Some real nice routines, deserve to be put in a library. Xcommon.c Some routines that are used by procmail and formail. Xexopen.c Collection of routines about an NFS secure excl. open. Xexopen.h The very same. Xprocmail.h Include file with all declarations. X Xincludes.h System include files are all referenced here. Xconfig.h The file to edit if you want to change, yes, the configuration. Xautoconf The shell script that seizes your compiler and machine, X and then creates a file called autoconf.h describing the X kludges that are going to be applied for your installation. X Xshell.h Defines a few 'shell' macros for malloc and the like. Xman/* Yes, the man pages (made in a labour camp). Xinclude/* A few files that are supposed to fool your compiler into X thinking that it has ANSI and POSIX conforming include files. Xexamples/?procmailrc X Sample .procmailrc files. Xexamples/?rmail X Sample shell scripts that demonstrate how to use X lockfiles while reading the mail (to ensure mail integrity X as soon as you exit the mail program). Xexamples/forward X A sample .forward file. Xexamples/advanced X Some extra info for network mounted mailboxes, examples of X advanced .procmailrc expressions and using procmail as X a local delivery agent. SHAR_EOF chmod 0644 procmail/Manifest || echo 'restore of procmail/Manifest failed' Wc_c="`wc -c < 'procmail/Manifest'`" test 1814 -eq "$Wc_c" || echo 'procmail/Manifest: original size 1814, current size' "$Wc_c" fi # ============= procmail/lockfile.c ============== if test -f 'procmail/lockfile.c' -a X"$1" != X"-c"; then echo 'x - skipping procmail/lockfile.c (File already exists)' else echo 'x - extracting procmail/lockfile.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/lockfile.c' && X/************************************************************************ X * lockfile.c a conditional semaphore-file creator * X * * X * Is relatively bug free. * X * * X * Created by S.R.van den Berg, The Netherlands * X * This file can be freely copied for any use. * X ************************************************************************/ X#ifdef RCS Xstatic char rcsid[]="$Id: lockfile.c,v 2.1 1991/06/11 14:00:41 berg Rel $"; X#endif Xstatic char rcsdate[]="$Date: 1991/06/11 14:00:41 $"; X#include "config.h" /* overkill, I know, only need DIRSEP */ X#include "includes.h" X Xvolatile int exitflag; Xpid_t thepid; Xconst char dirsep[]=DIRSEP; X Xvoid failure(){ X exitflag=1;} X Xmain(argc,argv)const int argc;const char*argv[];{const char**p,*cp; X int sleepsec,retries,i,invert,force,suspend,retval=0; X static char usage[]= X "Usage: lockfile -nnn | -rnnn | -! | -lnnn | -snnn | file ...\n"; X sleepsec=8;force=retries=invert=0;suspend=16;thepid=getpid(); X if(argc<2){ X putse(usage);return EX_USAGE;} Xagain: X p=argv+1;signal(SIGHUP,failure);signal(SIGINT,failure); X signal(SIGQUIT,failure);signal(SIGTERM,failure); X while(*p) X if(*(cp= *p++)=='-') X switch(cp[1]){ X case '!':invert=1;break; X case 'r':retries=strtol(cp+2,(char**)0,10);break; X case 'l':force=strtol(cp+2,(char**)0,10);break; X case 's':suspend=strtol(cp+2,(char**)0,10);break; X default: X if(cp[1]-'0'>(unsigned)9){ X putse(usage);retval=EX_USAGE;goto failure;} X if(sleepsec>=0) X sleepsec=strtol(cp+1,(char**)0,10);} X else X if(sleepsec<0) X unlink(cp); X else{ X while(0>NFSxopen(cp)){struct stat buf;time_t t; X if(exitflag||retries==1){ Xfailure: sleepsec= -1;p[-1]=0;goto again;} X if(force&&(t=time((time_t*)0),!stat(cp,&buf))&& X force 'procmail/goodies.c' && X/************************************************************************ X * Collection of library-worthy routines * X * * X * Copyright (c) 1990-1991, S.R.van den Berg, The Netherlands * X * The sources can be freely copied for non-commercial use. * X * #include "README" * X * * X * #include "STYLE" * X * * X ************************************************************************/ X#ifdef RCS Xstatic char rcsid[]="$Id: goodies.c,v 2.1 1991/06/11 12:59:16 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 Xreadparse(p,fgetc,sarg)register char*p;int(*const fgetc)(); X const int sarg;{static i;int got;char*startb; X for(got=NOTHING_YET;;){ /* buf2 is used as scratch space */ Xloop: X i=fgetc(); X if(buf+linebuf-3NORMAL_TEXT) Xearly_eof: log(unexpeof); Xready: if(got!=SKIPPING_SPACE||sarg) /* not terminated yet or sarg==2 ? */ Xready0: *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 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 *p++='\\';break; X case '"': X if(got!=DOUBLE_QUOTED) /* missing closing backquote? */ X break; Xforcebquote: case EOF:case '`':*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, drop source buffer */ X startb=fromprog(p=startb,startb); /* read from program */ X if(got!=DOUBLE_QUOTED){ X i=0;startb=p;goto simplsplit;} /* split it up */ X if(i=='"') /* was there a missing closing ` ? */ X got=NORMAL_TEXT; /* yes, terminate " */ X p=startb;goto loop; X case '\n':i=';';} /* newlines separate commands */ Xescaped: *p++=i;} X case '"': X switch(got){ X case DOUBLE_QUOTED:got=NORMAL_TEXT;continue; /* closing " */ X case SINGLE_QUOTED:goto nodelim;} 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 startb=buf2; X if(i=='{'){ /* ${name} */ X while(EOF!=(i=fgetc())&&alphanum(i)) X *startb++=i; X *startb='\0'; X if(i!='}'){ Xbadsubst: log("Bad substitution of");logqnl(buf2);continue;} X i='\0';} 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 else if(i=='$'){ /* $$=pid */ X ultostr(0,(unsigned long)thepid,p);i='\0';goto eofstr;} X else{ X *p++='$';goto newchar;} /* not a substitution */ X startb=(char*)tgetenv(buf2); X if(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 *p++= *startb;got=NORMAL_TEXT;} X else{ X strcpy(p,startb); /* simply copy it */ Xeofstr: p=strchr(p,'\0');} 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 case '\n': X if(got<=NORMAL_TEXT) X goto ready;} /* EOL means we're ready */ Xnodelim: X *p++=i; /* ah, a normal character */ X if(got<=SKIPPING_SPACE) /* should we bother to change mode? */ X got=NORMAL_TEXT;}} X Xultostr(minwidth,val,dest)unsigned long val;char*dest;{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 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 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 for(preenv=environ;*preenv;preenv++) X if(!strncmp(a,*preenv,i)){ /* is it in the standard environment? */ Xwipenv: X while(*preenv=preenv[1]) /* wipe this entry out of the environment */ X preenv++; X break;} 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 curr=malloc(curr->name-(char*)curr+strlen(a)+1); X strcpy(*preenv=curr->name,a);free(a);preenv[1]=0;curr->next=myenv; X myenv=curr;}} SHAR_EOF chmod 0644 procmail/goodies.c || echo 'restore of procmail/goodies.c failed' Wc_c="`wc -c < 'procmail/goodies.c'`" test 7091 -eq "$Wc_c" || echo 'procmail/goodies.c: original size 7091, current size' "$Wc_c" fi # ============= procmail/procmail.c ============== if test -f 'procmail/procmail.c' -a X"$1" != X"-c"; then echo 'x - skipping procmail/procmail.c (File already exists)' else echo 'x - extracting procmail/procmail.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/procmail.c' && X/************************************************************************ X * procmail.c an autonomous mail processor * X * * X * Seems to be relatively bug free. * X * * X * Copyright (c) 1990-1991, S.R.van den Berg, The Netherlands * X * The sources can be freely copied for non-commercial use. * X * #include "README" * X * * X * #include "STYLE" * X * * X ************************************************************************/ X#ifdef RCS Xstatic char rcsid[]="$Id: procmail.c,v 2.3 1991/06/12 10:23:06 berg Rel $"; X#endif X#include "config.h" X#define MAIN X#include "procmail.h" X#include "shell.h" X X#define VERSION "procmail v2.02 1991/06/12 written by Stephen R.van den Berg\n\ X\t\t\t\tberg@messua.informatik.rwth-aachen.de\n\ X\t\t\t\tberg@physik.tu-muenchen.de\n" X Xchar*buf,*buf2,*globlock,*loclock,*tolock,*lastfolder; Xconst char grep[]="GREP",shellflags[]="SHELLFLAGS",shell[]="SHELL", X shellmetas[]="SHELLMETAS",lockext[]="LOCKEXT",newline[]="\n",binsh[]=BinSh, X unexpeof[]="Unexpected EOL\n",*const*gargv,*sgetcp,*rcfile=PROCMAILRC, X dirsep[]=DIRSEP,msgprefix[]="MSGPREFIX",devnull[]=DevNull, X executing[]="Executing",oquote[]=" \"",cquote[]="\"\n", X whilstwfor[]=" whilst waiting for "; Xstatic const char linebufs[]="LINEBUF",tokey[]=TOkey,eumask[]="UMASK", X tosubstitute[]=TOsubstitute,lockfile[]="LOCKFILE",defaultf[]="DEFAULT", X maildir[]="MAILDIR",couldnread[]="Couldn't read",logfile[]="LOGFILE", X orgmail[]="ORGMAIL",user[]="USER",tmp[]=Tmp,home[]="HOME",sfolder[]=FOLDER, X sendmail[]="SENDMAIL",host[]="HOST"; Xstruct varval strenvvar[]={{"LOCKSLEEP",DEFlocksleep}, X {"LOCKTIMEOUT",DEFlocktimeout},{"SUSPEND",DEFsuspend}, X {"NORESRETRY",DEFnoresretry}}; Xlong lastdump; Xint retval=EX_CANTCREAT,sh,pwait,lcking,locknext,verbose,linebuf=DEFlinebuf, X rc= -1; Xvolatile int flaggerd=2,nextexit; Xpid_t thepid; X Xmain(argc,argv)const char*const argv[];{static char flags[NRRECFLAGS];int i; X char*themail,*thebody,*chp,*startchar,*chp2;long tobesent,filled; X if((chp=(char*)argv[argc=1])&&*chp=='-'&&*++chp&&!chp[1]) X switch(*chp){ /* these options are mutually exclusive */ X case VERSIONOPT:log(VERSION);return EX_OK; X case DEBUGOPT:verbose=1; X default:*environ=0; X case PRESERVOPT:argc++;} X else X *environ=0; /* drop the environment */ X gargv=argv+argc;umask(077);thepid=getpid();fclose(stdout);fclose(stderr); X rclose(STDOUT);rclose(STDERR); /* don't trust the stdio library */ X if(0>opena(devnull)||0>opena(console)) X return EX_OSFILE; X setbuf(stdin,(char*)0);buf=malloc(linebuf);buf2=malloc(linebuf);chdir(tmp); X ultostr(0,(unsigned long)(i=getuid()),buf); X setpwent(); X {struct passwd*pass; X if(pass=getpwuid(i)){ /* find user defaults in /etc/passwd */ X setdef(home,pass->pw_dir);chdir(pass->pw_dir); X setdef(user,pass->pw_name?pass->pw_name:buf);setdef(shell,pass->pw_shell);} X else{ /* user could not be found, set reasonable defaults */ X setdef(home,tmp);setdef(user,buf);setdef(shell,binsh);}} X endpwent();setdef(shellmetas,DEFshellmetas);setdef(shellflags,DEFshellflags); X setdef(maildir,DEFmaildir);setdef(defaultf,DEFdefault); X setdef(orgmail,DEForgmail);setdef(grep,DEFgrep);setdef(sendmail,DEFsendmail); X setdef(lockext,DEFlockext);setdef(msgprefix,DEFmsgprefix); X chdir(getenv(maildir));nextrcfile();thebody=themail=malloc(1);filled=0; X signal(SIGTERM,sterminate);signal(SIGINT,sterminate); X signal(SIGHUP,sterminate);signal(SIGQUIT,flagger); Xchangedmail: X themail=readdyn(themail,&filled); /* read in the mail */ Xonlyhead: X startchar=filled+(thebody=themail); X while(thebody(thebody=findnl(thebody,startchar))) X switch(*thebody++){ X case '\n':goto eofheader; /* empty line marks end of header */ X case '\t':case ' ':thebody[-2]=' ';} /* concatenate continuated lines */ Xeofheader: X if((chp=thebody)=sscanf(chp,FromSCAN,buf)){ X chp--;continue;} /* no match, back up, and on we go */ X tmemmove(chp+1,chp,startchar++-chp);*chp='>'; /* insert '>' before */ X themail=realloc(chp2=themail,++filled+1); /* bogus header */ X#define ADJUST(x) ((x)=themail+((x)-chp2)) X ADJUST(thebody);ADJUST(startchar);ADJUST(chp);}/* find next empty line */ X while(startchar>(chp=findnl(chp,startchar)));} Xdo{ /* main rcfile interpreter loop */ X while(chp=(char*)argv[argc]){ /* interpret command line specs first */ X argc++;strcpy(buf,chp); X if(chp=strchr(buf,'=')){ X strcpy(sgetcp=buf2,++chp);readparse(chp,sgetc,0);goto argenv;}} X if(rc<0) /* open new rc file */ X while(*buf='\0',0>bopen(strcat( X strchr(dirsep,*rcfile)?buf:cat(tgetenv(home),MCDIRSEP),rcfile))){ X log(couldnread);logqnl(buf); X if(!nextrcfile()) /* not available? try the next */ X goto nomore_rc;} X unlock(&loclock); /* unlock any local lockfile after every parsed line */ X do skipspace(); /* skip whitespace */ X while(testb('\n')); X if(testb(':')){ /* check for a recipe */ X readparse(buf,getb,2);i=sh=1;*buf2='\0'; X if(0>=sscanf(buf,"%d%[^\n]",&sh,buf2)) /* nr of conditions */ X strcpy(buf2,buf); X *buf= *flags='\0'; X if(0>=sscanf(buf2,RECFLAGS,flags,buf)) /* read the flags */ X strcpy(buf,buf2); X if(tolock) /* clear temporary buffer for lockfile name */ X free(tolock); X tolock=0;*buf2='\0'; X if((locknext=':'==*buf)&&0>name') */ X if(chp++,i=='>'&&*chp=='>'){ X while((i= *++chp)==' '||i=='\t'); X sscanf(chp,EOFName,buf2);break;} X lcllock(); X if(strchr(flags,'f')){ X if(startchar==themail&&tobesent!=filled){ /* if only 'h' */ X long dfilled=0; X if(pipthrough(buf,startchar,tobesent)) X continue; X chp=readdyn(malloc(1),&dfilled);filled-=tobesent; X if(tobesentopena(chp)) X if(0>opena(console)) X retval=EX_OSFILE; /* bad news, but can't tell anyone */ X else X writeerr(chp);} X else if(!strcmp(buf,lockfile)) X lockit(chp,&globlock); X else if(!strcmp(buf,eumask)){ X sscanf(chp,"%o",&i);umask(i);} X else if(!strcmp(buf,host)){ X if(strcmp(chp,chp2=(char*)hostname())){ X yell("HOST mismatched",chp2); X if(rc<0||!nextrcfile()){ /* if no rcfile opened yet */ X retval=EX_OK;terminate();} /* exit gracefully as well */ X rclose(rc);rc= -1;}} X else{ X i=MAXvarvals; X do /* several numeric assignments */ X if(!strcmp(buf,strenvvar[i].name)){ X strenvvar[i].val=renvint(strenvvar[i].val,chp);break;} X while(i--);}} Xmainloop: X ;} X while(rc<0||!testb(EOF)); /* main interpreter loop */ Xnomore_rc: X if(dump(deliver(tgetenv(defaultf)),themail,filled)){ /* default */ X writeerr(buf); /* if it fails, don't panic, try the last resort */ X if(dump(deliver(tgetenv(orgmail)),themail,filled)) X writeerr(buf);goto mailerr;} /* now you can panic */ Xmailed: X retval=EX_OK; /* we're home free, mail delivered */ Xmailerr: X unlock(&loclock); /* any local lock file still around? */ X for(;themail 'procmail/nonint.c' && X/************************************************************************ X * Collection of routines that don't return int * X * * X * Copyright (c) 1990-1991, S.R.van den Berg, The Netherlands * X * The sources can be freely copied for non-commercial use. * X * #include "README" * X * * X * #include "STYLE" * X * * X ************************************************************************/ X#ifdef RCS Xstatic char rcsid[]="$Id: nonint.c,v 2.0 1991/06/10 14:35:35 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;{void*p;int i; /* this malloc can survive */ X if(p=malloc(len)) /* a temporary "out of swap space" condition */ 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 nomemerr();} X Xvoid*trealloc(old,len)void*const old;const size_t len;{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 nomemerr();} X /* line buffered to keep concurrent entries untangled */ Xlog(new)const char*const new;{int lnew,i;static lold;static char*old;char*p; X if(lnew=strlen(new)){ /* anything? */ X if(nextexit) X goto direct; /* carefull, in terminate code */ X i=lold+lnew; X if(p=lold?realloc(old,i):malloc(i)){ 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 else{ /* no memory, force flush */ X if(lold){ X rwrite(STDERR,old,i);lold=0;free(old);} Xdirect: X rwrite(STDERR,new,lnew);}}} X X#include "shell.h" X Xpid_t sfork(){pid_t i;int r; /* this fork can survive a temporary */ X r=noforkretry; /* "process table full" condition */ X while((i=fork())==-1){ X lcking=3; X if(!(r<0||r--)) X break; X suspend();} X lcking=0;return i;} X Xextern char*backblock; /* see retint.c for comment */ Xextern long backlen; Xpid_t pidfilt,pidchild; Xextern pbackfd[2]; X Xvoid sterminate(){static const char*const msg[]={newline,0, X "memory\n","fork\n","file descriptor\n"}; X signal(SIGTERM,SIG_IGN);signal(SIGHUP,SIG_IGN);signal(SIGINT,SIG_IGN); X if(pidchild) /* don't kill what is not ours, we might be root */ X kill(pidchild,SIGTERM); X if(!nextexit){ X nextexit=1;log("Terminating prematurely"); X if(1!=lcking){ X if(1=0){ X lastdump=len; X while(i=rwrite(s,source,BLKSIZ