/* @(#) proclist	-- dispatch listserv requests to catmail
 *
 *	Due to a bug in SCO Unix's MMDF, we can't use listserv's
 *	/usr/server/catmail program within an aliased pipe.  For
 *	some odd reason, MMDF strips the first internet header line
 *	in pipes.  Listserv absolutely requires that this
 *	"From address Date" header line be present.
 *
 *	The work-around is to force incoming mail for listserv and
 *	mailing lists to land in physical mailboxes belonging to
 *	pseudo-users.  This will require creating /etc/passwd users
 *	to receive requests for the listserver and each mailing list.
 *	The names are insignificant and may be aliased in
 *	/usr/mmdf/table/alias.list.  You will need one for 'listserv'
 *	and one for each supported mailing list.
 *
 *	This program, intended to be run either as a crontab entry or
 *	interactively, will look for mail for the selected user and,
 *	if the mail file exists, will strip the MMDF Ctrl-A separators
 *	(which confuse listserv) from each message and pipe them through
 *	/usr/server/catmail using the arguments specified on the command
 *	line.  See the server.nr dox for more information about catmail
 *	arguments.
 *
 *	Finally, we will launch the server program to process incoming
 *	mail.  The 'serverd' field in /usr/server/config file must
 *	specify '-1' to tell the server not to load itself into the
 *	background.  The server will only run if we have found mail and
 *	successfully piped it through catmail.
 *
 *	Usage:	proclist <user> <catmail_args>
 *	 i.e.:	proclist listserv -r -f
 *		proclist mcycle -L MCYCLE -f
 *
 *	Author: Steve Manes	manes@magpie.nycenet.edu
 *
 *		Manes and Associates, NYC
 *		July 1, 1992
 * 
 *	Developed for Learning Link, Linknet, Inc.
 *	Public Broadcasting System, NYC
 */
 
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <varargs.h>
#include <time.h>

#define	SPOOLDIR	"/usr/spool/mail"
#define	CATMAIL		"/usr/server/catmail"
#define	SERVER		"/usr/server/start"
#define	LOGFILE		"/usr/spool/listserv/errlog"
#define	CTRL_A		1
#define	FILENAMESIZE	128
#define	RECBUFSIZE	512
#define	MAXRETRIES	10
#define	ERROR		-1

void	usage();
void	log();

