berg@marvin.e17.physik.tu-muenchen.de (Stephen R. van den Berg) (03/02/91)
Submitted-by: Stephen R. van den Berg <berg@marvin.e17.physik.tu-muenchen.de> Posting-number: Volume 17, Issue 32 Archive-name: procmail/part02 Supersedes: procmail: Volume 16, Issue 103 ---- Cut Here and feed the following to sh ---- #!/bin/sh # This is part 02 of procmail # ============= procmail/retint.c ============== if test ! -d 'procmail'; then echo 'x - creating directory procmail' mkdir 'procmail' fi if test -f 'procmail/retint.c' -a X"$1" != X"-c"; then echo 'x - skipping procmail/retint.c (File already exists)' else echo 'x - extracting procmail/retint.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/retint.c' && X/************************************************************************ X * Collection of routines that return an int (sort of anyway :-) * 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 * See the accompanying README file for more info. * X * * X * Don't complain about the formatting, I know it's * X * unconventional, but it's my standard format (I have my * X * reasons). If you don't like it, feed it through your * X * favourite C beautifier. * X ************************************************************************/ X X#include "config.h" X#include "procmail.h" X#include "sysexits.h" X#include "shell.h" X Xsetdef(name,contents)char*name,*contents;{ X strcat(strcat(strcpy(buf2,name),"="),contents);parse();sputenv(buf);} X Xskipblanks(){ X fscanf(rc,"%*[ \t]");} X Xskiptoeol(){ X fscanf(rc,"%*[^\n]");return fgetc(rc);} X Xskipcomment(){char i[2]; /* no comment :-) */ X if(tscrc("%1[\n]",i)) X return 1; X skipblanks();return scrc("%1[#]",i)?skiptoeol():feof(rc);} X Xpipthrough(line,source,len)char*line,*source;long len;{pid_t pidf,pid; X int pinfd[2],poutfd[2]; X#define PWR2 (pinfd[1]) X#define PRD2 (pinfd[0]) X pipe(poutfd);pipe(pinfd); X if(!(pidf=sfork())){ /* the filter is started here */ X rclose(PWRITE);redirect(PREAD);rclose(STDOUT);dup(PWR2);rclose(PWR2); X rclose(PRD2);callnewprog(line);} X rclose(PREAD);rclose(PWR2); X if(forkerr(pidf,line)){ X rclose(PRD2);return 1;} X flaggerd=0;signal(SIGHUP,flagger); X if(!(pid=sfork())){ /* this process will read back the filtered mail */ X rclose(PWRITE);signal(SIGHUP,flagger);kill(getppid(),SIGHUP);redirect(PRD2); X if(!pwait&&mother){ /* if the wait chain ends here, notify mother */ X kill(mother,SIGHUP);mother=0;} /* everything ok mam, go ahead and die */ X return 0;} /* we will go ahead however (with or without mother) */ X rclose(PRD2); X if(forkerr(pid,"procmail")){ /* An ingenious signal communication system */ X kill(pidf,SIGTERM);return 1;} /* will ensure that NO lock file and */ X if(dump(PWRITE,source,len)){ /* NO mail gets lost. */ X kill(pidf,SIGTERM);kill(pid,SIGTERM);writeerr(line);return 1;} X waitflagger();verrgrandchild=flaggerd=0;signal(SIGQUIT,errgrandchild); X if(pwait){ X if(waitfor(pidf)!=EX_OK){ /* if pipe fails, we will continue ourselves */ X progerr(line);kill(pid,SIGTERM);return 1;} X for(kill(pid,SIGHUP),loclock=0;;sleep(SUSPENSION)){ X if(flaggerd) X break; X if(verrgrandchild){ /* Check if one of my grandchildren has paniced */ X signal(SIGQUIT,sterminate);return 1;}}} X else X kill(pid,SIGHUP); /* if we don't wait, signal that we're bailing out */ X exit(EX_OK);} /* go ahead child, you have control */ X Xwaitflagger(){ X while(!flaggerd) X sleep(SUSPENSION);} X Xgrepin(expr,source,len,casesens)char*expr,*source;long len;{pid_t pid; X int poutfd[2];static char*newargv[5]={0,"-e"}; X newargv[3]=casesens?(char*)0:"-i";*newargv=getenv(grep);newargv[2]=expr; X pipe(poutfd); X if(!(pid=sfork())){ X rclose(PWRITE);redirect(PREAD);shexec(newargv);} X rclose(PREAD);dump(PWRITE,source,len); X if(!forkerr(pid,*newargv)) X return waitfor(pid)!=EX_OK; X return 127;} X Xwaitfor(pid)pid_t pid;{int i; X while(pid!=wait(&i)||(i&127)==127); X return i>>8&255;} X Xredirect(pip){ X getstdinpipe(pip);fclose(rc);} X Xgetstdinpipe(pip){ X rclose(STDIN);dup(pip);rclose(pip);} X Xcallnewprog(newname)char*newname;{int argc;char*endp,**newargv; X register char*p; X p=newname; X if(sh){char*newargv[4]; /* should we start a shell? */ X newargv[3]=0;newargv[2]=p;newargv[1]=getenv(shellflags); X *newargv=getenv(shell);shexec(newargv);} X argc=2; X while(*p){ /* If no shell, we'll have to chop up the arguments ourselves */ X if(*p==' '||*p=='\t'){ X argc++;*p='\0'; X while(*++p==' '||*p=='\t') X *p='\0'; X continue;} X p++;} X endp=p;*(newargv=malloc(argc*sizeof*newargv))=p=newname;argc=1; X for(;;){ X while(*p) X p++; X while(!*p){ X if(p==endp){ X newargv[argc]=0;shexec(newargv);} X p++;} X newargv[argc++]=p;}} X Xparse(){int i;char*org,*dest,*bd; /* Implicitly copies from buf2 to buf */ X dest=buf;org=buf2; X while(i=*org++) X if(i!='$') X *dest++=i; X else{ /* substitute the thing... */ X bd=buf2; X while((i=*org)>='A'&&i<='Z'||i>='a'&&i<='z'||i>='0'&&i<='9'||i=='_'){ X *bd++=i;org++;} X *bd='\0'; X if(bd=getenv(buf2)){ X strcpy(dest,bd); X while(*dest) X dest++;}} X *dest='\0';} X Xwriteerr(line)char*line;{ X log("Error while writing to");logqnl(line);} X Xforkerr(pid,a)pid_t pid;char*a;{ X if(pid==-1){ X log("Failed forking");logqnl(a);return 1;} X return 0;} X Xprogerr(line)char*line;{ X log("Program failure of");logqnl(line);} X Xlog(a)char*a;{char*b; X b=a-1; X while(*++b); X rwrite(STDERR,a,b-a);} X Xopena(a)char*a;{ Xreturn open(a,O_WRONLY|O_APPEND|O_CREAT,0666);} X Xfdreopena(a,fd)char*a;{ /* Currently only works for 0,1,2 */ X rclose(fd);return opena(a);} X Xshexec(argv)char**argv;{int i;char**newargv,**p; X execvp(*argv,argv); /* if this one fails, we retry it as a shell script */ X for(p=argv,i=1;i++,*p++;); 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);} X Xunlock(lockp)char**lockp;{ X lcking=1; X if(*lockp){ X if(unlink(*lockp)){ X log("Couldn't unlink");logqnl(*lockp);} X free(*lockp);*lockp=0;} X if(nextexit==1){ /* make sure we are not inside terminate already */ X log(newline);terminate();} X lcking=0;} X Xnomemerr(){ X log("Out of memory\nbuffer 0: \"");log(buf);log("\"\nbuffer 1:"); X logqnl(buf2);retval=EX_OSERR;terminate();} X Xlogqnl(a)char*a;{ X log(" \"");log(a);log("\"\n");} X Xnextrcfile(){char*p; /* find the next rcfile specified on the command line */ X while(p=*gargv){ X gargv++; X if(!strchr(p,'=')){ X rcfile=p;return 1;}} X return 0;} X Xrclose(fd){int i; /* a sysV secure close (signal immune) */ X while((i=close(fd))&&errno==EINTR); X return i;} X Xrwrite(fd,a,len)void*a;{int i; /* a sysV secure write (signal immune) */ X while(0>(i=write(fd,a,len))&&errno==EINTR); X return i;} X Xlockit(name,lockp)char*name,**lockp;{int i;struct stat stbuf; X unlock(lockp); /* unlock any previous lockfile FIRST */ X for(lcking=1;;){ /* to prevent deadlocks (I hate deadlocks) */ X if(0<=(i=open(name,O_WRONLY|O_CREAT|O_EXCL|O_SYNC,0))){ X rclose(i);*lockp=tstrdup(name); Xterm: if(nextexit){ X log(" whilst waiting for lockfile");logqnl(name);terminate();} X lcking=0;return;} X if(errno==EEXIST){ /* check if it's time for a lock override */ X if((i=renvint(0,locktimeout))&&!stat(name,&stbuf)&& X i<time((time_t*)0)-stbuf.st_mtime){ X log("Forcing lock on");logqnl(name); X if(unlink(name)){ X log("Forced unlock denied on");logqnl(name);}}} X else{ /* maybe filename too long, shorten and retry */ X if(0<(i=strlen(name)-1)&&name[i-1]!='/'){ X name[i]='\0';continue;} X log("Lockfailure on");logqnl(name);return;} X sleep(DEFlocksleep,locksleep); X if(nextexit) X goto term;}} X Xlcllock(){ /* lock a local file (if need be) */ X if(locknext) X if(tolock) X lockit(tolock,&loclock); X else X lockit(strcat(buf2,getenv(lockext)),&loclock);} 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 remove=0;a=strdup(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 if(alloced) /* have we ever alloced the environ array before? */ X environ=realloc(environ,(preenv-environ+2)*sizeof*environ); X else{ X alloced=1;i=(preenv-environ+2)*sizeof*environ; X 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;}} X Xrenvint(init,env)char*env;{int i; X i=init; X if(env=getenv(env)) /* check if the env var exists */ X sscanf(env,"%i",&i); /* parse it like a C constant */ X return i;} X Xterminate(){ X nextexit=2; /* prevent multiple invocations of terminate */ X unlock(&loclock); /* local lock files are removed in any case */ X if(mother&&mother!=getpid()) X if(retval!=EX_OK) /* tell mam we're in trouble, let her clean up the mess */ X kill(mother,SIGQUIT); /* don't try this at home, kids :-) */ X else{ X kill(mother,SIGHUP);goto allok;} /* You can go home mam, everything ok */ X else Xallok: /* global lockfile should be removed */ X unlock(&globlock); /* by the last surviving procmail */ X sync(); /* sorry, but this is mail we're dealing with, safety first */ X exit(retval);} SHAR_EOF chmod 0644 procmail/retint.c || echo 'restore of procmail/retint.c failed' Wc_c="`wc -c < 'procmail/retint.c'`" test 9549 -eq "$Wc_c" || echo 'procmail/retint.c: original size 9549, current size' "$Wc_c" fi # ============= procmail/examples/forward ============== if test ! -d 'procmail/examples'; then echo 'x - creating directory procmail/examples' mkdir 'procmail/examples' fi if test -f 'procmail/examples/forward' -a X"$1" != X"-c"; then echo 'x - skipping procmail/examples/forward (File already exists)' else echo 'x - extracting procmail/examples/forward (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/examples/forward' && X"|exec /global/bin/procmail" SHAR_EOF chmod 0644 procmail/examples/forward || echo 'restore of procmail/examples/forward failed' Wc_c="`wc -c < 'procmail/examples/forward'`" test 29 -eq "$Wc_c" || echo 'procmail/examples/forward: original size 29, current size' "$Wc_c" fi # ============= procmail/examples/1rmail ============== if test -f 'procmail/examples/1rmail' -a X"$1" != X"-c"; then echo 'x - skipping procmail/examples/1rmail (File already exists)' else echo 'x - extracting procmail/examples/1rmail (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/examples/1rmail' && X#!/bin/sh X# X# specify the mailbox file you want to read on the command line X# XMAILDIR=$HOME/Mail Xcd $MAILDIR XLOCKFILE=$HOME/.lockmail Xif lockfile -! -r1 $LOCKFILE Xthen X echo Mail is currently arriving, please wait... X while X lockfile -! -4 -r2 $LOCKFILE X do X echo Mail is still arriving... X done Xfi X# X# Call you favourite mailer here. X# X/usr/ucb/mail -f $* X# X# X# Xrm -f $LOCKFILE SHAR_EOF chmod 0644 procmail/examples/1rmail || echo 'restore of procmail/examples/1rmail failed' Wc_c="`wc -c < 'procmail/examples/1rmail'`" test 381 -eq "$Wc_c" || echo 'procmail/examples/1rmail: original size 381, current size' "$Wc_c" fi # ============= procmail/examples/2procmailrc ============== if test -f 'procmail/examples/2procmailrc' -a X"$1" != X"-c"; then echo 'x - skipping procmail/examples/2procmailrc (File already exists)' else echo 'x - extracting procmail/examples/2procmailrc (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/examples/2procmailrc' && X# Please check if all the paths in PATH are reachable, remove the ones that X# are not. X XPATH=$HOME/bin:/usr/bin:/global/bin:/usr/ucb:/bin:/usr/local/bin: XMAILDIR=$HOME/Mail # You'd better make sure it exists XDEFAULT=$MAILDIR/mbox XLOGFILE=$MAILDIR/from X # We don't use a global lockfile here now X # Instead we use local lockfiles everywhere X X# Notice the double : in the next recipe, this will cause a lockfile X# named "$MAILDIR/todd.lock" to be used if and only if this mail is going X# into the file "todd". X X:: # Anything from thf X^From.*thf\@somewhere.someplace Xtodd # will go to $MAILDIR/todd X X X# The next recipe will likewise use $MAILDIR/uunetbox.lock as a lock file. X X:: # Anything from people at uunet X^From.*\@uunet Xuunetbox # will go to $MAILDIR/uunetbox X X X# And here the lockfile will be $MAILDIR/henries.lock of course. X X:I: # Anything from Henry or henry (ignore case) X^From.*henry Xhenries # will go to $MAILDIR/henries X X X# But you can specify any lockfile you want, like "myfile". The following X# recipe will use "$MAILDIR/myfile" as the lock file. X X::myfile # All 'questions' will go to X^Subject:.*questions Xtoread # $MAILDIR/toread X X# Anything that has not been delivered by now will go to $DEFAULT X# BUT, since we have not specified any global lock file because we X# were working with local lock files, and since we are sending the mail to X# $DEFAULT now, we need to specify a lockfile for that too. We use X# a global lockfile for that (it won't be created until procmail X# has parsed (needed to parse) up till here. X XLOCKFILE=$DEFAULT.lock SHAR_EOF chmod 0644 procmail/examples/2procmailrc || echo 'restore of procmail/examples/2procmailrc failed' Wc_c="`wc -c < 'procmail/examples/2procmailrc'`" test 1587 -eq "$Wc_c" || echo 'procmail/examples/2procmailrc: original size 1587, current size' "$Wc_c" fi # ============= procmail/examples/3procmailrc ============== if test -f 'procmail/examples/3procmailrc' -a X"$1" != X"-c"; then echo 'x - skipping procmail/examples/3procmailrc (File already exists)' else echo 'x - extracting procmail/examples/3procmailrc (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/examples/3procmailrc' && X# Please check if all the paths in PATH are reachable, remove the ones that X# are not. X XPATH=$HOME/bin:/usr/bin:/global/bin:/usr/ucb:/bin:/usr/local/bin: XMAILDIR=$HOME/Mail # You'd better make sure it exists XDEFAULT=$MAILDIR/mbox XLOGFILE=$MAILDIR/from XLOCKFILE=$HOME/.lockmail X X # This will create a local lockfile named todd.lock X:: # *if* the condition matches X^From.*thf Xtodd X XLOCKFILE=$MAILDIR/whatever # This will remove the global lockfile X # $HOME/.lockmail and the new lockfile X # will be $MAILDIR/whatever X X X # The next recipe will X # filter out all messages from "at" X # jobs and will put them in a terse format in X # a file called $MAILDIR/atjunk X:2fh X^From root X^Subject: Output from "at" job X|echo "From at job";echo;egrep "^Date:" X:b X^From at job Xatjunk X X X XMAILDIR=$HOME/News # This will change the current directory X X X # The next recipe will create a local lockfile X # named $HOME/News/dustbin.lock (*if* the condition X # matches), and will feed the body of the message X # through `sort` (sorry, couldn't come up with anything X # better :-), after which the result will be X # appended to $HOME/News/dustbin X:b: X^Subject:.*rubbish X|sort >>dustbin X X# Anything not delivered by now will go to $HOME/Mail/mbox SHAR_EOF chmod 0644 procmail/examples/3procmailrc || echo 'restore of procmail/examples/3procmailrc failed' Wc_c="`wc -c < 'procmail/examples/3procmailrc'`" test 1255 -eq "$Wc_c" || echo 'procmail/examples/3procmailrc: original size 1255, current size' "$Wc_c" fi # ============= procmail/examples/2rmail ============== if test -f 'procmail/examples/2rmail' -a X"$1" != X"-c"; then echo 'x - skipping procmail/examples/2rmail (File already exists)' else echo 'x - extracting procmail/examples/2rmail (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/examples/2rmail' && X#!/bin/sh X# X# specify the mailbox file you want to read on the command line X# XMAILDIR=$HOME/Mail Xcd $MAILDIR XLOCKFILE=$1.lock Xif lockfile -! -r1 $LOCKFILE Xthen X echo Mail is currently arriving, please wait... X while X lockfile -! -4 -r2 $LOCKFILE X do X echo Mail is still arriving... X done Xfi X# X# Call you favourite mailer here. X# X/usr/ucb/mail -f $* X# X# X# Xrm -f $LOCKFILE SHAR_EOF chmod 0644 procmail/examples/2rmail || echo 'restore of procmail/examples/2rmail failed' Wc_c="`wc -c < 'procmail/examples/2rmail'`" test 373 -eq "$Wc_c" || echo 'procmail/examples/2rmail: original size 373, current size' "$Wc_c" fi # ============= procmail/examples/3rmail ============== if test -f 'procmail/examples/3rmail' -a X"$1" != X"-c"; then echo 'x - skipping procmail/examples/3rmail (File already exists)' else echo 'x - extracting procmail/examples/3rmail (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/examples/3rmail' && X#!/bin/sh X# X# specify the mailbox file you want to read on the command line X# Use a relative path from your $HOME directory X# X# For this kind of chaotic procmailrc there is no uniform neat solution X# to determine which lockfiles to use. I'll give just one (suboptimal) X# solution here. Use your imagination to extend it :-). X# XMAILDIR=$HOME/Mail Xcd $HOME # this means all paths are relative to $HOME XLOCKFILE=$HOME/.lockmail XLOCKFILE2=$HOME/Mail/whatever Xif lockfile -! -r1 $LOCKFILE $LOCKFILE2 Xthen X echo Mail is currently arriving, please wait... X while X lockfile -! -4 -r2 $LOCKFILE $LOCKFILE2 X do X echo Mail is still arriving... X done Xfi X# X# Call you favourite mailer here. X# X/usr/ucb/mail -f $* X# X# X# Xrm -f $LOCKFILE $LOCKFILE2 SHAR_EOF chmod 0644 procmail/examples/3rmail || echo 'restore of procmail/examples/3rmail failed' Wc_c="`wc -c < 'procmail/examples/3rmail'`" test 738 -eq "$Wc_c" || echo 'procmail/examples/3rmail: original size 738, current size' "$Wc_c" fi # ============= procmail/examples/1procmailrc ============== if test -f 'procmail/examples/1procmailrc' -a X"$1" != X"-c"; then echo 'x - skipping procmail/examples/1procmailrc (File already exists)' else echo 'x - extracting procmail/examples/1procmailrc (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/examples/1procmailrc' && X# Please check if all the paths in PATH are reachable, remove the ones that X# are not. X XPATH=$HOME/bin:/usr/bin:/global/bin:/usr/ucb:/bin:/usr/local/bin: XMAILDIR=$HOME/Mail # You'd better make sure it exists XDEFAULT=$MAILDIR/mbox XLOGFILE=$MAILDIR/from XLOCKFILE=$HOME/.lockmail X X: # Anything from thf X^From.*thf\@somewhere.someplace Xtodd # will go to $MAILDIR/todd X X: # Anything from people at uunet X^From.*\@uunet Xuunetbox # will go to $MAILDIR/uunetbox X X:I # Anything from Henry or henry (ignore case) X^From.*henry Xhenries # will go to $MAILDIR/henries X X# Anything that has not been delivered by now will go to $DEFAULT SHAR_EOF chmod 0644 procmail/examples/1procmailrc || echo 'restore of procmail/examples/1procmailrc failed' Wc_c="`wc -c < 'procmail/examples/1procmailrc'`" test 638 -eq "$Wc_c" || echo 'procmail/examples/1procmailrc: original size 638, current size' "$Wc_c" fi # ============= procmail/includes.h ============== if test -f 'procmail/includes.h' -a X"$1" != X"-c"; then echo 'x - skipping procmail/includes.h (File already exists)' else echo 'x - extracting procmail/includes.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'procmail/includes.h' && X#include <stdio.h> X#ifndef NO_ANSI_PROT /* define this if your headers are not ansi */ X# include <stdlib.h> X# include <time.h> X#else X# include <malloc.h> X char*getenv(); X typedef int pid_t; X#endif X#include <fcntl.h> X#include <pwd.h> X#include <sys/wait.h> X#ifndef NOuname X#include <sys/utsname.h> X#endif X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/time.h> X#include <signal.h> X#include <string.h> X#include <errno.h> X X#ifndef SEEK_SET X#define SEEK_SET 0 X#endif X#ifndef O_SYNC X#define O_SYNC 0 X#endif Xextern char**environ; X X#define STDIN 0 X#define STDOUT 1 X#define STDERR 2 SHAR_EOF chmod 0644 procmail/includes.h || echo 'restore of procmail/includes.h failed' Wc_c="`wc -c < 'procmail/includes.h'`" test 589 -eq "$Wc_c" || echo 'procmail/includes.h: original size 589, current size' "$Wc_c" fi exit 0 -- exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.