: This is a shar archive.  Extract with sh, not csh.
echo x - export-listserv/ADDHELP
cat > export-listserv/ADDHELP << '!Funky!Stuff!'

ADD listname	 

ADD address listname	 
	adds the given address to or from the list specified.  
	Mail is sent to the address given to confirm the add 
	operation.  We strongly recommend that you use your 
	campus registered mailname when subscribing (i.e., use 
	the second form of the command which includes a 
	specification of the address).  If you omit the 'address'
	the command will assume the mailbox that is in the From:
	line of the message.  Note that SUBSCRIBE is a synonym for
	ADD.
                             
!Funky!Stuff!
echo x - export-listserv/BADADDR
cat > export-listserv/BADADDR << '!Funky!Stuff!'

Your file request cannot be completed.
!Funky!Stuff!
echo x - export-listserv/COMMANDS
cat > export-listserv/COMMANDS << '!Funky!Stuff!'
add	ADDHELP
subscribe	ADDHELP
sub	ADDHELP
delete	DELHELP
del	DELHELP
unsubscribe	DELHELP
unsub	DELHELP
delete-all	D-ALLHELP
del-all	D-ALLHELP
unsubscribe-all	D-ALLHELP
unsub-all	D-ALLHELP
list	LISTHELP
index INDEXHELP
longindex INDEXHELP
help	HELP
faq	FAQHELP
info	INFOHELP
get	INFOHELP
!Funky!Stuff!
echo x - export-listserv/DELHELP
cat > export-listserv/DELHELP << '!Funky!Stuff!'

DELETE listname	
 
DELETE address listname	
	deletes the given address to or from the list specified.
	Mail is sent to the address given to confirm the delete
	operation.  We strongly recommend that you use your campus 
	registered mailname when subscribing(i.e., use the second 
	form of the command which includes a specification of the 
	address). If you omit the 'address' the command will assume 
	the mailbox that is in the From: line of the message.  Note 
	that UNSUBSCRIBE is a synonym for DELETE.
!Funky!Stuff!
echo x - export-listserv/D_ALLHELP
cat > export-listserv/D_ALLHELP << '!Funky!Stuff!'

DELETE-ALL
UNSUBSCRIBE-ALL

DELETE-ALL address
UNSUBSCRIBE-ALL address
	unsubscribes given address from all mailing lists. Mail is sent 
	the address given to confirm the deletions. If you omit 
	the 'address' the command will assume the mailbox that is in 
	the From: line of the message.
!Funky!Stuff!
echo x - export-listserv/FAQHELP
cat > export-listserv/FAQHELP << '!Funky!Stuff!'
FAQ

FAQ listname
	sends a list of "Frequently Asked Questions" for the appropriate
	mailing list. The command "FAQ" by itself sends an index of 
	available FAQ's.

!Funky!Stuff!
echo x - export-listserv/HELP
cat > export-listserv/HELP << '!Funky!Stuff!'
You can subscribe or unsubscribe to any of the various campus mailing
lists and the local redistributions of global mailing lists by sending
email to "listserv@ucsd".  The commands understood by the listserv
program are:

HELP
	lists this file.  This is also sent whenever a message to
	listserv is received from which no valid command could be
	parsed.

HELP groupname
	lists a brief description of the group requested.

INDEX
	lists all the groups available for subscription.

LONGINDEX
	lists all the groups and their descriptions.

ADD listname	 
DELETE listname	

ADD address listname	 
DELETE address listname	
	adds or deletes the given address to or from the list
	specified.  Mail is sent to the address given to confirm
	the add or delete operation.  We strongly recommend that
	you use your campus registered mailname when subscribing
	(i.e., use the second form of the command which includes
	a specification of the address).  If you omit the 'address'
	the command will assume the mailbox that is in the From:
	line of the message.  Note that SUBSCRIBE is a synonym for
	ADD; UNSUBSCRIBE for DELETE.

DELETE-ALL
UNSUBSCRIBE-ALL

DELETE-ALL address
UNSUBSCRIBE-ALL address
	unsubscribes given address from all mailing lists. Mail is sent 
	the address given to confirm the deletions. If you omit 
	the 'address' the command will assume the mailbox that is in 
	the From: line of the message.
LIST

LIST address
	lists all mailing lists to which the given address is subscribed.
	If you omit the 'address' the command will assume the mailbox is
	in the from line. 

FAQ

FAQ listname
	sends a list of "Frequently Asked Questions" for the appropriate
	mailing list. The command "FAQ" by itself sends an index of 
	available FAQ's.

INFO

INFO filename1 filename2 ... filenameN
GET  filename1 filename2 ... filenameN
	sends a copy of the appropriate information sheet associated 
	with each file name. The command "INFO" by itself will send 
	an index of available info sheets.


A command must be the first word on each line in the message.  Lines
which do not start with a command word are ignored.  If no commands were
found in the entire message, this help file will be returned to the
user.  A single message may contain multiple commands; a separate
response will be sent for each.

Please note that is IS possible to add or delete someone else's
subscription to a mailing list.  This facility is provided so that
subscribers may alter their own subscriptions from a new or different
computer account.  There is therefore some potential for abuse; we
have chosen to limit this by mailing a confirmation notification
of any addition or deletion to the address added or deleted including
a copy of the message which requested the operation.  At least you
can find out who's doing it to you.

Examples:

	add sunusers

	add ewombat foodlovers

	delete wombat@cyberpunk.ucsd.edu connectionists

	help eggbeaters

Note that although you would mail submissions to a campus mailing list by
addressing mail to e.g., sunusers@ucsd.edu, in a subscription request
you specify the name of the list simply (without the @ucsd part) as in 
the first example above.
!Funky!Stuff!
echo x - export-listserv/INDEXHELP
cat > export-listserv/INDEXHELP << '!Funky!Stuff!'

