[alt.sources] A simple e-mail response server

bill@twwells.com (T. William Wells) (02/08/90)

I just finished rewriting and documenting my archive server code
and have decided to make it generally available.

This server is *simple*. It is so simple that the documentation
is over twice as large as the server!

On the other hand, if all you need is a simple way to make your
files available to others via e-mail, this is a much better deal
than trying to wade through either the decwrl or netlib servers.

BTW, if you do find that this server is too simple for your
tastes, I do have the other servers on my system. Send `help' to
comp-archives-server@twwells.com to find out how to get it.

echo x - README
sed 's/^X//' >README <<'*-*-END-of-README-*-*'
X			 The KISS server v1.0
X		    (As in: Keep It Simple, Stupid!)
X
XINTRODUCTION
X
XThis code implements a simple mail-response server and is public domain.  It
Xis designed for a UNIX system; at any rate, it depends on the Bourne shell
Xand a number of UNIX utilities.
X
XThough I may provide fixes and help, I make no promises or guarantees of any
Xkind. However, I'm always interested in input so feel free to let me know your
Xexperiences with this code.
X
XTo use it, you have to have some way of getting mail messages to the server.
XOne way is for your mail system to run the server on receipt of a message.
XAlternately, you can have the messages stored in a mailbox and have cron
Xstart a job to interpret the mailbox for you. As a last resort, you could put
Xsomething in your .profile to run the server every time you log on.  :-)
X
XWhen the server is given a message, it interprets it to determine what is
Xwanted and where to send it. Each request in the message is passed to a
Xprogram you supply, the "service program". The service program is typically a
Xshell script; you get to write it.  A simple example is provided later.
XThere is nothing preventing you from having more than one server address and
Xmultiple service programs; I do this.
X
XEach request in a message results in one mail message being returned to the
Xrequestor; there is no batching. Nor is there any kind of quota or scheduling.
X
XSERVER COMMANDS
X
XThe server recognizes five commands. They are:
X
Xpath <path>     This lets the requestor override the address that would
X		normally be extracted from the header.
X
Xhelp            This is equivalent to the command `send help'.
X
Xindex           This is equivalent to the command `send index'.
X
Xsend <whatever> The whatever is passed to the service program; thus its
X		interpretation is entirely up to you.
X
Xquit            Nothing past this point is interpreted. This is provided so
X		that the occasional lost soul whose signature contains a line
X		that looks like a command can still use the server without
X		getting a bogus response.
X
XThe server interpets the subject line of the message.
X
XThe server translates the characters (` ' " \) (excluding the parentheses) to
Xspaces before processing the message (this is just to simplify writing the
Xshell scripts; it would be a real pain to properly deal with the quoting
Xcharacters in a shell script).
X
XThe server considers a # as the start of a comment; everything from one to the
Xend of its line is ignored.
X
XIf there are no requests in the message, the server responds as if the
Xmessage had a help request in it.
X
XSend commands that are rejected by the service program are returned to the
Xrequestor in a separate message.
X
XIn deciding where to send the responses, the server considers the path
Xcommand and the Reply-To:, From:, and From_ header lines, in that order.  It
Xstops when it finds one of those and uses the address on that line.  Header
Xcontinuation lines are ignored.
X
XINSTALLATION
X
XWarning: I never followed these instructions since my copy of the server got
Xinstalled as I wrote it. This should work but I make no guarantees.
X
X	1) Put the server script wherever you want it run from.
X
XI have a special account for the mail system, so I've put it in the bin for
Xthat account: /home/mail/bin/server.
X
X	2) Write and install the mailrc to be used when sending server mail.
X
XWhether this step is necessary or not depends on the mailer you are using.
XWith mine, mailx, I want it to wait for the delivery agent to finish so that
Xthe server won't be running zillions of processes all at the same time.
XAccordingly, my mailrc contains one line: `set sendwait'.  I put this in the
Xlib directory of my mail account, /home/mail/lib/server.mailrc.
X
X	3) Edit the assignments at the top of the server script.
X
XThere are four defines you might want to change.
X
X    ADMIN is a mail address to send problem reports to. There aren't a lot of
X	    possible problems, but this lets the server have some chance to
X	    report them. I send them off to the `mail' account.
X
X    MAILRC is the file you installed in the previous step. If you didn't
X	    create one, make this /dev/null.
X
X    MAIL is the name of the program to deliver mail. It needs to have a -s
X	    option to specify the subject and to read the body of the message
X	    from standard in.
X
X    LOG is the name of a log file to write information about server requests
X	    to. If you don't want logging, set it to /dev/null. If you do want
X	    logging, you have to do two other things. First, you have to
X	    create the log file with permissions permitting world write and
X	    you have to arrange to truncate the log file periodically.
X
X	4) Create a service program.
X
XThe server itself doesn't know beans about how to get to your archived data.
XSo you have to write a program to do this.  The first argument to the program
Xis the archive name passed to the server script; the remaining arguments are
Xthe arguments to the send command.
X
XYour program should do two things: verify that the request is valid and then
Xgenerate the text that is to be returned as the response to the request.
X
XIf the request is invalid, your program should exit with a non zero exit code.
XIf it is valid, your program should exit with a sero exit code after having
Xprinted the text to standard output. Once you've created the program, you
Xshould put it somewhere convenient; since I have a separate account for
Xcomp.archives, I put the program in its bin, as /home/comparc/bin/dbsend.
X
XI expect that most service programs will be a shell script; that is what I use
Xfor comp.archives. Here is a script (not the one I use) that will suffice for
Xan archive where everything is contained in one directory. You should read the
Xcomments for hints in writing your own code:
X
X    # Fail if more than one name in the send request.
X
X    if [ $# -ne 2 ]; then
X	    exit 1
X    fi
X
X    # Fail if there is a .. in the name. This takes care of attempts like:
X    # `send ../../../../../../../../etc/passwd'.
X
X    # Note the quotes around each reference to $2. Think about what would
X    # happen with file name expansion and a `send *'....
X
X    if expr "$2" : '.*\.\.' >/dev/null; then
X	    exit 1
X    fi
X
X    # Put us in the proper directory.
X
X    cd /usr/archive
X
X    # Fail if the file doesn't exist or is empty.
X
X    if [ ! -s "$2" ]; then
X	    exit 1
X    fi
X
X    # Print the file.
X
X    cat "$2"
X
X    # Done, request satisfied.
X
X    exit 0
X
X	5) Set up your index and help responses.
X
XYour server should always respond to `send help' and `send index' commands.
X
XNote that the sample script doesn't explicitly deal with these commands; the
Xpresumption is that there are files `help' and `index' in the directory.
X
X	5) Connect the server to the mail system.
X
XIf your system has the ability to run a program in response to mail to an
Xaddress, all you have to do is to set up the mail alias to run the server
Xscript. You must run the server script with two arguments.  The first
Xargument is the name of the server. I just use the server alias. The second
Xis the path for the service script. You should use an absolute path name.
X
XIf you have a system which won't do this, all is not lost. You'll can write a
Xshort script and run it from cron. What you do is have the mail system store
Xthe messages in a mailbox somewhere and process them later. At one time, I
Xhad a script vaguely like:
X
X	MAILRC=/dev/null ; export MAILRC
X	while [ -s /usr/mail/comparcd ]; do
X		/usr/bin/mail -n -N -f /usr/mail/comparcd <<\+
X		pipe "/home/mail/bin/server comp-archives-server \
X		     /home/comparc/bin/dbsend"
X		q
X		+
X	done
X
Xand I ran it once an hour.  This is gross and ugly but it can be made to work.
X
X	7) Test it out!
X
XOnce you've gone through the installation, you should send a message to the
Xserver and see what it does. With any luck you'll get a response back.
X
X---
XBill                    { uunet | novavax | ankh } !twwells!bill
Xbill@twwells.com
*-*-END-of-README-*-*
echo x - Makefile
sed 's/^X//' >Makefile <<'*-*-END-of-Makefile-*-*'
X# Well, I don't really need a makefile for a shell script, but this does make
X# building the shar more reliable.
X
Xserver.shar : README Makefile server
X	shar README Makefile server >$@
X
Xclean :
X	rm server.shar
*-*-END-of-Makefile-*-*
echo x - server
sed 's/^X//' >server <<'*-*-END-of-server-*-*'
X# The KISS server. v1.0
X
X# You might want to change these...see the README for an explanation.
X
XADMIN=mail
XMAILRC=/home/mail/lib/server.mailrc ; export MAILRC
XMAIL=/usr/bin/mail
XLOG=/home/mail/logs/archive.log
X
X# Set up variables we'll use.
X
XT=/tmp/serv$$
XARC="$1"
XCMD="$2"
X
X# This'll zap any temp files created.
X
Xtrap "rm -f $T.[a-z]; exit 0" 0 1 2 3 15
X
X# If the archive name was not specified or if the server shell does not
X# exist, complain.
X
Xif [ $# -lt 2 -o ! -x "$CMD" ]; then
X	$MAIL -s "problem with $0 $*" $ADMIN
X	exit 0
Xfi
X
X# First thing, save the message in a temp file and turn tabs into spaces.
X# Also translate dangerous characters into something harmless.
X
Xtr '\011'\''`"\\' '     ' >$T.a
X
X# Construct a file from the message, suitable for running through the shell
X# to extract relevant info. The address parameters are changed into shell
X# variable assignments; the send requests are turned into echos.
X
Xsed -n \
X's/#.*//
Xs/  */ /g
Xs/ $//
X1,/^$/{
X	s/([^)]*)//g
X	/^From /s/^From \([^ ]*\).*/env='\''\1'\''/p
X	/^From: /s/^From: \(.*\)/from='\''\1'\''/p
X	/^Reply-To: /s/^Reply-To: \(.*\)/reply='\''\1'\''/p
X	/^Subject: /!d
X	s/^Subject: //
X}
Xs/^ //
X/^path /s/^path \([^ ]*\).*/path='\''\1'\''/p
X/^index$/s/.*/echo '\''"index"'\''/p
X/^help$/s/.*/echo '\''"help"'\''/p
X/^send /{
X	s/^send //
X	s/ /"'\'' '\''"/g
X	s/$/"'\''/
X	s/^/echo '\''"/p
X}
X/^quit$/q' <$T.a >$T.b
X
X# Next, run the file by the shell. The addresses are captured from the
X# assignments; the commands get redirected to a temp file
X
Xenv=''
Xfrom=''
Xreply=''
Xpath=''
Xaddr=''
X
X. $T.b >$T.c
X
X# Determine the address to send to.
X
Xif   [ -n "$path"  ]; then addr="$path"
Xelif [ -n "$reply" ]; then addr="$reply"
Xelif [ -n "$from"  ]; then addr="$from"
Xelif [ -n "$env"   ]; then addr="$env"
Xfi
X
X# If the address contains <stuff>, make 'stuff' the address.
X
Xbaddr=`expr "$addr" : '.*<\([^>]*\)>.*'`
Xif [ -n "$baddr" ]; then
X	addr="$baddr"
Xfi
X
X# No address means there is a problem.
X
Xif [ -z "$addr" ]; then
X	$MAIL -s "message with no address sent to $ARC" $ADMIN <$T.a
X	rm -f $T.[a-z]
X	exit 0
Xfi
X
X# Turn empty requests into a help request.
X
Xif [ ! -s $T.c ]; then
X	echo '"help"' >$T.c
Xfi
X
X# Sort and uniq the commands to run. We don't do this when capturing the
X# commands, since putting the . command into a pipe doesn't do what we want.
X# Then run a command and return its standard output to the requester. If the
X# command returns failure, save the command arguments in a file.
X
X>$T.e
Xsort -u $T.c \
X    | while read args; do
X	eval set -- $args
X	if $CMD $ARC "$@" >$T.d; then
X		$MAIL -s "Reply from $ARC re: send $*" "$addr" <$T.d
X		echo sent "$addr" $ARC $CMD "$@" >>$LOG
X	else
X		echo "$@" >>$T.e
X		echo fail "$addr" $ARC $CMD "$@" >>$LOG
X	fi
Xdone
X
X# If any commands failed, make a special return to the sender.
X
Xif [ -s $T.e ]; then
X	{
X		echo "The following requests could not be honored. Sorry."
X		echo
X		sed 's/^/        send /' $T.e
X	} | $MAIL -s "Reply from $ARC re: invalid requests" "$addr"
Xfi
X
X# We're done.
X
Xexit 0
*-*-END-of-server-*-*
exit