#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  doc/server.1 src/README src/REGISTRATION src/makefile
#   src/defs.h src/global.h src/list.h src/listserv.h src/serverd.h
#   src/start.h src/struct.h src/list.c src/listserv.c src/misc.c
#   src/sender.c src/serverd.c src/signals.c src/start.c src/tlock.c
#   redux flocks ulock config .awk .stats .grep .ignored setup
# Wrapped by tasos@bacardi on Wed Jul 24 21:33:50 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if [ ! -d doc ]; then
  mkdir doc
fi
if [ ! -d src ]; then
  mkdir src
fi
if test -f doc/server.1 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"doc/server.1\"
else
echo shar: Extracting \"doc/server.1\" \(14616 characters\)
sed "s/^X//" >doc/server.1 <<'END_OF_doc/server.1'
X.TH server 1 "Discussion List Server version 5.0"
X.SH NAME
Xstart \- start/stop the server system
X.SH SYNOPSIS
X\fBstart\fP [\fB-c\fP] [\fB-r\fP] [\fB-k\fP]
X.SH NAME
Xserverd \- background daemon
X.SH SYNOPSIS
X\fBserverd\fP [\fB-l load\fP]
X.SH NAME
Xlist \- process a specified discussion list
X.SH SYNOPSIS
X\fBlist -L\fP <\fBlist_alias\fP> [\fB-1\fP] [\fB-f\fP] [\fB-v\fP]
X.SH NAME
Xlistserv \- process requests sent to the server
X.SH SYNOPSIS
X\fBlistserv\fP [\fB-1\fP] {[\fB-r\fP <\fBlistserv command\fP>]}*
X.SH OVERVIEW
XThis is a system that implements various discussion lists with one
Xlist server. It is automated, and obliterates the need for user intervention
Xand maintenance of multiple aliases of the form "list, list-owner,
Xlist-request", etc.
X.SH DESCRIPTION:\ start
X\fIstart\fP is used to start or stop the discussion list server system.
XWhen starting the system, any
Xprevious server processes still running are killed, all necessary
Xlock files are created, any previous reports are archived into files with
Xextension \fI.acc\fP, new files and directories are created for any
Xnew discussion lists, and \fIserverd\fP is spawned. \fIstart\fP reads the
X\fIconfig\fP file (see below).
X.PP
XThe following command line options are recognized:
X.TP
X-c
XSuppress confirmation when killing processes.
X.TP
X-k
XJust kill any old server system processes and exit.
X.TP
X-r
XRestrict reporting to stdout.
X.SH DESCRIPTION:\ serverd
X\fIserverd\fP silently looks for any new messages for the list
Xserver and for any of the supported discussion lists. It then spawns
X\fIlistserv\fP and/or \fIlist\fP as necessary, and sleeps until the
Xmessage(s) is (are) processed. \fIserverd\fP reads the \fIconfig\fP file
X(see below).
X.PP
XThe following command line option is recognized:
X.TP
X-l load
XEnforce load restrictions; \fIserverd\fP will postpone spawning if the
Xsystem load is above the limit specified.
X.SH DESCRIPTION:\ list
X\fIlist\fP distributes the message(s) sent to \fIlist_alias@site\fP; the file
X".ignored" in the list's subdirectory is used to filter out any unwanted
Xmessages (see below). Messages from mailer daemons are forwarded to
X\fImanager\fP (see description of the \fIconfig\fP file), whereas messages
Xfrom non-subscribers are returned to the original senders (the -f flag
Xoverrides this).
X.PP
XThe following command line options are recognized:
X.TP
X-L list_alias
XProcess any messages sent to this \fIlist_alias\fP.
X.TP
X-1
XExecute only once; process the discussion list and return control to
X\fIserverd\fP; any new messages that may have arrived in the meantime will
Xbe processed at a later time.
X.TP
X-f
XForward any messages from non-subscribers to \fImanager\fP.
X.TP
X-v
XPrint version information.
X.SH DESCRIPTION:\ listserv
X\fIlistserv\fP processes user requests sent to \fIlistserv@site\fP.
XA request is a list of one or more 
Xcommands (each on a separate line) with possible arguments. The file
X".ignored" is used in the system's home directory to filter out unwanted
Xmessages. The sender is notified of the first invalid request; all subsequent
Xcommands are ignored. For each successfully completed command,
Xa confirmation is sent back to the sender.
X\fIlistserv\fP reads the \fIconfig\fP file. The following
Xcommands are recognized:
X.TP
X\fIhelp\fP [command]
XSend a help message on all commands or the selected one only.
X.TP
X\fIset\fP list option value
XSet subscriber preferences for \fIlist\fP;
Xoption can be:
X.in +2
X.sp
X\fImail\fP: set mail preferences.
X.in -2
X.sp
Xvalue can be:
X.in +2
X.sp
X\fIack\fP: send a copy of the current message to the original sender.
X.sp
X\fInoack\fP: do not send a copy of the current message to the original sender.
X.sp
X\fIneverack\fP: do not send a copy of the current message to the original
Xsender and never let him change the status again -- meant for peer lists.
X.sp
X\fIpostpone\fP: do not send any messages to the particular subscriber until
Xhe changes status again.
X.in -2
X.TP
X\fIsubscribe\fP list full_name
XSubscribe the sender to \fIlist\fP (note, his email address is used for
Xsubscription, not his \fIfull_name\fP). When subscribing a peer list, it
Xis recommended that it 'set list mail neverack'.
X.TP
X\fIunsubscribe\fP list
XRemove the sender from the specified \fIlist\fP.
X.TP
X\fIsignoff\fP list
XAlias of the unsubscribe request.
X.TP
X\fIrecipients\fP list
XGet a list of all subscribers to \fIlist\fP.
X.TP
X\fIreview\fP list
XAlias of the recipients request.
X.TP
X\fIinformation\fP list
XGet an informative message about a particular \fIlist\fP.
X.TP
X\fIstatistics\fP list [subscriber email address(es)]
XObtain a count of messages sent per subscriber to the specified \fIlist\fP,
Xor by those subscribers given as argument only.
X.TP
X\fIlists\fP
XObtain a list of discussion list addresses that are serviced by this system,
Xwith a small description of their purpose.
X.PP
XThe following command line options are recognized:
X.TP
X-1
XExecute only once; process any requests and return control to
X\fIserverd\fP; any new messages that may have arrived in the meantime will
Xbe processed at a later time.
X.TP
X-r command
XPlace a restriction on the specified server \fIcommand\fP as outlined above.
XIf the number of users on the system at the time the command is about
Xto be processed is above the limit
Xgiven in the \fIconfig\fP file (see the \fIrestriction\fP directive),
Xthe request is rejected \-\- meant for requests that may take a 
Xconsiderable amount
Xof resources such as the \fIstatistics\fP request \-\- this option may
Xbe repeated any number of times.
X.SH CONFIG
XThe server system is defined in the \fIconfig\fP file; an example
Xfile is provided with the system. The following keywords are recognized:
X.TP
X\fIserver listserv\fP@site command-line-options
XThis defines the list server; the first argument is the full email address
Xof the server (e.g. listserv@foo.edu) followed by any command line
Xoptions to be used (see \fIlistserv\fP above).
X.TP
X\fIlist\fP alias alias@site command-line-options
XThis defines a discussion list; the first argument is the list's alias
Xin /etc/aliases or /usr/lib/aliases (see below), followed by its full email
Xaddress, followed by any command line options to be used (see \fIlist\fP
Xabove).
X.TP
X\fIserverd\fP command-line-options
XThis defines the command line options that \fIstart\fP is supposed to use
Xwhen spawning \fIserverd\fP.
X.TP
X\fIrestriction\fP nusers
XIf a restriction is placed on a listserv command (via a -r command line
Xoption), this request defines the threshold number of users, above which
Xthe restriction will take effect.
X.TP
X\fImanager\fP email-address@site
XThis defines the recipient of all error messages; \fIemail-address\fP
Xcan be any valid user name that can be reached via email by your system.
X.TP
X\fIcomment server\fP # Actual comment
X.TP
X\fIcomment\fP list_alias # Actual comment
XA 'Comment:' line is included in every outgoing list/server message, and the
Xtext following the pound sign will be copied every time; note that the pound
Xsign is mandatory but is not copied in the header
Xof the message. A list's comment is also used for the 'lists' request to
Xspecify the purpose of the particular discussion list.
X.TP
X\fIfrequency\fP seconds
XHow often should \fIserverd\fP check for new mail.
X.TP
X\fIoption\fP keyword
XThis defines a series of system-dependent options; valid keywords are:
X.in +2
X.sp
X\fIsysv_ps\fP: the system will use the System V version of ps.
X.sp
X\fIbsd_ps\fP: the system will use the BSD version of ps.
X.sp
X\fIbsd_mail\fP: define it if BSD (UCB) mail is available on your system; if this
Xis the case, make sure that /usr/ucb/mail is the path to it.
X.sp
X\fIbad_telnet\fP: if the mail method used (see below) is 'telnet' and 
Xthe system seems to send out only one message and then go to sleep, use
Xthis option.
X.in -2
X.TP
X\fImailmethod\fP method [arguments]
XEvery outgoing
Xmessage should begin with a line of the form:
X.in +2
X.sp
XFrom listserv@site ...
X.sp
Xor
X.sp
XFrom list_alias@site ...
X.sp
X.in -2
Xwhich depends on the mail \fImethod\fP used:
X.in +2
X.sp
X\fItelnet\fP: the recommended method.
X.sp
X\fIenv_var\fP: usually followed by \fILOGNAME /bin/mail\fP,
Xor \fILOGNAME /usr/lib/sendmail -t\fP (-t is mandatory).
X.in -2
X.SH SYSTEM\ SETUP
XThe server system is installed under the /usr/server directory, or
Xany other place with /usr/server as a link to it; a
X\fIserver\fP user account has to be setup. Then, the following alias
Xhas to be defined in /etc/aliases or /usr/lib/aliases (depending
Xon your system) for listserv:
X.sp
X.in +2
Xlistserv: "|/bin/cat >> /usr/server/requests"
X.in -2
X.sp
XThe server has to be defined in the \fIconfig\fP file by
Xadding a 'server' directive, as well as any other preferences (serverd,
Xrestriction, manager, comment server, frequency, option, mailmethod).
XOnce the system is loaded, you will have to run the script \fIsetup\fP;
Xthis will ensure that all necessary files are present. Before starting the
Xsystem, you may wish to edit the ".ignored" file in /usr/server.
XFinally, experiment with the various mail methods described above, until
Xyou get the proper "From " line in the beginning of the outgoing message.
X.SH ADDING\ A\ NEW\ LIST
XTo add a new list, first shut the system down by executing
X.sp
X.in +2
X\fI% start -k\fP
X.in -2
X.sp
XThen edit the \fIconfig\fP file and add a line defining the
Xnew list -- you may wish to add a comment also.
XA new alias has to be set up in /etc/aliases or /usr/lib/aliases
Xof the following form:
X.nf
X.sp
Xlist_alias: "|/bin/cat >> /usr/server/lists/LIST_ALIAS/mail"
X.sp
X.fi
XNote that \fIlist_alias\fP is all lower case and \fILIST_ALIAS\fP is all
Xupper case.
XFinally, restart the system. At this point, the file ".ignored" in the
X/usr/server/lists/LIST_ALIAS directory may be edited to add more
Xunwanted senders. You should also edit the files ".welcome" and ".info"
Xin the list's subdirectory; The former is included in the return message
Xfor a new subscription request for that list, and the latter
Xis included in the returned message for an 'information' request for that
Xlist.
X.SH .IGNORED
XAs described before, the system's home directory as well as every list's
Xsubdirectory contains a ".ignored" file which is used to filter out messages
Xsent by certain users. The default file contains entries for root, server,
Xbin, sys and uucp; you may wish to add an entry for every list alias that is
Xdefined on your system.
X.SH COMMUNICATING
XUsers send requests to \fIlistserv@site\fP and public messages to the
Xvarious \fIlist_alias@site\fP.
X.SH REPORTS
XAll programs report to their own files; these are:
X.sp
X/usr/server/.report.start: generated by \fIstart\fP every time the system
Xis started.
X.sp
X/usr/server/.report.daemon: messages from \fIserverd\fP every time new mail has
Xarrived.
X.sp
X/usr/server/.report.server: messages from \fIlistserv\fP every time new 
Xrequests are processed.
X.sp
X/usr/server/.report.list: error messages from \fIlist\fP when attempting to
Xprocess public messages.
X.sp
X/usr/server/lists/LIST_ALIAS/.report.list: messages from \fIlist\fP when
Xprocessing public messages.
X.sp
X/usr/server/.*.acc: accumulated reports since the system was first installed.
X.sp
X/usr/server/lists/*/.*.acc: accumulated reports for each discussion list.
X.PP
XEvery report includes a time stamp; every list and server report also
Xincludes the actual sender of the message, and every server report
Xincludes all requests made by the sender.
X.PP
XWhenever a program dies abnormally (and BSD mail is present) a message
Xis sent to 'manager'. The message is usually sent by \fIserverd\fP which
Xthen exits. The daemon dies with a different message according to the exit
Xstatus of the child; the various error conditions that may occur are:
X.TP
XCould not open file
XCheck protections, run setup and \fIstart\fP.
X.TP
XCould not lock file
XRun tlock, and if necessary, ulock.
X.TP
XCommand line option error
XCheck the \fIconfig\fP file and restart.
X.TP
XSyntax error in file
XCheck \fIconfig\fP, all reports and .subscriber files.
X.TP
XCould not spawn
XNo more processes.
X.TP
XReceived system signal
XSIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGBUS, SIGSEGV.
X.SH FILES
X.TP
X/usr/server/.awk
Xawk program for formatting the 'statistics' request.
X.TP
X/usr/server/.grep
Xscript used for counting the number of messages send by a subscriber.
X.TP
X/usr/server/.ignored
XList of unwanted senders.
X.TP
X/usr/server/.lock.list
XLock file for \fIlist\fP.
X.TP
X/usr/server/.lock.server
XLock file for \fIlistserv\fP.
X.TP
X/usr/server/.lock.serverd
XLock file for \fIserverd\fP.
X.TP
X/usr/server/.rep.server.acc
XArchived \fIlistserv\fP messages.
X.TP
X/usr/server/.rep.serverd.acc
XArchived \fIserverd\fP messages.
X.TP
X/usr/server/.rep.start.acc
XArchived \fIstart\fP messages.
X.TP
X/usr/server/.report.daemon
XCurrent \fIserverd\fP messages.
X.TP
X/usr/server/.report.list
XError messages from \fIlist\fP.
X.TP
X/usr/server/.report.server
XCurrent \fIlistserv\fP messages.
X.TP
X/usr/server/.report.start
XLast \fIstart\fP action (system started or shut down).
X.TP
X/usr/server/.stats
XScript used for the 'statistics' request.
X.TP
X/usr/server/config
XThe configuration file.
X.TP
X/usr/server/list
XThe discussion list program.
X.TP
X/usr/server/listserv
XThe system's server.
X.TP
X/usr/server/requests
XNewly arrived user requests.
X.TP
X/usr/server/serverd
XThe system's daemon.
X.TP
X/usr/server/setup
XSetup script.
X.TP
X/usr/server/start
XThe system's housekeeping program.
X.TP
X/usr/server/tlock
XChecks for any locked files.
X.TP
X/usr/server/lists/LIST_ALIAS/.headers
XAn archive of who sent each message.
X.TP
X/usr/server/lists/LIST_ALIAS/.ignored
XA list of unwanted senders.
X.TP
X/usr/server/lists/LIST_ALIAS/.info
XText sent on an 'information' request.
X.TP
X/usr/server/lists/LIST_ALIAS/.rep.list.acc
XArchived messages from \fIlist\fP.
X.TP
X/usr/server/lists/LIST_ALIAS/.report.list
XCurrent messages from \fIlist\fP.
X.TP
X/usr/server/lists/LIST_ALIAS/.subscribers
XA list of all subscribers along with preferences.
X.TP
X/usr/server/lists/LIST_ALIAS/.welcome
XText sent on a 'subscribe' request.
X.TP
X/usr/server/lists/LIST_ALIAS/mail
XNewly arrived public messages to be distributed.
X.TP
X/usr/server/lists/LIST_ALIAS/mbox
XAn archive of all messages sent to date.
X.SH LIMITATIONS
X.TP
X\-
XUp to 10 discussion lists can be supported.
X.SH REQUIREMENTS
XThe $path for the server account should include all necessary paths to
Xcut(1), paste(1), awk(1), grep(1), uptime(1) and telnet(1).
XIn addition, uptime(1) should report the number of users and the system
Xload; otherwise \fIserverd\fP cannot be run with the -l option, and
X\fIlistserv\fP cannot be run with the -r option.
X.SH AUTHOR
X.nf
XAnastasios C. Kotsikonas, Boston University.
XCopyright (c) 1991, Anastasios Kotsikonas
XComments to tasos@cs.bu.edu or tasos@bu-pub.bu.edu
X.fi
END_OF_doc/server.1
if test 14616 -ne `wc -c <doc/server.1`; then
    echo shar: \"doc/server.1\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/README\"
else
echo shar: Extracting \"src/README\" \(4273 characters\)
sed "s/^X//" >src/README <<'END_OF_src/README'
X
X
X                    DISCUSSION LIST SERVER SYSTEM
X                    -----------------------------
X
X             Copyright 1991 (c), Anastasios C. Kotsikonas
X
X                           tasos@cs.bu.edu
X                         tasos@bu-pub.bu.edu
X                          Boston University
X                             July 22 1991
X
XAGREEMENT: This software can be used and distributed freely as long as you
Xdo not remove or alter the Copyright notice in the file defs.h -- this notice
Xis #define'd in the symbol VERSION. Although you may alter the code provided,
Xyou may not alter the functions create_header() in list.c and listserv.c.
XBy using this software you are bound by this agreement.
X
X            The discussion list server system includes 28 files: 
X
X REGISTRATION	-- registration form for using this software
X server.1	-- the man page for the system (use nroff -man)
X setup		-- a script to be run before starting for the first time
X README		-- this file
X redux		-- script that reduces the size of mbox by removing unnecessary
X		   fields from the header of each message
X ulock		-- in conjunction with the 'flocks' file, this utility
X 		   removes all locked files (unlocks them).
X flocks		-- shell script to remove locked files.
X global.h	-- global variables definitions
X struct.h	-- defines the server structure
X defs.h		-- general definitions
X tlock.c	-- tests for locks, i.e. whether any server programs are running
X		   on another machine via NFS
X list.h		-- specific definitions for list.c
X list.c		-- the list's server
X listserv.h	-- specific definitions for listserv.c
X listserv.c	-- server for individual requests
X serverd.h	-- specific definitions for serverd.c
X serverd.c	-- parent program that spawns list or listserv
X start.h	-- specific definitions for start.c
X start.c	-- does housekeeping before spawning serverd, makes sure that
X		   files exist, kills any running server processes, etc.
X signals.c	-- signal processing routines
X sender.c	-- sender address manipulation routines
X misc.c		-- general purpose routines
X makefile	-- to build your own server
X .awk		-- awk program used for the 'statistics' and 'recipients'
X		   listserv commands
X .stats		-- shell script used for the 'statistics' listserv command
X .grep		-- shell program used for the 'statistics' listserv command
X .ignored	-- a list of email addresses whose messages are ignored
X config		-- the system's configuration file.
X
X
X               |-->-----------> START
X               |                  |
X               |                  |
X               ^          (spawns-and-dies)
X               |                  |
X               |                  |
X               |--<--restart--- SERVERD <--shutdown-<--|
X                                  /\                   r
X                                 /  \                  e
X                                /    \                 s
X                    (spawns either one as necessary)   t       
X                              /        \               a
X                             /          \              r
X                           LIST        LISTSERV --->---t
X
X
XThe diagram shows that 'start' spawns 'serverd' and then dies, and that
X'listserv' may request 'serverd' to die (shutdown) or request that the
Xsystem is restarted, in which case 'serverd' spawns 'start' and dies.
X
XEnjoy!
X
XCOMPILING: Do not use the gcc preprocessor; the code is written in ANSI C
Xand you will have to port the code if there is no ANSI C compiler on your
Xsystem.
X
XRevision history:
X
XVersion      Date     Status    Comments
X------------------------------------------------------------------------------
X 3.45      12/20/90  Outdated  First version; bugs with listserv
X 3.67      01/03/91  Outdated  Bugs fixed
X 3.68      01/04/91  Outdated  v3.67 + tlock utility
X 4.0       04/09/91  Exprmntl  v3.68 + STATISTICS listserv command
X 4.1       04/16/91  Outdated  v4.0 + redux utility
X 4.2       05/02/91  Outdated  v4.1 w/ optimized source code, better doc.
X 4.21      05/03/91  Outdated  v4.2 w/ redux which was left out by mistake
X 4.3       05/03/91  Outdated  v4.21 w/ better mailer-daemon msg handling
X 4.4       05/22/91  Exprmntl  v4.3 w/ enhanced tlock,control,start,listserv
X 5.0	   07/17/91  Released  v4.4 + support for multiple lists
END_OF_src/README
if test 4273 -ne `wc -c <src/README`; then
    echo shar: \"src/README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/REGISTRATION -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/REGISTRATION\"
else
echo shar: Extracting \"src/REGISTRATION\" \(288 characters\)
sed "s/^X//" >src/REGISTRATION <<'END_OF_src/REGISTRATION'
X
X		  DISCUSSION LIST SERVER SYSTEM
X		  -----------------------------
X
X			Copy Registration
X
X		tasos@cs.bu.edu, tasos@bu-pub.bu.edu
X
XIf you use this software, please email me the following information:
X
XName:
XEmail address:
XOrganization:
XAddress:
XPhone:
XType of computer system:
XComments:
END_OF_src/REGISTRATION
if test 288 -ne `wc -c <src/REGISTRATION`; then
    echo shar: \"src/REGISTRATION\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/makefile\"
else
echo shar: Extracting \"src/makefile\" \(1237 characters\)
sed "s/^X//" >src/makefile <<'END_OF_src/makefile'
XHOMEDIR = /usr/server
XCC = cc
XCFLAGS=-g
XARCHIVED = signals.o sender.o misc.o
XARCHIVE_LIB = libserver.a
X
Xall:   listserv list serverd start tlock
X
Xlistserv: $(HOMEDIR)/listserv
X
Xlist: $(HOMEDIR)/list
X
Xserverd: $(HOMEDIR)/serverd
X
Xstart: $(HOMEDIR)/start
X
Xtlock: $(HOMEDIR)/tlock
X
X$(HOMEDIR)/listserv:	$(HOMEDIR)/listserv listserv.o $(ARCHIVE_LIB)
X	$(CC) $(CFLAGS) listserv.o $(ARCHIVE_LIB) -o $(HOMEDIR)/listserv
X
X
X$(HOMEDIR)/list:	$(HOMEDIR)/list list.o $(ARCHIVE_LIB)
X	$(CC) $(CFLAGS) list.o $(ARCHIVE_LIB) -o $(HOMEDIR)/list
X
X$(HOMEDIR)/serverd:	$(HOMEDIR)/serverd serverd.o $(ARCHIVE_LIB)
X	$(CC) $(CFLAGS) serverd.o $(ARCHIVE_LIB) -o $(HOMEDIR)/serverd -lm
X
X$(HOMEDIR)/start:	$(HOMEDIR)/start start.o $(ARCHIVE_LIB)
X	$(CC) $(CFLAGS) start.o $(ARCHIVE_LIB) -o $(HOMEDIR)/start
X
X$(HOMEDIR)/tlock:	$(HOMEDIR)/tlock tlock.o
X	$(CC) $(CFLAGS) tlock.o -o $(HOMEDIR)/tlock
X
Xlistserv.o:	defs.h listserv.h struct.h global.h
X
Xlist.o:		defs.h list.h struct.h global.h
X
Xserverd.o:	defs.h struct.h global.h
X
Xstart.o:	defs.h start.h struct.h global.h
X
Xsender.o:	defs.h
X
Xsignals.o:	defs.h struct.h
X
Xmisc.o:		defs.h struct.h
X
Xtlock.o:	defs.h
X
X$(ARCHIVE_LIB):	$(ARCHIVED)
X	ar cr $(ARCHIVE_LIB) $(ARCHIVED)
X	ar ts $(ARCHIVE_LIB)
X#	ranlib $(ARCHIVE_LIB)
END_OF_src/makefile
if test 1237 -ne `wc -c <src/makefile`; then
    echo shar: \"src/makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/defs.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/defs.h\"
else
echo shar: Extracting \"src/defs.h\" \(3836 characters\)
sed "s/^X//" >src/defs.h <<'END_OF_src/defs.h'
X/*
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in this file;
X  this notice is #define'd in the symbol VERSION. Although you may alter
X  the code provided, you may not alter the functions create_header() in
X  list.c and listserv.c. By using this software you are bound by this
X  agreement.
X  COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas
X
X  General system definitions.
X
X  WARNING: Do not use the gcc preprocessor.
X
X  WARNING: DO NOT CHANGE THE NAME OF THE 'SERVERD', 'LIST' or 'LISTSERV'
X  PROGRAMS in the #define's below. If you do so, make sure that command line
X  options are separated by at least a space from the filename.
X  DO NOT INCLUDE ANY CHARACTERS SUCH AS &, |, <, >. etc.
X
X  Preserve any quotes and new lines that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#define VERSION		  "5.0 -- Copyright 1991 (c), Anastasios Kotsikonas"
X#define PATH              "/usr/server
X#define COMPLETE_FILE(f)  fprintf (f, ".\nQUIT\n")
X#define TELNET		  "telnet `hostname` 25 > /dev/null 2>&1"
X#define BINMAIL		  "/bin/mail > /dev/null 2>&1"
X#define SUBSCRIBERS       ".subscribers"
X#define HEADERS		  ".headers"
X#define RESTRICTED        ".restricted"
X#define IGNORED           ".ignored"
X#define INFO_FILE         ".info"
X#define WELCOME_FILE      ".welcome"
X#define REPORT_LIST       ".report.list"
X#define REPORT_SERVER     PATH/.report.server"
X#define REPORT_SERVERD    PATH/.report.daemon"
X#define CONFIG		  PATH/config"
X#define LIST              PATH/list -1 -L "
X#define SERVER            PATH/listserv -1 "
X#define LIST_MAIL_FILE	  "mail"
X#define SERVER_MAIL_FILE  PATH/requests"
X#define SERVERD           PATH/serverd"
X#define START		  PATH/start"/* Do not give ANY command line options */
X#define START_OPTIONS	  "-cr"		/* provide them here */
X#define SERVERD_LOCK_FILE PATH/.lock.serverd"
X#define LIST_LOCK_FILE    PATH/.lock.list"
X#define SERVER_LOCK_FILE  PATH/.lock.server"
X#define CUT		  "cut"
X#define AWK               "awk" /* Do a 'which awk' for proper path */
X#define UPTIME            "uptime"  /* 'which uptime' */
X#define MAILER_DAEMON     "MAILER|MAILER-DAEMON|MAILER-DEAMON|MAILER-DEMON|\
XDAEMON|DEAMON|DEMON|POSTMASTER|POST-MASTER"
X			  /* Preserve upper case; put all possibilities above
X			     separated by a '|'; all of the above entries will
X			     be used to identify mailer daemon messages so
X			     they can get special treatment */
X#define UCB_MAIL	  "/usr/ucb/mail" /* Path to UCB mail program. Remove
X			  it if UCB mail is not installed on your system,
X			  in which case no mail is sent to MANAGER by 'serverd'
X			  or when the programs receive various signals. */
X
X/*
X  These #define's should not be altered.
X*/
X
X#define DEFAULT_SERVER_ADDRESS 	  "listserv"
X#define DEFAULT_SERVER_CMDOPTIONS ""
X#define DEFAULT_SERVER_COMMENT	  "Boston University List Server"
X#define DEFAULT_MANAGER		  "server"
X#define MAX_LINE	  156
X#define EOS		  '\0'
X#define RESET(var)	  (var[0]) = EOS
X#define BSD_PS		  0x01
X#define	SYSV_PS		  0x02
X#define	USE_TELNET	  0x04
X#define USE_ENV_VAR	  0x08
X#define USE_MY_SYSTEM	  0x10
X#define BSD_MAIL	  0x20
X#define START_OF_MESSAGE  "From " /* UNIX mail messages start w/ this string */
X#define ACK		  "ACK"   /* Send message back to sender */
X#define NOACK		  "NOACK" /* Do not send message back to sender */
X#define NEVERACK	  "NEVERACK"  /* For peer lists */
X#define POSTPONE	  "POSTPONE"  /* Postpone sending mail */
X#define MAX_SIGNAL	  NSIG    /* Highest system signal caught */
X#define BOOLEAN		  unsigned short int
X#define TRUE		  1
X#define FALSE		  0
X#define SUBSCRIBED	  TRUE    /* Subscribed sender */
X#define NOTSUBSCRIBED	  FALSE   /* Sender is not subscribed */
X#define NEW_ARRIVAL	  "\n--- NEW MAIL HAS ARRIVED ---\n"
X#define MAX_LISTS	  10
END_OF_src/defs.h
if test 3836 -ne `wc -c <src/defs.h`; then
    echo shar: \"src/defs.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/global.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/global.h\"