INDEX
	lists all the groups available for subscription.

LONGINDEX
	lists all the groups and their descriptions.
!Funky!Stuff!
echo x - export-listserv/INFOHELP
cat > export-listserv/INFOHELP << '!Funky!Stuff!'
INFO

INFO filename1 filename2 ... filenameN
GET  filename1 filename2 ... filenameN
	sends a copy of the appropriate information sheet associated 
	with each file name. The command "INFO" by itself will send 
	an index of available info sheets.
!Funky!Stuff!
echo x - export-listserv/LISTHELP
cat > export-listserv/LISTHELP << '!Funky!Stuff!'

LIST

LIST address
	lists all mailing lists to which the given address is subscribed.
	If you omit the 'address' the command will assume the mailbox is
	in the From: line of the message.
!Funky!Stuff!
echo x - export-listserv/Makefile
cat > export-listserv/Makefile << '!Funky!Stuff!'
CFLAGS = -g 

all:	listserv

clean::
	rm -f core listserv *.o

install:	all
	install -c -m 2711 -g mail listserv /usr/mail/bin

listserv:	main.o commands.o subscribe.o str.o listsearch.o info.o faq.o 
	cc -O -Bstatic -o listserv main.o commands.o subscribe.o str.o listsearch.o info.o faq.o 

main.o:	main.c listserv.h
	cc -c $(CFLAGS) main.c

commands.o:	commands.c listserv.h
	cc -c $(CFLAGS) commands.c

subscribe.o:	subscribe.c listserv.h
	cc -c $(CFLAGS) subscribe.c

str.o:	str.c listserv.h
	cc -c $(CFLAGS) str.c

listsearch.o: listsearch.c listserv.h
	cc -c $(CFLAGS) listsearch.c

info.o:	      info.c listserv.h
	cc -c $(CFLAGS) info.c

faq.o:	     faq.c listserv.h
	cc -c $(CFLAGS) faq.c

!Funky!Stuff!
echo x - export-listserv/README
cat > export-listserv/README << '!Funky!Stuff!'
To install LISTSERV on your machine:
-----------------------------------

1.  Edit the listserv.h file

    This file contains definitions for the directories in which mailing
    lists are stored, as well as the location of help files, control
    files, FAQs, and .info and .pub files.  Set these to locally sensible
    directories.  Their definitions are:

    HELPFILE - This is the helpfile automatically sent to users who send
    unparsable input to LISTSERV

    SERVDIR - This is the directory in which mailing lists and their
    associated .pub and .info files reside.

    LOGFILE - This is the file where, when logging is enabled, all requests
    sent to LISTSERV are logged.

    COMMANDFILE - This file is a sort of 'alias' file for obtaining help.
    It describes which specific helpfile is mailed to users requesting help
    for a particular command; for example, HELP ADD and HELP SUB should
    probably return the same helpfile, ADDHELP.

    LISTSERVADDR - The address to which people mail to talk to the LISTSERV
    program.

    LISTSERVMANAGER - The address of a human being at your local site who
    maintains the LISTSERV program.

    DOMAIN - This is, in general, your Internet domain; in regards to
    LISTSERV's behaviour, it also serves to define 'local' and 'nonlocal'
    for the purposes of .pub and .info files.  That is, the presence of
    a .info file will allow subscription to a mailing list from anyone
    within this local domain; the presence of a .pub file will allow
    subscription by people outside of this domain.

    INFODIR - The directory in which information files are maintained.
    You should create a file an index file detailing the contents of the
    info directory. This file is sent people when listserv receives
    the command "info" without any arguments. An example index file has
    been included with this distribution.

    INFOFILE - The index file for listserv information files.

2.  Edit the Makefile.  Ensure that install is installing the listserv
    program in a place sensible for your system, or alter the location.

3.  Run 'make install'.

4.  Enter an alias for listserv in your /usr/lib/aliases file.  Our local
    aliases look like this, for a copy of listserv stored in
    /usr/mail/listserv:
		    listserv:"|/usr/mail/listserv"
		    listserve:listserv
		    listserver:listserv
		    infoserver:listserv

5.  If you run into questions or complications, please contact
		listserv-manager@ucsd.edu
!Funky!Stuff!
echo x - export-listserv/commands.c
cat > export-listserv/commands.c << '!Funky!Stuff!'
#include "listserv.h"

static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/commands.c,v 1.4 92/09/14 11:03:42 andy Exp Locker: andy $";

extern FILE *msg;
extern FILE *mailer;

sendhelp(from,request)
char *from, *request;
	{
	printf("called sendhelp with %s %s\n", from, request);
	callmailer("", from, request);
	mailcat(HELPFILE,"");
	pclose(mailer);
	return;
	}

listhelp(from,grp,request,outsider)
char *from,*grp,*request;
int outsider;
	{
	FILE *commands;
	int i;
	char tmp[128], hlp[128], cmd[128];
	printf("called listhelp with %s %s %s\n", from,grp,request);
	
	callmailer("", from, request);
	strcpy(tmp, COMMANDFILE);
	commands = fopen(tmp, "r");
	if( commands == NULL)
		{
		perror(tmp);
		exit(1);
		}
	while(i = fscanf(commands, "%s %s",cmd, hlp) > 0)
		if (!strcasecmp(cmd, grp))
			{
 			sprintf(tmp, "%s/%s", SERVDIR, hlp);
			mailcat(tmp, "\t");
			fflush(mailer);
			pclose(mailer);
			fclose(commands);
			return;
			}
	fclose(commands);

	if (outsider)
		sprintf(tmp,"%s/%s.pub", SERVDIR, grp);
	else
		sprintf(tmp,"%s/%s.info", SERVDIR, grp);
	if (access(tmp,R_OK) != 0)
		{
		fprintf(mailer,"The mailing list \"%s\" could not be found.\n",
			grp);
		fprintf(mailer,"You may use the INDEX command to get a listing\n");
		fprintf(mailer,"of available mailing lists.\n");
		fflush(mailer);
		pclose(mailer);
		return;
		}

	fprintf(mailer,"Mailing list \"%s\":\n", grp);
	mailcat(tmp,"\t");
	pclose(mailer);
	return;
	}

