#ifndef lint
static char sccsid[] = "@(#)autores.c	1.6 (UKC) 14/2/86";
#endif  lint
/***

* program name:
	autores.c
* function:
	Send an automatic reply to mail depending on the subject field
	The subject field is used to select a file in 
	/usr/lib/mmdf/auto which is interpreted as a mail item to send out
	Subject lines starting with 'Re:' are ignored
	If the first line of the file starts with '~', it is taken
	to be the Subject line for the outgoing mail.
	A line should be installed in the mmdf aliases file of the form
		information:mmdf|/usr/lib/mmdf/autores "$(sender)"
	If there are two parameters, the subject line is ignored and the
	second parameter is used to select a file
* switches:
	Called with the return address
	and optionally a named file
* libraries used:
	standard
* compile time parameters:
	cc -o autores autores.c
* history:
	Written Feb 1986
	Peter Collinson UKC

	Timothy Wood AMC-LSSA Jan. 1988
	Modified to accomodate SYS5 and added a Makefile.real and
	"gen" (so that the unsupported directory looks like the other
	directories) that will use libmmdf.a and the standard set of 
	Makefile.com compiler and linker options and so that a call to
	cnvtdate can be used instead of the Berkeley time code.  Added
	some security so that only the "srcdir" can be searched (could
	use relative pathnames, before).

	Timothy Wood AMC-LSSA Jan. 1988
	Modifications made for the Plexus P-60 Sys 3.2

***/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>

#ifdef SYS5
#  include <time.h>
#  include "cnvtdate.h"
#  ifdef PLEXUS
	char *index(), *rindex();
	char *strcpy();
#  else PLEXUS
#    include <string.h>
#  endif PLEXUS
#else
#  include <sys/time.h>
#  include <sys/timeb.h>
#  include <strings.h>		/* added TW */
#  include <sys/wait.h>
#endif SYS5

/* Externals from conf.c used in building pathnames for autores */
	extern char *locname;
	extern char *locdomain;
	extern char *cmddfldir;
	extern char *logdfldir;

/*
 *	Source directory
 */
char *badpath; /* added TW */
/* char *srcdir = "/prog/mmdf2/distribution"; /* requested file storage. */
char *srcdir[128];

/*
 *	who to reply to
 */
char *sender; 

/*
 *	system log file
 */
/* char *logfile = "/prog/mmdf2/log/autores.log"; /* Change for your sys */
char *logfile[128];
int	logfd = -1;
int	logpid;

/*
 *	file to look for
 */
char *command;
int	fromarg;
/* char	submit[]	= "/prog/mmdf2/submit";  /* Change for your sys */
char	submit[128];
char	subargs[]	= "-vmltnqxto,*";
/* char	signature[]	= "info-request@xls-plexus02"; /* Your address */
char	signature[128];

int	pipein[2];	/* pipes for communication */
int	pipeout[2];

FILE	*pout;		/* re-opened FILE pointer for pipeout */
FILE	*pin;		/* re-opened FILE pointer for pipein */

/*
 *	error response line
 */
char	errline[BUFSIZ];
int	senderror;

char	stdinfo[] =	/* Put out in each message */
"\n\
An index of the available information can be obtained by mailing to\n\
info-request@xls-plexus01.amc-hq.arpa with a Subject: line containing\n\
only the word \"index\"\n\
i.e.\n\
	Subject: index\n\
\n";

/*
 *	Data structure used to scan and recognise an inbound mail message
 *	All this is overkill but I had the code hanging around and I am
 *	never one to re-write stuff which works
 */
typedef struct
{	char	*header;
	char	*dataline;
	int	hadthis;
} Header_line;

Header_line header_line[] =
{	{ "subject", },
	{ 0, }
};
#define HSUBJECT	0