else
echo shar: Extracting \"src/global.h\" \(698 characters\)
sed "s/^X//" >src/global.h <<'END_OF_src/global.h'
X/*
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in the file defs.h;
X  this notice is #define'd in the symbol VERSION. Although you may alter
X  the code provided, you may not alter the functions create_header() in
X  list.c and listserv.c. By using this software you are bound by this
X  agreement.
X  COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas
X*/
X
Xchar subscribersf [MAX_LINE];
Xchar headersf [MAX_LINE];
Xchar restrictedf [MAX_LINE];
Xchar ignoredf [MAX_LINE];
Xchar list_mail_f [MAX_LINE];
Xchar report_listf [MAX_LINE];
Xchar server_ignoredf [MAX_LINE];
Xchar infof [MAX_LINE];
Xchar welcomef [MAX_LINE];
XSYS  sys;
END_OF_src/global.h
if test 698 -ne `wc -c <src/global.h`; then
    echo shar: \"src/global.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/list.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/list.h\"
else
echo shar: Extracting \"src/list.h\" \(1832 characters\)
sed "s/^X//" >src/list.h <<'END_OF_src/list.h'
X/* 
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in the file defs.h;
X  this notice is #define'd in the symbol VERSION. Although you may alter
X  the code provided, you may not alter the functions create_header() in
X  list.c and listserv.c. By using this software you are bound by this
X  agreement.
X  COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas
X
X  Below are the #define's pertinent to list.c
X
X  Preserve any quotes and new lines that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#define MAIL_COPY         ".messages"
X#define MSG               ".msg"
X#define MAILFORWARD       ".mailforward"
X#define MSG_NO            ".msgno"
X#define MBOX              "mbox"            /* place to save list's messages */
X
Xchar mboxf [MAX_LINE];
Xchar msg_nof [MAX_LINE];
Xchar mailforwardf [MAX_LINE];
Xchar msgf [MAX_LINE];
Xchar mail_copyf [MAX_LINE];
Xchar *list_alias = NULL; /* arg to the -L command option */
X
XFILE *mail        = NULL; /* Source of messages */
XFILE *report      = NULL; /* Progress report to the administrator */
XFILE *subscribers = NULL; /* List of subscribers */
XFILE *restricted  = NULL; /* List of people whose messages require special
X                              handling */
XFILE *ignored     = NULL; /* List of people whose messages are ignored */
XFILE *msg_no      = NULL; /* Last message count */
XFILE *headers     = NULL; /* File containing headers of messages only */
X
XBOOLEAN send_to_subscribers = TRUE; /* -r option off */
XBOOLEAN execute_once        = FALSE;/* -1 option off */
XBOOLEAN errors_to_manager   = FALSE;/* -f option off */
Xint     returned_msg = 0;           /* Counts the invalid messages */
Xint     public_msg = 0;             /* Counts the public messages */
Xint     listid = -1;
END_OF_src/list.h
if test 1832 -ne `wc -c <src/list.h`; then
    echo shar: \"src/list.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/listserv.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/listserv.h\"
else
echo shar: Extracting \"src/listserv.h\" \(1821 characters\)
sed "s/^X//" >src/listserv.h <<'END_OF_src/listserv.h'
X/*
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in the file defs.h;
X  this notice is #define'd in the symbol VERSION. Although you may alter
X  the code provided, you may not alter the functions create_header() in
X  list.c and listserv.c. By using this software you are bound by this
X  agreement.
X  COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas
X
X  Below are the #define's pertinent to listserv.c
X
X  Preserve any quotes and new lines that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#define MAIL_COPY         PATH/.messages"
X#define OLD_SUBSCRIBERS   PATH/.oldsubscribers"
X#define MAILFORWARD       PATH/.mailforward"
X#define MSG_NO            PATH/.msgno"
X#define RECIP_FILE	  ".recip"
X#define AWK_PROG	  PATH/.awk"
X#define STATS_PROG	  PATH/.stats"
X#define SERVER_MBOX       PATH/mbox.server"/* place to save server's messages */
X#define STATS_FILE        "/tmp/stats"
X#define USERS_FILE	  "/tmp/users"
X#define MAX_COMMANDS      12
X
Xchar recipf [MAX_LINE];	  /* path to list + RECIP_FILE */
X
XFILE *mail        = NULL; /* Source of messages */
XFILE *report      = NULL; /* Progress report to the administrator */
XFILE *subscribers = NULL; /* List of subscribers */
XFILE *ignored     = NULL; /* List of people whose messages are ignored */
XFILE *msg_no      = NULL; /* Last message count */
X
Xint     listid = -1;
Xint     request_no = 0;             /* Counts the public messages */
Xint     restricted_commands = 0;    /* Mask of restrictions */
Xint     nlists = -1;                /* # of lists defined in CONFIG */
XBOOLEAN one_rejection = FALSE;
XBOOLEAN restart_sys = FALSE;
X
Xstruct {
X  char *name;
X  int  mask;
X  void (*func)();
X} commands [MAX_COMMANDS];          /* Set of recognizable commands */
END_OF_src/listserv.h
if test 1821 -ne `wc -c <src/listserv.h`; then
    echo shar: \"src/listserv.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/serverd.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/serverd.h\"
else
echo shar: Extracting \"src/serverd.h\" \(613 characters\)
sed "s/^X//" >src/serverd.h <<'END_OF_src/serverd.h'
X/*
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in the file defs.h;
X  this notice is #define'd in the symbol VERSION. Although you may alter
X  the code provided, you may not alter the functions create_header() in
X  list.c and listserv.c. By using this software you are bound by this
X  agreement.
X  COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas
X
X  Below are the #define's pertinent to serverd.c
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#define LOAD_FILE	 PATH/.load"
X#define MAX_TRIES	 10      /* wait MAX_TRIES * 30 seconds */
END_OF_src/serverd.h
if test 613 -ne `wc -c <src/serverd.h`; then
    echo shar: \"src/serverd.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/start.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/start.h\"
else
echo shar: Extracting \"src/start.h\" \(854 characters\)
sed "s/^X//" >src/start.h <<'END_OF_src/start.h'
X/*
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in the file defs.h;
X  this notice is #define'd in the symbol VERSION. Although you may alter
X  the code provided, you may not alter the functions create_header() in
X  list.c and listserv.c. By using this software you are bound by this
X  agreement.
X  COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas
X
X  Below are the #define's pertinent to start.c
X
X  Preserve any quotes that appear below; change only path names.
X
X  ALWAYS SPECIFY ABSOLUTE PATHS.
X
X*/
X
X#define REPORT_LIST_ACC    ".rep.list.acc"
X#define REPORT_SERVER_ACC  PATH/.rep.server.acc"
X#define REPORT_SERVERD_ACC PATH/.rep.serverd.acc"
X#define REPORT_START_ACC   PATH/.rep.start.acc"
X#define REPORT_START	   PATH/.report.start"
X
Xchar report_list_accf [MAX_LINE];
END_OF_src/start.h
if test 854 -ne `wc -c <src/start.h`; then
    echo shar: \"src/start.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/struct.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/struct.h\"
else
echo shar: Extracting \"src/struct.h\" \(1693 characters\)
sed "s/^X//" >src/struct.h <<'END_OF_src/struct.h'
X/*
X  AGREEMENT: This software can be used and distributed freely as long
X  as you do not remove or alter the Copyright notice in the file defs.h;
X  this notice is #define'd in the symbol VERSION. Although you may alter
X  the code provided, you may not alter the functions create_header() in
X  list.c and listserv.c. By using this software you are bound by this
X  agreement.
X  COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas
X*/
X
Xtypedef struct {
X  struct {
X    char alias [MAX_LINE],	/* as it appears in the aliases file */
X	 comment [MAX_LINE],	/* Comment: line in the outgoing mail message */
X         address [MAX_LINE],	/* full email address of the list */
X         cmdoptions [MAX_LINE];	/* list-specific command line options */
X  } lists [MAX_LISTS];		/* structure for each list */
X  struct {
X    char address [MAX_LINE],	/* full email address of the server */
X	 comment [MAX_LINE],	/* Comment: line in the outgoing mail message */
X         cmdoptions [MAX_LINE],	/* listserv-specific command line options */
X	 password [MAX_LINE];	/* for 'shutdown' and 'restart' */
X  } server;			/* structure for the server */
X  struct {
X    char *method;		/* mail method to use */
X    char env_var [MAX_LINE];	/* environment variable to use */
X    char mail_prog [MAX_LINE];	/* which mail program to use */
X  } mail;			/* mail method structure */
X  char serverd_cmdoptions [MAX_LINE];/* command line options */
X  char manager [MAX_LINE];	/* manager of the system */
X  char arg [MAX_LINE];		/* temporary storage */
X  int users;			/* used by listserv when -r is given */
X  int frequency;		/* how often to read mail */
X  unsigned char options;	/* various flags: BSD_PS, USE_TELNET, etc. */
X} SYS;
END_OF_src/struct.h
if test 1693 -ne `wc -c <src/struct.h`; then
    echo shar: \"src/struct.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/list.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/list.c\"