sendindex(from,request,l,outsider)
char *from,*request;
int l,outsider;
	{
	FILE *ls;
	char tmp[128];
	char buf[128];
	int i;
	printf("called sendindex with %s %s %d %d\n",
		from,request,l,outsider);

	if (outsider)
		sprintf(tmp,"cd %s; ls *.pub | sed -e 's/.pub//", SERVDIR);
	else
		sprintf(tmp,"cd %s; ls *.info | sed -e 's/.info//", SERVDIR);
	ls = popen(tmp,"r");
	if (ls == NULL)
		{
		perror(tmp);
		exit(1);
		}
	callmailer("", from, request);
	fprintf(mailer,"Index of mailing lists\n");

	/* read the result of the ls */
	while (fgets(tmp, sizeof(tmp), ls))
		{
		while (tmp[(i=strlen(tmp)-1)] == '\n')
			tmp[i] = '\0';
		fprintf(mailer, "%s\n", tmp);

		/* if he wanted the long listing, cat the .info file */
		if (l)
			{
			if (outsider)
				sprintf(buf, "%s/%s.pub", SERVDIR, tmp);
			else
				sprintf(buf, "%s/%s.info", SERVDIR, tmp);
			mailcat(buf,"\t");
			fprintf(mailer, "\n");
			}
		}
	pclose(ls);
	pclose(mailer);
	return;
	}

	
		
		
!Funky!Stuff!
echo x - export-listserv/faq.c
cat > export-listserv/faq.c << '!Funky!Stuff!'
#include "listserv.h"

static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/faq.c,v 1.1 92/09/14 11:03:46 andy Exp Locker: andy $";

extern FILE *mailer;

sendfaq(from, command, outsider)
char *from, *command;
int outsider;
	{
	int i;
	char grp[256], tmp[512];

	printf("called sendfaq with %s %s %s\n", from, command, outsider);
	i = sscanf(command, "%s%s", tmp, grp);
	if(i != 2)
		{
		callmailer("", from, command);
		sprintf(tmp, "%s/FAQHELP", SERVDIR);
		mailcat(tmp, "\t");
		fflush(mailer);
		pclose(mailer);
		return;
		}
	
	sprintf(tmp, "%s/%s.pub", SERVDIR, grp);
	callmailer("", from, command);
	if(access(tmp,R_OK) == 0)
		{
		sprintf(tmp, "%s/%s.faq", SERVDIR, grp);
		if(access(tmp, R_OK) == 0)
			{
			mailcat(tmp, "\t");
			fflush(mailer);
			pclose(mailer);
			return;
			}
		else
			{
			fprintf(mailer, "There is no FAQ available for");
			fprintf(mailer, " mailing list \"%s\".\n", grp);
			fflush(mailer);
			pclose(mailer);
			return;
			}
		}
	else if(outsider)
		{
		fprintf(mailer,"The mailing list \"%s\" could not be found.\n",
			grp);
		fprintf(mailer,"You may use the INDEX command to get a listing\n");
		fprintf(mailer,"of available mailing lists.\n");
		fflush(mailer);
		pclose(mailer);
		return;
		}
	else	{
		sprintf(tmp, "%s/%s.faq", SERVDIR, grp);
		if(access(tmp, R_OK) == 0)
			{
			mailcat(tmp, "\t");
			fflush(mailer);
			pclose(mailer);
			return;
			}
		else
			{
			fprintf(mailer, "There is no FAQ available for");
			fprintf(mailer, " mailing list \"%s\".\n", grp);
			fflush(mailer);
			pclose(mailer);
			return;
			}
		}

	}
		

faqindex(from,request)
char *from,*request;
	{
	FILE *ls;
	char tmp[128];
	char buf[128];
	int i;
	printf("called faqindex with %s %s\n", from,request);

	sprintf(tmp,"cd %s; ls *.faq | sed -e 's/.faq//", SERVDIR);
	ls = popen(tmp,"r");
	if (ls == NULL)
		{
		perror(tmp);
		exit(1);
		}
	callmailer("", from, request);
	fprintf(mailer,"Index of Frequently Asked Questions\n");

	/* read the result of the ls */
	while (fgets(tmp, sizeof(tmp), ls))
		{
		while (tmp[(i=strlen(tmp)-1)] == '\n')
			tmp[i] = '\0';
		fprintf(mailer, "%s\n", tmp);
		}
	pclose(ls);
	pclose(mailer);
	return;
	}

	
		
		
!Funky!Stuff!
echo x - export-listserv/info.c
cat > export-listserv/info.c << '!Funky!Stuff!'
#include "listserv.h"

static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/info.c,v 1.1 92/09/14 11:03:52 andy Exp Locker: andy $";

extern FILE *msg;
extern FILE *mailer;

infoindex(from,request)
char *from, *request;
	{
	printf("called sendhelp with %s %s\n", from, request);
	callmailer("", from, request);
	mailcat(INFOFILE,"");
	pclose(mailer);
	return;
	}