main(argc, argv)
char **argv;
{
	strcpy(srcdir, cmddfldir);
	strcat(srcdir, "/distribution");
	strcpy(logfile, logdfldir);
	strcat(logfile, "/autores.log");
	strcpy(submit, cmddfldir);
	strcat(submit, "/submit");
	strcpy(signature, "info-request@");
	strcat(signature, locname);
	strcat(signature, ".");
	strcat(signature, locdomain);
	openlog();	
	if (argc == 3)
	{	command = argv[2];
		fromarg++;
	}
	else
	if (argc != 2)
		fatal("Usage: autores sender\n");
	sender = argv[1];
	if (chdir(srcdir) < 0)
		fatal("No source directory\n");	
	/*
	 *	Mail header decoding
	 *	all we want is the subject line
	 */
	if (fromarg == 0)
	{	if (readheader(stdin)) /* if not OK */
			command = NULL;
	}
	clear_input(stdin);
	if (command)
		/* Make sure they aren't getting out of bounds. TW */
		/* Use "rindex" to maintain BSD compat */
		if((badpath = rindex(command, '/')) == NULL)  /* TW */
			send_info(command);
		else	/* TW */
		{	/* TW */
			log("Caught %s snooping around.\n", sender); /* TW */
			log("His command was %s.\n", command);	/* TW */
			usererror("Attempted to access unauthorized info.\n");
		}	/* TW */
	if (senderror)
		send_error();
	closelog();
	exit(0);
}

/*
 *	Read an incoming mail item looking for the subject line
 *	This all borrowed from another program
 */
readheader(fin)
FILE *fin;
{	register char *cp;
	char *index();

	if (get_header_lines(fin))
		return(1);
	if (header_line[HSUBJECT].hadthis == 0)
	{	usererror("No Subject: line found\n");
		return(1);
	}
	command = header_line[HSUBJECT].dataline;		
	/*
	 * look for Re: bits in lines
	 */
	while ((*command == 'R' || *command == 'r') &&
	       (command[1] == 'e' || command[1] == 'E' ) &&
	       (command[2] == ':'))
	{	log("Re: found request from %s ignored\n", sender);
		return(1);
	}
	return(0);
}

/*
 *	Scan the inbound mail header
 */
get_header_lines(fin)
FILE *fin;
{	register char *p;
	register char *startfield;
	register Header_line *hp;
	char	line[BUFSIZ];
	int	peekc;
	Header_line *look_header();
	char	*storestr();
	char	*storepair();
	char 	*index();
	
	for (;;)
	{	if (fgets(line, sizeof line - 1, fin) == NULL)
		{	usererror("Unexpected end of mail input file\n");
			return(1);
		}
		peekc = getc(fin);
		(void) ungetc(peekc, fin);
		if (line[0] == '\n')
			break;
		hp = (Header_line *)0;
		if (p = index(line, ':'))
		{	*p++ = '\0';
			if (hp = look_header(line))
			{	/* we want to know about this line */
				if (hp->hadthis++)
				{	/* we have had this line before */
					usererror("More than one %s header line found\n", hp->header);
					return(1);
				}
				/* clean up this line */
				if (*p == ' ') p++;
				startfield = p;
				p = index(startfield, '\n');
				if (p == NULL)
					line[BUFSIZ-1] = '\0';
				else
					*p = '\0';
				hp->dataline = storestr(startfield);
			}
		}
		/*
		 *	cope with continuation lines
		 */
		while (peekc == ' ' || peekc == '\t')
		{	if (fgets(line, sizeof line -1, fin) == NULL)
			{	usererror("Unexpected end of mail input file\n");
				return(1);
			}
			peekc = getc(fin);
			(void) ungetc(peekc, fin);
			/*
			 * if hp is set then we need to concatenate the
			 * new bit onto the old stored piece
			 */
			if (hp)
			{	if (p = index(line, '\n'))
					*p = '\0';
				else	line[BUFSIZ-1] = '\0';
				hp->dataline = storepair(hp->dataline, line, 1);
			}
		}
	}
	return(0);
}

/*
 *	scan the Header_line structure looking for a matching
 *	string which is a header - use equstr to match
 */
Header_line *
look_header(str)
char *str;
{	register Header_line *hp;
	for (hp = header_line; hp->header; hp++)
		if (equstr(str, hp->header) == 0)
			return(hp);
	return((Header_line *)0);
}

/*
 *	read very hard on the file descriptor throwing the data away
 */
clear_input(fin)
FILE *fin;
{	register c;
	while ((c = getc(fin)) != EOF);
}

/*
 *	Send information
 */
