[comp.sources.unix] v18i087: Elm mail system, release 2.2, Part08/24

rsalz@uunet.uu.net (Rich Salz) (04/12/89)

Submitted-by: dsinc!syd@uunet.UU.NET (Syd Weinstein)
Posting-number: Volume 18, Issue 87
Archive-name: elm2.2/part08

#!/bin/sh
# this is part 8 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file doc/tmac.n continued
#
CurArch=8
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file doc/tmac.n"
sed 's/^X//' << 'SHAR_EOF' >> doc/tmac.n
X.	ef				\" short and sweet ...
X.\}
X..
X.de ux					\" print "UNIX"
X.ie \\n(ux \\&\\$2\\s-1UNIX\\s0\\$1
X.el \{\
X.	nr ux +1			\" mark footnote as dropped
X\\&\\$2\\s-1UNIX\\s0\\*(rg\\$1
X.	fn				\" put out the footnote
X\\&\\*(rgUNIX is a registered trademark of AT&T.
X.	ef				\" short and sweet ...
X.\}
X..
X.de vx					\" print "VAX"
X.ie \\n(vx \\&\\$2\\s-1VAX\\s0\\$1
X.el \{\
X.	nr vx +1			\" mark footnote as dropped
X\\&\\$2\\s-1VAX\\s0\\*(rg\\$1
X.	fn				\" put out the footnote
X\\&\\*(rgVAX is a trademark of Digital Equipment Corporation.
X.	ef				\" short and sweet ...
X.\}
X..
X.\" **********
X.\" set up string and number registers
X.					\" set up for the date
X.if \n(mo=1  .ds mo January
X.if \n(mo=2  .ds mo February
X.if \n(mo=3  .ds mo March
X.if \n(mo=4  .ds mo April
X.if \n(mo=5  .ds mo May
X.if \n(mo=6  .ds mo June
X.if \n(mo=7  .ds mo July
X.if \n(mo=8  .ds mo August
X.if \n(mo=9  .ds mo September
X.if \n(mo=10 .ds mo October
X.if \n(mo=11 .ds mo November
X.if \n(mo=12 .ds mo December
X.ds dy "\*(mo \n(dy, 19\n(yr
X.if \n(dw=1  .ds dw Sunday
X.if \n(dw=2  .ds dw Monday
X.if \n(dw=3  .ds dw Tuesday
X.if \n(dw=4  .ds dw Wednesday
X.if \n(dw=5  .ds dw Thursday
X.if \n(dw=6  .ds dw Friday
X.if \n(dw=7  .ds dw Saturday
X.					\" NROFF dependencies
X.if n \{\
X.					\" string registers
X.	ds rg (R)
X.	ds lq ""
X.	ds rq ""
X.	ds f1 "\*(dy
X.					\" number registers
X.	nr hs 1v			\" space before section header
X.	nr pd 1v			\" inter-paragraph spacing
X.	nr bm 1.0i			\" height of bottom margin
X.\}
X.					\" NROFF dependencies
X.if t \{\
X.					\" string registers
X.	ds rg \\u\\s-2\\(rg\\s0\\d
X.	ds lq ``
X.	ds rq ''
X.					\" number registers
X.	nr hs 1v			\" space before section header
X.	nr pd 0.3v			\" inter-paragraph spacing
X.	nr bm 1.0i+1v			\" height of bottom margin (wacky laser)
X.\}
X.					\" these are the same for [NT]ROFF
X.ds dg \(dg
X.ds vr "News Version B2.11
X.ds pv "News macros 1.5
X.ds h1 - % -
X.nr bt 0.5i+1v				\" bottom of page to footer
X.nr cm 0				\" no cut marks
X.nr fc 0 1				\" init footnote count
X.nr fl 5.5i				\" footnote line length
X.nr fp 0-\n(bmu				\" fo macro trap location
X.nr h0 0				\" init section header level 0
X.nr h1 0				\" init section header level 1
X.nr h2 0				\" init section header level 2
X.nr h3 0				\" init section header level 3
X.nr id 0				\" 1 in display
X.nr if 0				\" 1 in keep
X.nr it 0				\" 1 when beyond title, etc.
X.nr li 5n				\" indent for labelled paragraph
X.nr ll 6.5i				\" line length
X.nr lm 0				\" left margin
X.nr l0 0				\" first indent level
X.nr mt 1.5i+1v				\" title goes down this far
X.nr pi 5n				\" regular paragraph indent
X.nr po 1.0i				\" page offset
X.nr ps 10				\" point size
X.nr tm 1.0i				\" height of top margin
X.nr tt 0.5i-0.5v			\" top of page to header
X.nr p1 0				\" no PDP-TM message yet
X.nr ux 0				\" no UNIX-TM message yet
X.nr vx 0				\" no VAX-TM message yet
X.nr vs 12				\" vertical spacing
X.\" set things up
X.\" DSINC changes for XROFF
X.nr f1 1
X.nr f2 1
X.nr s1 10
X.nr s2 10
X.nr v1 12
X.nr v2 12
X.ps 10
X.vs 12
X.\" DSINC end changes for XROFF
X.po \n(pou				\" set page offset
X.ps \n(ps				\" set previous, current
X.ps \n(ps				\"   point sizes
X.vs \n(vs				\" set previous, current
X.vs \n(vs				\"   vertical spacings
X.ll \n(llu				\" set line length
X.lt \n(llu				\" set title line length
X.ev 1					\" *** footnote environment
X.ps \n(ps-2p				\" set previous, current
X.ps \n(ps-2p				\"   point sizes
X.vs \n(vs-2p				\" set previous, current
X.vs \n(vs-2p				\"   vertical spacings
X.ll \n(flu				\" set line length
X.lt \n(flu				\" set title line length
X.ev					\" *** pop environment
X.ev 2					\" *** footnote environment
X.ps \n(ps				\" set previous, current
X.ps \n(ps				\"   point sizes
X.vs \n(vs				\" set previous, current
X.vs \n(vs				\"   vertical spacings
X.ll \n(llu				\" set line length
X.lt \n(llu				\" set title line length
X.ev					\" *** pop environment
X.\" now set internal registers (for the first header section)
X.nr f1 \n(.f				\" saved font #1
X.nr f2 \n(.f				\" saved font #2
X.nr s1 \n(.s				\" saved point size #1
X.nr s2 \n(.s				\" saved point size #2
X.nr v1 \n(.v				\" saved vertical spacing #1
X.nr v2 \n(.v				\" saved vertical spacing #2
X.\" install traps
X.wh 0i hd				\" position header trap
X.wh -\n(bmu fo				\" position footer trap
X.wh \n(.pu+1i fx			\" put footnote overflow trap here
X.ch fx -\n(bmu				\" move it over fo
X.wh -\n(btu pf				\" print the bottom margin here
X.em et					\" at end of file, call et
X.\" couple of miscellaneous requests
X.bd S 3 3				\" embolden special font chars if B
X.hy 2					\" don't hyphenate last lines
SHAR_EOF
echo "File doc/tmac.n is complete"
chmod 0444 doc/tmac.n || echo "restore of doc/tmac.n fails"
echo "x - extracting doc/wnewmail.1 (Text)"
sed 's/^X//' << 'SHAR_EOF' > doc/wnewmail.1 &&
X.TH WNEWMAIL 1L "Elm Version 2.2" "USENET Community Trust"
X.ad b
X.SH NAME
Xwnewmail - daemon to asynchronously notify of new mail
X.SH SYNOPSIS
X.B wnewmail
X.br
X.B wnewmail
Xfilename
X.PP
X.SH DESCRIPTION
X.I Wnewmail\^
Xis a daemon designed to run in \fBa window\fR on a windowing
Xsystem (such as an HP or Sun system) and check every 10 seconds
Xto see if there is any new mail for the user that
Xstarted it up.
X.P
XIf there is new mail, the program will "beep", and write to
Xthe window for each of the new messages;
X.nf
X
X   Mail from <name> -- <subject>
X
X.fi
Xwhere <name> is either the name of the person sending it,
Xif available (the ARPA 'From:' line) or machine!login where
Xmachine is the machine the mail was sent from.  If there
Xis no subject, the message "<no subject>" will appear on
Xthe screen.
X.P
XIf the message is a \fIpriority\fR message (that is, the
Xheader contains a line "Priority:"), then the line output
Xwill be "PRIORITY mail from ..." rather than just "Mail from".
X.P
XThis program will run forever, and can internally reset 
Xitself if mail is deleted from the incoming mailbox while
Xtrying to monitor it.
X.P
XIf \fBwnewmail\fR is started up with a filename, it will
Xperform exactly the same, but with the specified file as
Xthe one to check rather than the default users mailbox.
X.SH AUTHOR
XDave Taylor, Hewlett-Packard Laboratories.
X.SH SEE ALSO
Xnotify in sh(1) or csh(1), newmail(1L)
X.SH NOTE
XThis is almost identical to the program \fBnewmail\fR...
X.SH BUG REPORTS TO
XSyd Weinstein	elm@dsinc.UUCP	(dsinc!elm)
X.SH COPYRIGHTS
X.ps 18
X\fB\(co\fR\s12 Copyright 1986, 1987 by Dave Taylor
X.br
X.ps 18
X\fB\(co\fR\s12 Copyright 1988, 1989 by The USENET Community Trust
SHAR_EOF
chmod 0444 doc/wnewmail.1 || echo "restore of doc/wnewmail.1 fails"
echo "x - extracting filter/Makefile.SH (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/Makefile.SH &&
Xcase $CONFIG in
X'')
X    if test ! -f config.sh; then
X	ln ../config.sh . || \
X	ln ../../config.sh . || \
X	ln ../../../config.sh . || \
X	(echo "Can't find config.sh."; exit 1)
X    fi
X    . ./config.sh
X    ;;
Xesac
Xcase "$0" in
X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
Xesac
X
Xecho "Extracting filter/Makefile (with variable substitutions)"
Xcat >Makefile <<!GROK!THIS!
X#
X# @(#)$Id: Makefile.SH,v 2.11 89/03/25 21:45:09 syd Exp $
X#  Makefile for the Elm system filter program
X#
X#	Copyright (c) 1986, 1987 Dave Taylor
X#	Copyright (c) 1988, 1989 USENET Community Trust
X#
X# Bug reports, patches, comments, suggestions should be sent to:
X#
X#	Syd Weinstein - elm@dsinc.UUCP
X#			dsinc!elm
X#
X#  $Log:	Makefile.SH,v $
X# Revision 2.11  89/03/25  21:45:09  syd
X# Initial 2.2 Release checkin
X# 
X#
X# Variables
X#	Variables established by Configure
XCC		=	$cc
XCCFLAGS		=	$ccflags $xencf
XCHGRP		=	$chgrp
XCHMOD		=	$chmod
XCP		=	$cp
XDEST		=	$bin
XECHO		=	$echo
XLFLAGS		=	$ldflags $xenlf
XLIB		=	$lib
XLIB2		= 	$libs
XLIBS		=	$termlib $dbm
XLINT		=	$lint
XMAILGRP		=	$mailgrp
XMAILERMODE	=	$mailermode
XMAKE		=	$make
XMV		=	$mv
XOPTIMIZE	=	$optimize
XRM		= 	$rm -f
XTOUCH		=	$touch
X
X!GROK!THIS!
X
Xcat >>Makefile <<'!NO!SUBS!'
X#	Variables you may want to manually edit
X#		If you want debug logging then you'll
X#		want to uncomment the following.
X#DEBUG		=	-DDEBUG
X
X#	Other variables
XBIN		=	../bin
XINCLDIR		=	../hdrs
XCFLAGS		=	$(CCFLAGS) $(OPTIMIZE) -I$(INCLDIR) $(DEBUG) $(DACSNET) 
XLINTFLAGS	=	-I$(INCLDIR)
XSHELL		=	/bin/sh
X
X# Definitions of variables
XFILTER_SRC	=	actions.c	\
X			buffer.c	\
X			filter.c	\
X			lock.c		\
X			parse.c		\
X			rules.c		\
X			summarize.c	\
X			utils.c		\
X			../src/opt_utils.c
X
XFILTER_OBJ	=	actions.o	\
X			buffer.o	\
X			filter.o	\
X			lock.o		\
X			parse.o		\
X			rules.o		\
X			summarize.o	\
X			utils.o		\
X			../src/opt_utils.o
X
X# Standard targets
Xall:		$(BIN)/filter
X		
Xinstall:	$(DEST)/filter
X
Xuninstall:	
X		$(RM) $(DEST)/filter
X
X#	This is the only target that gets installed even if not out-of-date
X#	with respect the files from which it is installed.
Xrmt-install:	rmt-defined
X		-$(MV) $(DEST)/filter $(DEST)/filter.old
X		-$(RM) $(DEST)/filter.old
X		$(CP) $(REMOTE)$(DEST)/filter $(DEST)/filter
X		$(CHGRP) $(MAILGRP) $(DEST)/filter
X		$(CHMOD) $(MAILERMODE) $(DEST)/filter
X
Xrmt-defined:
X	@(if [ "$(REMOTE)" = "" ];\
X	  then\
X	    $(ECHO) "You need to define 'REMOTE' as the remote file system";\
X	    $(ECHO) "for this particular command. The easiest way to do this";\
X	    $(ECHO) "to type:";\
X	    $(ECHO) "        make REMOTE=<remote file system> rmt-install";\
X	    exit 1;\
X	  fi);
X
Xlint:		
X		$(LINT) $(LINTFLAGS) $(FILTER_SRC) > LINT.OUT
X
Xclean:		
X		$(RM) $(FILTER_OBJ) $(BIN)/filter
X
X# Dependencies and rules
X#	Dependencies of header files upon other header files they include
X$(INCLDIR)/defs.h:	$(INCLDIR)/../config.h $(INCLDIR)/sysdefs.h
X			$(CHMOD) u+w $@
X			$(TOUCH) $@
X
X$(INCLDIR)/elm.h:	$(INCLDIR)/curses.h $(INCLDIR)/defs.h
X			$(CHMOD) u+w $@
X			$(TOUCH) $@
X
X$(INCLDIR)/headers.h:	$(INCLDIR)/curses.h $(INCLDIR)/defs.h
X			$(CHMOD) u+w $@
X			$(TOUCH) $@
X
X#	Dependencies and rules for C object files
Xactions.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xfilter.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xlock.o:			$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xparse.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xrules.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xsummarize.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
Xutils.o:		$(INCLDIR)/defs.h $(INCLDIR)/filter.h
X../src/opt_utils.o:
X			cd ../src; $(MAKE) -$(MAKEFLAGS) $(@F)
X
X#	Dependencies and rules for compiling programs
X$(BIN)/filter:	$& $(FILTER_OBJ)
X		$(CC) $(LFLAGS) -o $@ $(FILTER_OBJ) $(LIB2)
X
X#	Dependencies and rules for installing programs from bin directory
X$(DEST)/filter:		$(BIN)/filter
X			-$(MV) $(DEST)/filter $(DEST)/filter.old
X			-$(RM) $(DEST)/filter.old
X			$(CP) $? $@
X			$(CHGRP) $(MAILGRP) $@
X			$(CHMOD) $(MAILERMODE) $@
X!NO!SUBS!
SHAR_EOF
chmod 0444 filter/Makefile.SH || echo "restore of filter/Makefile.SH fails"
echo "x - extracting filter/actions.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/actions.c &&
X
Xstatic char rcsid[] ="@(#)$Id: actions.c,v 2.4 89/03/25 21:45:10 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.4 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm@dsinc.UUCP
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	actions.c,v $
X * Revision 2.4  89/03/25  21:45:10  syd
X * Initial 2.2 Release checkin
X * 
X *
X ******************************************************************************/
X
X
X/** RESULT oriented routines *chuckle*.  These routines implement the
X    actions that result from either a specified rule being true or from
X    the default action being taken.
X**/
X
X#include <stdio.h>
X#include <pwd.h>
X#include <ctype.h>
X#include <fcntl.h>
X
X#include "defs.h"
X#include "filter.h"
X
XFILE *emergency_local_delivery();
X
Xmail_message(address)
Xchar *address;
X{
X	/** Called with an address to send mail to.   For various reasons
X	    that are too disgusting to go into herein, we're going to actually
X	    open the users mailbox and by hand add this message.  Yech.
X	    NOTE, of course, that if we're going to MAIL the message to someone
X	    else, that we'll try to do nice things with it on the fly...
X	**/
X
X	FILE *pipefd, *tempfd, *mailfd;
X	int  in_header = TRUE, line_count = 0;
X	char tempfile[SLEN], mailbox[SLEN], lockfile[SLEN],
X	     buffer[VERY_LONG_STRING];
X
X	if (verbose && ! log_actions_only && outfd != NULL)
X	  fprintf(outfd, "filter (%s): Mailing message to %s\n", 
X		   username, address);
X
X	if (! show_only) {
X	  sprintf(tempfile, "%s.%d", filter_temp, getpid());
X
X	  if ((tempfd = fopen(tempfile, "r")) == NULL) {
X	    if (outfd != NULL)
X	      fprintf(outfd, "filter (%s): Can't open temp file %s!!\n", 
X		    username, tempfile);
X	    if (outfd != NULL) fclose(outfd);
X	    exit(1);
X	  }
X	 	
X	  if (strcmp(address, username) != 0) {	/* mailing to someone else */
X	    
X	    if (already_been_forwarded) {	/* potential looping! */
X	      if (contains(from, username)) {
X		if (outfd != NULL)
X	          fprintf(outfd, 
X	"filter (%s): Filter loop detected!  Message left in file %s.%d\n", 
X			username, filter_temp, getpid());
X	        if (outfd != NULL) fclose(outfd);
X	        exit(0);
X	      }
X	    }
X
X	    sprintf(buffer, "%s %s %s", sendmail, smflags, address);
X
X	    if ((pipefd = popen(buffer, "w")) == NULL) {
X	      if (outfd != NULL)
X	        fprintf(outfd, "filter (%s): popen %s failed!\n", buffer);
X	      sprintf(buffer, "((%s %s %s ; %s %s) & ) < %s &",
X		      sendmail , smflags, address, remove_cmd, tempfile, tempfile);
X	      system(buffer);
X	      return;
X	    }
X
X	    fprintf(pipefd, "Subject: \"%s\"\n", subject);
X	    fprintf(pipefd, "From: The Filter of %s@%s <%s>\n", 
X		    username, hostname, username);
X	    fprintf(pipefd, "To: %s\n", address);
X	    fprintf(pipefd, "X-Filtered-By: filter, version %s\n\n", VERSION);
X
X	    fprintf(pipefd, "-- Begin filtered message --\n\n");
X	
X	    while (fgets(buffer, SLEN, tempfd) != NULL)
X	      if (already_been_forwarded && in_header)
X	        in_header = (strlen(buffer) == 1? 0 : in_header);
X	      else
X	        fprintf(pipefd," %s", buffer);
X
X	    fprintf(pipefd, "\n-- End of filtered message --\n");
X	    fclose(pipefd);
X	    fclose(tempfd);
X	
X	    return;		/* YEAH!  Wot a slick program, eh? */
X	  
X	  }
X	  
X	  /** OTHERWISE it is to the current user... **/
X
X	  sprintf(mailbox,  "%s%s", mailhome, username);
X	  
X	  if (! lock()) {
X	    if (outfd != NULL) {
X	      fprintf(outfd, "filter (%s): Couldn't create lockfile %s\n",
X		    username, lockfile);
X	      fprintf(outfd, "filter (%s): Can't open mailbox %s!\n",
X			username, mailbox);
X	    }
X	    if ((mailfd = emergency_local_delivery()) == NULL)
X	      exit(1);
X	  }
X	  else if ((mailfd = fopen(mailbox,"a")) == NULL)
X	    if ((mailfd = emergency_local_delivery()) == NULL)
X	      exit(1);
X
X	  while (fgets(buffer, sizeof(buffer), tempfd) != NULL) {
X	    line_count++;
X	    if (the_same(buffer, "From ") && line_count > 1)
X	      fprintf(mailfd, ">%s", buffer);
X	    else
X	      fputs(buffer, mailfd);
X	  }
X
X	  fputs("\n", mailfd);
X
X	  fclose(mailfd);
X	  unlock();		/* blamo or not?  Let it decide! */
X	  fclose(tempfd);
X	} /* end if show only */
X}
X
Xsave_message(foldername)
Xchar *foldername;
X{
X	/** Save the message in a folder.  Use full file buffering to
X	    make this work without contention problems **/
X
X	FILE  *fd, *tempfd;
X	char  filename[SLEN], buffer[SLEN];
X
X	if (verbose && outfd != NULL)
X	  fprintf(outfd, "filter (%s): Message saved in folder %s\n", 
X		  username, foldername);
X	
X	if (!show_only) {
X	  sprintf(filename, "%s.%d", filter_temp, getpid());
X
X	  if ((fd = fopen(foldername, "a")) == NULL) {
X	    if (outfd != NULL)
X	      fprintf(outfd, 
X		 "filter (%s): can't save message to requested folder %s!\n",
X		    username, foldername);
X	    return(1);
X	  }
X
X	  if ((tempfd = fopen(filename, "r")) == NULL) {
X	    if (outfd != NULL)
X	      fprintf(outfd, 
X		     "filter (%s): can't open temp file for reading!\n",
X		     username);
X	     return(1);
X	  }
X
X	  while (fgets(buffer, sizeof(buffer), tempfd) != NULL)
X	    fputs(buffer, fd);
X	
X	  fclose(fd);
X	  fclose(tempfd);
X	}
X
X 	return(0);
X}
X
Xexecute(command)
Xchar *command;
X{
X	/** execute the indicated command, feeding as standard input the
X	    message we have.
X	**/
X
X	char buffer[SLEN];
X
X	if (verbose && outfd != NULL)
X	  fprintf(outfd, "filter (%s): Executing %s\n", 
X		  username, command);
X
X	if (! show_only) {
X	  sprintf(buffer, "%s %s.%d | %s", cat, filter_temp, getpid(), command);
X	  system(buffer);
X	}
X}
X
XFILE *
Xemergency_local_delivery()
X{
X	/** This is called when we can't deliver the mail to the usual
X	    mailbox in the usual way ...
X	**/
X
X	FILE *tempfd;
X	char  mailbox[SLEN];
X
X	sprintf(mailbox, "%s/%s", home, EMERGENCY_MAILBOX);
X
X	if ((tempfd = fopen(mailbox, "a")) == NULL) {
X	  if (outfd != NULL)
X	    fprintf(outfd, "filter (%s): Can't open %s either!!\n",
X		    username, mailbox);
X
X	  sprintf(mailbox,"%s/%s", home, EMERG_MBOX); 
X
X	  if ((tempfd = fopen(mailbox, "a")) == NULL) {
X
X	    if (outfd != NULL) {
X	      fprintf(outfd,"filter (%s): Can't open %s either!!!!\n",
X		      username, mailbox);
X	      fprintf(outfd, 
X		      "filter (%s): I can't open ANY mailboxes!  Augh!!\n",
X		       username);
X	     }
X
X	     fclose(tempfd);
X	     leave("Cannot open any mailbox");		/* DIE DIE DIE DIE!! */
X	   }
X	   else
X	     if (outfd != NULL)
X	       fprintf(outfd, "filter (%s): Using %s as emergency mailbox\n",
X		       username, mailbox);
X	  }
X	  else
X	    if (outfd != NULL)
X	      fprintf(outfd, "filter (%s): Using %s as emergency mailbox\n",
X		      username, mailbox);
X
X	return((FILE *) tempfd);
X}
SHAR_EOF
chmod 0444 filter/actions.c || echo "restore of filter/actions.c fails"
echo "x - extracting filter/buffer.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/buffer.c &&
X
Xstatic char rcsid[] ="@(#)$Id: buffer.c,v 2.3 89/03/25 21:45:12 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.3 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm@dsinc.UUCP
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	buffer.c,v $
X * Revision 2.3  89/03/25  21:45:12  syd
X * Initial 2.2 Release checkin
X * 
X *
X ******************************************************************************/
X
X#include <stdio.h>
X
Xchar  _vbuf[5*BUFSIZ];		/* space for file buffering */
SHAR_EOF
chmod 0444 filter/buffer.c || echo "restore of filter/buffer.c fails"
echo "x - extracting filter/filter.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/filter.c &&
X
Xstatic char rcsid[] ="@(#)$Id: filter.c,v 2.6 89/03/25 21:45:13 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.6 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm@dsinc.UUCP
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	filter.c,v $
X * Revision 2.6  89/03/25  21:45:13  syd
X * Initial 2.2 Release checkin
X * 
X *
X ******************************************************************************/
X
X
X/** This program is used as a filter within the users ``.forward'' file
X    and allows intelligent preprocessing of mail at the point between
X    when it shows up on the machine and when it is actually put in the
X    mailbox.
X
X    The program allows selection based on who the message is FROM, TO, or
X    what the subject is.  Acceptable actions are for the program to DELETE_MSG
X    the message, SAVE the message in a specified folder, FORWARD the message
X    to a specified user, SAVE the message in a folder, but add a copy to the
X    users mailbox anyway, or simply add the message to the incoming mail.
X
X    Filter also keeps a log of what it does as it goes along, and at the
X    end of each `quantum' mails a summary of actions, if any, to the user.
X
X    Uses the files: $HOME/.filter for instructions to this program, and
X    $HOME/.filterlog for a list of what has been done since last summary.
X
X    Rev 2.0: knows about From: and Reply-To: 
X    Fix: knows that mail lines are approx. ~5K or greater...
X    enhanced to have the '-o output-file' specifier
X
X**/
X
X#include <stdio.h>
X#include <pwd.h>
X#include <ctype.h>
X#include <time.h>
X#include <fcntl.h>
X
X#include "defs.h"
X
X#define  MAIN_ROUTINE			/* for the filter.h file, of course! */
X#include "filter.h"
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	extern char *optarg;
X	FILE   *fd;				/* for output to temp file! */
X	struct passwd  *passwd_entry, 
X		       *getpwuid();		/* for /etc/passwd          */
X	char filename[SLEN],			/* name of the temp file    */
X	     buffer[MAX_LINE_LEN];		/* input buffer space       */
X	int  in_header = TRUE,			/* for header parsing       */
X	     in_to     = FALSE,			/* are we on 'n' line To: ? */
X	     summary   = FALSE,			/* a summary is requested?  */
X	     c;					/* var for getopt routine   */
X
X	/* first off, let's get the info from /etc/passwd */ 
X	
X	if ((passwd_entry = getpwuid(getuid())) == NULL) 
X	  leave("Cannot get password entry for this uid!");
X
X	strcpy(home, passwd_entry->pw_dir);
X	strcpy(username, passwd_entry->pw_name);
X	outfname[0] = to[0] = '\0';	/* nothing read in yet, right? */
X
X#ifdef HOSTCOMPILED
X	strncpy(hostname, HOSTNAME, sizeof(hostname));
X#else
X	gethostname(hostname, sizeof(hostname));
X#endif
X
X	/* now parse the starting arguments... */
X
X	while ((c = getopt(argc, argv, "clno:rSsv")) != EOF) {
X	  switch (c) {
X	    case 'c' : clear_logs = TRUE;			break;
X	    case 'l' : log_actions_only = TRUE;			break;
X	    case 'o' : strcpy(outfname, optarg);		break;
X	    case 'r' : printing_rules = TRUE;			break;
X	
X	    case 's' : summary = TRUE;				break;
X	    case 'S' : long_summary = TRUE;			break;
X
X	    case 'n' : show_only = TRUE;			break;
X	    case 'v' : verbose = TRUE;				break;
X	    case '?' : fprintf(stderr, 
X		       "Usage: | filter [-nrv]\n   or: filter [-c] -[s|S]\n");
X          	       exit(1);
X	  }
X	}
X
X	if (c < 0) {
X	}
X
X	/* let's open our outfd logfile as needed... */
X
X	if (outfname[0] == '\0') 	/* default is stdout */
X	  outfd = stdout;
X	else 
X	  if ((outfd = fopen(outfname, "a")) == NULL) {
X	    if (isatty(fileno(stderr)))
X	      fprintf(stderr,"filter (%s): couldn't open log file %s\n",
X		      username, outfname);
X	  }
X
X	if (summary || long_summary) {
X          if (get_filter_rules() == -1) {
X	    exit(1);
X	    if (outfd != NULL) fclose(outfd);
X	  }
X	  show_summary();
X	  if (outfd != NULL) fclose(outfd);
X	  exit(0);
X	}
X
X	if (printing_rules) {
X          if (get_filter_rules() == -1)
X	    fprintf(outfd,"filter (%s): Couldn't get rules!\n", username);
X          else
X	    print_rules();
X	  if (outfd != NULL) fclose(outfd);
X          exit(0);
X	}
X
X	/* next, create the tempfile and save the incoming message */
X
X	sprintf(filename, "%s.%d", filter_temp, getpid());
X
X	if ((fd = fopen(filename,"w")) == NULL)
X	  leave("Cannot open temporary file!");
X
X	while (fgets(buffer, MAX_LINE_LEN, stdin) != NULL) {
X
X	  remove_return(buffer);
X
X	  if (in_header) {
X
X	    if (! whitespace(buffer[0])) 
X		in_to = FALSE;
X
X	    if (the_same(buffer, "From ")) 
X	      save_from(buffer);
X	    else if (the_same(buffer, "Subject:")) 
X	      save_subject(buffer);
X	    else if (the_same(buffer, "To:") || the_same(buffer, "Cc:")) {
X	      in_to++;
X	      save_to(buffer);
X	    }
X	    else if (the_same(buffer, "X-Filtered-By:")) 
X	      already_been_forwarded++;	/* could be a loop here! */
X#ifdef USE_EMBEDDED_ADDRESSES
X	    else if (the_same(buffer, "From:"))
X	      save_embedded_address(buffer, "From:");
X	    else if (the_same(buffer, "Reply-To:"))
X	      save_embedded_address(buffer, "Reply-To:");
X#endif
X	    else if (strlen(buffer) < 2) 
X	      in_header = 0;
X	    else if (whitespace(buffer[0]) && in_to)
X	      strcat(to, buffer);
X	  }
X	
X          fprintf(fd, "%s\n", buffer);	/* and save it regardless! */
X	  fflush(fd);
X	  lines++;
X	}
X
X	fclose(fd);
X
X	/** next let's see if the user HAS a filter file, and if so what's in
X            it (and so on) **/
X
X	if (get_filter_rules() == -1)
X	  mail_message(username);
X	else {
X	  switch (action_from_ruleset()) {
X
X	    case DELETE_MSG : if (verbose && outfd != NULL)
X			    fprintf(outfd, "filter (%s): Message deleted\n",
X				    username);
X			  log(DELETE_MSG);				break;
X
X	    case SAVE   : if (save_message(rules[rule_choosen].argument2)) {
X			    mail_message(username);
X			    log(FAILED_SAVE);
X			  }
X			  else
X		 	    log(SAVE);					break;
X
X	    case SAVECC : if (save_message(rules[rule_choosen].argument2))
X			    log(FAILED_SAVE);
X			  else
X		            log(SAVECC);					
X			  mail_message(username);			break;
X
X	    case FORWARD: mail_message(rules[rule_choosen].argument2);
X			  log(FORWARD);					break;
X
X	    case EXEC   : execute(rules[rule_choosen].argument2);
X			  log(EXEC);					break;
X
X	    case LEAVE  : mail_message(username);
X			  log(LEAVE);					break;
X	  }
X	}
X
X	(void) unlink(filename);	/* remove the temp file, please! */
X	if (outfd != NULL) fclose(outfd);
X	exit(0);
X}
X
Xsave_from(buffer)
Xchar *buffer;
X{
X	/** save the SECOND word of this string as FROM **/
X
X	register int i, j;
X
X	for (i=0; buffer[i] != ' '; i++) 	;	/* get to word     */
X
X	for (i++, j=0; buffer[i] != ' ' && i < strlen(buffer); i++) 
X	  from[j++] = buffer[i];			/* copy it and     */
X
X	from[j++] = '\0';				/* Null terminate! */
X}
X
Xsave_subject(buffer)
Xchar *buffer;
X{
X	/** save all but the word "Subject:" for the subject **/
X
X	register int skip = 8;  /* skip "Subject:" initially */
X
X	while (buffer[skip] == ' ') skip++;
X
X	strcpy(subject, (char *) buffer + skip);
X}
X
Xsave_to(buffer)
Xchar *buffer;
X{
X	/** save all but the word "To:" or "Cc:" for the to list **/
X
X	register int skip = 3;	/* skip "To:" or "Cc:" initially */
X
X	while (buffer[skip] == ' ') skip++;
X
X	strcat(to, (char *) buffer + skip);
X}
X
X#ifdef USE_EMBEDDED_ADDRESSES
X
Xsave_embedded_address(buffer, fieldname)
Xchar *buffer, *fieldname;
X{
X	/** this will replace the 'from' address with the one given, 
X	    unless the address is from a 'reply-to' field (which overrides 
X	    the From: field).  The buffer given to this routine can have one 
X            of three forms:
X		fieldname: username <address>
X		fieldname: address (username)
X		fieldname: address
X	**/
X	
X	static int processed_a_reply_to = 0;
X	char address[LONG_STRING];
X	register int i, j = 0;
X
X	/** first let's extract the address from this line.. **/
X
X	if (buffer[strlen(buffer)-1] == '>') {	/* case #1 */
X	  for (i=strlen(buffer)-1; buffer[i] != '<' && i > 0; i--)
X		/* nothing - just move backwards .. */ ;
X	  i++;	/* skip the leading '<' symbol */
X	  while (buffer[i] != '>')
X	    address[j++] = buffer[i++];
X	  address[j] = '\0';
X	}
X	else {	/* get past "from:" and copy until white space or paren hit */
X	  for (i=strlen(fieldname); whitespace(buffer[i]); i++)
X	     /* skip past that... */ ;
X	  while (buffer[i] != '(' && ! whitespace(buffer[i]) && buffer[i]!='\0')
X	    address[j++] = buffer[i++];
X	  address[j] = '\0';
X	}
X
X	/** now let's see if we should overwrite the existing from address
X	    with this one or not.. **/
X
X	if (processed_a_reply_to)
X	  return;	/* forget it! */
X
X	strcpy(from, address);			/* replaced!! */
X
X	if (strcmp(fieldname, "Reply-To:") == 0)
X	  processed_a_reply_to++;
X}
X#endif
SHAR_EOF
chmod 0444 filter/filter.c || echo "restore of filter/filter.c fails"
echo "x - extracting filter/lock.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/lock.c &&
X
Xstatic char rcsid[] ="@(#)$Id: lock.c,v 2.4 89/03/25 21:45:14 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.4 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm@dsinc.UUCP
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	lock.c,v $
X * Revision 2.4  89/03/25  21:45:14  syd
X * Initial 2.2 Release checkin
X * 
X *
X ******************************************************************************/
X
X
X/** The lock() and unlock() routines herein duplicate exactly the
X    equivalent routines in the Elm Mail System, and should also be
X    compatible with sendmail, rmail, etc etc.
X
X  
X**/
X
X#include <stdio.h>
X#include <fcntl.h>
X#include "defs.h"
X#include "filter.h"
X
Xint  we_locked_it = 0;
Xchar lockfile[SLEN];
X
Xint
Xlock()
X{
X	/** This routine will return 1 if we could lock the mailfile,
X	    zero otherwise.
X	**/
X
X	int attempts = 0, ret;
X
X	sprintf(lockfile,  "%s%s.lock", mailhome, username);
X
X	while ((ret = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 0777)) < 0 
X	       && attempts++ < 10) {
X	  sleep(3);	/* wait three seconds each pass, okay?? */
X	}
X
X	if (ret > 0) {
X	  we_locked_it++;
X	  close(ret);			/* no need to keep it open! */
X	}
X
X	return( (ret >= 0) );
X}
X
Xunlock()
X{
X	/** this routine will remove the lock file, but only if we were
X	    the people that locked it in the first place... **/
X
X	if (we_locked_it)
X	  unlink(lockfile);	/* blamo! */
X
X	we_locked_it = 0;
X}
SHAR_EOF
chmod 0444 filter/lock.c || echo "restore of filter/lock.c fails"
echo "x - extracting filter/parse.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/parse.c &&
X
Xstatic char rcsid[] ="@(#)$Id: parse.c,v 2.4 89/03/25 21:45:16 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.4 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm@dsinc.UUCP
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	parse.c,v $
X * Revision 2.4  89/03/25  21:45:16  syd
X * Initial 2.2 Release checkin
X * 
X *
X ******************************************************************************/
X
X
X/** This is the parser for the filter program.  It accepts a wide variety of
X    constructs, building the ruleset table as it goes along.  Check the 
X    data structure in filter.h for more information on how the rules are
X    stored.  The parser is a cunning state-table based program.
X
X**/
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "defs.h"
X#include "filter.h"
X
X#define NONE			0
X#define AND			10
X
X#define NEXT_CONDITION		0
X#define GETTING_OP		1
X#define READING_ARGUMENT	2
X#define READING_ACTION		3
X#define ACTION_ARGUMENT		4
X
Xchar *strtok(), *whatname(), *actionname();
X
Xint
Xget_filter_rules()
X{
X	/** Given the users home directory, open and parse their rules table,
X	    building the data structure as we go along.
X	    returns -1 if we hit an error of any sort...
X	**/
X
X	FILE *fd;				/* the file descriptor     */
X	char  buffer[SLEN], 			/* fd reading buffer       */
X	      *str, 				/* ptr to read string      */
X	      *word,				/* ptr to 'token'          */
X	      filename[SLEN], 			/* the name of the ruleset */
X	      action_argument[SLEN], 		/* action arg, per rule    */
X	      cond_argument[SLEN];		/* cond arg, per condition */
X	int   not_condition = FALSE, 		/* are we in a "not" ??    */
X	      type=NONE, 			/* what TYPE of condition? */
X	      lasttype, 			/* and the previous TYPE?  */
X	      state = NEXT_CONDITION,		/* the current state       */
X	      in_single, in_double, 		/* for handling spaces.    */
X	      i, 				/* misc integer for loops  */
X	      relop = NONE,			/* relational operator     */
X	      action, 				/* the current action type */
X	      line = 0;				/* line number we're on    */
X
X	struct condition_rec	*cond, *newcond;
X
X	sprintf(filename,"%s/%s", home, filterfile);
X
X	if ((fd = fopen(filename,"r")) == NULL) {
X	  if (outfd != NULL)
X	   fprintf(outfd,"filter (%s): Couldn't read user filter rules file!\n",
X		  username);
X	  return(-1);
X	}
X
X	cond_argument[0] = action_argument[0] = '\0';
X
X	/* Now, for each line... **/
X
X	if ((cond = (struct condition_rec *) 
X		     malloc(sizeof(struct condition_rec))) == NULL) {
X	  if (outfd != NULL)
X	    fprintf(outfd,"filter (%s): couldn't malloc first condition rec!\n",
X		    username);
X	  return(-1);
X	}
X	
X	rules[total_rules].condition = cond;	/* hooked in! */
X
X	while (fgets(buffer, SLEN, fd) != NULL) {
X	  line++;
X
X	  if (buffer[0] == '#' || strlen(buffer) < 2)
X	    continue;		/* nothing to look at! */
X
X	  in_single = in_double = 0;
X
X	  for (i=0; i < strlen(buffer); i++) {
X	    if (buffer[i] == '"') 
X	      in_double = ! in_double;
X	    else if (buffer[i] == '\'')
X	      in_single = ! in_single;
X	    if ((in_double || in_single) && buffer[i] == ' ')
X	      buffer[i] = '_';
X	  }
X
X	  lasttype = type;
X	  type = NONE;
X	  str = (char *) buffer;
X
X	  /** Three pieces to this loop - get the `field', the 'relop' (if
X	      there) then, if needed, get the argument to check against (not 
X	      needed for errors or the AND, of course)
X	  **/
X
X	  while ((word = strtok(str, " ()[]:\t\n")) != NULL) {
X
X	    str = (char *) NULL;		/* we can start stomping! */
X	  
X	    lowercase(word);
X
X	    if (strcmp(word, "if") == 0) {	/* only ONE 'if' allowed */
X	      if ((word = strtok(str, " ()[]:\t\n")) == NULL)	/* NEXT! */
X	        continue;
X	      lowercase(word);
X	    }
X	
X	    if (state == NEXT_CONDITION) {
X	      lasttype = type;
X	      type = NONE;
X
X	      if (the_same(word, "not") || the_same(word, "!")) {
X	        not_condition = TRUE;
X	        if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	          continue;
X	      }
X
X	           if (the_same(word, "from")) 	    type = FROM;
X	      else if (the_same(word, "to")) 	    type = TO;
X	      else if (the_same(word, "subject"))   type = SUBJECT;
X	      else if (the_same(word, "lines"))     type = LINES;
X	      else if (the_same(word, "contains"))  type = CONTAINS;
X	      else if (the_same(word, "and") || 
X	               the_same(word, "&&")) 	    type = AND;
X
X	      else if (the_same(word,"?") || the_same(word, "then") || 
X		       the_same(word, "always")) {
X
X		/** shove THIS puppy into the structure and let's continue! **/
X
X	        if (lasttype == AND) {
X		  if (outfd != NULL)
X	            fprintf(outfd,
X         "filter (%s): Error reading line %d of rules - badly placed \"and\"\n",
X		    username, line);
X		  return(-1);
X	        }
X
X	        if (the_same(word, "always"))
X		  cond->matchwhat = ALWAYS;	/* so it's a hack... */
X		else
X		  cond->matchwhat = lasttype;
X
X	        if (relop == NONE) relop = EQ;	/* otherwise can't do -relop */
X	        cond->relation  = (not_condition? - (relop) : relop);
X
X		for (i=0;i<strlen(cond_argument);i++)
X	          if (cond_argument[i] == '_') cond_argument[i] = ' ';
X
X		strcpy(cond->argument1, cond_argument);
X	        if ((newcond = (struct condition_rec *)
X		     malloc(sizeof(struct condition_rec))) == NULL) {
X		  if (outfd != NULL)
X	            fprintf(outfd,
X		     	    "filter (%s): Couldn't malloc new cond rec!!\n",
X		            username);
X		  return(-1);
X	        }
X	        cond->next = NULL;
X
X	        relop = EQ;	/* default relational condition */
X
X	        state = READING_ACTION;
X	        if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	          continue;
X	        goto get_outta_loop;
X	      }
X
X	      if (type == NONE) {
X		if (outfd != NULL)
X	          fprintf(outfd,
X      "filter (%s): Error reading line %d of rules - field \"%s\" unknown!\n",
X		     username, line, word);
X		return(-1);
X	      }
X
X	      if (type == AND) {
X
X		/** shove THIS puppy into the structure and let's continue! **/
X
X		cond->matchwhat = lasttype;
X	        cond->relation  = (not_condition? - (relop) : relop);
X		strcpy(cond->argument1, cond_argument);
X	        if ((newcond = (struct condition_rec *)
X	             malloc(sizeof(struct condition_rec))) == NULL) {
X		  if (outfd != NULL)
X	            fprintf(outfd,
X			"filter (%s): Couldn't malloc new cond rec!!\n",
X			username);
X		  return(-1);
X	        }
X	        cond->next = newcond;
X		cond = newcond;
X		cond->next = NULL;
X
X	        not_condition = FALSE;
X	        state = NEXT_CONDITION;
X	      }
X	      else {
X	        state = GETTING_OP;
X	      }
X	    }
X
Xget_outta_loop: 	/* jump out when we change state, if needed */
X
X	    if (state == GETTING_OP) {
X
X	       if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	         continue;
X
X	       lowercase(word);
X
X	       relop = NONE;
X
X	       if (the_same(word, "=") || the_same(word, "in") || 
X                   the_same(word, "contains")) {
X                 state = READING_ARGUMENT;
X	         relop = EQ;
X	       }
X	       else {
X	         if (the_same(word, "<=")) 	relop = LE;
X	         else if (the_same(word, ">="))	relop = GE;
X	         else if (the_same(word, ">"))	relop = GT;
X	         else if (the_same(word, "<>")||
X		          the_same(word, "!="))	relop = NE;
X	         else if (the_same(word, "<"))	relop = LT;
X
X		 /* maybe there isn't a relop at all!! */
X
X		 state=READING_ARGUMENT;
X
X	       }
X	    }
X		 
X	    if (state == READING_ARGUMENT) {
X	      if (relop != NONE) {
X	        if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	          continue;
X	      }
X	      for (i=0;i<strlen(word);i++)
X	        if (word[i] == '_') word[i] = ' ';
X
X	      strcpy(cond_argument, word);
X	      state = NEXT_CONDITION;
X	    }
X
X	    if (state == READING_ACTION) {
X	      action = NONE;
X
X	      not_condition = FALSE;
X
X	      if (the_same(word, "delete"))       action = DELETE_MSG;
X	      else if (the_same(word, "savec"))   action = SAVECC;
X	      else if (the_same(word, "save"))    action = SAVE;
X	      else if (the_same(word, "forward")) action = FORWARD;
X	      else if (the_same(word, "exec"))    action = EXEC;
X	      else if (the_same(word, "leave"))   action = LEAVE;
X	      else {
X		if (outfd != NULL)
X	          fprintf(outfd,
X	"filter (%s): Error on line %d of rules - action \"%s\" unknown\n",
X			username, line, word);
X	      }
X
X	      if (action == DELETE_MSG || action == LEAVE) {
X	        /** add this to the rules section and alloc next... **/
X
X	        rules[total_rules].action = action;
X		rules[total_rules].argument2[0] = '\0';	/* nothing! */
X	        total_rules++;
X	         
X	        if ((cond = (struct condition_rec *)
X		     malloc(sizeof(struct condition_rec))) == NULL) {
X		  if (outfd != NULL)
X	            fprintf(outfd,
X			"filter (%s): couldn't malloc first condition rec!\n",
X			username);
X	          return(-1);
X	        }
X	
X	        rules[total_rules].condition = cond;	/* hooked in! */
X	        state = NEXT_CONDITION;	
X	      }
X	      else {
X	        state = ACTION_ARGUMENT;
X	      }
X
X	      if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	        continue;
X
X	    }
X	
X	    if (state == ACTION_ARGUMENT) {
X	      strcpy(action_argument, word);
X
X	      /** add this to the rules section and alloc next... **/
X
X	      rules[total_rules].action = action;
X	      expand_macros(action_argument, rules[total_rules].argument2,line,
X			    printing_rules);
X	      total_rules++;
X	         
X	      if ((cond = (struct condition_rec *)
X		     malloc(sizeof(struct condition_rec))) == NULL) {
X		if (outfd != NULL)
X	          fprintf(outfd,
X			  "filter (%s): couldn't malloc first condition rec!\n",
X			  username);
X	        return(-1);
X	      }
X	
X	      rules[total_rules].condition = cond;	/* hooked in! */
X
X	      state = NEXT_CONDITION;
X	      if ((word = strtok(str, " ()[]'\"\t\n")) == NULL)
X	        continue;
X	    }
X	  }
X	}
X
X	return(0);
X}
SHAR_EOF
chmod 0444 filter/parse.c || echo "restore of filter/parse.c fails"
echo "x - extracting filter/rules.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > filter/rules.c &&
X
Xstatic char rcsid[] ="@(#)$Id: rules.c,v 2.4 89/03/25 21:45:17 syd Exp $";
X
X/*******************************************************************************
X *  The Elm Mail System  -  $Revision: 2.4 $   $State: Exp $
X *
X * 			Copyright (c) 1986, 1987 Dave Taylor
X * 			Copyright (c) 1988, 1989 USENET Community Trust
X *******************************************************************************
X * Bug reports, patches, comments, suggestions should be sent to:
X *
X *	Syd Weinstein - elm@dsinc.UUCP
X *			dsinc!elm
X *
X *******************************************************************************
X * $Log:	rules.c,v $
X * Revision 2.4  89/03/25  21:45:17  syd
X * Initial 2.2 Release checkin
X * 
X *
X ******************************************************************************/
X
X/** This file contains all the rule routines, including those that apply the
X    specified rules and the routine to print the rules out.
X
X**/
X
X#include <stdio.h>
X#include <pwd.h>
X#include <ctype.h>
X#include <time.h>
X#include <fcntl.h>
X
X#include "defs.h"
X#include "filter.h"
X
Xchar *listrule();
X
Xint
Xaction_from_ruleset()
X{
X	/** Given the set of rules we've read in and the current to, from, 
X	    and subject, try to match one.  Return the ACTION of the match
X            or LEAVE if none found that apply.
X	**/
X
X	register int iindex = 0, not, relation, try_next_rule, x;
X	struct condition_rec *cond;
X
X	while (iindex < total_rules) {
X	  cond = rules[iindex].condition;
X	  try_next_rule = 0;
X
X	  while (cond != NULL && ! try_next_rule) {
X	    
X	    not = (cond->relation < 0);
X	    relation = abs(cond->relation);
X	
X	    switch (cond->matchwhat) {
X
X	      case TO     : x = contains(to, cond->argument1); 		break;
X	      case FROM   : x = contains(from, cond->argument1); 	break;
X	      case SUBJECT: x = contains(subject, cond->argument1);	break;
X	      case LINES  : x = compare(lines, relation, cond->argument1);break;
X		       
X	      case CONTAINS: if (outfd != NULL) fprintf(outfd,
X       "filter (%s): Error: rules based on 'contains' are not implemented!\n",
X			    username);
X			    if (outfd != NULL) fclose(outfd);
X			    exit(0); 		
X
X	      case ALWAYS: not = FALSE; x = TRUE;			break;
X	    }
X
X	    if ((not && x) || ((! not) && (! x))) /* this test failed (LISP?) */
X	      try_next_rule++;
X	    else
X	      cond = cond->next;		  /* next condition, if any?  */
X	  }
X
X	  if (! try_next_rule) {
X	    rule_choosen = iindex;
X 	    return(rules[rule_choosen].action);
X	  }
X	  iindex++;
X	}
X
X	rule_choosen = -1;
X	return(LEAVE);
X}
X
X#define get_the_time()	if (!gotten_time) { 		  \
X			   thetime = time( (long *) 0);   \
X			   timerec = localtime(&thetime); \
X			   gotten_time++; 		  \
X			}
X
Xexpand_macros(word, buffer, line, display)
Xchar *word, *buffer;
Xint  line, display;
X{
X	/** expand the allowable macros in the word;
X		%d	= day of the month  
X		%D	= day of the week  
X	        %h	= hour (0-23)	 
X		%m	= month of the year
X		%r	= return address of sender
X	   	%s	= subject of message
X	   	%S	= "Re: subject of message"  (only add Re: if not there)
X		%t	= hour:minute 	
X		%y	= year		  
X	    or simply copies word into buffer. If "display" is set then
X	    instead it puts "<day-of-month>" etc. etc. in the output.
X	**/
X
X	struct tm *localtime(), *timerec;
X	long     time(), thetime;
X	register int i, j=0, gotten_time = 0, reading_a_percent_sign = 0;
X
X	for (i = 0; i < strlen(word); i++) {
X	  if (reading_a_percent_sign) {
X	    reading_a_percent_sign = 0;
X	    switch (word[i]) {
X
X	      case 'r' : buffer[j] = '\0';
X			 if (display)
X	 		   strcat(buffer, "<return-address>");
X			 else
X			   strcat(buffer, from);
X	                 j = strlen(buffer);
X			 break;
X
X	      case 's' : buffer[j] = '\0';
X			 if (display)
X	 		   strcat(buffer, "<subject>");
X			 else {
X			   strcat(buffer, "\"");
X			   strcat(buffer, subject);
X			   strcat(buffer, "\"");
X			 }
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'S' : buffer[j] = '\0';
X			 if (display)
X	 		   strcat(buffer, "<Re: subject>");
X			 else {
X			   if (! the_same(subject, "Re:")) 
X			     strcat(buffer, "\"Re: ");
X			   strcat(buffer, subject);
X			   strcat(buffer, "\"");
X			 }
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'd' : get_the_time(); buffer[j] = '\0';
X			 if (display)
X			   strcat(buffer, "<day-of-month>");
X			 else
X			   strcat(buffer, itoa(timerec->tm_mday,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'D' : get_the_time(); buffer[j] = '\0';
X			 if (display)
X			   strcat(buffer, "<day-of-week>");
X			 else
X			   strcat(buffer, itoa(timerec->tm_wday,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'm' : get_the_time(); buffer[j] = '\0';
X			 if (display)
X			   strcat(buffer, "<month>");
X			 else
X			   strcat(buffer, itoa(timerec->tm_mon+1,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'y' : get_the_time(); buffer[j] = '\0';
X			 if (display)
X			   strcat(buffer, "<year>");
X			 else
X			   strcat(buffer, itoa(timerec->tm_year,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 'h' : get_the_time(); buffer[j] = '\0';
X			 if (display)
X			   strcat(buffer, "<hour>");
X			 else
X			   strcat(buffer, itoa(timerec->tm_hour,FALSE));
X	                 j = strlen(buffer);
X			 break;
X
X	      case 't' : get_the_time(); buffer[j] = '\0';
X			 if (display)
X			   strcat(buffer, "<time>");
X		         else {
X			   strcat(buffer, itoa(timerec->tm_hour,FALSE));
X			   strcat(buffer, ":");
X			   strcat(buffer, itoa(timerec->tm_min,TRUE));
X			 }
X	                 j = strlen(buffer);
X			 break;
X
X	      default  : if (outfd != NULL) fprintf(outfd,
X   "filter (%s): Error on line %d translating %%%c macro in word \"%s\"!\n",
X			         username, line, word[i], word);
X			 if (outfd != NULL) fclose(outfd);
X			 exit(1);
X	    }
X	  }
X	  else if (word[i] == '%') 
X	    reading_a_percent_sign++;
X	  else 
X	    buffer[j++] = (word[i] == '_' ? ' ' : word[i]);
X	}
X	buffer[j] = '\0';
X}
X
Xprint_rules()
X{
X	/** print the rules out.  A double check, of course! **/
X
X	register int i = -1;
X	char     *whatname(), *actionname();
X	struct   condition_rec *cond;
X
X	if (outfd == NULL) return;	/* why are we here, then? */
X
X	while (++i < total_rules) {
X	  if (rules[i].condition->matchwhat == ALWAYS) {
X	    fprintf(outfd, "\nRule %d:  ** always ** \n\t%s %s\n", i+1,
X		 actionname(rules[i].action), listrule(rules[i].argument2));
X	    continue;
X	  }
X
X	  fprintf(outfd, "\nRule %d:  if (", i+1);
X
X	  cond = rules[i].condition;
X
X	  while (cond != NULL) {
X	    if (cond->relation < 0)
X	      fprintf(outfd, "not %s %s %s%s%s", 
X		      whatname(cond->matchwhat),
X		      relationname(- (cond->relation)),
X		      quoteit(cond->matchwhat),
X		      cond->argument1,
X		      quoteit(cond->matchwhat));
X	    else
X	      fprintf(outfd, "%s %s %s%s%s",
X		      whatname(cond->matchwhat),
X		      relationname(cond->relation),
X		      quoteit(cond->matchwhat),
X		      cond->argument1,
X		      quoteit(cond->matchwhat));
X
X	    cond = cond->next;
X
X	    if (cond != NULL) fprintf(outfd, " and ");
X	  }
X	    
X	  fprintf(outfd, ") then\n\t  %s %s\n", 
X		 actionname(rules[i].action), 
X		 listrule(rules[i].argument2));
X	}
X	fprintf(outfd, "\n");
X}
X
Xchar *whatname(n)
Xint n;
X{
X	static char buffer[10];
X
X	switch(n) {
X	  case FROM   : return("from");
X	  case TO     : return("to");
X	  case SUBJECT: return("subject");
X	  case LINES  : return ("lines");
X	  case CONTAINS: return("contains");
X	  default     : sprintf(buffer, "?%d?", n); return((char *)buffer);
X	}
X}
X
Xchar *actionname(n)
Xint n;
X{
X	switch(n) {
X	  case DELETE_MSG : return("Delete");
X	  case SAVE       : return("Save");
X	  case SAVECC     : return("Copy and Save");
X	  case FORWARD    : return("Forward");
SHAR_EOF
echo "End of part 8"
echo "File filter/rules.c is continued in part 9"
echo "9" > s2_seq_.tmp
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.