sendinfo(from, command)
char *from, *command;
	{
	int i;
	char *p;
	char doc[256], 
	     tmp[512],
	     buf[BUFSIZ];

	printf("called sendfaq with %s %s\n", from, command);
	/* Grab filenames trailing after word "info" */
	strcpy(buf, command);
	while(p = index(buf, ' ')) 
		{
		/* Get rid of extra blanks 
		   and return if only trailing whitespace remains */
		while(*(++p) == ' ')
			;
		if(!strlen(p))
			return;

		strcpy(buf, p);
		sscanf(buf, "%s", doc);
		if (index(doc, '*'))
			{
			callmailer("", from, command);
			sprintf(tmp, "%s/INFOHELP", SERVDIR);
			mailcat(tmp, "\t");
			fflush(mailer);
			pclose(mailer);
			continue;
			}
		sprintf(tmp, "%s/%s", INFODIR, doc);
		callmailer("", from, command);
		if(access(tmp,R_OK) == 0)
			{
			mailcat(tmp, "\t");
			fflush(mailer);
			pclose(mailer);
			}
		else
			{
			fprintf(mailer, "File %s does not exist.", doc);
			fflush(mailer);
			pclose(mailer);
			}
		}

	}
!Funky!Stuff!
echo x - export-listserv/listsearch.c
cat > export-listserv/listsearch.c << '!Funky!Stuff!'
#include "listserv.h"

static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/listsearch.c,v 1.3 92/02/24 12:54:25 andy Exp Locker: andy $";

extern FILE *msg;
extern FILE *mailer;

listsearch(from,command)
char *from,*command;
	{
	FILE *list;
	FILE *listtmp;
	FILE *subslist;
	DIR *listdir;
	struct dirent *entry;
 	struct stat statbuf;	
	char *s, *p;
	char *template = "/tmp/listservXXXXXX";
	char adr[256];
	char tmp[512];
	char request[256];
	char buf[BUFSIZ];
	int i;

	printf("listsearch %s %s \n", from, command);
	i = sscanf(command,"%s%s", request, adr);
	if ((i < 1) || (i > 2))
		sendhelp(from, command);
	if (i == 1)
		strcpy(adr, from);
	cleanup(adr,&i);
	listdir = opendir(SERVDIR);
	if (listdir == NULL)
		{
		perror(SERVDIR);
		exit(1);
		}
	i = mkstemp(template);
	if ( i == -1)
		{
		callmailer(LISTSERVMANAGER, from, "");
		fprintf(mailer,"Error[5] processing request. Please try later.\n");
		fprintf(mailer,">%s\n", command);
		fflush(mailer);
		pclose(mailer);
		return(-1);
		}
	else
		{
		subslist = fdopen(i, "w+");
		if (subslist == NULL)
			{
			callmailer(LISTSERVMANAGER, from, "");
			fprintf(mailer,"Error[5] processing request. Please try later.\n");
			fprintf(mailer,">%s\n", command);
			fflush(mailer);
			pclose(mailer);
			return(-1);
			}
		}

	while (entry = readdir(listdir))	
		{
		if ((index(entry->d_name, '.'))
		|| (isupper(entry->d_name[0])))
			continue;
		sprintf(tmp, "%s/%s", SERVDIR, entry->d_name);
		if (stat(tmp,&statbuf))
			{
			perror(tmp);
			exit(1);
			}
		/*If it's a directory, go to next entry*/
		else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
			continue;
		/*Skip the restricted access ones*/
		if (access(tmp,R_OK) != 0)
			{
			callmailer(LISTSERVMANAGER, from, "");
			p = rindex(tmp, '/');
			fprintf(mailer,"Unable to access %s\n", p+1);
			fprintf(mailer,">%s\n", command);
			fflush(mailer);
			pclose(mailer);
			continue;
			}
		
		list = fopen(tmp, "r");
		if (list == NULL)
			{
			callmailer(LISTSERVMANAGER, from, "");
			fprintf(mailer,"Error[6] processing request. Please try later.\n");
			fprintf(mailer,">%s\n", command);
			fflush(mailer);
			pclose(mailer);
			return(-1);
			}
     	        flock(fileno(list), LOCK_EX);
		while(fgets(buf, sizeof(buf), list))
			{
			buf[strlen(buf)-1] = '\0';
			if (!strcasecmp(buf, adr))
				{
				fputs(entry->d_name, subslist);
				fputs("\n", subslist);
				}	
			}
		fflush(list);
		flock(fileno(list), LOCK_UN);
		fclose(list);	
		}
	closedir(listdir);
	if (!strcasecmp(request, "delete-all")
	|| !strcasecmp(request, "unsubscribe-all")
	|| !strcasecmp(request, "unsub-all")
	|| !strcasecmp(request, "del-all"))
		{
		rewind(subslist);
		while(fgets(tmp, sizeof(tmp), subslist))
			{
			strcpy(buf, SERVDIR);
			strcat(buf, "/");
			strcat(buf, tmp);
			buf[strlen(buf) -1] = '\0';
			list = fopen(buf, "r");
			if (list == NULL)
				{
				callmailer(LISTSERVMANAGER, from, "");
				fprintf(mailer, "Error[7] processing request. Please try later.\n");				
				fprintf(mailer, ">%s\n", command);
				fflush(mailer);
				pclose(mailer);
				return(-1);
				}
			flock(fileno(list), LOCK_EX);
	
			strcat(buf, ".tmp");
			listtmp = fopen(buf, "w");
			if (listtmp == NULL)
				{
				callmailer(LISTSERVMANAGER, from, "");
				fprintf(mailer,"Error[8] processing request. Please try later.\n");
				fprintf(mailer,">%s\n", command);
				fflush(mailer);
				pclose(mailer);
				return(-1);
				}
			/* copy the list, omitting the one address */
			while (fgets(tmp, sizeof(tmp), list))
				{
				tmp[strlen(tmp)-1] = '\0';
				if (strcasecmp(tmp, adr))
					{
					fputs(tmp, listtmp);
					fputs("\n", listtmp);
					}
				}
			fflush(listtmp);
			fclose(listtmp);

			/* replace the old list with the shortened one */
			strcpy(tmp, buf);
			s = rindex(buf, '.');
			*s = '\0';
			unlink(buf);
			rename(tmp, buf);	/* put updated one in place */
			flock(fileno(list), LOCK_UN);	/* release lock */
			}
		}
#ifndef DEBUG
	if (strcmp(from, adr))
		{
		callmailer(adr, from, command);
		fprintf(mailer,"Per request by %s\n", from);
		}
	else
		{
		callmailer("", from, command);
		fprintf(mailer,"Per your request\n");
		}
#else
	callmailer("", from, command);
	fprintf(mailer,"Per your request\n");

#endif
	fprintf(mailer,"\t\"%s\"\n", command);
	rewind(subslist);
	if ((i = getc(subslist)) == EOF)
		fprintf(mailer, "'%s' is not subscribed to any mailing lists.\n", adr);
	else
		{
		if (strcasecmp(request, "delete-all")
		&& strcasecmp(request, "unsubscribe-all")
		&& strcasecmp(request, "unsub-all")
		&& strcasecmp(request, "del-all"))
			fprintf(mailer,"'%s' is subscribed to the following mailing lists:\n", adr);
		else
			fprintf(mailer,"'%s' was DELETED from the following mailing lists:\n", adr);
		mailcat(template, "\t");
		}
	fflush(mailer);
	pclose(mailer);
	unlink(template);
	fflush(subslist);
	fclose(subslist);
	}