send_info(cmd)
register char *cmd;
{	register char *p;
	register bytes;
	char	line[BUFSIZ];
	FILE *fin;
	
	/* This is ok as far as it goes but does not cover the case
	   of subdir/../../someotherdirectory/someotherfile, etc  TW.
	*/
	/*
	 *	first clean up the command
	 *	All must start with an alpha character
	 */
	while (!isalpha(*cmd))
		cmd++;
	for (p = cmd; *p; p++)
	{	if (isupper(*p))
			*p = tolower(*p);
		if (isspace(*p) || ! isprint(*p))
		{	*p = '\0';
			break;
		}
	}
	if ((fin = fopen(cmd, "r")) == NULL)
	{	usererror("No information on: %s\n", cmd);
		return;
	}
	log("%s requests %s\n", sender, cmd);
	/* Subject is the 1st line of source - assuming is starts with '~' */
	fgets(line, sizeof line, fin);
	init_mail();
	mail_header(cmd, line[0] == '~' ? &line[1] : NULL);
	fprintf(pout, "Thank you for your information request\n");
	fprintf(pout, stdinfo);
	fprintf(pout, "-----%s-----\n", cmd);
	/*
	 * replace initial line if it is not a comment
	 */
	if (line[0] != '~')
		fputs(line, pout);
	/*
	 *	now read the remainder and send to pipe
	 */
	while (bytes = fread(line, 1, sizeof line, fin))
		fwrite(line, 1, bytes, pout);
	(void) fclose(pout);
	mail_termination();
}

/*
 *	Send an error reply
 */
send_error()
{	init_mail();
	mail_header("Error response", NULL);
	if (command)
		fprintf(pout, "Your request for information with a subject of `%s'\n", command);
	else
		fprintf(pout, "Your request for information\n");
	fprintf(pout, "has failed. The reason was:\n\n%s\n", errline);
	fprintf(pout, stdinfo);
	(void) fclose(pout);
	mail_termination();
}

/*
 *	Initialise a pipe to submit
 */
init_mail()
{	register pid;
	register i;
	char	*subcmd;
#ifndef SYS5
	char	*rindex();
#endif SYS5
	char	*maildate();
		
	subcmd = rindex(submit, '/');
	if (subcmd)
		subcmd++;
	else
		subcmd = submit;
		
	(void) pipe(pipein);	/* parent will read from pipein[0] */
	(void) pipe(pipeout);	/* parent will write to pipeout[1] */
	
	if ((pid = fork()) < 0)
	{	fatal("Cannot create a process for mail submission\n");
		return;
	}
	if (pid == 0)
	{	/* child */
		/* stdin is pipeout[0] */
		/* stdout/stderr is pipein[1] */
		(void) close(pipeout[1]);
		(void) close(pipein[0]);
		(void) close(0);
		(void) dup(pipeout[0]);
		(void) close(pipeout[0]);
		(void) close(1);
		(void) dup(pipein[1]);
		(void) close(pipein[1]);
		(void) close(2);
		(void) dup(1);
		for (i = 3; i < 20; i++)
			(void) close(i);
		execl(submit, subcmd, subargs, 0);
		(void) close(0);
		(void) close(1);
		(void) close(2);
		exit(1);
	}
	/*
	 *	Parent
	 */
	(void) close(pipein[1]);
	(void) close(pipeout[0]);
	pout = fdopen(pipeout[1], "w");
	pin = fdopen(pipein[0], "r");
}

/*
 *	Deal with the termination of the mail process
 */
mail_termination()
{	char	logbuf[BUFSIZ];
#ifndef SYS5
	union	wait retstat;
#else SYS5
	int *retstat;
	int retcode;
#endif SYS5

	while(fgets(logbuf, sizeof logbuf, pin))
		log("%s", logbuf);
	(void) fclose(pin);
	
#ifndef SYS5
	if (wait(&retstat) > 0)
	{	if (retstat.w_retcode == 9 &&
			retstat.w_termsig == 0)
		{	log("Autoresend - successful submit\n");
			return;
		}
	}
	log("Submit failed, returning %x %d\n", retstat, retstat);
#else SYS5
	retcode = wait(&retstat); 
	if(retcode < 0)
	{
		log("Submit failed, returned %d - %d\n", retcode, &retstat);
		return;
	}
	else
	{
		log("Autoresend - successful submission.\n");
		return;
	}
#endif SYS5
}

/*
 *	Do a mail header
 */