void	main( int argc, char *argv[] )
{
	FILE	*ofd, *catfd;
	int	ifd, i, sz, count;
	struct	stat Stat;
	struct	flock Flock;
	int	try;
	
	char	buf[RECBUFSIZE+1],
		mailfile[FILENAMESIZE+1],
		workfile[FILENAMESIZE+1],
		catfile[FILENAMESIZE+1];
	
	if (argc < 2)
		usage();

	/* make sure that we have read/write access to
	 * the user's mailfile */
	sprintf(mailfile, "%s/%s", SPOOLDIR, argv[1]);
	if (access(mailfile, F_OK)) {
		log("Mailfile %s not found!\n", mailfile);
		exit(1);
	}
	else if (access(mailfile, R_OK)) {
		log("Mailfile %s: no read permission!\n", mailfile);
		exit(1);
	}
	else if (access(mailfile, W_OK)) {
		log("Mailfile %s: no write permission!\n", mailfile);
		exit(1);
	}
	else if (stat(mailfile, &Stat)) {
		log("Can't stat %s!\n", mailfile);
		exit(1);
	}
	else if (Stat.st_size < 1) {
		log("%s has no mail for me\n", mailfile);
		exit(1);
	}
	
	/* make sure we can open a pipe to listserv's catmail */
	strcpy(catfile, CATMAIL);
	if (access(catfile, X_OK)) {
		log("Can't execute %s\n", catfile);
		exit( 1 );
	}
	for (i=2; i < argc; i++) {	/* append catmail's command line */
		strcat(catfile, " ");
		strcat(catfile, argv[i]);
	}

	/* open the mailfile */
	if ((ifd = open(mailfile, O_RDWR)) == ERROR) {
		log("Can't open input %s\n", mailfile);
		exit(1);
	}
	
	/* lock the mailfile from other processes */
	Flock.l_type = F_WRLCK;			/* write-lock only */
	Flock.l_whence = 0L;			/* from beginning of file */
	Flock.l_start = Flock.l_len = 0L;	/* till the end of file */

	/* wait twenty seconds to assert a lock */	
	for (try=0; fcntl(ifd, F_SETLK, &Flock) < 0; try++) {
		if (try == MAXRETRIES) {
			log("Can't lock %s\n", mailfile);
			exit(1);
		}
		sleep(2);		/* wait a coupla seconds... */
	}
		
	/* copy mail file to tmp directory for processing */
	sprintf(workfile, "/tmp/PROCLIST.%d", getpid());
	if ((ofd = fopen(workfile, "w+")) == NULL) {
		log("Can't open output %s\n", workfile);
		exit(1);
	}
	while( (sz = read(ifd, buf, RECBUFSIZE)) > 0)
		fwrite(buf, sizeof(char), sz, ofd);
	
	chsize(ifd, 0L);		/* make file empty */

	/* unlock the file */	
	Flock.l_type = F_UNLCK;
	Flock.l_whence = 0L;			/* from beginning of file */
	Flock.l_start = Flock.l_len = 0L;	/* till the end of file */
	fcntl(ifd, F_SETLK, &Flock);
	close(ifd);				/* and close it */
		
	/* At this point, we have the user's mailfile copied to /tmp,
	 * where we can operate on it without being disturbed by other
	 * processes.  We also zapped his SPOOLDIR mail so it's ready
	 * to accept mail without us getting in the way. */
	 
	rewind(ofd);			/* back to the top */
	catfd = NULL;
	count = 0;
	
	/* page through the mail file, piping each message to catmail */
	while (fgets(buf, RECBUFSIZE, ofd) != NULL) {
	
		/* MMDF's message separators make this easy, but
		 * listserv will choke on 'em so stifle 'em */
		if (buf[0] == CTRL_A) {
			if (catfd != NULL) {
				pclose(catfd);
				catfd = NULL;
			}
			continue;
		}
		if (catfd == NULL) {
			if ((catfd = popen(catfile, "w")) == NULL) {
				log("Can't open pipe to %s\n", buf);
				exit( 1 );
			}
			count++;
		}
		fputs(buf, catfd);
	}
	
	if (catfd != NULL)
		pclose(catfd);		/* close the CATMAIL pipe */
	fclose(ofd);			/* close the /tmp workfile... */
	unlink(workfile);		/*  and delete it. */
	
	log("%d message(s) processed\n", count);
	if (execl(SERVER, "start", "-c", NULL) == ERROR)
		perror(SERVER);
}

/*---[ usage ]-------------------------------------------------------------
	show program arguments	
 --------------------------------------------------------------------------*/
void	usage()
{
	fprintf(stderr, "usage: proclist <user> <catmail arguments>\n");
	exit( 1 );
}

/*---[ log ]---------------------------------------------------------------
	log error messages to file LOGFILE
	if LOGFILE, doesn't exist, don't create it
 --------------------------------------------------------------------------*/
void	log( va_alist )
va_dcl
{
	va_list	args;
	char	*fmt, tbuf[20];
	FILE	*fd;
	time_t	t;
	
	if ( !access(LOGFILE, W_OK) ) {		/* is there a logfile? */
		va_start(args);	
		fmt = va_arg(args, char *);
		time(&t);
		if ((fd = fopen(LOGFILE, "a")) != NULL) {
			strftime(tbuf, 20, "%m/%d %H:%M:%S  ", localtime(&t));
			fprintf(fd, "%s", tbuf);
			vfprintf(fd, fmt, args);
			va_end(args);
			fclose(fd);
		}
	}
}

/* END PROCLIST */