!Funky!Stuff!
echo x - export-listserv/listserv.h
cat > export-listserv/listserv.h << '!Funky!Stuff!'
#include <sys/types.h>
#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include <sys/file.h>
#include <time.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAILER		"/usr/lib/sendmail"
#define HELPFILE	"/usr/mail/listserver/HELP"
#define INFOFILE	"/usr/mail/infoserver/INDEX"
#define SERVDIR		"/usr/mail/listserver"
#define LOGFILE		"/var/log/listserv"
#define COMMANDFILE	"/usr/mail/listserver/COMMANDS"
#define AFLAGS		(O_APPEND | O_CREAT | O_RDWR)
#define AMODE		(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
#define	LISTSERVADDR	"Listserv@FOOBAR.EDU"
#define LISTSERVMANAGER	"listserv-manager@foobar.edu"
#define INFODIR		"/usr/mail/infoserver"
#define	DOMAIN		"foobar.edu"
!Funky!Stuff!
echo x - export-listserv/listserv.l
cat > export-listserv/listserv.l << '!Funky!Stuff!'
.\" @(#)listserv.l 1.03 92/03/03 SMI; UCSD Edition
.TH LISTSERV L "03 March 1992"
.SH NAME
listserv \- manage mailing lists via sendmail interface
.SH SYNOPSIS
.B /usr/mail/listserv
.SH DESCRIPTION
.LP
.B listserv
is a mail server which allows users to easily list the available mailing
lists on a machine, and add or delete themselves from lists, through
electronic mail.  A user communicates with
.B listserv
by mailing it a file containing
.B listserv
commands, one to each line.  The general syntax for mail to a listserver is
.IB mail
.IB listserv@host.domain
.LP
The
.B listserv
commands available on the UCSD
.B listserv
program are as follows.  Several commands may be included in a single mail
message, but must be on separate lines in the message.
.TP 15
.B help
Returns the contents of the HELPFILE, including instructions on the use of
all
.B listserv
commands.
.TP
\fBhelp \fIcommand\fR
Returns the help file for the indicated
.B listserv
command.
.TP
\fBhelp \fIlistname\fR
Returns the .info file for the indicated
.I listname.
.TP
.B index
Gives a list of which mailing lists are available on the machine on which
the listserver resides.
.TP
.B longindex
Returns a list of all mailing lists available, including information
contained in the .info and .pub files for those lists.
.TP
\fBadd \fIlistname\fR
Adds the user sending the message to the indicated listname with the
address found in the From: line in the mail message.
.TP
\fBadd \fIaddress listname\fR
Adds the user indicated in
.I address
to the indicated listname.
.TP
\fBdelete \fIlistname\fR
Attempts to unsubscribe the user sending the message from the
indicated listname, using the
address found in the From: line in the mail message.
.TP
\fBdelete \fIaddress listname\fR
Attempts to unsubscribe the user indicated in
.I address
from the indicated listname.
.TP
.B list
Provides a list of the mailing lists to which the user whose address is
found in the From: line of the mail message is subscribed.
.TP
\fBlist \fIaddress\fR
Provides a list of the mailing lists to which the user whose address is
.I address
is subscribed.
.TP
\fBfaq \fIlistname\fR
If available, returns a list of Frequently Asked Questions about the
indicated
.I listname
and their answers.
.TP
.B info
Returns a list of all information sheets and listserv sendable files 
available.
.TP
\fBinfo \fIfilename\fR
Returns the indicated file or info sheet.
.SH FILES
.IP /usr/mail/maillists/*.info 30
Short descriptions of locally-subscribable mailing lists.  These files
must exist for a mailing list to be accessable through
.B listserv.
.IP /usr/mail/maillists/*.pub 30
Short descriptions of publicly-subscribable mailing lists.  These files
must exist for a mailing list to be accessable through
.B listserv
from machines outside the local domain.
.IP /usr/mail/maillists/*.intro 30
Optional; provides an introductory posting which, if it exists, is
automatically sent to new users when they subscribe.
.IP /usr/mail/maillists/*.faq 30
An optional FAQ (Frequently Asked Questions) posting which, if it exists,
is sent to users when they submit the
.B faq
.BI listname
command to
.B listserv
for a particular mailing list.
.SH "SEE ALSO"
.BR mail (1),
.BR sendmail (8).
.SH BUGS
.LP
Not a bug, but if the user's home machine's sendmail.cf file has changed
since she subscribed to a list, she may be unable to unsubscribe using the
.B delete
.BI listname
syntax.  If she does not know how her From: line appeared previously, it
may be necessary for her to contact a human to unsubscribe from the
mailing list.
.SH NOTES
.LP
Please contact
.B listserv-manager@ucsd.edu
for questions or updates to this program.
!Funky!Stuff!
echo x - export-listserv/main.c
cat > export-listserv/main.c << '!Funky!Stuff!'
#include "listserv.h"

static char rcsid[] = "$Header: /nfs/ucsd/local/src/mail/listserv/RCS/main.c,v 1.9 93/02/25 16:04:13 andy Exp Locker: andy $";

FILE *mailer;
FILE *logfile;

main(argc,argv)
int argc;
char **argv;
	{
	char from[256];
	char buf[BUFSIZ];
	char grp[64];
	char dat[64];
	char *p;
	int i;
	int gotcommand = 0;
	int outsider = 0;
	int inheader;
	int log;
	int garbage = 0;

	/* gotta have some default if the From: line is missing */
	strcpy(from, "ListServ");

	inheader = 1;
	while (fgets(buf,BUFSIZ,stdin) != NULL)
		{
		/* drop trailing newline */
		while (buf[(i=strlen(buf)-1)] == '\n')
			buf[i] = '\0';

		/* drop trailing blanks */
		p = buf + (strlen(buf) - 1);
		while (p && *p && p >= buf && isspace(*p))
			*p-- = '\0';

		/* deblank beginning of line */
		p = buf;
		while (p && *p && isspace(*p))
			p++;
		if (p != buf)
			strcpy(buf, p);

		if (strlen(buf) == 0)	/* blank line ends header */
			{
			inheader = 0;
			continue;
			}
		
		/*
		get the From: line so we can return the answer

		Note that we DON'T check that we're in the header when
		picking up the From: line; that's so that we'll take the
		last from line we encounter which makes it handle
		forwarded messages correctly.
		*/
		if (strncasecmp(buf,"From: ",6) == 0)
			{
			strcpy(from, &buf[6]);
			cleanup(from, &outsider);
			printf("outsider = %d\n", outsider);
			continue;
			}
		if (strncasecmp(buf, "From: listserv", 14) == 0
		|| strncasecmp(buf,
			"subject: re: your listserve request", 35) == 0)
			{
			printf("loop detected, flushing mail\n");
			exit(0);
			}
		
		/* 
		don't look for commands in the header 
		*/
		if (inheader)
			continue;
		
		/* avoid mail loops - ignore requests from ourself! */
		if (!strncasecmp(from, "listserv", 8))
			{
			printf("I refuse to talk to myself.\n");
			exit(0);
			}

		/* Throw away bounced mail */
		if (strstr(from, "daemon") != NULL)
			{
			printf("We don't do business with your type.\n");
			exit(0);
			}

		/* force to lower case and scan for commands */
		p = buf;
		while (p && *p)
			{
			if (isupper(*p))
				*p = tolower(*p);
			p++;
			}
		
		/* log the request */
		log = open(LOGFILE, AFLAGS, AMODE);
		if (log == -1)
			perror(LOGFILE);
		else
			{	
			logfile = fdopen(log, "a"); 
			if (logfile != NULL)
				{
				long clock = time(0);
				strcpy(dat,ctime(&clock));
				/* drop trailing newline */
				while (dat[(i=strlen(dat)-1)] == '\n')
					dat[i] = '\0';
				printf("logfile: %s|%s|%s\n", dat, from, buf);
				fprintf(logfile,"%s|%s|%s\n", dat, from, buf);
				fflush(logfile);
				close(log); 
				}
			else
				perror(LOGFILE);
			}

		if (!strncasecmp(buf, "list ",5)
		|| !strncasecmp(buf, "delete-all ",11)
		|| !strncasecmp(buf, "del-all ",8)
		|| !strncasecmp(buf, "unsubscribe-all ",16)
		|| !strncasecmp(buf, "unsub-all ",10)
		|| !strcasecmp(buf, "list")
		|| !strcasecmp(buf, "delete-all")
		|| !strcasecmp(buf, "del-all")
		|| !strcasecmp(buf, "unsubscribe-all")
		|| !strcasecmp(buf, "unsub-all"))
			{
			gotcommand++;
			listsearch(from, buf);
			continue;
			}
	
		if (!strncasecmp(buf,"add ", 4)
		|| !strncasecmp(buf,"subscribe ", 10)
		|| !strncasecmp(buf,"sub ",4))
			{
			gotcommand++;
			subscription(from,buf,1,outsider);
			continue;
			}

		if (!strncasecmp(buf,"delete ", 7)
		|| !strncasecmp(buf,"del ",4)
		|| !strncasecmp(buf,"unsubscribe ", 12)
		|| !strncasecmp(buf,"unsub ",6))
			{
			gotcommand++;
			subscription(from,buf,0,outsider);
			continue;
			}
		
		if (!strcasecmp(buf,"index")
		|| !strcasecmp(buf,"longindex"))
			{
			gotcommand++;
			sendindex(from,buf,!strcasecmp(buf,"longindex"),
				outsider);
			continue;
			}

		if (!strncasecmp(buf, "faq ", 4))
			{
			gotcommand++;
			sendfaq(from, buf, outsider);
			continue;
			}

		if (!strcasecmp(buf, "faq"))
			{
			gotcommand++;
			faqindex(from, buf);
			continue;
			}

		if (!strcasecmp(buf, "info"))
			{
			gotcommand++;
			infoindex(from, buf);
			continue;
			}

		if (!strncasecmp(buf, "info ", 5)
		|| !strncasecmp(buf, "get ",4))
			{
			gotcommand++;
			sendinfo(from, buf);
			continue;
			}

		if (!strcasecmp(buf,"help"))
			{
			gotcommand++;
			sendhelp(from,buf);
			continue;
			}
		
		if (!strncasecmp(buf,"help ",5))
			{
			gotcommand++;
			sscanf(buf,"%*s%s", grp);
			listhelp(from,grp,buf,outsider);
			continue;
			}
		/* Flush message if > 5 lines of garbage */
		if (++garbage > 5)
			{
			printf("Garbled message, flushing...\n");
			exit(0);
			}
		}
	if (gotcommand == 0)
		sendhelp(from, "indecipherable");
	exit(0);
	}