else
echo shar: Extracting \"src/list.c\" \(25371 characters\)
sed "s/^X//" >src/list.c <<'END_OF_src/list.c'
X/*
X  ----------------------------------------------------------------------------
X  |		   DISCUSSION LIST MAIL-DISTRIBUTION PROGRAM		     |
X  |									     |
X  |				 Version 5.0				     |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |									     |
X  |		       Written by Anastasios Kotsikonas                      |
X  |		    (tasos@cs.bu.edu, tasos@bu-pub.bu.edu)		     |
X  |	                                                                     |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the Copyright notice in the file defs.h;   |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header() in    |
X  | list.c and listserv.c. By using this software you are bound by this      |
X  | agreement.                                                               |
X  | COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas                  |
X  ----------------------------------------------------------------------------
X
X  NOTE: Anything appearing in capital letters refers to #define's in the
X  header files provided.
X
X  PURPOSE: Create a discussion list. Members of the list send messages to
X  it, and each of these messages is forwarded to the rest of the members.
X  Any message(s) not from subscribers are returned to the original senders,
X  or can be forwarded to MANAGER. Messages from "undesired" senders can be
X  ignored by placing their email addresses in the IGNORED file (this would
X  usually include root and the list's login name to avoid infinite loops).
X
X  OVERVIEW: When the program is killed or it abnormally dies, a message
X  is sent to MANAGER (if using UCB mail) along with a copy of the current 
X  report file (more on that below). When you run the program, put it in the
X  background. The program reports any progress to the file REPORT_LIST
X  and to the administrator's terminal. All messages sent to this list are
X  saved in the MBOX file. The program can be run as stand-alone, or in
X  conjunction with the other programs provided (start, serverd and listserv).
X  Please note that a line beginning with "From " and another one beginning
X  with "From: " must appear in the header of each message, and a blank line
X  must separate the header from the body of the message.
X
X  COMMAND LINE OPTIONS:
X    -r: Restricted mail; since it is possible to have another distribution
X        list at some other site as a subscriber, then the following problem
X	arises: if a message is coming from a member of the other list, his/
X	her message was forwarded to the members of that list from the other
X	site; we do not want to send this message back to them, because they
X	will receive this message twice. Therefore, when this flag is on,
X	for every message received, its sender is checked against a list
X	of "restricted" mail addresses (a subset of the subscribers). If a
X	match is found, then mail is forwarded to the people listed in the
X	filename following this mail address -- for the appropriate format
X	see below. If no match is found, then the message is forwarded to
X	to all of the subscribers. See also below.
X    -1: Execute only once; this is used when the program is running in
X        conjunction with listserv, in which case execution is interchanged
X        and controlled by the serverd program.
X    -v: Display the version number of this package.
X    -f: Forward any message(s) from unsubscribed senders to MANAGER. This
X        way, any ordinary user account can be used as the list's address.
X    -L: The argument following is the list name to process.
X
X  WARNING: The account that this program is running on, will not see
X  any mail messages. All of them will be redirected either to the members of
X  the list, or to MANAGER.
X
X  DISCLAIMER: If for any reason during the use of this program, implied
X  or not, you happen to die, or suffer any injury of any kind (physical
X  or mental), I, Mr. Anastasios Kotsikonas, AM NOT RESPONSIBLE at all.
X  In fact, I AM NOT RESPONSIBLE FOR ANYTHING that may happen to you, or
X  your computer and operating system.
X
X  PLEASE: If you upgrade the code, send me a copy, and do not even attempt to
X  put your name for credit. Send to tasos@cs.bu.edu, tasos@bucsf.bu.edu or
X  tasos@bu-pub.bu.edu
X
X  EXIT CODES:
X    0: OK
X    1: Could not open file
X    2: Could not lock file
X    3: Command line option error
X    4: Syntax error in file
X    5: Could not spawn
X    6: Shutdown request
X    7: Restart request
X    8: Received system signal
X
X  ENJOY!!!
X
X  Approximate algorithm:
X  {
X    Place a lock so that no other list program will access any files.
X    Read LIST_MAIL_FILE
X    If new message(s) have arrived then {
X      Append messages to MBOX
X      For each message do {
X        If the person is in the IGNORED file, go on to the next message.
X        If the person sending it is subscribed (listed in SUBSCRIBERS) then
X	  If the person does not acknowledge his/her message, he/she
X	    never receives his/her message back
X	  If the -r flag is on then
X	    check if the sender is listed in RESTRICTED
X	    If so then
X	      forward mail to all people listed in the file after the 
X	      restricted-sender's address
X	    Else
X	      distribute to all people in SUBSCRIBERS
X	  Else
X	    distribute to all people in SUBSCRIBERS
X        Else 
X          if -f specified, forward it to MANAGER; otherwise return the
X	  message to the sender.
X      }
X      Remove mail files.
X    }
X    Repeat process after IDLE_TIME, or die if -1 specified.
X  }
X
X  Required files:
X    SUBSCRIBERS     <-- The list of subscribed people
X    RESTRICTED      <-- Addresses of senders whose messages
X                        require special handling (usually 
X		        addresses of other lists): mail is 
X			forwarded only to people found in the 
X			file after the sender's address; see below
X    IGNORED         <-- The list of undesired people
X    LIST_LOCK_FILE  <-- Lock file
X
X  Input files:
X    LIST_MAIL_FILE  <-- File where new messages go
X    MAIL_COPY       <-- Copy of this file (actual work file)
X    MSG_NO          <-- Current message count
X    SUBSCRIBERS
X    RESTRICTED
X    IGNORED
X
X  Output files:
X    MBOX            <-- A log of all messages sent to date
X    REPORT_LIST     <-- Progress report
X    HEADERS	    <-- A log of all emails sent (just the sender's address)
X    MSG             <-- Body of message (no header)
X    MSG_NO          <-- Write last message count
X    MAILFORWARD     <-- Completed message (with header and a copy
X    			of MSG) to be forwarded
X
X  Format of the SUBSCRIBERS file:
X    One entry per line; each entry is the full mail address of the subscriber
X    as it appears in the "From " field, followed by the word "ACK" (in which
X    case his/her message will be sent back to him/her as an acknowledgement)
X    or "NOACK", followed by the subscriber's name -- this option may be
X    reset by the subscriber at any moment by sending a proper request to 
X    listserv. Do not include any blank lines.
X
X  Format of the RESTRICTED file:
X    Two entries per line; the first is the full mail address of the
X    subscriber; the second is a file name where mail addresses of recipients
X    are listed (just like in the SUBSCRIBERS file). When mail arrives from a 
X    sender listed in the RESTRICTED file and the -r flag is on, then mail 
X    will not be forwarded to SUBSCRIBERS, but instead to the people 
X    listed in the file following the restricted-sender's address. Example:
X      tasos@bu-pub.bu.edu /grad/tasos/.recipients
X      tasos@bucsb.bu.edu  /grad/tasos/.otherrecipients
X    If the recipient file given is the word "NONE", then no one will receive
X    any messages. This is useful in the case that other distribution sites
X    are subscribers and do forward the message back to the sender, 
X    in which case we protect ourselves from multiple reception of the same
X    messages. 
X    For example, when two (or more) lists are mutual subscribers, and a 
X    message originated by someone in our list, this message, after being 
X    distributed locally, is sent to the other list; if the other site does
X    send messages back to the sender, this message will be forwarded to us
X    to be distributed again, something which is highly undesirable. 
X    This precludes that the other site sends messages identified only by
X    their original senders, something which is unlikely but possible.
X    In this case, we would put our list as one of the entries in the 
X    RESTRICTED file, with the word NONE next to it. Do not include 
X    any blank lines.
X
X  Format of the IGNORED file:
X    One entry per line -- the email addresses of the people whose messages
X    are to be ignored. This is a good place to put root, sys and other
X    such undesired logins.
X
X  Recommended usage:
X    % list [-r] &
X  or
X    % list [-r] -1
X
X  System requirements:
X    You should be able to execute the UNIX command "sendmail -t" for the
X    program to work.
X*/
X
X#include <stdio.h>
X#include <string.h>
X#include <unistd.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#include "defs.h"
X#include "list.h"
X#include "struct.h"
X#include "global.h"
X
X#define FROM              "From: " /* Keep this line and the following from */
X#define SUBJECT           "Subject: " /* the original message */
X#define NO_RECIPIENT_FILE "NONE"
X
X/* 
X  Function prototypes:
X*/
X
X#ifdef __STDC__
X#include <stdarg.h>
Xextern int  syscom (char *, ...);
X#else
X#include <varargs.h>
Xextern int  syscom ();
X#endif
Xextern int  sys_config (FILE *, SYS *);
Xextern void report_progress (FILE *, char *, int);
Xextern void setup_string (char *, char *, char *);
Xextern void init_signals (void);
Xextern void catch_signals (void);
Xextern void get_list_name (char *, char *);
Xextern int  get_list_id (char *, SYS *, int);
Xextern int  getopt (int, char **, char *);
Xextern char *upcase (char *);
Xextern char *locase (char *);
Xextern void distribute (FILE *, void (*)(), FILE *, char *);
Xextern BOOLEAN strinstr (char *, char *, char *);
Xextern BOOLEAN ignore_sender (FILE *, char *, FILE *);
X
Xvoid   main (int, char **, char **);
Xvoid   create_header (FILE **, char *, char *, char *, char *);
Xvoid   process_message (char *, char *, BOOLEAN);
Xvoid   usage (void);
Xvoid   list_config (char *);
Xvoid   version (void);
X
X/*
X  The control structure of the mail-distributor. Check if mail has arrived.
X  If so, copy it to MAIL_COPY and proceed to lower level.
X*/
X
Xvoid main (int argc, char **argv, char **envp)
X{
X  struct stat stat_buf;
X  char command[MAX_LINE], c, *options = "1fvrL:";
X  char error [MAX_LINE];
X  int nlists;
X  extern char *optarg;
X  extern int optopt;
X
X  while ((c = getopt (argc, argv, options)) != EOF)
X    switch (c) {
X      case '1': execute_once = TRUE; break;
X      case 'f': errors_to_manager = TRUE; break;
X      case 'r': send_to_subscribers = FALSE; break;
X      case 'L': list_alias = optarg; break;
X      case 'v': version ();
X      case ':': 
X	fprintf (stderr, "list: Option '%c' requires an argument.\n", optopt);
X	exit (3);
X      case '?':
X      default:
X	usage ();
X    }
X
X  if (lockf (open (LIST_LOCK_FILE, O_RDWR), F_TLOCK, 0))
X    fprintf (stderr, "list: Unable to lock %s. Aborting.\n", LIST_LOCK_FILE),
X    exit (2);
X  init_signals();
X  catch_signals();
X  list_config (list_alias);
X  if ((report = fopen (report_listf, "a")) == NULL)
X    if ((report = fopen (REPORT_LIST, "a")) == NULL)
X      fprintf (stderr, "list: Could not open %s and %s\n", report_listf,
X	       REPORT_LIST),
X      exit (1);
X  if (list_alias == NULL)
X    report_progress (report, "\nlist: No list to process", TRUE),
X    exit (3);
X  nlists = sys_config (report, &sys);
X  if ((listid = get_list_id (list_alias, &sys, nlists)) < 0)
X    sprintf (error, "\nlist: Unknown list %s", list_alias),
X    report_progress (report, error, TRUE),
X    exit (3);
X  if (!execute_once)
X    printf ("%s\n", VERSION);
X  if (sys.options & USE_ENV_VAR)
X    sys.mail.method = (char *) malloc (256 * sizeof (char)),
X    sprintf (sys.mail.method, "env - %s=%s %s ", sys.mail.env_var,
X             sys.lists[listid].address, sys.mail.mail_prog);
X  if ((msg_no = fopen (msg_nof, "r")) != NULL)
X    fscanf (msg_no, "%d %d\n", &public_msg, &returned_msg),
X    fclose (msg_no);
X  
X  do {
X    if (!stat (list_mail_f, &stat_buf) && stat_buf.st_size > 0) {
X      syscom ("cp %s %s", list_mail_f, mail_copyf);
X      syscom ("cat %s >> %s", mail_copyf, mboxf);
X      syscom ("echo >> %s", mboxf);
X      if (unlink (list_mail_f))
X	syscom ("touch %s", list_mail_f); /* rewrite file */
X      if ((mail = fopen (mail_copyf, "r")) == NULL)
X	sprintf (error, "\nlist: Could not open %s", mail_copyf),
X	report_progress (report, error, TRUE),
X	exit (1);
X      if ((subscribers = fopen (subscribersf, "r")) == NULL)
X        sprintf (error, "\nlist: Could not open %s", subscribersf),
X        report_progress (report, error, TRUE),
X	exit (1);
X      if ((restricted = fopen (restrictedf, "r")) == NULL)
X	sprintf (error, "\nlist: Could not open %s", restrictedf),
X	report_progress (report, error, TRUE),
X	exit (1);
X      if ((ignored = fopen (ignoredf, "r")) == NULL)
X	sprintf (error, "\nlist: Could not open %s", ignoredf),
X        report_progress (report, error, TRUE),
X        exit (1);
X      if ((headers = fopen (headersf, "a")) == NULL)
X	sprintf (error, "\nlist: Could not open %s", headersf),
X	report_progress, (report, error, TRUE),
X	exit (1);
X      report_progress (report, NEW_ARRIVAL, FALSE);
X      distribute (mail, (void *) process_message, report, subscribersf);
X      fclose (mail);  /* Done */
X      fclose (subscribers);
X      fclose (restricted);
X      fclose (ignored);
X      fclose (headers);
X      unlink (mail_copyf);  /* Done delivering */
X    }
X    else if (!execute_once) /* No mail to deliver */
X      if (sys.frequency > 0)
X        sleep (sys.frequency);
X  } while (!execute_once);
X  fclose (report);
X  free ((char *) sys.mail.method);
X  exit (0);
X}
X
X/* 
X  Create a mail header.
X*/
X
Xvoid create_header (FILE **f, char *filename, char *sender, char *recipient,
X		    char *subject)
X{
X  char error [MAX_LINE];
X
X  if ((*f = fopen (filename, "w")) == NULL)
X    RESET (error),
X    sprintf (error, "\ncreate_header(): Could not open %s", filename),
X    report_progress (report, error, TRUE),
X    exit (1);
X  locase (recipient);
X  if (sys.options & USE_TELNET)
X    fprintf (*f, "HELO\nMAIL From: <%s>\nRCPT To: <%s>\nDATA\nComment: %s\n\
XErrors-To: %s\nVersion: %s\nFrom: %s\nTo: %s\nSubject: %s\n\n",
X	     sys.lists[listid].address, recipient, sys.lists[listid].comment,
X	     sys.manager, VERSION, sender, recipient, subject);
X  else
X    fprintf (*f, "Comment: %s\nErrors-To: %s\nVersion: %s\nFrom: %s\n\
XTo: %s\nSubject: %s\n\n",
X	     sys.lists[listid].comment, sys.manager, VERSION, sender, recipient,
X	     subject);
X}
X
X/*
X  The heart of the distribution. Since we are now at the beginning of the
X  message (as guaranteed by distribute ()), gather the rest of the message
X  -- that is until the beginning of the next message, or EOF. Note that
X  only the "From: " and "Subject: " lines from the header are saved.
X  The beginning of the next and every message is "From ". The actual
X  text of each message is separated from its header by a blank line (universal
X  format). The message is processes only if the sender is not listed in
X  IGNORED.
X  The entire message is stored in MSG. To distribute the message, prepare
X  a new header; the completed message is stored in MAILFORWARD. Actually,
X  for each subscriber we prepare a different header (different "To:" field) 
X  and append MSG to MAILFORWARD. Distribute that mail to the members,
X  or, either return it to the sender if he/she is not subscribed, or
X  forward it to MANAGER if -f specified. Messages from MAILER_DAEMON are
X  always forwarded to MANAGER. A message from a mailer daemon is identified
X  by the presence of MAILER_DAEMON anywhere in the sender's email address,
X  to cover local and remote mailer daemon addresses. Please look at the
X  documentation for defs.h for the syntax of the MAILER_DAEMON string.
X  The parameter "sender" is used for report purposes and when we are sending
X  the sender's message back to him/her.
X  In the case of restricted mail, check if the sender is listed in 
X  RESTRICTED; if so, forward the message to the people listed in the
X  filename found after the restricted-sender's address in RESTRICTED.
X*/
X
Xvoid process_message (char *sender, char *linecopy, BOOLEAN sender_subscribed)
X{
X  char line [MAX_LINE];            /* ... from the the current message */
X  char subscriber [MAX_LINE];      /* holds each subscriber's address */
X  char name [MAX_LINE];            /* holds each subscribers name */
X  char senders_subject [MAX_LINE]; /* the sender's subject */
X  char subject_copy [MAX_LINE];    /* a copy of the subject */
X  char recipient_file [MAX_LINE];  /* file of subscribers to use when -r */
X  char report_msg [MAX_LINE];      /* place for report/error messages */
X  char original_sender [MAX_LINE]; /* as it appears in his message */
X  char mode [MAX_LINE];            /* send message back to sender or not */
X  char command [MAX_LINE];         /* holds the mail command to be executed */
X  char restricted_subscriber [MAX_LINE]; /* name of subscriber in RESTRICTED */
X  FILE *msg = NULL;                /* file with a copy of the actual message */
X  FILE *forwardmail = NULL;        /* completed message to be forwarded */
X  FILE *recipient_list = NULL;     /* file with message recipients */
X
X  line[0] = 
X    subscriber[0] = 
X      senders_subject[0] = 
X	subject_copy[0] = 
X	  recipient_file[0] = 
X	    original_sender[0] = 
X	      restricted_subscriber[0] = 
X		RESET (mode);
X
X  /* Remove message header, but store the "From: " and "Subject: " fields to
X     be used later when sending the message to all subscribers. */
X
X  while (!feof (mail) && line[0] != '\n') {
X    if (!strncmp (line, FROM, strlen (FROM)))
X      strcpy (original_sender, line + strlen (FROM)), /* Remove From: */
X      original_sender [strlen (original_sender) - 1] = EOS; /* \n -> \0 */
X    if (!strncmp (line, SUBJECT, strlen (SUBJECT)))
X      strcpy (senders_subject, line + strlen (SUBJECT)), /* Remove Subject: */
X      senders_subject [strlen (senders_subject) - 1] = EOS, /* \n -> \0 */
X      strcpy (subject_copy, senders_subject);
X    RESET (line);
X    fgets (line, MAX_LINE - 2, mail);
X  }
X
X  /* Gather message into MSG; check if we have reached the next message, 
X     or EOF */
X  if ((msg = fopen (msgf, "w")) == NULL)
X    sprintf (report_msg, "\nprocess_message(): Could not open %s", msgf),
X    report_progress (report, report_msg, TRUE),
X    exit (1);
X  while (!feof (mail) && 
X	 (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE))))
X    fprintf (msg, "%s", line),
X    fgets (line, MAX_LINE - 2, mail);
X  fclose (msg);
X
X  strcpy (linecopy, line); /*** VERY IMPORTANT: we are now at the next msg ***/
X  /* Check the IGNORED file */
X  if (ignore_sender (ignored, sender, report))
X    return;
X
X  RESET (report_msg);
X  if (sender_subscribed == NOTSUBSCRIBED) { /* Send error message to sender */
X    sprintf (report_msg, "Invalid message #%04d. Forwarding error message to \
X%s.\n%s",  ++returned_msg,
X	     ((strinstr (MAILER_DAEMON, sender, "|") || errors_to_manager) ? 
X	      sys.manager : sender), 
X	     sender);
X    report_progress (report, report_msg, TRUE);
X    if ((msg_no = fopen (msg_nof, "w")) == NULL)
X      sprintf (report_msg, "\nprocess_message(): Could not open %s", msg_nof),
X      report_progress (report, report_msg, TRUE),
X      exit (1);
X    fprintf (msg_no, "%d %d\n", public_msg, returned_msg);
X    fclose (msg_no);
X    if ((msg = fopen (msgf, "r")) == NULL)
X      sprintf (report_msg, "\nprocess_message(): Could not open %s", msgf),
X      report_progress (report, report_msg, TRUE),
X      exit (1);
X    /* Prepare header */
X    create_header (&forwardmail, mailforwardf, sys.lists[listid].address,
X      ((strinstr (MAILER_DAEMON, sender, "|") || errors_to_manager) ?
X       sys.manager : sender),
X      senders_subject);
X    if (errors_to_manager || strinstr (MAILER_DAEMON, sender, "|"))
X      fprintf (forwardmail, "\nRejected message sent to %s by %s\n\
Xfollows.\n",
X	       sys.lists[listid].address, sender);
X    else
X      fprintf (forwardmail, "\n%s: You are not subscribed to %s\n\
XYour message is returned to you unprocessed.\n", sender, 
Xsys.lists[listid].address);
X    fprintf (forwardmail, "-----------------------------------------------\
X--------------------------------\n");
X    while (!feof (msg))  /* Copy actual message */
X      RESET (line),
X      fgets (line, MAX_LINE - 2, msg),
X      fprintf (forwardmail, "%s", line);
X    if (sys.options & USE_TELNET)
X      COMPLETE_FILE (forwardmail);
X    fclose (msg);
X    fclose (forwardmail);
X    /* Form UNIX sendmail command */
X    syscom ("%s %s < %s", sys.mail.method, 
X	    (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X	    mailforwardf);
X    return;
X  }
X  /* Distribute to all subscribers */
X  fprintf (headers, "%s\n", sender);
X  fflush (headers);
X  sprintf (report_msg, "Public message #%04d. Distributing to all subscribers.\
X\n%s", 
X	   ++public_msg, sender);
X  report_progress (report, report_msg, TRUE);
X  if ((msg_no = fopen (msg_nof, "w")) == NULL)
X    sprintf (report_msg, "\nprocess_message(): Could not open %s", msg_no),
X    report_progress (report, report_msg, TRUE),
X    exit (1);
X  fprintf (msg_no, "%d %d\n", public_msg, returned_msg);
X  fclose (msg_no);
X  recipient_list = subscribers; /* Default */
X  if (!send_to_subscribers) {  /* -r: Check if restricted mail has arrived */
X    rewind (restricted);
X    while (!feof (restricted)) {
X      restricted_subscriber[0] = RESET (recipient_file);
X      fscanf (restricted, "%s %s", restricted_subscriber, recipient_file);
X      upcase (restricted_subscriber);
X      if (!strcmp (restricted_subscriber, sender)) {
X	if (!strcmp (recipient_file, NO_RECIPIENT_FILE)) {
X	  /* Do not forward to anyone */
X	  report_progress (report, "No alternate recipient file specified. \
XMessage ignored.\n", FALSE);
X	  return;
X	}
X	/* Restricted mail; forward message only to the people listed
X	   in recipient_file */
X	if ((recipient_list = fopen (recipient_file, "r")) == NULL)
X	  sprintf (report_msg, "\nprocess_message(): Could not open alternate \
Xrecipient file %s", recipient_file),
X	  report_progress (report, report_msg, TRUE),
X	  exit (1);
X	RESET (report_msg);
X	sprintf (report_msg, "Restricted mail. Distributing only to \
Xindividuals listed in %s", recipient_file);
X	report_progress (report, report_msg, TRUE);
X	break;
X      }
X    }
X  }
X  rewind (recipient_list);
X  while (!feof (recipient_list)) { /* Send to all of them */
X    subscriber[0] = mode[0] = RESET (name);
X    fscanf (recipient_list, "%s %s ", subscriber, mode);
X    if (subscriber[0] == EOS)
X      continue;
X    fgets (name, MAX_LINE - 2, recipient_list); /* Skip name */
X    upcase (mode);
X    if (!strcmp (mode, NOACK) || !strcmp (mode, NEVERACK)) {
X      upcase (subscriber);
X      if (!strcmp (subscriber, sender))
X	continue;
X    }
X    else if (!strcmp (mode, POSTPONE))  /* No email to this subscriber */
X      continue;
X    else if (strlen (mode) > 0 && strcmp (mode, ACK))
X      sprintf (report_msg, "\nprocess_message(): Unrecognized mode %s in %s",
X	       mode, subscribersf), 
X      report_progress (report, report_msg, TRUE),
X      exit (4);
X    senders_subject[0] = RESET (command);
X    strcpy (senders_subject, subject_copy);
X    if ((msg = fopen (msgf, "r")) == NULL)
X      sprintf (report_msg, "\nprocess_message(): Could not open %s", msgf),
X      report_progress (report, report_msg, TRUE),
X      exit (1);
X    /* Prepare header */
X    create_header (&forwardmail, mailforwardf, original_sender,
X			subscriber, senders_subject);
X    while (!feof (msg))  /* Copy actual message */
X      RESET (line),
X      fgets (line, MAX_LINE - 2, msg),
X      fprintf (forwardmail, "%s", line);
X    if (sys.options & USE_TELNET)
X      COMPLETE_FILE (forwardmail);
X    fclose (msg);
X    fclose (forwardmail);
X    /* Form UNIX sendmail command */
X    syscom ("%s %s < %s", sys.mail.method, 
X	    (((sys.options & USE_TELNET) == 0) ? locase (subscriber) : ""),
X	    mailforwardf);
X  }
X  if (recipient_list != subscribers)  /* Close alternate recipient file */
X    fclose (recipient_list);
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: list <-L list_alias> [-r] [-1] [-f] [-v]\n\
X-L: Process list_alias (list_alias has to be all in capital letters).\n\
X-r: Restricted mail.\n\
X-1: Execute only once.\n\
X-f: Forward rejected messages to manager.\n\
X-v: Version number.\n");
X  exit (3);
X}
X
Xvoid list_config (char *alias)
X{
X  if (alias == NULL)
X    return;
X  setup_string (subscribersf, alias, SUBSCRIBERS);
X  setup_string (headersf, alias, HEADERS);
X  setup_string (restrictedf, alias, RESTRICTED);
X  setup_string (ignoredf, alias, IGNORED);
X  setup_string (list_mail_f, alias, LIST_MAIL_FILE);
X  setup_string (mboxf, alias, MBOX);
X  setup_string (msg_nof, alias, MSG_NO);
X  setup_string (mailforwardf, alias, MAILFORWARD);
X  setup_string (msgf, alias, MSG);
X  setup_string (mail_copyf, alias, MAIL_COPY);
X  setup_string (report_listf, alias, REPORT_LIST);
X}
X
Xvoid version ()
X{
X  fprintf (stderr, "%s\n", VERSION);
X  exit (0);
X}
END_OF_src/list.c
if test 25371 -ne `wc -c <src/list.c`; then
    echo shar: \"src/list.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/listserv.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/listserv.c\"