mail_header(cmd, subject)
char *cmd;
char *subject;
{
	fprintf(pout, "%s\n", signature);	/* special validation argument */
	fprintf(pout, "To: %s\n", sender);
 	fprintf(pout, "From: Information service <%s>\n", signature);
	if (subject)
		fprintf(pout, "Subject: Re: %s - %s", cmd, subject);
	else	fprintf(pout, "Subject: Re: %s\n", cmd);
	fprintf(pout, "Date: %s\n", maildate());
	fprintf(pout, "\n");
}

/*
 *	return a static string with the data in RFC822 format
 */
char *
maildate()
{
	static	char datbuf[64];
#ifndef SYS5
	static char *day[] = {
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
	};
	static char *month[] = {
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
	};
	struct timeb    timeb;
	extern char *timezone();
	extern struct tm  *localtime ();
 	register struct tm  *i;
		
	ftime(&timeb);
	i = localtime ((time_t *) & timeb.time);
	(void) sprintf(datbuf, "%s, %d %s %d %d:%02d:%02d %s",
		day[i -> tm_wday], i -> tm_mday, month[i -> tm_mon],
		i -> tm_year, i -> tm_hour, i -> tm_min, i -> tm_sec,
			timezone (timeb.timezone, i -> tm_isdst));
#else SYS5
	cnvtdate(TIMREG, datbuf);
#endif SYS5
	return(datbuf);
}
	 
/*
 *	See if two strings are the same irrespective of case
 *	return 0 if equal, 1 otherwise
 */
equstr(a, b)
register char *a, *b;
{	register ca, cb;
	while (ca = *a)
	{	cb = *b;
		if (cb == 0)
			return(1);
		if (ca != cb)
		{	if (isupper(ca))
				ca = tolower(ca);
			if (isupper(cb))
				cb = tolower(cb);
			if (ca != cb)
				return(1);
		}
		a++;
		b++;
	}
	return(*b == '\0' ? 0 : 1);
}

/*
 *	string management routines
 */
char *
storestr(str)
char *str;
{	register nchs;
	char *area;
	char *malloc();
	
	nchs = strlen(str) + 1;
	if ((area = malloc((unsigned) nchs)) == NULL)
		fatal("No memory for string storage\n");
#ifdef SYS5
	strcpy(area, str);
#else SYS5
	bcopy(str, area, nchs);
#endif SYS5
	return (area);
}

/*
 *	Concatenate and store a pair of strings
 *	the last parameter is used as a mask to indicate that one or other
 *	of the paramaters can be free()'d
 */
char *
storepair(a, b, msk)
char *a;
char *b;
{	register nchs;
	register alen;
	register blen;
	char *area;

	alen = strlen(a);
	blen = strlen(b);
	nchs = alen + blen + 1;
	if ((area = malloc((unsigned) nchs)) == NULL)
		fatal("No memory for string storage\n");
#ifdef SYS5
	strcpy(area, a);
	strcat(&area[alen], b);
#else SYS5
	bcopy(a, area, alen);
	bcopy(b, &area[alen], blen+1);
#endif SYS5
	if (msk & 01)
		free(a);
	if (msk & 02)
		free(b);
	return(area);
}

/*
 *	set up an error reply line
 */
/*VARARGS1*/
usererror(fmt, a, b, c, d)
char *fmt;
{	(void) sprintf(errline, fmt, a, b, c, d);
	senderror++;
	log(fmt, a, b, c, d);

}

/*
 *	open the log file
 */
openlog()
{
	logpid = getpid();
	
	/* May as well create it if it isn't there.  TW */
	logfd = open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0644);

}

/*VARARGS1*/
log(fmt, a, b, c, d)
char *fmt;
{	char line[BUFSIZ];
	time_t ti;
	char *ctime();
	register hdrlen;
	
	if (logfd < 0)
		return;
	
	(void) time(&ti);
	(void) sprintf(line, "%6d %15.15s: ", logpid, ctime(&ti)+4);
	hdrlen = strlen(line);
	(void) sprintf(&line[hdrlen], fmt, a, b, c, d);
	write(logfd, line, strlen(line));
}

closelog()
{	if (logfd >= 0)
		close(logfd);
}

/*VARARGS1*/
fatal(fmt, a, b, c, d)
char *fmt;
{	log(fmt, a, b, c, d);
	closelog();
	exit(0);
}