callmailer(redirect, toaddr, request)
char *redirect, *toaddr, *request;
	{
	char cbuf[BUFSIZ];

	printf("callmailer \"%s\",\"%s\",\"%s\"\n", redirect, toaddr, request);

	strcpy(cbuf, MAILER);
	strcat(cbuf, " -f");
	strcat(cbuf, LISTSERVMANAGER);
	strcat(cbuf, " -F\"Mailing List Processor\"");
	strcat(cbuf, " -t -oi"); 
	strcat(cbuf, " "); strcat(cbuf, redirect);
	mailer = popen(cbuf,"w");
	printf("mailer popen '%s'\n", cbuf);
	if (mailer == NULL)
		{
		perror(cbuf);
		exit(1);
		}
	fprintf(mailer,"From: ");
	fprintf(mailer, LISTSERVADDR);
	fprintf(mailer, " (Mailing List Processor)\n");
	fprintf(mailer,"To: %s\n", toaddr);
	fprintf(mailer,"Subject: Re: your LISTSERV request \"%s\"\n", request);
	fprintf(mailer,"\n");
	}

mailcat(mfname,prefix)
char *mfname, *prefix;
	{
	FILE *mf;
	char buf[BUFSIZ];

	printf("mailcat \"%s\", \"%s\"\n", mfname, prefix);

	mf = fopen(mfname,"r");
	if (mf == NULL)
		{
		perror(mfname);
		fprintf(mailer,"This should have been the contents of\n");
		fprintf(mailer,"file '%s' but it's missing!\n",mfname);
		return;
		}

	/* copy the message file into the mailer */
	while (fgets(buf,BUFSIZ,mf) != NULL)
		{
		fputs(prefix,mailer);
		fputs(buf,mailer);
		}
	fclose(mf);
	return;
	}