else
echo shar: Extracting \"src/listserv.c\" \(36375 characters\)
sed "s/^X//" >src/listserv.c <<'END_OF_src/listserv.c'
X/*
X  ----------------------------------------------------------------------------
X  |                        DISCUSSION LIST SERVER                            |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                 (tasos@cs.bu.edu, tasos@bu-pub.bu.edu)                   |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the Copyright notice in the file defs.h;   |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header() in    |
X  | list.c and listserv.c. By using this software you are bound by this      |
X  | agreement.                                                               |
X  | COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas                  |
X  ----------------------------------------------------------------------------
X
X  This is the accompanying program for your discussion list. People send
X  requests to the listserv for subscription, removal of subscription, 
X  general information request, etc. The recognized commands are as 
X  follows:
X    help [command]
X    set <list> <option> <value>         option: mail
X                                        value: ack, noack, neverack, postpone
X    subscribe <list> <name>
X    unsubscribe <list> (alias signoff)
X    recipients <list> (alias review)
X    information <list>
X    statistics <list> [subscriber email address(es)]
X    shutdown <password>
X    restart <password>
X    lists
X
X  Commands are sent to listserv one on each line of the message. The user
X  is notified of the first invalid request; all subsequent commands
X  are ignored -- imagine someone sending an entire book!! For each 
X  successfully completed command, a confirmation is sent back to the sender.
X  Commands/requests may be abbreviated.
X
X  The user may request help in general, or on a specific command; he can
X  'set mail ack' in which case his/her messages to the list will be
X  echoed back to him/her, and 'set mail noack' (the opposite); 'set mail
X  neverack' will not forward messages back to the sender and it will never
X  allow him/her to change the setting again -- this is only intended for
X  peer lists (addresses that are distribution sites themselves) for
X  security reasons (avoiding the case where the same email is going back
X  and forth between peer lists); subscribed peer lists should set mail
X  to this mode immediately. A 'set mail postpone' request will not send any
X  messages to the subscriber until he resets it to one of the other options
X  (used to suppress sending email temporarily).
X  To subscribe to the list, a user has to give his/her full name; 
X  he/she may leave the list by issuing an 'unsubscribe' request;
X  a list of the current subscribers is obtained through a 'recipients' request;
X  moreover, general information is available by means of an 'information'
X  request, and finally 'statistics' compiles a count of messages sent by
X  each subscriber (unless specific subscriber address are given) as well as
X  a total count of messages on file.
X
X  The entire system may be remotely shut down by way of a 'shutdown' request;
X  this request must be followed by a password that must match the one defined
X  in listserv.h. Note that this may result in requests being queued with the
X  'shutdown' one not being serviced; note thought that a copy of the original
X  requests can be found in MAIL_COPY as defined in listserv.h.
X  Likewise, the system may be remotely restarted by issuing a 'restart'
X  request with the proper password. Note that a 'restart' request has no
X  effect after a 'shutdown' (because the server is not running), and that
X  any requests queued with the 'restart' will be serviced (the system will
X  not restart until all requests are serviced).
X
X  A list of all discussion lists served by this server can be obtained by
X  a 'lists' request.
X
X  All commands may be abbreviated except 'shutdown' and 'restart'.
X
X  COMMAND LINE OPTIONS:
X    -1: Same as for the list program.
X    -r: This option may be repeated an infinite number of times and is
X        always followed by a valid listserv command (as outlined above).
X        This forces a restriction to be placed on the specified command;
X        whenever a user makes such a request, the request will be rejected
X        if the number of users currently on the system is greater than
X        the threshold specified in listserv.h. This option was added due
X        to the fact that the 'statistics' request may take up a lot of
X        resources to complete.
X
X  EXIT CODES:
X    0: OK
X    1: Could not open file
X    2: Could not lock file
X    3: Command line option error
X    4: Syntax error in file
X    5: Could not spawn
X    6: Shutdown request
X    7: Restart request
X    8: Received system signal
X
X  Approximate algorithm:
X  {
X    Place a lock so that no other programs will access the same files.
X    Read the SERVER_MAIL_FILE
X    if new messages have arrived then {
X      For each message do {
X        If the message is sent by MAILER-DAEMON forward it to MANAGER
X        else {
X          Scan each line of the body of the message and treat it as a
X          command with possible arguments. Reply to the sender for each such
X          request. If a request cannot be processed, send an error message to
X          the sender and ignore all subsequent requests.
X        }
X      }
X    }
X  }
X
X  Required files:
X    SUBSCRIBERS       <-- The list of subscribed people (diff. for each list)
X    IGNORED           <-- The list of undesired people (one for the server
X			  and one for each list)
X    SERVER_LOCK_FILE  <-- Lock file
X
X  Input files:
X    HEADERS           <-- Used for the 'statistics' request (see list.c)
X    SERVER_MAIL_FILE  <-- Where new messages go
X    MAIL_COPY         <-- Copy of this file (actual work file)
X    MSG_NO            <-- Read last message count
X    SUBSCRIBERS
X    IGNORED     
X
X  Output files:
X    SERVER_MBOX       <-- A log of all messages sent
X    SUBSCRIBERS       <-- After updating
X    OLD_SUBSCRIBERS   <-- Temporary
X    MSG_NO            <-- Write last message count
X    REPORT_SERVER     <-- Progress report
X    MAILFORWARD       <-- Completed message (with header and the
X                          the body of the message) to be forwarded
X
X  Format of the SUBSCRIBERS and IGNORED files:
X    See comments for list.c
X
X  Recommended usage:
X    % listserv [-r statistics] &
X  or
X    % listserv [-r statistics] -1
X
X  System requirements:
X    You should be able to execute the UNIX command "sendmail -t" for the
X    program to work.
X*/
X
X#include <stdio.h>
X#include <string.h>
X#include <unistd.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#include "defs.h"
X#include "listserv.h"
X#include "struct.h"
X#include "global.h"
X
X/* 
X  Function prototypes:
X*/
X
X#ifdef __STDC__
X#include <stdarg.h>
Xextern int  syscom (char *, ...);
X#else
X#include <varargs.h>
Xextern int  syscom ();
X#endif
Xextern int  sys_config (FILE *, SYS *);
Xextern void report_progress (FILE *, char *, int);
Xextern void init_signals (void);
Xextern void catch_signals (void);
Xextern void setup_string (char *, char *, char *);
Xextern char *upcase (char *);
Xextern char *locase (char *);
Xextern void distribute (FILE *, void (*)(), FILE *, char *);
Xextern char *cleanup_name (char *);
Xextern int  getopt (int, char **, char *);
Xextern void get_list_name (char *, char *);
Xextern int  get_list_id (char *, SYS *, int);
Xextern BOOLEAN subscribed (FILE *, char *, char *);
Xextern BOOLEAN strinstr (char *, char *, char *);
Xextern BOOLEAN ignore_sender (FILE *, char *, FILE *);
X
Xvoid   main (int, char **, char **);
Xvoid   process_message (char *, char *);
Xvoid   action (char *, char *, char *);
Xvoid   create_header (FILE **, char *, char *, char *);
Xvoid   reject_mail (char *sender, char *text);
Xvoid   help (char *, char *, char *);
Xvoid   unsubscribe (char *, char *, char *);
Xvoid   subscribe (char *, char *, char *);
Xvoid   set (char *, char *, char *);
Xvoid   recipients (char *, char *, char *);
Xvoid   info (char *, char *, char *);
Xvoid   stats (char *, char *, char *);
Xvoid   shutdown (char *, char *, char *);
Xvoid   restart (char *, char *, char *);
Xvoid   lists (char *, char *, char *);
Xvoid   init_commands (void);
Xvoid   usage(void);
Xvoid   server_config (char *);
X
X/*
X  The control structure of the server. Check if mail has arrived.
X  If so, copy it to MAIL_COPY and proceed to lower level.
X  First, the command line options are analyzed (for restrictions, etc.).
X*/
X
Xvoid main (int argc, char **argv, char **envp)
X{
X  struct stat stat_buf;
X  char c, *options = "1r:";
X  BOOLEAN execute_once = FALSE, notok;
X  int i, j, k;
X  char error [MAX_LINE];
X
X  extern char *optarg;
X  extern int optopt;
X
X  init_commands();
X  while ((c = getopt (argc, argv, options)) != EOF)
X    switch (c) {
X      case '1': execute_once = TRUE; break;
X      case 'r': 
X        notok = TRUE;
X        k = 0;
X        upcase (optarg);
X        for (i = 0; i < MAX_COMMANDS; ++i) {
X          notok &= (((j = strncmp (optarg, commands[i].name, strlen (optarg)))
X                    != 0) ? 1 : 0);
X          if (!j)
X            ++k,
X            restricted_commands |= commands[i].mask;
X        }
X        if (notok)
X          fprintf (stderr, "listserv: Unrecognized request '%s'\n", optarg),
X          exit (3);
X        if (k > 1) /* ambiguous command */
X          fprintf (stderr, "listserv: Ambiguous request '%s'\n", optarg),
X          exit (3);
X        break;
X      case ':':
X          fprintf (stderr, "listserv: Option '%c' requires an argument.\n",
X		   optopt);
X          exit (3);
X      case '?':
X      default:
X        usage ();
X    }
X
X  if (lockf (open (SERVER_LOCK_FILE, O_RDWR), F_TLOCK, 0))
X    fprintf (stderr, "listserv: Unable to lock %s. Aborting.\n", 
X             SERVER_LOCK_FILE),
X    exit (2);
X  if (!execute_once)
X    printf ("%s\n", VERSION);
X  init_signals();
X  catch_signals();
X  if ((report = fopen (REPORT_SERVER, "a")) == NULL)
X    fprintf (stderr, "listserv: Unable to open %s\n", REPORT_SERVER),
X    exit (1);
X  nlists = sys_config (report, &sys);
X  if (sys.options & USE_ENV_VAR)
X    sys.mail.method = (char *) malloc (256 * sizeof (char)),
X    sprintf (sys.mail.method, "env - %s=%s %s ", sys.mail.env_var,
X	     sys.server.address, sys.mail.mail_prog);
X  if ((msg_no = fopen (MSG_NO, "r")) != NULL)
X    fscanf (msg_no, "%d\n", &request_no),
X    fclose (msg_no);
X    
X  do {
X    if (!stat (SERVER_MAIL_FILE, &stat_buf) && stat_buf.st_size > 0) {
X      syscom ("cp %s %s", SERVER_MAIL_FILE, MAIL_COPY);
X      syscom ("cat %s >> %s", MAIL_COPY, SERVER_MBOX);
X      syscom ("echo >> %s", SERVER_MBOX);
X      if (unlink (SERVER_MAIL_FILE))
X	syscom ("touch %s", SERVER_MAIL_FILE); /* rewrite file */
X      if ((mail = fopen (MAIL_COPY, "r")) == NULL)
X	sprintf (error, "listserv: Could not open %s", MAIL_COPY),
X        report_progress (report, error, TRUE),
X        exit (1);
X      report_progress (report, NEW_ARRIVAL, FALSE);
X      distribute (mail, (void *) process_message, report, NULL);
X      fclose (mail);  /* Done */
X      unlink (MAIL_COPY);  /* Done delivering */
X    }
X    else if (!execute_once) /* No mail to deliver */
X      if (sys.frequency > 0)
X        sleep (sys.frequency);
X  } while (!execute_once);
X  fclose (report);
X  free ((char *) sys.mail.method);
X  if (restart_sys)
X    exit (7); /* Exit status of 7 signifies a restart request */
X  exit (0);
X}
X
X/*
X  Process each message. Isolate each command and pass it to action().
X  Before that, check if the message is from MAILER_DAEMON, in which case
X  forward the message to MANAGER. Any messages from people listed in the
X  IGNORED file are not processed.
X  Note: Please look at the documentation for list.c for the definition of a
X  mailer daemon.
X*/
X
Xvoid process_message (char *sender, char *linecopy)
X{
X  char line [MAX_LINE];          /* ... from the the current message */
X  char request [MAX_LINE];       /* holds each command */
X  char report_msg [MAX_LINE];    /* message to be written to REPORT */
X  char sender_copy [MAX_LINE];
X  FILE *f;
X
X  report_msg[0] = line[0] = RESET (sender_copy);
X  strcpy (sender_copy, sender);  /* we do not like converting the actual */
X  upcase (sender_copy);          /* address to upper case */
X  sprintf (report_msg, "Request #%04d:%s\n", ++request_no, sender);
X  report_progress (report, report_msg, FALSE);
X  if ((msg_no = fopen (MSG_NO, "w")) == NULL)
X    sprintf (report_msg, "\nprocess_message(): Could not open %s", MSG_NO),
X    report_progress (report, report_msg, TRUE),
X    exit (1);
X  fprintf (msg_no, "%d\n", request_no);
X  fclose (msg_no);
X
X  while (!feof (mail) && line[0] != '\n')  /* Skip to beginning of message */
X    RESET (line),
X    fgets (line, MAX_LINE - 2, mail);
X
X  if (strinstr (MAILER_DAEMON, sender_copy, "|")) {
X    /* Send message to MANAGER */
X    create_header (&f, MAILFORWARD, sys.manager, sender);
X    fprintf (f, "Returned mail by %s\n--------------------------------------\
X-----------------------------------------\n",
X             sender);
X    while (!feof (mail) && 
X           (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))) {
X      RESET (line);
X      fgets (line, MAX_LINE - 2, mail);
X      fprintf (f, "%s", line);
X    }
X    if (sys.options & USE_TELNET)
X      COMPLETE_FILE (f);
X    fclose (f);
X    syscom ("%s %s < %s", sys.mail.method, 
X            (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X            MAILFORWARD);
X    RESET (report_msg);
X    sprintf (report_msg, "Forwarding message to %s\n", sys.manager);
X    report_progress (report, report_msg, FALSE);
X  }
X  else /* Isolate request and call action() */
X    while (!feof (mail) && 
X           (strncmp (line, START_OF_MESSAGE, strlen (START_OF_MESSAGE)))) {
X      RESET (request);
X      sscanf (line, "%s ", request);
X      upcase (request);
X      if (strlen (request) > 0 && !one_rejection)
X        action (line, request, sender);
X      RESET (line);
X      fgets (line, MAX_LINE - 2, mail);
X    }
X  strcpy (linecopy, line);
X  one_rejection = FALSE;
X  report_progress (report, "", -TRUE); /* -TRUE for no leading newline */
X}
X
X/*
X  Recognize the 'request' and call the appropriate routine, or reject the
X  message if the request is not recognized. Isolate any parameters.
X  If a restriction is in force for the recognized request, then get
X  the number of users currently on the system and reject the request if
X  this number is greater that the predetermined threshold.
X*/
X
Xvoid action (char *line, char *request, char *sender)
X{
X  char params [MAX_LINE];
X  char error [MAX_LINE];
X  char list_name [MAX_LINE];
X  int i, nusers;
X  FILE *f;
X
X  report_progress (report, line, FALSE);
X  RESET (error);
X  sprintf (server_ignoredf, "%s/%s", PATH", IGNORED);
X  if ((ignored = fopen (server_ignoredf, "r")) == NULL)
X    sprintf (error, "action(): Could not open %s", server_ignoredf),
X    report_progress (report, error, TRUE),
X    exit (1);
X  if (ignore_sender (ignored, sender, report))
X    goto abort;
X  fclose (ignored);
X  upcase (sender);
X  RESET (params);
X  sprintf (params, "%s", line + strlen (request)); /* Get parameters */
X  upcase (params);
X  if (strncmp (request, "SHUTDOWN", strlen (request)) &&
X      strncmp (request, "RESTART", strlen (request)) &&
X      strncmp (request, "LISTS", strlen (request)) &&
X      strncmp (request, "HELP", strlen (request))) {
X    get_list_name (params, list_name);
X    listid = get_list_id (list_name, &sys, nlists);
X    server_config (list_name);
X    if (listid < 0) {
X      if (list_name[0] == EOS)
X        sprintf (error, "%s: Missing list name\n", request);
X      else
X        sprintf (error, "%s: Unknown list name %s\n", request, list_name);
X      reject_mail (sender, error);
X      return;
X    }
X    if ((ignored = fopen (ignoredf, "r")) == NULL)
X      sprintf (error, "action(): Could not open %s", ignoredf),
X      report_progress (report, error, TRUE),
X      exit (1);
X    if (ignore_sender (ignored, sender, report))
X      goto abort;
X  }
X  for (i = 0; i < MAX_COMMANDS; i++) /* Walk through the valid requests */
X    if (!strncmp (request, commands[i].name, strlen (request))) {
X      if (restricted_commands & commands[i].mask) { /* Restriction set */
X        syscom ("%s | %s -F, '{ print $3 }' > %s", UPTIME, AWK, USERS_FILE);
X        if ((f = fopen (USERS_FILE, "r")) == NULL)
X	  sprintf (error, "action(): Could not open %s", USERS_FILE),
X          report_progress (report, error, TRUE),
X          exit (1);
X        fscanf (f, "%d", &nusers);
X        fclose (f);
X        unlink (USERS_FILE);
X        if (nusers > sys.users) {
X          create_header (&f, MAILFORWARD, sender, request);
X          fprintf (f, "This request takes a considerable amount of resources \
Xand certain restrictions\nare currently in force. Please resubmit your \
Xrequest at a later time.\n");
X          if (sys.options & USE_TELNET)
X            COMPLETE_FILE (f);
X          fclose (f);
X          syscom ("%s %s < %s", sys.mail.method,
X                  (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X                  MAILFORWARD);
X          strcat (request, ": restriction enforced\n");
X          report_progress (report, request, FALSE);
X          goto abort;
X        }
X      }
X      commands[i].func (request, params, sender); /* Call routine */
X      goto abort;
X    }
X  sprintf (error, "Unrecognized request %s\n", request);
X  reject_mail (sender, error);
X  abort:
X  fclose (ignored);
X}
X
X/*
X  Create a message header addressed to 'sender' with the given 'subject'.
X  Note: some mailers require the following format when using telnet:
X*/
X
Xvoid create_header (FILE **f, char *filename, char *recipient, char *subject)
X{
X  char error [MAX_LINE];
X
X  if ((*f = fopen (filename, "w")) == NULL)
X    RESET (error),
X    sprintf (error, "\ncreate_header(): Could not open %s", filename),
X    report_progress (report, error, TRUE),
X    exit (1);
X  locase (recipient);
X  if (sys.options & USE_TELNET)
X    fprintf (*f, "HELO\nMAIL From: <%s>\nRCPT To: <%s>\nDATA\nComment: %s\n\
XErrors-To: %s\nVersion: %s\nFrom: %s\nTo: %s\nSubject: %s\n\n",
X             sys.server.address, recipient, sys.server.comment,
X             sys.manager, VERSION, sys.server.address, recipient, subject);
X  else
X    fprintf (*f, "Comment: %s\nErrors-To: %s\nVersion: %s\nFrom: %s\n\
XTo: %s\nSubject: %s\n\n"
X,
X             sys.server.comment, sys.manager, VERSION, sys.server.address,
X	     recipient, subject);
X}
X
X/*
X  Send a message to 'sender' indicating an invalid request. The body of
X  the message is given in 'text'. Only one such mail is sent to the user
X  for each of his/her invalid requests. All subsequent requests are ignored.
X*/
X
Xvoid reject_mail (char *sender, char *text)
X{
X  FILE *f;
X 
X  if (one_rejection)
X    return;
X  report_progress (report, text, FALSE);
X  one_rejection = TRUE;
X  create_header (&f, MAILFORWARD, sender, "Invalid request");
X  fprintf (f, "%s\nReport any problems to '%s'.\nFor a list of the available \
Xcommands send a message to %s\nwith the word 'help' in the body of the \
Xmessage.\n\nPS: Any subsequent requests that you might have submitted have \
Xbeen ignored.\n", text, sys.manager, sys.server.address);
X  if (sys.options & USE_TELNET)
X    COMPLETE_FILE (f);
X  fclose (f);
X  syscom ("%s %s < %s", sys.mail.method,
X          (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X          MAILFORWARD);
X}
X
X/*
X  Provide help on the various commands to 'sender'.
X*/
X
Xvoid help (char *request, char *params, char *sender)
X{
X  FILE *f;
X  char error [MAX_LINE];
X  char param [MAX_LINE];
X  char moreparams [MAX_LINE];
X  BOOLEAN notok = TRUE;
X  int i;
X
X  sprintf (request + strlen (request), " %s", params); /* Used as a subject */
X  RESET (error);
X  param[0] = RESET (moreparams);
X  sscanf (params, "%s %s\n", param, moreparams);
X  if (strlen (param) > 0) {  /* Check option validity */
X    for (i = 0; i < MAX_COMMANDS; ++i)
X      notok &= ((strncmp (param, commands[i].name, strlen (param)) != 0) ? 
X                1 : 0);
X    if (notok) {
X      sprintf (error, "Invalid HELP topic %s\n", params);
X      reject_mail (sender, error);
X      return;
X    }
X  }
X  if (strlen (moreparams) > 0) {  /* More than one option given */
X    sprintf (error, "Too many HELP topics: %s\n", moreparams);
X    reject_mail (sender, error);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sender, request);
X  fprintf (f, "Available commands are:\n\n\
Xhelp [command]\n\
Xset <list> <option> <value>\n\
Xsubscribe <list> <your name>\n\
Xunsubscribe <list>\n\
Xrecipients <list>\n\
Xinformation <list>\n\
Xstatistics <list> [subscriber email address(es)]\n\
Xlists\n\
X");
X  if (!strncmp (param, "SET", strlen (param)))
X    fprintf (f, "\nSyntax: set <list> <option> <value>\n\
X\toption can be: mail\n\tvalue can be: ack, noack, postpone\n\t\
X'set <list> mail ack': your message is sent back to you\n\
X\t'set <list> mail noack': your message is not sent back to you\n\
X\t'set <list> mail postpone': no messages will be sent to you until you\n\t\t\
Xchange mode again\n");
X  if (!strncmp (param, "SUBSCRIBE", strlen (param)))
X    fprintf (f, "\nSyntax: subscribe <list> <your full name>\n\t\
XThe only way to subscribe to a list\n");
X  if (!strncmp (param, "UNSUBSCRIBE", strlen (param)))
X    fprintf (f, "\nSyntax: unsubscribe <list>\n\t\
XRemove yourself from a list\n");
X  if (!strncmp (param, "RECIPIENTS", strlen (param)))
X    fprintf (f, "\nSyntax: recipients <list>\n\tGet a listing of the current \
Xsubscribers for the specified list\n");
X  if (!strncmp (param, "INFORMATION", strlen (param)))
X    fprintf (f, "\nSyntax: information <list>\n\t\
XGet various information about a list\n");
X  if (!strncmp (param, "STATISTICS", strlen (param)))
X    fprintf (f, "\nSyntax: statistics <list> [subscriber email address(es)]\n\t\
XGet statistical information for all subscribers, or for the specified\n\t\
Xone(s) only, in the specified list\n");
X  if (!strncmp (param, "LISTS", strlen (param)))
X    fprintf (f, "\nSyntax: lists\n\t\
XGet a list of all discussion lists served by this server\n");
X  if (sys.options & USE_TELNET)
X    COMPLETE_FILE (f);
X  fclose (f);
X  syscom ("%s %s < %s", sys.mail.method,
X          (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X          MAILFORWARD);
X}
X
X/*
X  Unsubscribe a member if he/she is listed in SUBSCRIBERS.
X*/
X
Xvoid unsubscribe (char *request, char *params, char *sender)
X{
X  FILE *f;
X  char error [MAX_LINE];
X  char param [MAX_LINE];
X  
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  RESET (param);
X  RESET (error);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS) { /* No parameters accepted */
X    sprintf (error, "Invalid UNSUBSCRIBE option %s", params);
X    reject_mail (sender, error);
X    return;
X  }
X  if (!subscribed (report, sender, subscribersf)) {
X    sprintf (error, "%s: %sYou are not subscribed to %s\n", sender, request,
X             sys.lists[listid].address);
X    reject_mail (sender, error);
X    return;
X  }
X  /* Now move the current list of subscribers to a temporary file; then
X     copy each entry of this file to SUBSCRIBERS excluding the
X     user to be removed. */
X  syscom ("mv %s %s", subscribersf, OLD_SUBSCRIBERS);
X  syscom ("grep -i -v %s %s > %s", sender, OLD_SUBSCRIBERS, subscribersf);
X  unlink (OLD_SUBSCRIBERS);
X  create_header (&f, MAILFORWARD, sender, request);
X  fprintf (f, "You have been removed from list %s\nThanks for being with us.\n",
X           sys.lists[listid].address);
X  if (sys.options & USE_TELNET)
X    COMPLETE_FILE (f);
X  fclose (f);
X  syscom ("%s %s < %s", sys.mail.method,
X          (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X          MAILFORWARD);
X}
X
X/*
X  Subscribe a new user if he/she is not already subscribed.
X*/
X
Xvoid subscribe (char *request, char *params, char *sender)
X{
X  FILE *f;
X  char error [MAX_LINE];
X  char name [MAX_LINE];
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  RESET (name);
X  RESET (error);
X  cleanup_name (params);
X  sscanf (params, "%s\n", name);
X  if (name[0] == EOS) {  /* No user's name */
X    sprintf (error, "No name given to SUBSCRIBE\n");
X    reject_mail (sender, error);
X    return;
X  }
X  if (subscribed (report, sender, subscribersf)) {
X    sprintf (error, "%s: You are already subscribed to %s\n", sender, 
X             sys.lists[listid].address);
X    reject_mail (sender, error);
X    return;
X  }
X  if ((f = fopen (subscribersf, "a")) == NULL)
X    sprintf (error, "subscribe(): Could not open %s", subscribersf),
X    report_progress (report, error, TRUE),
X    exit (1);
X  fprintf (f, "%s %s %s\n", sender, NOACK, params);
X  fclose (f);
X  create_header (&f, MAILFORWARD, sender, request);
X  fprintf (f, "You have been added to list %s\nRequests to %s\n", 
X           sys.lists[listid].address, sys.server.address);
X  fclose (f);
X  syscom ("cat %s >> %s", welcomef, MAILFORWARD);
X  if (sys.options & USE_TELNET) {
X    if ((f = fopen (MAILFORWARD, "a")) == NULL)
X      sprintf (error, "subscribe(): Could not open %s", MAILFORWARD),
X      report_progress (report, error, TRUE),
X      exit (1);
X    COMPLETE_FILE (f);
X    fclose (f);
X  }
X  syscom ("%s %s < %s", sys.mail.method,
X          (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X          MAILFORWARD);
X}
X
X/*
X  Set options for user if he/she is subscribed.
X*/
X
Xvoid set (char *request, char *params, char *sender)
X{
X  FILE *f, *from, *to;
X  char error [MAX_LINE];
X  char mail [MAX_LINE];
X  char subscriber [MAX_LINE];
X  char oldmode [MAX_LINE];
X  char newmode [MAX_LINE];
X  char moreparams [MAX_LINE];
X  char name [MAX_LINE];
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  error[0] = mail[0] = moreparams[0] = RESET (newmode);
X  sscanf (params, "%s %s %s\n", mail, newmode, moreparams); /* Get parameters */
X  upcase (mail);
X  upcase (newmode);
X  if (!subscribed (report, sender, subscribersf)) {
X    sprintf (error, "%s: %sYou are not subscribed to %s\n", sender, request,
X             sys.lists[listid].address);
X    reject_mail (sender, error);
X    return;
X  }
X  if (strcmp (mail, "MAIL") || 
X      (strcmp (newmode, ACK) && strcmp (newmode, NOACK) &&
X       strcmp (newmode, NEVERACK) && strcmp (newmode, POSTPONE))) {
X    sprintf (error, "Invalid SET option %s %s\n", mail, newmode);
X    reject_mail (sender, error);
X    return;
X  }
X  if (strlen (moreparams) > 0) {  /* More than one option given */
X    sprintf (error, "Too many SET parameters: %s\n", moreparams);
X    reject_mail (sender, error);
X    return;
X  }
X  syscom ("mv %s %s", subscribersf, OLD_SUBSCRIBERS);
X  if ((from = fopen (OLD_SUBSCRIBERS, "r")) == NULL)
X    sprintf (error, "set(): Could not open %s", OLD_SUBSCRIBERS),
X    report_progress (report, error, TRUE),
X    exit (1);
X  if ((to = fopen (subscribersf, "w")) == NULL)
X    sprintf (error, "set(): Could not open %s", subscribersf),
X    report_progress (report, error, TRUE),
X    exit (1);
X  while (!feof (from)) {
X    subscriber[0] = oldmode[0] = RESET (name);
X    fscanf (from, "%s %s ", subscriber, oldmode);
X    fgets (name, MAX_LINE - 2, from);
X    upcase (subscriber);
X    if (!feof (from)) {
X      if (!strcmp (oldmode, NEVERACK)) {
X        RESET (error);
X        sprintf (error, "Cannot change mail mode to %s\n", newmode);
X        reject_mail (subscriber, error);
X        fprintf (to, "%s %s %s", subscriber, oldmode, name);
X        continue;
X      }
X      fprintf (to, "%s %s %s", subscriber,
X               ((strcmp (sender, subscriber) == 0) ? newmode : oldmode),
X               name);
X    }
X  }
X  fclose (from);
X  fclose (to);
X  unlink (OLD_SUBSCRIBERS);
X  if (! one_rejection) {
X    create_header (&f, MAILFORWARD, sender, request);
X    fprintf (f, "MAIL mode reset to %s\n", newmode);
X    if (sys.options & USE_TELNET)
X      COMPLETE_FILE (f);
X    fclose (f);
X    syscom ("%s %s < %s", sys.mail.method,
X            (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X            MAILFORWARD);
X  }
X}
X
X/*
X  Provide user with the current list of subscribers.
X*/
X
Xvoid recipients (char *request, char *params, char *sender)
X{
X  char error [MAX_LINE];
X  char param [MAX_LINE];
X  FILE *f;
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  RESET (param);
X  RESET (error);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS) {
X    sprintf (error, "Invalid RECIPIENTS option %s", params);
X    reject_mail (sender, error);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sender, request);
X  fprintf (f, "Here is the current list of subscribers:\n\n");
X  fclose (f);
X  syscom ("%s -d\" \" -f1,3-6 %s > %s", CUT, subscribersf, recipf);
X  syscom ("%s -f %s %s > %s", AWK, AWK_PROG, recipf, OLD_SUBSCRIBERS);
X  unlink (recipf);
X  syscom ("echo Total number of subscribers: `cat %s | wc -l` >> %s",
X          subscribersf, OLD_SUBSCRIBERS);
X  if (sys.options & USE_TELNET) {
X    if ((f = fopen (OLD_SUBSCRIBERS, "a")) == NULL)
X      sprintf (error, "recipients(): Could not open %s", OLD_SUBSCRIBERS),
X      report_progress (report, error, TRUE),
X      exit (1);
X    COMPLETE_FILE (f);
X    fclose (f);
X  }
X  syscom ("cat %s %s | %s %s", MAILFORWARD, OLD_SUBSCRIBERS,
X          sys.mail.method, 
X          (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""));
X}
X
X/*
X  Provide user with general information about the list. A reminder, the
X  actual text is in the INFO_FILE.
X*/
X
Xvoid info (char *request, char *params, char *sender)
X{
X  char error [MAX_LINE];
X  char param [MAX_LINE];
X  FILE *f;
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  RESET (param);
X  RESET (error);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS) {
X    sprintf (error, "Invalid INFORMATION option %s", params);
X    reject_mail (sender, error);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sender, request);
X  fclose (f);
X  syscom ("cat %s >> %s", infof, MAILFORWARD);
X  if (sys.options & USE_TELNET) {
X    if ((f = fopen (MAILFORWARD, "a")) == NULL)
X      sprintf (error, "info(): Could not open %s", MAILFORWARD),
X      report_progress (report, error, TRUE),
X      exit (1);
X    COMPLETE_FILE (f);
X    fclose (f);
X  }
X  syscom ("%s %s < %s", sys.mail.method,
X          (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X          MAILFORWARD);
X}
X
X/*
X  Collect and send statistics about all subscribers, by grepping through
X  HEADERS. If a user has selected particular names (asterisks are OK) then
X  give statistics about these people only. Also include a total count
X  of messages on file.
X*/
X
Xvoid stats (char *request, char *params, char *sender)
X{
X  FILE *f, *statsf;
X  char subscriber [MAX_LINE], error [MAX_LINE];
X  char junk [MAX_LINE];
X  struct stat stat_buf;
X
X  sprintf (request + strlen (request), " %s%s", sys.lists[listid].alias,
X           params); /* Used as a subject */
X  params [strlen (params) - 1] = EOS; /* Remove \n */
X  RESET (error);
X  RESET (junk);
X  sscanf (params, "%s", junk);
X  if (junk[0] == EOS) /* No specific subscribers to report on */
X    RESET (params);
X  if (stat (headersf, &stat_buf)) {
X    create_header (&f, MAILFORWARD, sender, request);
X    fprintf (f, "Sorry, no mail archive found.\n");
X    if (sys.options & USE_TELNET)
X      COMPLETE_FILE (f);
X    fclose (f);
X    syscom ("%s %s < %s", sys.mail.method,
X            (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X            MAILFORWARD);
X    return;
X  }
X  if ((statsf = fopen (STATS_FILE, "w")) == NULL) {
X    sprintf (error, "stats(): Could not open %s", STATS_FILE),
X    report_progress (report, error, TRUE),
X    exit (1);
X  }
X  fprintf (statsf, "Here are the number of messages per subscriber:\n\n");
X  fclose (statsf);
X  syscom ("%s %s %s %s %s >> %s", STATS_PROG, PATH", subscribersf,
X          headersf, params, STATS_FILE);
X  create_header (&f, MAILFORWARD, sender, request);
X  fclose (f);
X  if (sys.options & USE_TELNET) {
X    if ((f = fopen (STATS_FILE, "a")) == NULL)
X      sprintf (error, "stats(): Could not open %s", STATS_FILE),
X      report_progress (report, error, TRUE),
X      exit (1);
X    COMPLETE_FILE (f);
X    fclose (f);
X  }
X  syscom ("cat %s %s | %s %s", MAILFORWARD, STATS_FILE, sys.mail.method,
X          (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""));
X  unlink (STATS_FILE);
X}
X
X/*
X  Exit with a shutdown status if the correct password is provided.
X*/
X
Xvoid shutdown (char *request, char *params, char *sender)
X{
X  char error [MAX_LINE];
X  char passwd [MAX_LINE];
X
X  sscanf (params, "%s\n", passwd);
X  if (!strcmp (sys.server.password, passwd))
X    report_progress (report, "SHUTDOWN request accepted", TRUE),
X    exit (6); /* Exit status of 6 signifies a shutdown request */
X  RESET (error);
X  sprintf (error, "Unrecognized command %s\n", request),
X  reject_mail (sender, error);
X}
X
X/*
X  Set the global variable 'restart_sys' to TRUE if the correct password
X  is given.
X*/
X
Xvoid restart (char *request, char *params, char *sender)
X{
X  char error [MAX_LINE];
X  char passwd [MAX_LINE];
X
X  sscanf (params, "%s\n", passwd);
X  if (!strcmp (sys.server.password, passwd)) { 
X    report_progress (report, "RESTART request accepted\n", FALSE);
X    restart_sys = TRUE;
X    return;
X  }
X  RESET (error);
X  sprintf (error, "Unrecognized command %s\n", request);
X  reject_mail (sender, error);
X}
X
X/*
X  Provide 'sender' with a list of al discussion lists served by this server.
X*/
X
Xvoid lists (char *request, char *params, char *sender)
X{
X  char error [MAX_LINE];
X  char param [MAX_LINE];
X  FILE *f;
X  int i;
X
X  sprintf (request + strlen (request), " %s", params); /* Used as a subject */
X  RESET (param);
X  RESET (error);
X  sscanf (params, "%s", param);
X  if (param[0] != EOS) {
X    sprintf (error, "Invalid LISTS option %s", params);
X    reject_mail (sender, error);
X    return;
X  }
X  create_header (&f, MAILFORWARD, sender, request);
X  fprintf (f, "Here is the current active list of discussion lists served by \
Xthis server:\n\n");
X  for (i = 0; i < nlists; ++i)
X    fprintf (f, "%s\t%s\n", sys.lists[i].address, sys.lists[i].comment);
X  if (sys.options & USE_TELNET)
X    COMPLETE_FILE (f);
X  fclose (f);
X  syscom ("%s %s < %s", sys.mail.method,
X          (((sys.options & USE_TELNET) == 0) ? locase (sender) : ""),
X          MAILFORWARD);
X}
X
X/*
X  Initialize the commands[].
X*/
X
Xvoid init_commands ()
X{
X  commands[0].name = "HELP";
X  commands[0].mask = 0x001;
X  commands[0].func = help;
X  commands[1].name = "SET";
X  commands[1].mask = 0x002;
X  commands[1].func = set;
X  commands[2].name = "SUBSCRIBE";
X  commands[2].mask = 0x004;
X  commands[2].func = subscribe;
X  commands[3].name = "UNSUBSCRIBE";
X  commands[3].mask = 0x008;
X  commands[3].func = unsubscribe;
X  commands[4].name = "SIGNOFF";
X  commands[4].mask = 0x008;
X  commands[4].func = unsubscribe;
X  commands[5].name = "RECIPIENTS";
X  commands[5].mask = 0x010;
X  commands[5].func = recipients;
X  commands[6].name = "REVIEW";
X  commands[6].mask = 0x010;
X  commands[6].func = recipients;
X  commands[7].name = "INFORMATION";
X  commands[7].mask = 0x020;
X  commands[7].func = info;
X  commands[8].name = "STATISTICS";
X  commands[8].mask = 0x040;
X  commands[8].func = stats;
X  commands[9].name = "SHUTDOWN";
X  commands[9].mask = 0x080;
X  commands[9].func = shutdown;
X  commands[10].name = "RESTART";
X  commands[10].mask = 0x100;
X  commands[10].func = restart;
X  commands[11].name = "LISTS";
X  commands[11].mask = 0x200;
X  commands[11].func = lists;
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: listserv [-1] {[-r <listserv command>]}*\n\
X-1: Execute only once.\n-r: Set restriction on 'listserv command'.\n");
X  exit (3);
X}
X
Xvoid server_config (char *alias)
X{
X  setup_string (infof, alias, INFO_FILE);
X  setup_string (recipf, alias, RECIP_FILE);
X  setup_string (welcomef, alias, WELCOME_FILE);
X  setup_string (subscribersf, alias, SUBSCRIBERS);
X  setup_string (headersf, alias, HEADERS);
X  setup_string (ignoredf, alias, IGNORED);
X}
END_OF_src/listserv.c
if test 36375 -ne `wc -c <src/listserv.c`; then
    echo shar: \"src/listserv.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/misc.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/misc.c\"
else
echo shar: Extracting \"src/misc.c\" \(17217 characters\)
sed "s/^X//" >src/misc.c <<'END_OF_src/misc.c'
X/*
X  ----------------------------------------------------------------------------
X  |                      GENERAL PURPOSE FUNCTIONS                           |
X  |                                                                          |
X  |                              Version 2.0                                 |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                 (tasos@cs.bu.edu, tasos@bu-pub.bu.edu)                   |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the Copyright notice in the file defs.h;   |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header() in    |
X  | list.c and listserv.c. By using this software you are bound by this      |
X  | agreement.                                                               |
X  | COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas                  |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X#include <time.h>
X#include <string.h>
X#include <ctype.h>
X#include <signal.h>
X#include "defs.h"
X#include "struct.h"
X
X#ifdef __STDC__
X#include <stdarg.h>
Xint 	syscom (char *, ...);
X#else
X#include <varargs.h>
Xint     syscom ();
X#endif
Xint	sys_config (FILE *, SYS *);
Xchar    *locase (char *);
Xchar    *upcase (char *);
Xvoid    report_progress (FILE *, char *, int);
Xvoid    distribute (FILE *mail, void (*)(), FILE *, char *);
XBOOLEAN strinstr (char *, char *, char *);
Xchar    *extract_filename (char *);
Xint     getopt (int, char **, char *);
Xchar    *cleanup_name (char *);
Xvoid	get_list_name (char *, char *);
Xint	get_list_id (char *, SYS *, int nlists);
Xvoid	setup_string (char *, char *, char *);
Xint	my_system (char *);
X
X/*
X  Execute a system request.
X*/
X
X#ifdef __STDC__
Xint syscom (char *control, ...);
X#else
Xint syscom (control, va_alist)
Xchar *control;
Xva_dcl
X#endif
X{
X  char command [512];
X  extern SYS sys;
X  va_list ap;
X
X#ifdef __STDC__
X  va_start (ap, control);
X#else
X  va_start (ap);
X#endif
X  RESET (command);
X  vsprintf (command, control, ap);
X  if ((sys.options & USE_MY_SYSTEM) == 0)
X    return system (command);
X  else
X    return my_system (command);
X}
X
X/*
X  Initialize the 'sys' structure from the CONFIG file.
X*/
X
Xint sys_config (FILE *report, SYS *sys)
X{
X  FILE *config;
X  char cmd [MAX_LINE];
X  char args [MAX_LINE];
X  char error [MAX_LINE];
X  char mail_prog [MAX_LINE];
X  char env_var [MAX_LINE];
X  char *comment, *cmdarg;
X  int nlists = 0, i, id;
X
X  for (i = 0; i < MAX_LISTS; i++)
X    RESET (sys->lists[i].alias),
X    RESET (sys->lists[i].address),
X    RESET (sys->lists[i].comment),
X    RESET (sys->lists[i].cmdoptions);
X  RESET (sys->server.address), RESET (sys->server.cmdoptions),
X  RESET (sys->server.comment), RESET (sys->serverd_cmdoptions),
X  RESET (sys->server.password), RESET (sys->arg), RESET (sys->manager),
X  RESET (error);
X  strcpy (sys->server.address, DEFAULT_SERVER_ADDRESS);
X  strcpy (sys->server.cmdoptions, DEFAULT_SERVER_CMDOPTIONS);
X  strcpy (sys->server.comment, DEFAULT_SERVER_COMMENT);
X  strcpy (sys->manager, DEFAULT_MANAGER);
X  sys->mail.method = TELNET;
X  sys->options = 0;
X  sys->users = 100;
X  sys->frequency = 0;
X  if ((config = fopen (CONFIG, "r")) == NULL)
X    sprintf (error, "\nsys_config(): Could not open %s", CONFIG),
X    report_progress (report, error, TRUE),
X    exit (1);
X  chmod (CONFIG, 384);
X  while (! feof (config)) {
X    args [0] = RESET (cmd);
X    fscanf (config, "%s", cmd);
X    if (strlen (cmd) == 0)
X      continue;
X    fgets (args, MAX_LINE - 2, config);
X    args [strlen (args) - 1] = EOS;
X    if (cmd[0] == '#')
X      continue;
X    if (!strcmp (locase (cmd), "list")) {
X      if (nlists >= MAX_LISTS) {
X	sprintf (error, "\nsys_config(): List %s ignored: too many lists\n",
X		 args);
X	report_progress (report, error, FALSE);
X	continue;
X      }
X      comment = strchr (args, '#');
X      if (comment)
X	*comment = EOS;
X      sscanf (args, "%s%s", sys->lists [nlists].alias,
X	      sys->lists [nlists].address);
X      if (get_list_id (sys->lists [nlists].alias, sys, nlists) >= 0 &&
X	  nlists > 0)
X	sprintf (error, "\nsys_config(): Duplicate list %s in %s",
X		 sys->lists [nlists].alias, CONFIG),
X	report_progress (report, error, TRUE),
X	exit (4);
X      cmdarg = strchr (args, '-');
X      if (cmdarg)
X	strcat (sys->lists [nlists].cmdoptions, cmdarg);
X      upcase (sys->lists [nlists].alias);
X      locase (sys->lists [nlists].address);
X      ++nlists;
X    }
X    else if (!strcmp (cmd, "server")) {
X      comment = strchr (args, '#');
X      if (comment)
X        *comment = EOS;
X      sscanf (args, "%s", sys->server.address);
X      cmdarg = strchr (args, '-');
X      if (cmdarg)
X	strcat (sys->server.cmdoptions, cmdarg);
X      locase (sys->server.address);
X    }
X    else if (!strcmp (cmd, "frequency")) {
X      sscanf (args, "%d", &sys->frequency);
X      if (sys->frequency < 0)
X	sprintf (error, "\nsys_config(): Invalid 'frequency' argument %d in %s",
X		 sys->frequency, CONFIG),
X	report_progress (report, error, TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "option")) {
X      sscanf (args, "%s", sys->arg);
X      if (!strcmp (locase (sys->arg), "bsd_ps"))
X	sys->options |= BSD_PS;
X      else if (!strcmp (sys->arg, "sysv_ps"))
X	sys->options |= SYSV_PS;
X      else if (!strcmp (sys->arg, "bsd_mail"))
X	sys->options |= BSD_MAIL;
X      else if (!strcmp (sys->arg, "bad_telnet"))
X	sys->options |= USE_MY_SYSTEM;
X      else
X	sprintf (error, "\nsys_config(): Invalid argument %s to 'option' in %s",
X		 sys->arg, CONFIG),
X	report_progress (report, error, TRUE),
X	exit (4);
X    }
X    else if (!strcmp (cmd, "serverd")) {
X      comment = strchr (args, '#');
X      if (comment)
X        *comment = EOS;
X      cmdarg = strchr (args, '-');
X      if (cmdarg)
X	strcat (sys->serverd_cmdoptions, cmdarg);
X    }
X    else if (!strcmp (cmd, "restriction"))
X      sscanf (args, "%d", &sys->users);
X    else if (!strcmp (cmd, "manager"))
X      sscanf (args, "%s", sys->manager);
X    else if (!strcmp (cmd, "password"))
X      sscanf (args, "%s", sys->server.password),
X      upcase (sys->server.password);
X    else if (!strcmp (cmd, "comment")) {
X      sscanf (args, "%s", sys->arg);
X      if (!strcmp (locase (sys->arg), "server")) {
X	comment = strchr (args, '#');
X	if (comment == NULL)
X	  sprintf (error, "\nsys_config(): Missing # for comment in %s",CONFIG),
X          report_progress (report, error, TRUE),
X          exit (4);
X	RESET (sys->server.comment);
X	strcat (sys->server.comment, comment + 1);
X      }
X      else {  /* Some list name was specified */
X	upcase (sys->arg);
X	id = get_list_id (sys->arg, sys, nlists);
X	if (id < 0)
X	  sprintf (error, "sys_config(): Unrecognized list name %s for \
X'comment' in %s", sys->arg, CONFIG),
X	  report_progress (report, error, TRUE),
X	  exit (4);
X	comment = strchr (args, '#');
X	if (comment == NULL)
X	  sprintf (error, "\nsys_config(): Missing # for comment in %s", CONFIG),
X	  report_progress (report, error, TRUE),
X	  exit (4);
X	RESET (sys->lists[id].comment);
X	strcat (sys->lists[id].comment, comment + 1);
X      }
X    }
X    else if (!strcmp (cmd, "mailmethod")) {
X      sscanf (args, "%s", sys->arg);
X      if (!strcmp (locase (sys->arg), "telnet"))
X	sys->mail.method = TELNET;
X      else if (!strcmp (sys->arg, "sendmail"))
X	sys->mail.method = (char *) malloc (256 * sizeof (char)),
X	sscanf (args, "%s %s", sys->arg, sys->mail.method),
X	locase (sys->mail.method),
X	strcat (sys->mail.method, " > /dev/null 2>&1");
X      else if (!strcmp (sys->arg, "binmail"))
X	sys->mail.method = BINMAIL;
X      else if (!strcmp (sys->arg, "env_var"))
X	sys->options |= USE_ENV_VAR,
X	sscanf (args, "%s %s %s", sys->arg, sys->mail.env_var,
X	        sys->mail.mail_prog),
X	locase (sys->mail.mail_prog),
X	upcase (sys->mail.env_var);
X      else
X	sprintf (error, "\nsys_config(): Unrecognized mail method %s in %s",
X		 sys->arg, CONFIG),
X	report_progress (report, error, TRUE),
X	exit (4);
X    }
X    else
X      sprintf (error, "\nUnrecognized command %s in %s", cmd, CONFIG),
X      report_progress (report, error, TRUE),
X      exit (4);
X  }
X  fclose (config);
X  return nlists;
X}
X
X/*
X  Convert a string to lower case.
X*/
X
Xchar *locase (char *s)
X{
X  char *r = s;
X  while (*s != EOS) {
X    if (isupper (*s))
X      *s = tolower (*s);
X    ++s;
X  }
X  return r;
X}
X
X/*
X  Convert a string to upper case.
X*/
X
Xchar *upcase (char *s)
X{
X  char *r = s;
X
X  while (*s != EOS) {
X    if (islower (*s))
X      *s = toupper (*s);
X    ++s;
X  }
X  return r;
X}
X
X/*
X  Write messages and times to REPORT_LIST and stdout. If 'report_time'
X  is set to a negative value no leading newline is printed to 'report'.
X  NOTE: Use strftime(), if possible.
X*/
X
Xvoid report_progress (FILE *report, char *s, int report_time)
X{
X  long int time_is = 0;
X  struct tm *t;
X
X  fprintf (report, "%s", s);
X  if (report_time)
X    time (&time_is),
X    t = localtime (&time_is),
X    fprintf (report, "%cTime/Date: %2d:%.2d:%.2d, %2d/%.2d/%2d\n",
X	     (report_time > 0 ? '\n' : EOS),
X             t->tm_hour, t->tm_min, t->tm_sec, t->tm_mon + 1, t->tm_mday,
X             t->tm_year);
X  fflush (report);
X  printf ("%s", s);
X  if (report_time)
X    printf ("\n");
X  fflush (stdout);
X}
X
X/*
X  Start distribution. Call lower level routines; the algorithm is such that
X  this routine always looks at the beginning of each message when reading
X  into 'first_line', i.e. 'first_line' is always assigned a string of the form:
X		'From emailaddress Date Time'
X  which is the universal convention for the start of every message. It calls
X  subscribed() to find out if the sender is subscribed and passes the
X  result to process_message(). Since the call to extract_sender() alters
X  'first_line' to contain only the sender's email address a 'linecopy' is used.
X  When returning from process_message() we have already advanced to the
X  beginning of the next message, and 'linecopy' contains a string of the 
X  above form; therefore we need to copy that back to 'first_line'.
X*/
X
Xvoid distribute (FILE *mail, void (*process_message)(), FILE *report,
X		 char *subscribersf)
X{
X  char first_line[MAX_LINE], linecopy[MAX_LINE];
X
X  first_line[0] = RESET (linecopy);
X  while (!feof (mail))
X    if (strlen (first_line) > 0)
X      extract_sender (first_line),
X      process_message (first_line, linecopy, 
X		       subscribed (report, first_line, subscribersf)),
X      strcpy (first_line, linecopy);
X    else /* Read the first line of the very first message */
X      fgets (first_line, MAX_LINE - 2, mail), /* 'From email Date Time' */
X      strcpy (linecopy, first_line);
X}
X
X/*
X  Look for occurence of 'string' in 'substr' and return either TRUE or FALSE.
X  Look at the comments for defs.h for the syntax of 'substr'. A 'substr' of
X  "*" matches everything.
X*/
X
XBOOLEAN strinstr (char *substr, char *string, char *sep)
X{
X  char *origstr = string, *tok, *substrcopy;
X  int l;
X
X  if (*substr == '*')
X    return TRUE;
X  substrcopy = (char *) malloc ((strlen (substr) + 1) * sizeof (char));
X  strcpy (substrcopy, substr);
X  tok = strtok (substrcopy, sep); /* Get first token */
X  while (tok) { /* For every token in substr, check if it's in 'string' */
X    string = origstr;
X    l = strlen (tok);
X    if (strlen (string) < l) {
X      tok = strtok (NULL, sep);  /* get next token */
X      continue;
X    }
X    while (strlen (string) >= l)
X      if (! strncmp (tok, string, l)) {
X        free (substrcopy);
X        return TRUE;
X      }
X      else
X        ++string;
X    tok = strtok (NULL, sep); /* failed with this token; get the next one */
X  }
X  free (substrcopy);
X  return FALSE;
X}
X
X/*
X  Given a file path, extract the file name. In the process, any command line
X  options or any characters separated from the filename are discarded.
X*/
X
Xchar *extract_filename (char *s)
X{
X  char *p, *r, *t = s, nchar = 0;
X
X  while (*t != EOS && *t != ' ' && *t != '\t') /* Get to delimiting char */
X    ++t;
X  while (t != s && *t != '/') /* Go back till / or the beginning of s */
X    ++nchar,
X    --t;
X  if (t != s)
X    ++t;
X  r = p = (char *) malloc ((nchar + 1) * sizeof (char));
X  *p = EOS;
X  while (*t != EOS && *t != ' ' && *t != '\t')
X    *(p++) = *(t++);
X  *p = EOS;
X  return r;
X}
X
X/*
X  Recognize the command line parameters. It returns '?' on an unrecognized
X  option, ':' if an option requires an argument and the argument is missing,
X  or the recognized character itself. This is an upgraded and fixed-up version
X  of AT&T's code (which seg-faults).
X*/
X
X# define ERR(str, ch) if (opterr) \
X		        fprintf (stderr, "%s%s%c\n", argv[0], str, ch);
X
Xchar *optarg;
Xint  opterr = 0;
Xint  optind = 1;
Xint  optopt;
X
X
Xint getopt (int argc, char **argv, char *opts)
X{
X  static int sp = 1;
X  register int c;
X  register char *cp;
X
X  if (optind >= argc)
X    return EOF;
X  else if (argv[optind][0] != '-') {
X    optind++;
X    if (optind >= argc)
X      return EOF;
X  }
X  optopt = c = argv[optind][sp];
X  if (c == '\0')
X    optopt = c = argv[optind][0],
X    sp = 0;
X  if (c == ':' || (cp = strchr (opts, c)) == NULL) {
X    ERR (":: illegal option -- ", c);
X    if (argv[optind][++sp] == '\0')
X      optind++,
X      sp = 1;
X    return '?';
X  }
X  if (*++cp == ':') {
X    if (argv[optind][sp+1] != '\0')
X      optarg = &argv[optind++][sp+1];
X    else if (++optind >= argc) {
X      ERR (":: option requires an argument -- ", c);
X      sp = 1;
X      return ':';
X    }
X    else
X      optarg = argv[optind++];
X    sp = 1;
X  } 
X  else {
X    if (argv[optind][++sp] == '\0')
X      sp = 1,
X      optind++;
X    optarg = NULL;
X  }
X  return c;
X}
X
X/*
X  Remove any extraneous characters from a name and convert it to lower
X  case with the first character of each word capitalized.
X*/
X
Xchar *cleanup_name (char *s)
X{
X  char *tok, *copy, *sep = " ", *line;
X  int j, l;
X
X  if (s [strlen (s) - 1] == '\n')
X    s [strlen (s) - 1] = EOS;
X  line = (char *) malloc ((strlen (s) + 1) * sizeof (char));
X  strcpy (line, s);
X  *s = EOS;
X  tok = strtok (line, sep);
X  while (tok) {
X    copy = tok;
X    while (*copy != EOS) {  /* Remove extraneous characters */
X      if (! isalpha (*copy) && ! isspace (*copy)) {
X        for (j = 0, l = strlen (copy); j < l; copy[j] = copy[j+1], ++j);
X        continue;
X      }
X      ++copy;
X    }
X    locase (tok);
X    *tok = toupper (*tok);
X    sprintf (s + strlen (s), " %s", tok);
X    tok = strtok (NULL, sep);
X  }
X  free (line);
X  return s;
X}
X
X/*
X  Identify the list that the request refers to. Before doing so, remove
X  any unnecessary blanks from the parameters. Also, check each parameter
X  for proper syntax.
X*/
X
Xvoid get_list_name (char *params, char *list_name)
X{
X  int i;
X  char *s, *r, *t;
X  char param [MAX_LINE];
X
X  r = s = (char *) malloc ((strlen (params) + 1) * sizeof (char));
X  strcpy (s, params);
X  RESET (params);
X  RESET (param);
X  for (i = 0; s[i] != EOS;)
X    if (isspace (s[i]) && isspace (s[i + 1]))
X      sprintf (s + i, "%s", s + i + 1);
X    else
X      i++;
X  do {
X    RESET (param);
X    sscanf (s, "%s", param);
X    s = s + strlen (param) + 1; /* Skip over space */
X    t = strpbrk (param, "*?/");
X    if (t != NULL) {
X      if (t == param || (t != param && ! isalpha (*(t - 1)))) /* Invalid *,? */
X	*t = '+';
X      if (*t == '/') /* Invalid / */
X	*t = '%';
X    }
X    sprintf (params + strlen (params), " %s", param);
X  } while (param[0] != EOS);
X  strcat (params, "\n");
X  free (r);
X  RESET (list_name);
X  sscanf (params, "%s", list_name);
X  upcase (list_name);
X  sprintf (params, "%s", params + strlen (list_name) + 1);
X}
X
X/*
X  Given a list name, return its index in the array of known lists.
X*/
X
Xint get_list_id (char *list_name, SYS *sys, int nlists)
X{
X  int i;
X
X  for (i = 0; i < nlists; i++)
X    if (!strcmp (list_name, sys->lists[i].alias))
X      return i;
X  return -1;
X}
X
Xvoid setup_string (char *s, char *alias, char *filename)
X{
X  RESET (s);
X  sprintf (s, "%s/lists/%s/%s", PATH", alias, filename);
X}
X
X/*
X  Use my_system() when either list and listserv use TELNET and there is
X  an indication that the sessions do not exit.
X*/
X
Xint my_system (s)
Xchar *s;
X{
X  FILE *f;
X  int pid, status;
X  char command [512], line [MAX_LINE], *r;
X  extern SYS sys;
X
X  strcat (s, " &");
X  status = system (s);
X  if (status > 127)
X    return status;
X  RESET (command);
X  sscanf (s, "%s", command);
X  if (strcmp ((r = extract_filename (command)), "telnet")) {
X    free ((char *) r);
X    return 0;
X  }
X  free ((char *) r);
X  sleep (5);
X  if (sys.options & BSD_PS)
X    system ("ps -gx | grep telnet | grep -v \"grep telnet\" > ./telnet");
X  else if (sys.options & SYSV_PS)
X    system ("ps -ef | grep telnet | grep -v \"grep telnet\" > ./telnet");
X  if ((f = fopen ("./telnet", "r")) == NULL)
X    return 0;
X  while (! feof (f)) {
X    RESET (line);
X    fgets (line, MAX_LINE, f);
X    if (strlen (line) > 0)
X      sscanf (line, "%d", &pid),
X      sleep (15),
X      kill (pid, SIGKILL);
X  }
X  fclose (f);
X  unlink ("./telnet");
X  return 0;
X}
END_OF_src/misc.c
if test 17217 -ne `wc -c <src/misc.c`; then
    echo shar: \"src/misc.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/sender.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/sender.c\"
else
echo shar: Extracting \"src/sender.c\" \(3750 characters\)
sed "s/^X//" >src/sender.c <<'END_OF_src/sender.c'
X/*
X  ----------------------------------------------------------------------------
X  |                	 GENERAL PURPOSE FUNCTIONS			     |
X  |                                                                          |
X  |                              Version 2.0                                 |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                 (tasos@cs.bu.edu, tasos@bu-pub.bu.edu)                   |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the Copyright notice in the file defs.h;   |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header() in    |
X  | list.c and listserv.c. By using this software you are bound by this      |
X  | agreement.                                                               |
X  | COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas                  |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X#include "defs.h"
X
Xextern  void report_progress (FILE *, char *, int);
Xextern  char *upcase (char *);
X
XBOOLEAN subscribed (FILE *, char *, char *);
XBOOLEAN ignore_sender (FILE *, char *, FILE *);
Xvoid    extract_sender (char *);
X
X/*
X  Check if 'sender' is subscribed. Return SUBSCRIBED or NOTSUBSCRIBED.
X  Comparisons are done in upper case.
X*/
X
XBOOLEAN subscribed (FILE *report, char *sender, char *subscribersf)
X{
X  char subscriber [MAX_LINE];
X  char mode [MAX_LINE];
X  char name [MAX_LINE];
X  char error [MAX_LINE];
X  FILE *subscribers;
X
X  if (subscribersf == NULL) /* call made by distribute () */
X    return NOTSUBSCRIBED;
X  if ((subscribers = fopen (subscribersf, "r")) == NULL)
X    RESET (error),
X    sprintf (error, "\nsubscribed(): Could not open %s", subscribersf),
X    report_progress (report, error, TRUE),
X    exit (1);
X  upcase (sender);          /* Convert to upper case */
X  while (!feof (subscribers)) {  /* Check list of subscribers */
X    subscriber[0] = mode[0] = RESET (name);
X    fscanf (subscribers, "%s %s ", subscriber, mode);
X    fgets (name, MAX_LINE - 2, subscribers);
X    upcase (subscriber);
X    if (!strcmp (sender, subscriber)) {
X      fclose (subscribers);
X      return SUBSCRIBED;
X    }
X  }
X  fclose (subscribers);
X  return NOTSUBSCRIBED;
X}
X
X/*
X  Check if 'sender' appears in the IGNORED file.
X*/
X
XBOOLEAN ignore_sender (FILE *ignored, char *sender, FILE *report)
X{
X  char report_msg [MAX_LINE];
X  char ignored_user [MAX_LINE];
X
X  rewind (ignored);
X  while (!feof (ignored)) { /* Check the IGNORED file first */
X    RESET (ignored_user);
X    fscanf (ignored, "%s", ignored_user);
X    upcase (ignored_user);
X    if (!strcmp (ignored_user, sender)) {
X      RESET (report_msg);
X      sprintf (report_msg, "User %s and message ignored.\n", sender);
X      report_progress (report, report_msg, FALSE);
X      return TRUE;
X    }
X  }
X  return FALSE;
X}
X
X/*
X  Extract the sender's email address. To do that, skip over the leading
X  "From ". That's where the address starts. Then put an EOS character at
X  the end of this address (a blank terminates this address string).
X*/
X
Xvoid extract_sender (char *s)
X{
X  sprintf (s, "%s", s + strlen (START_OF_MESSAGE));
X  while (*s && *s != ' ')  /* Look for end of address substring */
X    ++s;
X  *s = EOS;  /* 's' is now pointing to the sender's address */
X}
END_OF_src/sender.c
if test 3750 -ne `wc -c <src/sender.c`; then
    echo shar: \"src/sender.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/serverd.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/serverd.c\"
else
echo shar: Extracting \"src/serverd.c\" \(7648 characters\)
sed "s/^X//" >src/serverd.c <<'END_OF_src/serverd.c'
X/*
X  ----------------------------------------------------------------------------
X  |                     DISCUSSION LIST SYSTEM DAEMON			     |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                 (tasos@cs.bu.edu, tasos@bu-pub.bu.edu)                   |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the Copyright notice in the file defs.h;   |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header() in    |
X  | list.c and listserv.c. By using this software you are bound by this      |
X  | agreement.                                                               |
X  | COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas                  |
X  ----------------------------------------------------------------------------
X
X  Spawn list or listserv upon arrival of new messages. Send mail to
X  MANAGER if something went wrong (only if using UCB mail).
X  serverd will not spawn if the current load average is above max_load
X  and the -l flag is on, until it has delayed MAX_TRIES * 30 seconds.
X  serverd communicates with listerv via exit status 6 and 7; this are
X  the request to shutdwon/restart the system; on status 7, serverd spawns
X  start and dies; if it cannot restart, it sends mail and commits suicide.
X
X  COMMAND LINE OPTION:
X    -l: Enforce restrictions based on the current load average.
X
X  EXIT CODES:
X    0: OK
X    1: Could not open file
X    2: Could not lock file
X    3: Command line option error
X    4: Syntax error in file
X    5: Could not spawn
X    6: Shutdown request
X    7: Restart request
X    8: Received system signal
X*/
X
X#include <stdio.h>
X#include <unistd.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#include "defs.h"
X#include "serverd.h"
X#include "struct.h"
X#include "global.h"
X
X#ifdef __STDC__
X#include <stdarg.h>
Xextern int  syscom (char *, ...);
X#else
X#include <varargs.h>
Xextern int  syscom ();
X#endif
Xextern double atof ();
Xextern int  sys_config (FILE *, SYS *);
Xextern int  getopt (int, char **, char *);
X
Xvoid   main (int, char **, char **);
Xvoid   serverd_config (char *alias);
Xvoid   usage (void);
X
Xchar *exit_string[] = {  /* Define exit status strings */
X/* 0 */ "OK", 
X/* 1 */ "Could not open file", 
X/* 2 */ "Could not lock file",
X/* 3 */ "Command line option error",
X/* 4 */ "Syntax error in file",
X/* 5 */ "Could not spawn",
X/* 6 */ "Shutdown request",
X/* 7 */ "Restart request",
X/* 8 */ "Received system signal"
X};
X  
Xvoid main (int argc, char **argv, char **envp)
X{
X  struct stat stat_buf;
X  int i, status, try, fd, nlists;
X  FILE *loadf, *report;
X  float load, max_load;
X  BOOLEAN loadr = FALSE;
X  char list [MAX_LINE];
X  char server [MAX_LINE];
X  char msg [MAX_LINE], c;
X  char *options = "l:";
X  extern char *optarg;
X  extern int optopt;
X
X  while ((c = getopt (argc, argv, options)) != EOF)
X    switch (c) {
X      case 'l': loadr = TRUE; 
X		max_load = (float) atof (optarg); 
X		break;
X      case ':': 
X        fprintf (stderr, "serverd:  Option '%c' requires an argument.\n",
X		 optopt);
X	exit (3);
X      case '?':
X      default:
X	usage ();
X    }
X
X  if (lockf ((fd = open (SERVERD_LOCK_FILE, O_RDWR)), F_TLOCK, 0))
X    fprintf (stderr, "serverd: Unable to lock %s. Aborting.\n", 
X             SERVERD_LOCK_FILE),
X    exit (2);
X  if ((report = fopen (REPORT_SERVERD, "a")) == NULL)
X    fprintf (stderr, "serverd: Could not open %s\n", REPORT_SERVERD),
X    exit (1);
X  
X  nlists = sys_config (report, &sys);
X  while (007) {
X    for (i = 0; i < nlists; ++i) {
X      serverd_config (sys.lists[i].alias);
X      if (!stat (list_mail_f, &stat_buf) && stat_buf.st_size > 0) {
X	sprintf (msg, "\n--- NEW MAIL FOR %s ---", sys.lists[i].alias);
X	report_progress (report, msg, TRUE);
X        if (loadr) { /* enforce restrictions */
X          try = 0;
X        again_list:
X          syscom ("%s | %s -F, '{ print $4 }' | %s -d' ' -f5 > %s",
X		  UPTIME, AWK, CUT, LOAD_FILE);
X          if ((loadf = fopen (LOAD_FILE, "r")) == NULL)
X	    sprintf (msg, "\nCould not open %s", LOAD_FILE),
X	    report_progress (report, msg, TRUE),
X	    exit (1);
X          fscanf (loadf, "%f", &load);
X          fclose (loadf);
X          unlink (LOAD_FILE);
X          if (load > max_load && try < MAX_TRIES) {
X	    ++try;
X            sleep (30);
X	    goto again_list;
X          }
X        }
X	RESET (list);
X	sprintf (list, "%s %s %s", LIST, sys.lists[i].alias,
X		 sys.lists[i].cmdoptions);
X        if ((status = system (list)) > 127) {   /* run list */
X          sprintf (msg, "serverd died: LIST: %s", exit_string[status / 256]);
X          if (sys.options & BSD_MAIL)
X	    syscom ("%s -s \"%s\" %s < /dev/null", UCB_MAIL, msg, sys.manager);
X	  report_progress (report, msg, TRUE);
X	  exit (status / 256);
X        }
X        else if (status == 127) {
X	  sprintf (msg, "serverd died: could not execute shell for list");
X	  if (sys.options & BSD_MAIL)
X	    syscom ("%s -s \"%s\" %s < /dev/null", UCB_MAIL, msg, sys.manager);
X	  report_progress (report, msg, TRUE);
X	  exit (5);
X        }
X      }
X      if (sys.frequency > 0)
X        sleep (sys.frequency);     /* do a quickie to The Spy Who Loved Him */
X    }
X    if (!stat (SERVER_MAIL_FILE, &stat_buf) && stat_buf.st_size > 0) {
X      report_progress (report, "\n--- NEW MAIL FOR SERVER ---", TRUE);
X      if (loadr) { /* Enforce restrictions */
X        try = 0;
X      again_server:
X        syscom ("%s | %s -F, '{ print $4 }' | %s -d' ' -f5 > %s",
X                UPTIME, AWK, CUT, LOAD_FILE);
X        if ((loadf = fopen (LOAD_FILE, "r")) == NULL)
X          sprintf (msg, "\nCould not open %s", LOAD_FILE),
X          report_progress (report, msg, TRUE),
X          exit (1);
X        fscanf (loadf, "%f", &load);
X        fclose (loadf);
X        unlink (LOAD_FILE);
X        if (load > max_load && try < MAX_TRIES) {
X          ++try;
X          sleep (30);
X          goto again_server;
X        }
X      }
X      RESET (server);
X      sprintf (server, "%s %s", SERVER, sys.server.cmdoptions);
X      if ((status = system (server)) > 127) { /* run server */
X        sprintf (msg,"serverd died: LISTSERV: %s", exit_string[status / 256]);
X	if (sys.options & BSD_MAIL)
X	  syscom ("%s -s \"%s\" %s < /dev/null", UCB_MAIL, msg, sys.manager);
X	report_progress (report, msg, TRUE);
X	if (status == 6 * 256)
X	  exit (6);
X	if (status == 7 * 256) {
X	  execl (START, START_OPTIONS, START_OPTIONS, NULL);
X	  sprintf (msg, "serverd commits suicide: Could not restart system");
X          report_progress (report, msg, TRUE);
X          if (sys.options & BSD_MAIL)
X            syscom ("%s -s \"%s\" %s < /dev/null", UCB_MAIL, msg, sys.manager);
X	  exit (5);
X	}
X	else
X	  exit (status / 256);
X      }
X      else if (status == 127) {
X	sprintf (msg, "serverd died: could not execute shell for listserv");
X	if (sys.options & BSD_MAIL)
X	  syscom ("%s -s \"%s\" %s < /dev/null", UCB_MAIL, msg, sys.manager);
X	report_progress (report, msg, TRUE);
X	exit (5);
X      }
X    }
X    if (sys.frequency > 0)
X      sleep (sys.frequency);     /* do a quickie to Money Penny */
X  }
X}
X
Xvoid serverd_config (char *alias)
X{
X  setup_string (list_mail_f, alias, LIST_MAIL_FILE);
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: serverd [-l]\n-l: Enforce load restrictions.\n");
X  exit (3);
X}
END_OF_src/serverd.c
if test 7648 -ne `wc -c <src/serverd.c`; then
    echo shar: \"src/serverd.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/signals.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/signals.c\"
else
echo shar: Extracting \"src/signals.c\" \(2727 characters\)
sed "s/^X//" >src/signals.c <<'END_OF_src/signals.c'
X/*
X  ----------------------------------------------------------------------------
X  |                      GENERAL PURPOSE FUNCTIONS                           |
X  |                                                                          |
X  |                              Version 2.0                                 |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                 (tasos@cs.bu.edu, tasos@bu-pub.bu.edu)                   |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the Copyright notice in the file defs.h;   |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header() in    |
X  | list.c and listserv.c. By using this software you are bound by this      |
X  | agreement.                                                               |
X  | COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas                  |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X#include <signal.h>
X#include "defs.h"
X#include "struct.h"
X
Xextern void report_progress (FILE *, char *, int);
X
Xvoid   init_signals (void);
Xvoid   catch_signals (void);
Xvoid   my_abort (int);
X
Xstatic char *signals[MAX_SIGNAL + 1];   /* Signal names */
X
X/*
X  Initialize the signals[].
X*/
X
Xvoid init_signals ()
X{
X  signals[SIGHUP] = "SIGHUP";
X  signals[SIGINT] = "SIGINT";
X  signals[SIGQUIT] = "SIGQUIT";
X  signals[SIGTERM] = "SIGTERM";
X  signals[SIGBUS] = "SIGBUS";
X  signals[SIGSEGV] = "SIGSEGV";
X}
X
X/*
X  Catch signals to print messages before aborting.
X*/
X
Xvoid catch_signals ()
X{
X  signal (SIGHUP, my_abort);
X  signal (SIGINT, my_abort);
X  signal (SIGQUIT, my_abort);
X  signal (SIGTERM, my_abort);
X  signal (SIGSEGV, my_abort);
X  signal (SIGBUS, my_abort);
X  signal (SIGKILL, my_abort);
X}
X
X/*
X  Kill program after sending message to MANAGER that this is
X  about to happen (message is sent only when using UCB mail).
X*/
X
Xvoid my_abort (int sig)
X{
X  char s [MAX_LINE];
X  char command [MAX_LINE];
X  extern SYS sys;
X
X  printf ("\n*** Received %s signal. ***\n", signals[sig]);
X  if (sys.options & BSD_MAIL)
X    RESET (command),
X    sprintf (command, "%s -s \"SERVER/LIST RECVD SIG %s\" %s &",
X             UCB_MAIL, signals[sig], sys.manager),
X    system (command);
X  exit (8);
X}
END_OF_src/signals.c
if test 2727 -ne `wc -c <src/signals.c`; then
    echo shar: \"src/signals.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/start.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/start.c\"
else
echo shar: Extracting \"src/start.c\" \(10212 characters\)
sed "s/^X//" >src/start.c <<'END_OF_src/start.c'
X/*
X  ----------------------------------------------------------------------------
X  |                  DISCUSSION LIST SYSTEM HOUSEKEEPER                      |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                 (tasos@cs.bu.edu, tasos@bu-pub.bu.edu)                   |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely as long	     |
X  | as you do not remove or alter the Copyright notice in the file defs.h;   |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header() in    |
X  | list.c and listserv.c. By using this software you are bound by this      |
X  | agreement.                                                               |
X  | COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas		     |
X  ----------------------------------------------------------------------------
X
X  This is the proper way of starting the discussion list. The program verifies
X  that no other serverd, list or listserv programs are running on the 
X  system (and kills them before proceeding -- after confirming), 
X  makes sure the required files exist (and creates new ones if missing -- 
X  after confirming), backs up all reports into files with extension .acc,
X  creates new directories for new discussion lists as necessary,
X  and finally spawns serverd.
X
X  COMMAND LINE OPTIONS:
X    -k: just kill all pertinent programs that may already be running; no
X	discussion list is started in this case.
X    -c: Do not confirm before killing a process.
X    -r: Do not report; useful when starting up when system is rebooted.
X  
X  WARNING: The program won't work correctly in the absence of an extended
X  egrep facility that does matching at the end of a line with a $ and 
X  accepts multiple regular expressions separated by a |. In this case,
X  the user may have to manually look for and terminate any such programs.
X  Also when the output of the 'ps' command exceeds 80 characters (due perhaps
X  to long path names) the user may again have to terminate programs by hand.
X  Note that a file locking mechanism  for serverd, list and listserv is used 
X  to ensure against multiple executions of the same program.
X
X  WARNING: When using the SYSV ps command and it chops output to 80 characters,
X  start may not be able to locate processes already running; this may occur if
X  the path to /usr/server is too long.
X*/
X
X#include <stdio.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "defs.h"
X#include "start.h"
X#include "struct.h"
X#include "global.h"
X
X#define GET_RESPONSE  {\
X			fflush (stdout); \
X			fflush (stdin); \
X			c = fgetc (stdin); \
X			if (c != '\n' && c != EOF) \
X			  while (fgetc (stdin) != '\n'); \
X			fflush (stdin);\
X		      }
X
X/*
X  Function prototypes:
X*/
X
X#ifdef __STDC__
X#include <stdarg.h>
Xextern int  syscom (char *, ...);
X#else
X#include <varargs.h>
Xextern int  syscom ();
X#endif
Xextern char *extract_filename (char *);
Xextern void report_progress (FILE *, char *, int);
Xextern int  getopt (int, char **, char *);
Xvoid   main (int, char **);
Xvoid   check_for (char *, FILE *, BOOLEAN, BOOLEAN);
Xvoid   usage (void);
Xvoid   start_abort (void);
Xvoid   backup (char *, char *);
Xvoid   start_config (char *);
X
Xvoid main (int argc, char **argv)
X{
X  char command [256];
X  char line [MAX_LINE];
X  char msg [MAX_LINE];
X  char dir [MAX_LINE];
X  char c, *addr1, *addr2, *addr3, *options = "ckr";
X  FILE *f, *p, *report;
X  int i, nlists, pid, procs_killed = 0, procs;
X  BOOLEAN just_kill = FALSE, confirm = TRUE, do_report = TRUE;
X  struct stat stat_buf;
X  extern char *optarg;
X  extern int optopt;
X  
X  while ((c = getopt (argc, argv, options)) != EOF)
X    switch (c) {
X      case 'c': confirm = FALSE; break;
X      case 'k': just_kill = TRUE; break;
X      case 'r': do_report = FALSE; break;
X      case '?':
X      default:
X	usage();
X    }
X
X  /* First make sure no other SERVERD programs are running. If so, kill
X     them all (after confirming) before proceeding. */
X  
X  backup (REPORT_START, REPORT_START_ACC);
X  if ((report = fopen (REPORT_START, "a")) == NULL)
X    fprintf (stderr, "start: Could not open %s\n", REPORT_START),
X    start_abort();
X  nlists = sys_config (report, &sys);
X  if (just_kill)
X    report_progress (report, "\n--- SHUTTING LISTSERV SYSTEM DOWN ---\n",FALSE);
X  else
X    report_progress (report, "\n--- STARTING LISTSERV SYSTEM ---\n", FALSE);
X  if (sys.options & BSD_PS)
X    syscom ("ps -gx > /tmp/ps");  /* do not combine the two "syscom"'s */
X  else
X    syscom ("ps -ef | grep %s > /tmp/ps", getenv ("USER"));
X  syscom ("egrep '%s$|%s|%s$|%s |%s$|%s ' /tmp/ps > /tmp/found",
X	  (addr1 = extract_filename (SERVERD)), addr1,
X 	  (addr2 = extract_filename (LIST)), addr2,
X	  (addr3 = extract_filename (SERVER)), addr3);
X  free ((char *) addr1);
X  free ((char *) addr2);
X  free ((char *) addr3);
X  unlink ("/tmp/ps");
X  if ((f = fopen ("/tmp/found", "r")) == NULL)
X    sprintf (msg, "Error opening /tmp/found. Aborting. %s",
X	     ((just_kill == FALSE) ? "No discussion list started." : "")),
X    report_progress (report, msg, TRUE),
X    start_abort ();
X  
X  syscom ("wc -l /tmp/found > /tmp/nprocs");
X  if ((p = fopen ("/tmp/nprocs", "r")) != NULL) {
X    fscanf (p, "%d", &procs);
X    if (do_report)
X      sprintf (msg, "OLD LISTSERV PROCESSES RUNNING:\t%d\n", procs),
X      report_progress (report, msg, FALSE);
X    fclose (p);
X    unlink ("/tmp/nprocs");
X  }
X  else
X    sprintf (msg, "Error opening /tmp/nprocs. Aborting. %s",
X	     ((just_kill == FALSE) ? "No discussion list started." : "")),
X    report_progress (report, msg, TRUE),
X    start_abort ();
X  
X  while (!feof (f)) {  /* get pid's and kill processes after confirming */
X    RESET (line);
X    fgets (line, MAX_LINE, f);
X    if (strlen (line) > 0) {
X      if (sys.options & BSD_PS)
X        sscanf (line, "%d ", &pid);
X      else
X        sscanf (line, "%s %d ", command, &pid);
X      if (confirm) {
X	c = EOS;
X        while (c != EOF && c != 'Y' && c != 'N') {
X	  sprintf (msg, "\n%s\n%c[7m%s%c[mKill it to proceed ? (Y/N) ",
X		   ((just_kill == FALSE) ? 
X		    "ERROR: another listserv-system program running:" : 
X		    "Listserv-system program found:"),
X		   27, line, 27);
X	  report_progress (report, msg, FALSE);
X	  GET_RESPONSE;
X        }
X        if (c == 'N' || c == EOF)
X	  sprintf (msg, "start aborted. %s\n",
X		   ((just_kill == FALSE) ? "Discussion list not started.":"")),
X	  report_progress (report, msg, TRUE),
X	  exit (1);
X      }
X      kill (pid, SIGKILL);
X      ++procs_killed;
X    }
X  }
X  fclose (f);
X  unlink ("/tmp/found");
X  if (do_report)
X    sprintf (msg, "OLD LISTSERV PROCESSES KILLED:\t%d\n", procs_killed),
X    report_progress (report, msg, FALSE);
X  
X  if (just_kill)  /* Done */
X    report_progress (report, "", -TRUE),
X    fclose (report),
X    exit (0);
X  
X  syscom ("echo Serverd lock file > %s", SERVERD_LOCK_FILE);
X  syscom ("echo List lock file > %s", LIST_LOCK_FILE);
X  syscom ("echo Server lock file > %s", SERVER_LOCK_FILE);
X
X  backup (REPORT_SERVER, REPORT_SERVER_ACC);
X  backup (REPORT_SERVERD, REPORT_SERVERD_ACC);
X  sprintf (server_ignoredf, "%s/%s", PATH", IGNORED);
X  check_for (server_ignoredf, report, do_report, confirm);
X  for (i = 0; i < nlists; ++i) {
X    start_config (sys.lists[i].alias);
X    sprintf (dir, "%s/lists/%s", PATH", sys.lists[i].alias);
X    if (stat (dir, &stat_buf)) {
X      if (mkdir (dir, 448))
X	sprintf (msg, "Could not create directory %s", dir),
X	report_progress (report, msg, TRUE),
X	start_abort ();
X      if (do_report)
X        sprintf (msg, "*** New list %s ***\n", sys.lists[i].alias),
X        report_progress (report, msg, FALSE);
X      syscom ("cp %s %s", server_ignoredf, dir);
X      syscom ("touch %s", infof);
X      syscom ("touch %s", welcomef);
X    }
X    check_for (subscribersf, report, do_report, confirm);
X    check_for (restrictedf, report, do_report, confirm);
X    check_for (ignoredf, report, do_report, confirm);
X    check_for (infof, report, do_report, confirm);
X    check_for (welcomef, report, do_report, confirm);
X    backup (report_listf, report_list_accf);
X  }
X  syscom ("%s %s &", SERVERD, sys.serverd_cmdoptions);
X  report_progress (report, "", -TRUE);
X  fclose (report);
X  exit (0);
X}
X
X/*
X  Make sure that file 's' exists. Create a new one if necessary.
X*/
X
Xvoid check_for (char *s, FILE *report, BOOLEAN do_report, BOOLEAN confirm)
X{
X  char c = EOS, msg [MAX_LINE];
X  struct stat stat_buf;
X
X  if (stat (s, &stat_buf)) { /* make sure we have file 's' */
X    if (confirm) {
X      while (c != EOF && c != 'Y' && c != 'N') {
X 	if (do_report)
X          sprintf (msg, "No %s file found. Create a new one? (Y/N) ", s),
X	  report_progress (report, msg, FALSE);
X        GET_RESPONSE;
X      }
X      if (c == 'N' || c == EOF)
X        start_abort ();
X    }
X    syscom ("touch %s", s);
X  }
X}
X
X/*
X  Append 'src' to 'dest' and create a brand new 'src'.
X*/
X
Xvoid backup (char *src, char *dest)
X{
X  struct stat stat_buf;
X
X  if (!stat (src, &stat_buf)) /* prepare for backup */
X    syscom ("cat %s >> %s", src, dest),
X    unlink (src),
X    syscom ("touch %s", src);
X}
X
X/*
X  Print usage.
X*/
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: start [-k] [-c] [-r]\n\
X-k: Just kill any listserv-system programs running.\n\
X-c: Do not confirm before killing processes.\n\
X-r: Restrict reporting to screen.\n");
X  exit (1);
X}
X
X/*
X  Abort after printing a message.
X*/
X
Xvoid start_abort ()
X{
X  fprintf (stderr, "### start aborted; discussion list not started. ###\n");
X  exit (1);
X}
X
Xvoid start_config (char *alias)
X{
X  setup_string (subscribersf, alias, SUBSCRIBERS);
X  setup_string (restrictedf, alias, RESTRICTED);
X  setup_string (ignoredf, alias, IGNORED);
X  setup_string (report_listf, alias, REPORT_LIST);
X  setup_string (infof, alias, INFO_FILE);
X  setup_string (welcomef, alias, WELCOME_FILE);
X  setup_string (report_list_accf, alias, REPORT_LIST_ACC);
X}
END_OF_src/start.c
if test 10212 -ne `wc -c <src/start.c`; then
    echo shar: \"src/start.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f src/tlock.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"src/tlock.c\"
else
echo shar: Extracting \"src/tlock.c\" \(2003 characters\)
sed "s/^X//" >src/tlock.c <<'END_OF_src/tlock.c'
X/*
X  ----------------------------------------------------------------------------
X  |                		tlock UTILITY 				     |
X  |                                                                          |
X  |                              Version 1.0                                 |
X  |                                                                          |
X  |                (or, when Computer Science gets to you)                   |
X  |                                                                          |
X  |                    Written by Anastasios Kotsikonas                      |
X  |                 (tasos@cs.bu.edu, tasos@bu-pub.bu.edu)                   |
X  |                                                                          |
X  | AGREEMENT: This software can be used and distributed freely as long      |
X  | as you do not remove or alter the Copyright notice in the file defs.h;   |
X  | this notice is #define'd in the symbol VERSION. Although you may alter   |
X  | the code provided, you may not alter the functions create_header() in    |
X  | list.c and listserv.c. By using this software you are bound by this      |
X  | agreement.                                                               |
X  | COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas                  |
X  ----------------------------------------------------------------------------
X*/
X
X#include <stdio.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <errno.h>
X#include "defs.h"
X
Xvoid main ();
X
Xchar *s[] = { 
X  SERVERD_LOCK_FILE, LIST_LOCK_FILE, SERVER_LOCK_FILE, NULL
X};
X
X/*
X  Test whether any locks exist on the above files by other processes.
X*/
X
Xvoid main ()
X{
X  int i, fd, locks = 0;
X
X  for (i = 0; s[i]; i++) {
X    if (lockf ((fd = open (s[i], O_RDWR)), F_TLOCK, 0))
X      if (errno == EBADF)
X	locks = -1,
X	fprintf (stderr, "Cannot access %s\n", s[i]);
X      else
X	++locks,
X	printf ("Lock placed on %s\n", s[i]);
X    close (fd);
X  }
X  if (!locks)
X    printf ("No files locked.\n");
X}
END_OF_src/tlock.c
if test 2003 -ne `wc -c <src/tlock.c`; then
    echo shar: \"src/tlock.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f redux -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"redux\"
else
echo shar: Extracting \"redux\" \(494 characters\)
sed "s/^X//" >redux <<'END_OF_redux'
X#!/bin/sh
X# Script to reduce the size of mboxes by removing unnecessary fields in
X# the header of each message.
X# Usage: redux [mbox-file] (default is ./mbox)
X
XFILE=$1
Xif [ $# = 0 ]; then
X  FILE=./mbox
Xfi
Xif [ ! -r $FILE ]; then
X  echo File $FILE not found.
X  exit
Xfi
Xecho Reducing $FILE\; reduced file in /tmp/reduced >&2
X/usr/bin/egrep -v "^Cc: |^          with BSMTP;|^Received: |^Status: |^Message-Id: |^Comment: |^Reply-To: |^Sender: |^To: |^Return-Path: |^	id " $FILE > /tmp/reduced
Xexit
END_OF_redux
if test 494 -ne `wc -c <redux`; then
    echo shar: \"redux\" unpacked with wrong size!
fi
chmod +x redux
# end of overwriting check
fi
if test -f flocks -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"flocks\"
else
echo shar: Extracting \"flocks\" \(86 characters\)
sed "s/^X//" >flocks <<'END_OF_flocks'
X#include "defs.h"
Xrm -f SERVERD_LOCK_FILE
Xrm -f LIST_LOCK_FILE
Xrm -f SERVER_LOCK_FILE
END_OF_flocks
if test 86 -ne `wc -c <flocks`; then
    echo shar: \"flocks\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f ulock -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"ulock\"
else
echo shar: Extracting \"ulock\" \(133 characters\)
sed "s/^X//" >ulock <<'END_OF_ulock'
X#!/bin/sh
X# ulock - in conjunction with the file 'flocks', this utility removes all
X#        locked files.
X/lib/cpp flocks | /bin/sh
END_OF_ulock
if test 133 -ne `wc -c <ulock`; then
    echo shar: \"ulock\" unpacked with wrong size!
fi
chmod +x ulock
# end of overwriting check
fi
if test -f config -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"config\"
else
echo shar: Extracting \"config\" \(2174 characters\)
sed "s/^X//" >config <<'END_OF_config'
X#
X#		Discussion list system configuration file
X#
X#			      version 5.0
X#
X#			       July 1991
X#
X# AGREEMENT: This software can be used and distributed freely as long
X# as you do not remove or alter the Copyright notice in the file defs.h;
X# this notice is #define'd in the symbol VERSION. Although you may alter
X# the code provided, you may not alter the functions create_header() in
X# list.c and listserv.c. By using this software you are bound by this
X# agreement.
X# COPYRIGHT: Copyright 1991 (c), Anastasios C. Kotsikonas
X#
X# Note: On SysV and BSD Unix systems 'sendmail' is located under /usr/lib/
X# but on SysVR4 it is located under /usr/ucblib/
X
Xserver listserv@vlsi -r statistics  # define the server w/ restriction (opt.)
Xlist ermis ermis@vlsi 		    # define a list called 'ermis' (-r opt.)
Xlist venus venus@vlsi 		    # define a list called 'venus' (-r opt.)
Xserverd -l 0.5	 # the serverd program should be run with the -l option
X		 # serverd will delay if the load average is above 0.5
Xrestriction 20	 # certain requests may not be serviced abover this # of users
Xmanager tasos	 # email address of the manager of the system
Xpassword ellines # password for the 'restart' and 'shutdown' requests
Xcomment server	 #Actual comment must be preceded by a pound sign
Xcomment ermis	 #Same here
Xcomment venus	 #Venus Distribution List
Xfrequency 0	 # how often to read mail (in seconds >= 0)
Xoption sysv_ps	 # forces use of the SYS V ps command; alternative is bsd_ps
Xoption bsd_mail	 # define it if BSD mail is available
Xmailmethod env_var LOGNAME /bin/mail	# Valid 'mailmethod' options:
X					# telnet
X					# sendmail /usr/lib/sendmail -t
X					# binmail
X					# env_var VARIABLE /bin/mail
X					# env_var VARIABLE /usr/lib/sendmail -t
X#option bad_telnet # use this flag when a telnet session is not terminating
X#
X# IMPORTANT NOTE:
X#
X# listserv also recognizes the requests 'restart' and 'shutdown' followed by
X# a password, that can be used to remotely restart and shutdown the server
X# system. The password is defined in the 'config' file using the 'password'
X# directive followed by the actual password.
X# This feature is not documented in the man page for obvious reasons.
END_OF_config
if test 2174 -ne `wc -c <config`; then
    echo shar: \"config\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f .awk -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \".awk\"
else
echo shar: Extracting \".awk\" \(423 characters\)
sed "s/^X//" >.awk <<'END_OF_.awk'
X{
X  tabcol = 40;
X  l = 0;
X  if (length ($1) < tabcol)
X    l = int ((tabcol + 7 - length ($1)) / 8);
X  printf "%s", $1;
X  k = 8 - int ((length ($1) % 8));
X  for (i = 0; i < k; i++) printf (" ");
X  for (i = 0; i < l; i++) printf "        ";
X  if ($2 != "") printf " %s", $2;
X  if ($3 != "") printf " %s", $3;
X  if ($4 != "") printf " %s", $4;
X  if ($5 != "") printf " %s", $5;
X  if ($6 != "") printf " %s", $6;
X  print "";
X}
END_OF_.awk
if test 423 -ne `wc -c <.awk`; then
    echo shar: \".awk\" unpacked with wrong size!
fi
chmod +x .awk
# end of overwriting check
fi
if test -f .stats -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \".stats\"
else
echo shar: Extracting \".stats\" \(343 characters\)
sed "s/^X//" >.stats <<'END_OF_.stats'
X#!/bin/sh
Xpth=$1
Xsubscribers=$2
Xheaders=$3
Xshift; shift; shift
Xif [ $# = 0 ]; then
X  cut -d" " -f1 $subscribers > emails
Xelse
X  for user 
X  do
X    echo $user >> emails
X  done
Xfi
Xcat emails | $pth/.grep $headers > counts
Xpaste emails counts | awk -f $pth/.awk -
Xecho Total number of messages on file: `cat $headers | wc -l`
Xrm counts
Xrm emails
END_OF_.stats
if test 343 -ne `wc -c <.stats`; then
    echo shar: \".stats\" unpacked with wrong size!
fi
chmod +x .stats
# end of overwriting check
fi
if test -f .grep -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \".grep\"
else
echo shar: Extracting \".grep\" \(114 characters\)
sed "s/^X//" >.grep <<'END_OF_.grep'
X#!/bin/sh
Xf="k"
Xwhile [ "$f" != "" ]; do
X  read f
X  if [ "$f" != "" ]; then
X    grep -i "$f" $1 | wc -l
X  fi
Xdone
END_OF_.grep
if test 114 -ne `wc -c <.grep`; then
    echo shar: \".grep\" unpacked with wrong size!
fi
chmod +x .grep
# end of overwriting check
fi
if test -f .ignored -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \".ignored\"
else
echo shar: Extracting \".ignored\" \(25 characters\)
sed "s/^X//" >.ignored <<'END_OF_.ignored'
Xroot
Xserver
Xbin
Xsys
Xuucp
END_OF_.ignored
if test 25 -ne `wc -c <.ignored`; then
    echo shar: \".ignored\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f setup -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"setup\"
else
echo shar: Extracting \"setup\" \(765 characters\)
sed "s/^X//" >setup <<'END_OF_setup'
X#!/bin/sh
Xcd /usr/server
Xif [ ! -d lists ]; then
X  echo Creating directory `pwd`/lists
X  mkdir lists
Xfi
Xif [ ! -r start ]; then
X  echo start not found
X  exit 2
Xfi
Xif [ ! -r serverd ]; then
X  echo serverd not found
X  exit 2
Xfi
Xif [ ! -r list ]; then
X  echo list not found
X  exit 2
Xfi
Xif [ ! -r listserv ]; then
X  echo listserv not found
X  exit 2
Xfi
Xif [ ! -r config ]; then
X  echo config not found
X  exit 2
Xfi
Xif [ ! -r .awk ]; then
X  echo .awk not found
X  exit 2
Xfi
Xif [ ! -r .stats ]; then
X  echo .stats not found
X  exit 2
Xfi
Xif [ ! -r .grep ]; then
X  echo .grep not found
X  exit 2
Xfi
Xif [ ! -r .ignored ]; then
X  echo .ignored not found
X  exit 2
Xfi
Xchmod 700 . start serverd list listserv .stats .grep lists
Xchmod 600 config
Xecho List Server System ready.
Xexit 0
END_OF_setup
if test 765 -ne `wc -c <setup`; then
    echo shar: \"setup\" unpacked with wrong size!
fi
chmod +x setup
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0