cleanup(from, outsider)
char *from;
int *outsider;
	{
	char *p, *q;

	printf("cleanup \"%s\"\n", from);

	while (p = index(from,'~'))
		*p = 'X';

	while (p = index(from,'|'))
		*p = 'X';

	/* elide stuff in parenthesis */
	if (p = index(from,'('))
		{
		if ( (q=rindex(from,')')) == NULL)
			{
			/* zap the from line; it's invalid */
			*p = '\0';
			return;
			}
		strcpy(p, q+1);
		}

	if (p = index(from,'<'))
		{
		if ( (q=index(from,'>')) == NULL)
			{
			/* zap the from line; it's invalid */
			*p = '\0';
			return;
			}
		*q = '\0';
		strcpy(from, p+1);
		}

	p = from;

	/* drop trailing blanks */
	p = from + (strlen(from) - 1);
	while (p && *p && p >= from && isspace(*p))
		*p-- = '\0';

	/* 'outsider' indicates whether it's mail from a local user
		or not.  'subscribe' uses that to determine whether
		to acknowledge the existence of the list.  (local users
		may subscribe to anything with a .info file; others
		may only subscribe if there is a .pub file)
	*/

	if (index(from, '!') != NULL)	/* uucp sites are foreign */
		{
		*outsider = 1;
		return;
		}
	if ((p = index(from, '@')) == NULL)	/* on-machine user */
		{
		*outsider = 0;
		return;
		}

	if (strcasecmp(p+1, DOMAIN) == 0)
		{
		*outsider = 0;
		return;
		}
	if ((p = index(p, '.')) == NULL)	/* in-domain user */
		{
		*outsider = 0;
		return;
		}
	if (strncasecmp(p+1, DOMAIN,8) == 0)
		{
		*outsider = 0;
		return;
		}

	*outsider = 1;
	return;
	}

!Funky!Stuff!
echo x - export-listserv/str.c
cat > export-listserv/str.c << '!Funky!Stuff!'
#include "listserv.h" 

static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/str.c,v 1.3 92/02/24 12:55:07 andy Exp Locker: andy $";

strcasecmp(a,b)
char *a, *b;
	{
	char *p;
	p = a;
	while (p && *p)
		{
		if (isupper(*p))
			*p = tolower(*p);
		p++;
		}
	p = b;
	while (p && *p)
		{
		if (isupper(*p))
			*p = tolower(*p);
		p++;
		}
	return(strcmp(a,b));
	}
strncasecmp(a,b,n)
char *a, *b;
int n;
	{
	char *p;
	p = a;
	while (p && *p)
		{
		if (isupper(*p))
			*p = tolower(*p);
		p++;
		}
	p = b;
	while (p && *p)
		{
		if (isupper(*p))
			*p = tolower(*p);
		p++;
		}
	return(strncmp(a,b,n));
	}
char *strstr(a,b)
char *a, *b;
	{
	char *p;
	p = a;
	while(p && *p)
		{
		if (isupper(*p))
			*p = tolower(*p);
		p++;
		}
	p = b;
	while (p && *p)
		{
		if (isupper(*p))
			*p = tolower(*p);
		p++;
		}
	p = a;
	while (p && *p)
		{
		if (!strncmp(p, b, strlen(b)))
			return p;
		p++;
		}
	p = 0;
	return p;
	}
!Funky!Stuff!
echo x - export-listserv/subscribe.c
cat > export-listserv/subscribe.c << '!Funky!Stuff!'
#include "listserv.h"

static char rcsid[] = "$Header: /usr/local/src/mail/listserv/RCS/subscribe.c,v 1.4 92/09/14 11:04:20 andy Exp Locker: andy $";

extern FILE *msg;
extern FILE *mailer;

subscription(from,command,add,outsider)
char *from,*command;
int add,outsider;
	{
	FILE *list;
	FILE *listtmp;
	char grp[256];
	char adr[256];
	char tmp[256];
	char buf[BUFSIZ];
	int del = 0;
	int i, l;
	int aliasok;

	printf("subscription %s %s %d\n", from, command, add);

	i = sscanf(command,"%s%s%s", tmp, adr, grp);
	if (i < 2 || i > 3)
		sendhelp(from, command);
	if (i == 2)
		{
		strcpy(grp, adr);
		strcpy(adr, from);
		}
	
	cleanup(grp,&i);
	cleanup(adr,&outsider);

	if (strcasecmp(grp, adr) == 0)
		{
		callmailer("", from, "");
		fprintf(mailer,"Subscription address loop: %s\n", adr);
		fflush(mailer);
		pclose(mailer);
		return(-1);
		}

	if (outsider)
		sprintf(tmp,"%s/%s.pub", SERVDIR, grp);
	else
		sprintf(tmp,"%s/%s.info", SERVDIR, grp);
	if (access(tmp,R_OK) != 0)
		{
		callmailer("", from, "");
		fprintf(mailer,"The mailing list \"%s\" could not be found.\n",
			grp);
		fprintf(mailer,"You may use the INDEX command to get a listing\n");
		fprintf(mailer,"of available mailing lists.\n");
		fflush(mailer);
		pclose(mailer);
		return(-1);
		}

	strcpy(tmp, SERVDIR);
	strcat(tmp, "/");
	strcat(tmp, grp);
	if (add)
		{
		l = open(tmp, AFLAGS, AMODE);
		if (l == -1)
			{
			callmailer(LISTSERVMANAGER, from, "");
			fprintf(mailer,"Error[1] processing request. Please try later.\n");
			fprintf(mailer,">%s\n", command);
			fflush(mailer);
			pclose(mailer);
			return(-1);
			}
		else
			{
			list = fdopen(l, "a");
			if (list == NULL)
				{
				callmailer(LISTSERVMANAGER, from, "");
				fprintf(mailer,"Error[1] processing request. Please try later.\n");
				fprintf(mailer,">%s\n", command);
				fflush(mailer);
				pclose(mailer);
				return(-1);
				}
			}
		flock(l, LOCK_EX);
		fprintf(list, "%s\n", adr);
		fflush(list);
		flock(l, LOCK_UN);
		close(l);
		}
	else
		{
		del = 0;
		list = fopen(tmp, "r");
		if (list == NULL)
			{
			callmailer(LISTSERVMANAGER, from, "");
			fprintf(mailer,"Error[2] processing request. Please try later.\n");
			fprintf(mailer,">%s\n", command);
			fflush(mailer);
			pclose(mailer);
			return(-1);
			}
		flock(fileno(list), LOCK_EX);
		
		strcpy(tmp, SERVDIR);
		strcat(tmp, "/");
		strcat(tmp, grp);
		strcat(tmp, ".tmp");
		listtmp = fopen(tmp, "w");
		if (listtmp == NULL)
			{
			callmailer(LISTSERVMANAGER, from, "");
			fprintf(mailer,"Error[3] processing request. Please try later.\n");
			fprintf(mailer,">%s\n", command);
			fflush(mailer);
			pclose(mailer);
			return(-1);
			}
		/* copy the list, omitting the one address */
		while (fgets(buf, sizeof(buf), list))
			{
			buf[strlen(buf)-1] = '\0';
			if (strcasecmp(buf, adr))
				{
				fputs(buf, listtmp);
				fputs("\n", listtmp);
				}
			else
				del++;
			}
		fflush(listtmp);
		fclose(listtmp);

		/* replace the old list with the shortened one */
		strcpy(buf, SERVDIR);
		strcat(buf, "/");
		strcat(buf, grp);
		unlink(buf);	/* delete original file */
		rename(tmp, buf);	/* put updated one in place */
		flock(fileno(list), LOCK_UN);	/* release lock */
		}
#ifndef DEBUG
	if (strcmp(from, adr))
		{
		callmailer(adr, from, command);
		fprintf(mailer,"Per request by %s\n", from);
		}
	else
		{
		callmailer("", from, command);
		fprintf(mailer,"Per your request\n");
		}
#else
		callmailer("", from, command);
		fprintf(mailer,"Per your request\n");
#endif

		fprintf(mailer,"\t\"%s\"\n", command);
	/* Mail subscription confirmation and info/intro files.*/
	if (add)
		{
		fprintf(mailer,"'%s' was ADDED to the '%s' mailing list.\n",
			adr, grp);
		fflush(mailer);
		pclose(mailer);

		sprintf(tmp, "%s/%s.intro", SERVDIR,grp);
		if (access(tmp,R_OK) == 0)
			{
#ifndef DEBUG
		callmailer("", adr, command);
#else		
		callmailer("", from, command);
#endif
		mailcat(tmp, "");
		sprintf(tmp, "%s/%s.faq", SERVDIR, grp);
		if(access(tmp,R_OK) == 0)
			mailcat(tmp,"\n\n");
		fflush(mailer);
		pclose(mailer);
			}
		}
	
	else
		if (del)
			{
			fprintf(mailer,
			"'%s' was DELETED from the '%s' mailing list.\n",
				adr, grp);
			fprintf(mailer, 
			"\nAlthough you have been deleted from the list,");
			fprintf(mailer, 
			" some mail sent previous to your deletion may be\n");
			fprintf(mailer,
			"queued in the system. Please don't panic if you");
			fprintf(mailer, 
			" receive a few last pieces of mail.\n");
			fflush(mailer);
			pclose(mailer);
			}
		else
			{
			fprintf(mailer,
			"'%s' was NOT FOUND on the '%s' mailing list.\n",
				adr, grp);
			fflush(mailer);
			pclose(mailer);
			}
	
	}
!Funky!Stuff!