[comp.sources.unix] v11i070: Smail, UUCP domain mailer, Part02/03

rsalz@uunet.UU.NET (Rich Salz) (09/23/87)

Submitted-by: Larry Auton <clyde.ATT.COM!lda>
Posting-number: Volume 11, Issue 70
Archive-name: smail3/Part02

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# Makefile alias.c defs.h deliver.c getopt.c getpath.c headers.c

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
# Makefile for smail (not a installation makefile)

# @(#)Makefile	2.5 (smail) 9/15/87

CFLAGS	=	-O
#
# System V Release 2.0 sites can use -lmalloc for a faster malloc
#
#LIBS	=	-lmalloc

OBJECTS =	main.o map.o resolve.o deliver.o misc.o alias.o pw.o headers.o getpath.o str.o getopt.o

all: smail svbinmail lcasep pathproc mkfnames nptx

smail:		$(OBJECTS)
		cc $(CFLAGS) $(OBJECTS) -o smail $(LIBS)

$(OBJECTS):	defs.h
		cc $(CFLAGS) -c $<

svbinmail:	svbinmail.c defs.h
		cc $(CFLAGS) svbinmail.c -o svbinmail

lcasep:		lcasep.c
		cc $(CFLAGS) lcasep.c -o lcasep

pathproc:	pathproc.sh
		cp pathproc.sh pathproc
		chmod 755 pathproc

mkfnames:	mkfnames.sh
		cp mkfnames.sh mkfnames
		chmod 755 mkfnames

nptx:		nptx.o pw.o str.o
		cc $(CFLAGS) nptx.o pw.o str.o -o nptx $(LIBS)

nptx.o:		nptx.c
		cc $(CFLAGS) -c nptx.c

install:	all
		@echo read doc/Install

clean:
		rm -f *.o *.ln a.out core

clobber:	clean
		rm -f smail rmail lcasep pathproc mkfnames svbinmail nptx
//E*O*F Makefile//

echo x - alias.c
cat > "alias.c" << '//E*O*F alias.c//'
#ifndef lint
static char *sccsid = "@(#)alias.c	2.5 (smail) 9/15/87";
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include "defs.h"
#include <ctype.h>

extern enum edebug debug;	/* verbose and debug modes		*/
extern char hostdomain[];
extern char hostname[];
extern char *aliasfile;

/*
**
** Picture of the alias graph structure
**
**	head
**       |
**       v
**	maps -> mark -> gjm -> mel -> NNULL
**       |
**       v
**	sys ->  root -> ron -> NNULL
**       |
**       v
**	root -> mark -> chris -> lda -> NNULL
**       |
**       v
**      NNULL
*/

typedef struct alias_node node;

static struct alias_node {
	char *string;
	node *horz;
	node *vert;
};

#ifndef SENDMAIL
static node aliases = {"", 0, 0}; /* this is the 'dummy header' */
#endif /* not SENDMAIL */

/*
** lint free forms of NULL
*/

#define NNULL	((node   *) 0)
#define CNULL	('\0')

/*
** string parsing macros
*/
#define SKIPWORD(Z)  while(*Z!=' ' && *Z!='\t' && *Z!='\n' && *Z!=',') Z++;
#define SKIPSPACE(Z) while(*Z==' ' || *Z=='\t' || *Z=='\n' || *Z==',') Z++;

static int nargc = 0;
static char *nargv[MAXARGS];

void	add_horz();
void	load_alias(), strip_comments();
int	recipients();
node	*pop();
#ifndef SENDMAIL
node	*v_search(), *h_search();
char	*tilde();
#endif	/* not SENDMAIL */

/* our horizontal linked list looks like a stack */
#define push		add_horz

#define escape(s)	((*s != '\\') ? (s) : (s+1))

char **
alias(pargc, argv)
int *pargc;
char **argv;
{
/*
**  alias the addresses
*/
	int	i;
	char	domain[SMLBUF], ubuf[SMLBUF], *user;
	node	*addr, addrstk;
	node	*flist,  fliststk, *u;

#ifndef SENDMAIL
	FILE	*fp;
	node	*a;
	char	*home, buf[SMLBUF];
	int	aliased;
	struct	stat st;
#endif /* not SENDMAIL */

#ifdef FULLNAME
	char *res_fname();	/* Does fullname processing */
#endif

	addr  = &addrstk;
	flist = &fliststk;
	user  = ubuf;

	addr->horz = NNULL;
	flist->horz  = NNULL;

	/*
	** push all of the addresses onto a stack
	*/
	for(i=0; i < *pargc; i++) {
		push(addr, argv[i]);
	}

	/*
	** for each adress, check for included files, aliases,
	** full name mapping, and .forward files
	*/

	while((nargc < MAXARGS) && ((u = pop(addr)) != NNULL)) {
#ifndef SENDMAIL
		if(strncmpic(u->string, ":include:", 9) == 0) {
			/*
			** make sure it's a full path name
			** don't allow multiple sourcing
			** of a given include file
			*/
			char *p = u->string + 9;

			if((*p == '/')
			&& (h_search(flist, p) == NULL)) {
				push(flist, p);
				if((stat(p, &st) >= 0)
				&&((st.st_mode & S_IFMT) == S_IFREG)
				&&((fp = fopen(p, "r")) != NULL)) {
					while(fgets(buf, sizeof buf, fp)) {
						(void) recipients(addr, buf);
					}
					(void) fclose(fp);
				}
			}
			continue;
		}
#endif /* not SENDMAIL */
		/*
		** parse the arg to see if it's to be aliased
		*/

		if(islocal(u->string, domain, ubuf) == 0) {
			goto aliasing_complete;
		}

		/*
		** local form - try to alias user
		** aliases file takes precedence over ~user/.forward
		** since that's the way that sendmail does it.
		*/

#ifdef LOWERLOGNAME
		/* squish 'user' into lower case */
		for(user = ubuf; *user ; user++) {
			*user = lower(*user);
		}
#endif
		user = escape(ubuf);

		(void) strcpy(u->string, user);	/* local => elide domain */
#ifndef SENDMAIL
		/*
		** check for alias - all this complication is necessary
		** to handle perverted aliases like these:
		** # mail to 's' resolves to 't' 'm' and 'rmt!j'
		** s	t,g,j,m
		** g	j,m
		** j	rmt!j
		** # mail to 'a' resolves to 'rmt!d'
		** a	b c
		** b	c
		** c	rmt!d
		** # mail to x resolves to 'x'
		** x	local!x
		** # mail to 'y' resolves to 'y' and 'z'
		** y	\y z
		*/
		if(((a = v_search(user)) != NNULL)) {
			char dtmpb[SMLBUF], utmpb[SMLBUF], *ut;
			int user_inalias = 0;
			node *t = a;

			for(a = a->horz; a != NNULL; a=a->horz) {
				if(islocal(a->string, dtmpb, utmpb)) {
#ifdef LOWERLOGNAME
					/* squish 'utmpb' into lower case */
					for(ut = utmpb; *ut ; ut++) {
						*ut = lower(*ut);
					}
#endif

					ut = escape(utmpb);
#ifdef CASEALIAS
					if(strcmp(ut, user) == 0)
#else
					if(strcmpic(ut, user) == 0)
#endif
					{
						user_inalias = 1;
					} else {
						push(addr, a->string);
					}
				} else {
					push(addr, a->string);
				}
			}
			t->horz = NNULL; /* truncate horz list of aliases */
			if(user_inalias == 0) {
				continue;
			}
		}

		if((home = tilde(user)) != NULL) {
			/* don't allow multiple sourcing
			** of a given .forward file
			*/

			if((h_search(flist, home) != NULL)) {
				continue;
			}
			push(flist, home);

			/*
			** check for ~user/.forward file
			** must be a regular, readable file
			*/

			(void) sprintf(buf, "%s/%s", home, ".forward");
			if((stat(buf, &st) >= 0)
			&&((st.st_mode & S_IFMT) == S_IFREG)
			&&((st.st_mode & 0444)   == 0444)
			&&((fp = fopen(buf, "r")) != NULL)) {
				aliased = 0;
				while(fgets(buf, sizeof buf, fp)) {
					aliased |= recipients(addr, buf);
				}
				(void) fclose(fp);
				if(aliased) {
					continue;
				}
			}
		}
#endif /* not SENDMAIL */

#ifdef FULLNAME
		/*
		** Do possible fullname substitution.
		*/
#ifdef DOT_REQD
		if (index(user, '.') != NULL)
#endif
		{
			static char t_dom[SMLBUF], t_unam[SMLBUF];
			char *t_user = res_fname(user);
			if (t_user != NULL) {
				if(islocal(t_user, t_dom, t_unam) == 0) {
					/* aliased to non-local address */
					push(addr, t_user);
					continue;
				}
				if(strcmp(t_unam, user) != 0) {
					/* aliased to different local address */
					push(addr, t_unam);
					continue;
				}
			}
		}
#endif

aliasing_complete:
		user = escape(u->string);
		for(i=0; i < nargc; i++) {
			if(strcmpic(nargv[i], user) == 0) {
				break;
			}
		}

		if(i == nargc) {
			nargv[nargc++] = user;
		}
	}
	*pargc     = nargc;
	return(nargv);
}

#ifndef SENDMAIL
/*
** v_search
**	given an string, look for its alias in
**	the 'vertical' linked list of aliases.
*/
node *
v_search(user)
char *user;
{
	node *head;
	node *a;
	static int loaded = 0;

	head = &aliases;
	if(loaded == 0) {
		load_alias(head, aliasfile);
		loaded = 1;
	}

	for(a = head->vert; a != NNULL; a = a->vert) {
#ifdef CASEALIAS
		if(strcmp(a->string, user) == 0)
#else
		if(strcmpic(a->string, user) == 0)
#endif
		{
			break;
		}
	}
	if(a == NNULL) {		/* not in graph */
		return(NNULL);
	}
	return(a);
}

/*
** h_search
**	given an string, look for it in
**	a 'horizontal' linked list of strings.
*/
node *
h_search(head, str)
node *head;
char *str;
{
	node *a;
	for(a = head->horz; a != NNULL; a = a->horz) {
#ifdef CASEALIAS
		if(strcmp(a->string, str) == 0)
#else
		if(strcmpic(a->string, str) == 0)
#endif
		{
			break;
		}
	}
	return(a);
}
#endif /* not SENDMAIL */

/*
** load_alias
**	parse an 'aliases' file and add the aliases to the alias graph.
**	Handle inclusion of other 'aliases' files.
*/

void
load_alias(head, filename)
node *head;
char *filename;
{
	FILE *fp;
	node *v, *h, *add_vert();
	char domain[SMLBUF], user[SMLBUF];
	char *p, *b, buf[SMLBUF];

	if((fp = fopen(filename,"r")) == NULL) {
DEBUG("load_alias open('%s') failed\n", filename);
		return;
	}

	while(fgets(buf, sizeof buf, fp) != NULL) {
		p = buf;
		if((*p == '#') || (*p == '\n')) {
			continue;
		}

		/*
		** include another file of aliases
		*/

		if(strncmp(p, ":include:", 9) == 0) {
			char *nl;
			p += 9;
			if((nl = index(p, '\n')) != NULL) {
				*nl = CNULL;
			}
DEBUG("load_alias '%s' includes file '%s'\n", filename, p);
			load_alias(head, p);
			continue;
		}

		/*
		**  if the first char on the line is a space or tab
		**  then it's a continuation line.  Otherwise,
		**  we start a new alias.
		*/
		if(*p != ' ' && *p != '\t') {
			b = p;
			SKIPWORD(p);
			*p++ = CNULL;
			/*
			** be sure that the alias is in local form
			*/
			if(islocal(b, domain, user) == 0) {
				/*
				** non-local alias format - skip it
				*/
				continue;
			}
			/*
			** add the alias to the (vertical) list of aliases
			*/
			if((h = add_vert(head, user)) == NNULL) {
DEBUG("load_alias for '%s' failed\n", b);
				return;
			}
		}
		/*
		**  Next on the line is the list of recipents.
		**  Strip out each word and add it to the
		**  horizontal linked list.
		*/
		(void) recipients(h, p);
	}
	(void) fclose(fp);
	/*
	** strip out aliases which have no members
	*/
	for(v = head; v->vert != NNULL; ) {
		if(v->vert->horz == NNULL) {
			v->vert = v->vert->vert;
		} else {
			v = v->vert;
		}
	}
}

/*
** add each word in a string (*p) of recipients
** to the (horizontal) linked list associated with 'h'
*/

recipients(h, p)
node *h;
char *p;
{

	char *b, d[SMLBUF], u[SMLBUF];
	int ret = 0;

	strip_comments(p);	/* strip out stuff in ()'s */

	SKIPSPACE(p);		/* skip leading whitespace on line */

	while((*p != NULL) && (*p != '#')) {
		b = p;
		if(*b == '"') {
			if((p = index(++b, '"')) == NULL) {
				/* syntax error - no matching quote */
				/* skip the rest of the line */
				return(ret);
			}
		} else {
			SKIPWORD(p);
		}

		if(*p != CNULL) {
			*p++ = CNULL;
		}

		/* don't allow aliases of the form
		** a	a
		*/
		if((islocal(b, d, u) == 0)
		|| (strcmpic(h->string, u) != 0)) {
			add_horz(h, b);
			ret = 1;
		}
		SKIPSPACE(p);
	}
	return(ret);
}

/*
** some aliases may have comments on the line like:
**
** moderators	moderator@somehost.domain	(Moderator's Name)
**		moderator@anotherhost.domain	(Another Moderator's Name)
**
** strip out the stuff in ()'s
**
*/

void
strip_comments(p)
char *p;
{
	char *b;
	while((p = index(p, '(')) != NULL) {
		b = p++;	/*
				** save pointer to open parenthesis
				*/
		if((p = index(p, ')')) != NULL) {/* look for close paren */
			(void) strcpy(b, ++p);	 /* slide string left    */
		} else {
			*b = CNULL;	/* no paren, skip rest of line  */
			break;
		}
	}
}

/*
** add_vert - add a (vertical) link to the chain of aliases.
*/

node *
add_vert(head, str)
node *head;
char *str;
{
	char *p, *malloc();
	void free();
	node *new;

	/*
	** strip colons off the end of alias names
	*/
	if((p = index(str, ':')) != NULL) {
		*p = CNULL;
	}
	if((new = (node *) malloc(sizeof(node))) != NNULL) {
		if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) {
			free(new);
			new = NNULL;
		} else {
			(void) strcpy(new->string, str);
			new->vert   = head->vert;
			new->horz   = NNULL;
			head->vert  = new;
/*DEBUG("add_vert %s->%s\n", head->string, new->string);/* */
		}
	}
	return(new);
}

/*
** add_horz - add a (horizontal) link to the chain of recipients.
*/

void
add_horz(head, str)
node *head;
char *str;
{
	char *malloc();
	node *new;

	if((new = (node *) malloc(sizeof(node))) != NNULL) {
		if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) {
			free(new);
			new = NNULL;
		} else {
			(void) strcpy(new->string, str);
			new->horz  = head->horz;
			new->vert  = NNULL;
			head->horz = new;
		}
/*DEBUG("add_horz %s->%s\n", head->string, new->string);/* */
	}
}

node *
pop(head)
node *head;
{
	node *ret = NNULL;


	if(head != NNULL) {
		ret = head->horz;
		if(ret != NNULL) {
			head->horz = ret->horz;
		}
	}
	return(ret);
}
//E*O*F alias.c//

echo x - defs.h
cat > "defs.h" << '//E*O*F defs.h//'
/*
**
**  Defs.h:  header file for rmail/smail.
**
**  Configuration options for rmail/smail.
**	default configuration is:
**	full domain name is 'hostname.uucp' (get registered!)
**	path file is /usr/lib/uucp/paths.
**	no log, no record, use sendmail.
** 
**  You can change these in the next few blocks.
**
*/

/*
**	@(#)defs.h	2.5 (smail) 9/15/87
*/

#ifndef VERSION
#define	VERSION	"smail2.5"
#endif

/*#define BSD				/* if system is a Berkeley system */

/*#define SENDMAIL "/usr/lib/sendmail"	/* Turn off to use /bin/(l)mail only */

#ifdef BSD
#define GETHOSTNAME			/* use gethostname() */
#else
#define UNAME 				/* use uname() */
#endif

/* if defined, HOSTNAME overrides UNAME and GETHOSTNAME */
/*#define HOSTNAME	"host"		/* literal name */

/*#define HOSTDOMAIN	"host.dom"	/* overrides default HOSTNAME.MYDOM */

/*
 * .UUCP here is just for testing, GET REGISTERED in COM, EDU, etc.
 * See INFO.REGISTRY for details.
 */

#define MYDOM		".UUCP"		/* literal domain suffix */

/*
 * WARNING: DOMGATE is only for qualified gateways - use caution.
 * If you don't fully understand it - don't use it!
 * If you are not completely sure you need it - don't use it!
 * If you are not prepared to handle all addresses to MYDOM - don't use it!
 *
 * if defined, DOMGATE (DOMain GATEway) will cause addresses of the form
 *
 *	user@MYDOM or MYDOM!user
 *
 * (with and without the leading '.' on MYDOM)
 * to be seen treated simply 'user' - a purely local address.
 * Then, it is left to the aliasing code to map it back to a
 * non-local address if necessary.
 */

/*#define DOMGATE		/* Act as Domain Gateway */

/*
 * HIDDENHOSTS allows hosts that serve as domain gateways to hide
 * the subdomains beneath them.  Mail that originates at any of
 * the hosts in the subdomain will appear to come from the gateway host.
 * Hence, mail from
 *
 * 		anything.hostdomain!user
 *
 * will appear to come from 
 *
 * 		hostdomain!user
 *
 * A consequence is that return mail to hostdomain!user would need to
 * be forwarded to the proper subdomain via aliases or other forwarding
 * facilities.
 *
 * If you're using sendmail, then if defined here,
 * it should be used in ruleset 4 of the sendmail.cf, too.
 */

/*#define HIDDENHOSTS			/* hide subdomains of hostdomain */

/*
 * Mail that would otherwise be undeliverable will be passed to the
 * aliased SMARTHOST for potential delivery.
 *
 * Be sure that the host you specify in your pathalias input knows that you're
 * using it as a relay, or you might upset somebody when they find out some
 * other way.  If you're using 'foovax' as your relay, and below you have
 * #define SMARTHOST "smart-host", then the pathalias alias would be:
 *
 *	smart-host = foovax
 */

#define SMARTHOST  "smart-host"	/* pathalias alias for relay host */

/*
**  ALIAS and CASEALIAS are used only if SENDMAIL is NOT defined.
**  Sites using sendmail have to let sendmail do the aliasing.
**  LOWERLOGNAME maps all local login names into lower case.  This
**  helps sites who have only upper case send mail to mixed case sites.
*/

#define ALIAS	"/usr/lib/aliases"	/* location of mail aliases       */
/*#define CASEALIAS			/* make aliases case sensitive    */
/*#define LOWERLOGNAME			/* map local logins to lower case */

/*
 * defining FULLNAME means that Full Name resolution
 * will be attempted when necessary.
 *
 * the Full Name information will be taken from a
 * list of {Full Name, address} pairs.
 * The names in the list must be sorted
 * without regard to upper/lower case.
 *
 * defining DOT_REQD says that the user name must contain a '.' for
 * the Full Name search to be done.
 *
 * All full name searches are case insensitive.
 *
 */

#define FULLNAME	"/usr/lib/fullnames"
					/* list of Full Name, address pairs */

/*#define DOT_REQD			/* Must be George.P.Burdell@gatech.EDU
					** not just  Burdell@gatech.EDU
					*/

/*
**	PATHS is name of pathalias file.  This is mandatory.
**	Define LOG if you want a log of mail.  This can be handy for
**	debugging and traffic analysis.
**	Define RECORD for a copy of all mail.  This uses much time and
**	space and is only used for extreme debugging cases.
*/

#ifndef PATHS
#define PATHS	"/usr/lib/uucp/paths"	/* location of the path database */
#endif

/*#define LOG	"/usr/spool/uucp/mail.log"	/* log of uucp mail */
/*#define RECORD	"/tmp/mail.log"		/* record of uucp mail */

/*
**  Mailer options:
**	RMAIL is the command to invoke rmail on machine sys.
**	RARG is how to insulate metacharacters from RMAIL. 
**	LMAIL is the command to invoke the local mail transfer agent.
**	LARG is how to insulate metacharacters from LMAIL. 
**	RLARG is LARG with host! on the front - to pass a uux addr to sendmail.
**	SENDMAIL selects one of two sets of defines below for either
**	using sendmail or /bin/lmail.
*/	

#ifndef UUX
#define UUX		"/usr/bin/uux"	/* location of uux command   */
#endif

#ifndef SMAIL
#define SMAIL		"/bin/smail"	/* location of smail command */
#endif

/*
** command used to retry failed mail, flag is used to set the routing level.
*/
#define VFLAG		((debug == VERBOSE)?"-v":"")
#define RETRY(flag)	"%s %s %s -f %s ", SMAIL, VFLAG, flag, spoolfile

/*
** use the -a if you have it.  This sometimes helps failed mail and warning
** messages get back to where the mail originated.
**
** some versions of uux can't do '-a' - pick one of the next two definitions
*/

#define RMAIL(flags,from,sys) "%s -a%s %s - %s!rmail",UUX,from,flags,sys /* */
/*#define RMAIL(flags,from,sys) "%s %s - %s!rmail",UUX,flags,sys /* */

#define RARG(user)		" '(%s)'",user
#define RFROM(frm,now,host) 	"From %s  %.24s remote from %s\n",frm,now,host

#ifdef SENDMAIL

#define HANDLE	JUSTUUCP	/* see HANDLE definition below */
#define ROUTING JUSTDOMAIN	/* see ROUTING definition below */

#define LMAIL(frm,sys) 	"%s -em -f%s",SENDMAIL,frm
#define LARG(user)		" '%s'",postmaster(user)
#define RLARG(sys,frm)		" '%s!%s'",sys,frm
#define LFROM(frm,now,host)	"From %s %.24s\n",frm,now

#else

#define HANDLE	ALL
#define ROUTING JUSTDOMAIN

#ifdef BSD
#define LMAIL(frm,sys)		"/bin/mail"	/* BSD local delivery agent */
#else
#define LMAIL(frm,sys)		"/bin/lmail"	/* SV  local delivery agent */
#endif

#define LARG(user)		" '%s'",postmaster(user)
#define RLARG(sys,frm)		" '%s!%s'",sys,frm
#define LFROM(frm,now,host)	"From %s %.24s\n",frm,now

#endif

/*
**	The following definitions affect the queueing algorithm for uux.
**
**	DEFQUEUE	if defined the default is to queue uux mail
**
**	QUEUECOST	remote mail with a cost of less than QUEUECOST
**			will be handed to uux for immediate delivery.
**
**	MAXNOQUEUE	don't allow more than 'n' immediate delivery
**			jobs to be started on a single invocation of smail.
**	
**	GETCOST		if defined, the paths file will be searched for
**			each address to discover the cost of the route.
**			this allows informed decisions about whether to
**			use the queue flags when calling uux.  The price
**			is in the overhead of a paths file search for
**			addresses that are not going to be routed.
*/

#define DEFQUEUE			/* default is to queue uux jobs */

#define QUEUECOST		100	/* deliver immediately if the cost
					/* is DEDICATED+LOW or better */

#define MAXNOQUEUE		2	/* max UUX_NOQUEUE jobs         */

#define GETCOST				/* search for cost		*/

#define UUX_QUEUE		"-r"	/* uux flag for queueing	*/
#define UUX_NOQUEUE		""	/* uux with immediate delivery	*/

/*
** Normally, all mail destined for the local host is delivered with a single
** call to the local mailer, and all remote mail is delivered with one call
** to the remote mailer for each remote host.  This kind of 'batching' saves
** on the cpu overhead.
**
** MAXCLEN is used to limit the length of commands that are exec'd by smail.
** This is done to keep other program's buffers from overflowing, or to
** allow for less intelligent commands which can take only one argument
** at a time (e.g., 4.1 /bin/mail).  To disable the batching, set MAXCLEN
** a small value (like 0).
*/

#define MAXCLEN			128	/* longest command allowed (approx.)
					/* this is to keep other's buffers
					** from overflowing
					*/

/*
** PLEASE DON'T TOUCH THE REST
*/

#define SMLBUF	512	/* small buffer (handle one item) */
#define BIGBUF	4096	/* handle lots of items */
 
#define MAXPATH	32	/* number of elements in ! path */
#define MAXDOMS	16	/* number of subdomains in . domain */
#define MAXARGS	500	/* number of arguments */
#ifndef NULL
#define NULL	0
#endif

#define DEBUG 		if (debug==YES) (void) printf
#define ADVISE 		if (debug!=NO) (void) printf
#define error(stat,msg,a)	{ (void) fprintf(stderr, msg, a); exit(stat); }
#define lower(c) 		( isupper(c) ? c-'A'+'a' : c )


enum eform {	/* format of addresses */
	ERROR, 		/* bad or invalidated format */
	LOCAL, 		/* just a local name */
	DOMAIN, 	/* user@domain or domain!user */
	UUCP,		/* host!address */
	ROUTE,		/* intermediate form - to be routed */
	SENT		/* sent to a mailer on a previous pass */
};

enum ehandle { 	/* what addresses can we handle? (don't kick to LMAIL) */
	ALL,		/* UUCP and DOMAIN addresses */
	JUSTUUCP,	/* UUCP only; set by -l  */
	NONE		/* all mail is LOCAL; set by -L */
};

enum erouting {	/* when to route A!B!C!D */
	JUSTDOMAIN,	/* route A if A is a domain */
	ALWAYS,		/* route A always; set by -r */
	REROUTE		/* route C, B, or A (whichever works); set by -R */
};

enum edebug {	/* debug modes */
	NO,		/* normal deliver */
	VERBOSE,	/* talk alot */
	YES		/* talk and don't deliver */
};

#ifdef BSD

#include <strings.h>
#include <sysexits.h>

#else

#include <string.h>
#include "sysexits.h"
#define	index	strchr
#define	rindex	strrchr

#endif
extern void exit(), perror();
extern unsigned sleep();
//E*O*F defs.h//

echo x - deliver.c
cat > "deliver.c" << '//E*O*F deliver.c//'
/*
**  Deliver.c
**
**  Routines to effect delivery of mail for rmail/smail. 
**
*/

#ifndef lint
static char 	*sccsid="@(#)deliver.c	2.5 (smail) 9/15/87";
#endif

# include	<stdio.h>
# include	<sys/types.h>
# include	<sys/stat.h>
# include	<ctype.h>
# include	<signal.h>
# include	"defs.h"

extern int  exitstat;		/* set if a forked mailer fails */
extern enum edebug debug;	/* how verbose we are 		*/ 
extern char hostname[];		/* our uucp hostname 		*/
extern char hostdomain[];	/* our host's domain 		*/
extern enum ehandle handle;	/* what we handle		*/
extern enum erouting routing;	/* how we're routing addresses  */
extern char *uuxargs;		/* arguments given to uux       */
extern int  queuecost;		/* threshold for queueing mail  */
extern int  maxnoqueue;		/* max number of uucico's       */
extern char *spoolfile;		/* file name of spooled message */
extern FILE *spoolfp;		/* file ptr  to spooled message */
extern int spoolmaster;		/* set if creator of spoolfile  */
extern char nows[];		/* local time in ctime(3) format*/
extern char arpanows[];		/* local time in arpadate format*/
char stderrfile[20];		/* error file for stderr traping*/

/*
**
**  deliver():  hand the letter to the proper mail programs.
**
**  Issues one command for each different host of <hostv>,
**  constructing the proper command for LOCAL or UUCP mail.
**  Note that LOCAL mail has blank host names.
**
**  The <userv> names for each host are arguments to the command.
** 
**  Prepends a "From" line to the letter just before going 
**  out, with a "remote from <hostname>" if it is a UUCP letter.
**
*/

deliver(argc, hostv, userv, formv, costv)
int argc;				/* number of addresses		*/
char *hostv[];				/* host names			*/
char *userv[];				/* user names			*/
enum eform formv[];			/* form for each address	*/
int costv[];				/* cost vector 			*/
{
	FILE *out;			/* pipe to mailer		*/
	FILE *popen();			/* to fork a mailer 		*/
#ifdef RECORD
	void record();			/* record all transactions	*/
#endif
#ifdef LOG
	void log();
#endif
	char *mktemp();
	char from[SMLBUF];		/* accumulated from argument 	*/
	char lcommand[SMLBUF];		/* local command issued 	*/
	char rcommand[SMLBUF];		/* remote command issued	*/
	char scommand[SMLBUF];		/* retry  command issued	*/
	char *command;			/* actual command		*/
	char buf[SMLBUF];		/* copying rest of the letter   */
	enum eform form;		/* holds form[i] for speed 	*/
	long size;			/* number of bytes of message 	*/
	char *flags;			/* flags for uux		*/
	char *sflag;			/* flag  for smail		*/
	int i, j, status, retrying;
	char *c, *postmaster();
	int failcount = 0;
	int noqcnt = 0;			/* number of uucico's started   */
	char *uux_noqueue = UUX_NOQUEUE;/* uucico starts immediately    */
	char *uux_queue   = UUX_QUEUE;	/* uucico job gets queued       */
	off_t message;
	struct stat st;

/*
** rewind the spool file and read the collapsed From_ line
*/
	(void) fseek(spoolfp, 0L, 0);
	(void) fgets(from, sizeof(from), spoolfp);
	if((c = index(from, '\n')) != 0) *c = '\0';
	message = ftell(spoolfp);

/*
**  We pass through the list of addresses.
*/
	stderrfile[0] = '\0';
	for(i = 0; i < argc; i++) {
		char *lend = lcommand;
		char *rend = rcommand;
		char *send = scommand;

/*
**  If we don't have sendmail, arrange to trap standard error
**  for inclusion in the message that is returned with failed mail.
*/
		(void) unlink(stderrfile);
		(void) strcpy(stderrfile, "/tmp/stderrXXXXXX");
		(void) mktemp(stderrfile);
		(void) freopen(stderrfile, "w", stderr);
		if(debug != YES) {
			(void) freopen(stderrfile, "w", stdout);
		}

		*lend = *rend = *send = '\0';

/*
**  If form == ERROR, the address was bad 
**  If form == SENT, it has been sent on a  previous pass.
*/
		form = formv[i];
		if (form == SENT) {
			continue;
		}
/*
**  Build the command based on whether this is local mail or uucp mail.
**  By default, don't allow more than 'maxnoqueue' uucico commands to
**  be started by a single invocation of 'smail'.
*/
		if(uuxargs == NULL) {	/* flags not set on command line */
			if(noqcnt < maxnoqueue && costv[i] <= queuecost) {
				flags = uux_noqueue;
			} else {
				flags = uux_queue;
			}
		} else {
			flags = uuxargs;
		}

		retrying = 0;
		if(routing == JUSTDOMAIN) {
			sflag = "-r";
		} else if(routing == ALWAYS) {
			sflag = "-R";
		} else {
			sflag = "";
		}

		(void) sprintf(lcommand, LMAIL(from, hostv[i]));
		(void) sprintf(rcommand, RMAIL(flags, from, hostv[i]));

/*
**  For each address with the same host name and form, append the user
**  name to the command line, and set form = ERROR so we skip this address
**  on later passes. 
*/
		/* we initialized lend (rend) to point at the
		 * beginning of its buffer, so that at
		 * least one address will be used regardless
		 * of the length of lcommand (rcommand).
		 */
		for (j = i; j < argc; j++) {
			if ((formv[j] != form)
			 || (strcmpic(hostv[i], hostv[j]) != 0)
			 || ((lend - lcommand) > MAXCLEN)
			 || ((rend - rcommand) > MAXCLEN)) {
				continue;
			}

			/*
			** seek to the end of scommand
			** and add on a 'smail' command
			** multiple commands are separated by ';'
			*/

			send += strlen(send);
			if(send != scommand) {
				*send++ = ';' ;
			}

			(void) sprintf(send, RETRY(sflag));
			send += strlen(send);

			lend += strlen(lend);
			rend += strlen(rend);

			if (form == LOCAL) {
				(void) sprintf(lend, LARG(userv[j]));
				(void) sprintf(send, LARG(userv[j]));
			} else {
				(void) sprintf(lend, RLARG(hostv[i], userv[j]));
				(void) sprintf(send, RLARG(hostv[i], userv[j]));
			}

			(void) sprintf(rend, RARG(userv[j]));
			formv[j] = SENT;
		}
retry:
/*
** rewind the spool file and read the collapsed From_ line
*/
		(void) fseek(spoolfp, message, 0);

		/* if the address was in a bogus form (usually DOMAIN),
		** then don't bother trying the uux.
		**
		** Rather, go straight to the next smail routing level.
		*/
		if(form == ERROR) {
			static char errbuf[SMLBUF];
			(void) sprintf(errbuf,
				"address resolution ('%s' @ '%s') failed",
					userv[i], hostv[i]);
			command = errbuf;
			size    = 0;
			goto form_error;
		}

		if (retrying) {
			command = scommand;
		} else if (form == LOCAL) {
			command = lcommand;
		} else {
			command = rcommand;
			if(flags == uux_noqueue) {
				noqcnt++;
			}
		}
		ADVISE("COMMAND: %s\n", command);

/*
** Fork the mailer and set it up for writing so we can send the mail to it,
** or for debugging divert the output to stdout.
*/

/*
** We may try to write on a broken pipe, if the uux'd host
** is unknown to us.  Ignore this signal, since we can use the
** return value of the pclose() as our indication of failure.
*/
		(void) signal(SIGPIPE, SIG_IGN);

		if (debug == YES) {
			out = stdout;
		} else {
			failcount = 0;
			do {
				out = popen(command, "w");
				if (out) break;
				/*
				 * Fork failed.  System probably overloaded.
				 * Wait awhile and try again 10 times.
				 * If it keeps failing, probably some
				 * other problem, like no uux or smail.
				 */
				(void) sleep(60);
			} while (++failcount < 10);
		}
		if(out == NULL) {
			exitstat = EX_UNAVAILABLE;
			(void) printf("couldn't execute %s.\n", command);
			continue;
		}

		size = 0;
		if(fstat(fileno(spoolfp), &st) >= 0) {
			size = st.st_size - message;
		}
/*
**  Output our From_ line.
*/
		if (form == LOCAL) {
#ifdef SENDMAIL
			(void) sprintf(buf, LFROM(from, nows, hostname));
			size += strlen(buf);
			(void) fputs(buf, out);
#else
			char *p;
			if((p=index(from, '!')) == NULL) {
				(void) sprintf(buf,
					LFROM(from, nows, hostname));
				size += strlen(buf);
				(void) fputs(buf, out);
			} else {
				*p = NULL;
				(void) sprintf(buf, RFROM(p+1, nows, from));
				size += strlen(buf);
				(void) fputs(buf, out);
				*p = '!';
			}
#endif
		} else {
			(void) sprintf(buf, RFROM(from, nows, hostname));
			size += strlen(buf);
			(void) fputs(buf, out);
		}

#ifdef SENDMAIL
/*
**  If using sendmail, insert a Received: line only for mail
**  that is being passed to uux.  If not using sendmail, always
**  insert the received line, since sendmail isn't there to do it.
*/
		if(command == rcommand && handle != ALL)
#endif
		{
			(void) sprintf(buf,
				"Received: by %s (%s)\n\tid AA%05d; %s\n",
					hostdomain, VERSION,
					getpid(), arpanows);
			size += strlen(buf);
			(void) fputs(buf, out);
		}

/*
**  Copy input.
*/
		while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
			(void) fputs(buf, out);
		}
/*
**  Get exit status and if non-zero, set global exitstat so when we exit
**  we can indicate an error.
*/
form_error:
		if (debug != YES) {
			if(form == ERROR) {
				exitstat = EX_NOHOST;
			} else if (status = pclose(out)) {
				exitstat = status >> 8;
			}
			/*
			 * The 'retrying' check prevents a smail loop.
			 */
			if(exitstat != 0) {
				/*
				** the mail failed, probably because the host
				** being uux'ed isn't in L.sys or local user
				** is unknown.
				*/

				if((retrying == 0)	/* first pass */
				&& (routing != REROUTE)	/* have higher level */
				&& (form != LOCAL)) {	/* can't route local */
					/*
					** Try again using a higher
					** level of routing.
					*/
					ADVISE("%s failed (%d)\ntrying %s\n",
						command, exitstat, scommand);
					exitstat = 0;
					retrying = 1;
					form = SENT;
					goto retry;
				}

				/*
				** if we have no other routing possibilities
				** see that the mail is returned to sender.
				*/

				if((routing == REROUTE)
			        || (form == LOCAL)) {

					/*
					** if this was our last chance,
					** return the mail to the sender.
					*/

					ADVISE("%s failed (%d)\n",
						command, exitstat);
					
					(void) fseek(spoolfp, message, 0);
#ifdef SENDMAIL
					/* if we have sendmail, then it
					** was handed the mail, which failed.
					** sendmail returns the failed mail
					** for us, so we need not do it again.
					*/
					if(form != LOCAL)
#endif
					{
						return_mail(from, command);
					}
					exitstat = 0;
				}
			}
# ifdef LOG
			else {
				if(retrying == 0) log(command, from, size); /* */
			}
# endif
		}
	}
/*
**  Update logs and records.
*/
# ifdef RECORD
	(void) fseek(spoolfp, message, 0);
	record(command, from, size);
# endif

/*
**  close spool file pointer.
**  if we created it, then unlink file.
*/
	(void) fclose(spoolfp);
	if(spoolmaster) {
		(void) unlink(spoolfile);
	}
	(void) unlink(stderrfile);
}

/*
** return mail to sender, as determined by From_ line.
*/
return_mail(from, fcommand)
char *from, *fcommand;
{
	char buf[SMLBUF];
	char domain[SMLBUF], user[SMLBUF];
	char *r;
	FILE *fp, *out, *popen();
	int i = 0;

	r = buf;

	(void) sprintf(r, "%s %s", SMAIL, VFLAG);
	r += strlen(r);

	if(islocal(from, domain, user)) {
		(void) sprintf(r, LARG(user));
	} else {
		(void) sprintf(r, RLARG(domain, user));
	}

	i = 0;
	do {
		out = popen(buf, "w");
		if (out) break;
		/*
		 * Fork failed.  System probably overloaded.
		 * Wait awhile and try again 10 times.
		 * If it keeps failing, probably some
		 * other problem, like no uux or smail.
		 */
		(void) sleep(60);
	} while (++i < 10);

	if(out == NULL) {
		(void) printf("couldn't execute %s.\n", buf);
		return;
	}

	(void) fprintf(out, "Date: %s\n", arpanows);
	(void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain);
	(void) fprintf(out, "Subject: failed mail\n");
	(void) fprintf(out, "To: %s\n", from);
	(void) fprintf(out, "\n");
	(void) fprintf(out, "=======     command failed      =======\n\n");
	(void) fprintf(out, " COMMAND: %s\n\n", fcommand);

	(void) fprintf(out, "======= standard error follows  =======\n");
	(void) fflush(stderr);
	if((fp = fopen(stderrfile, "r")) != NULL) {
		while(fgets(buf, sizeof(buf), fp) != NULL) {
			(void) fputs(buf, out);
		}
	}
	(void) fclose(fp);
	(void) fprintf(out, "======= text of message follows =======\n");
/*
**  Copy input.
*/
	(void) fprintf(out, "From %s\n", from);
	while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
		(void) fputs(buf, out);
	}
	(void) pclose(out);
}
//E*O*F deliver.c//

echo x - getopt.c
cat > "getopt.c" << '//E*O*F getopt.c//'
/*
**	@(#)getopt.c	2.5 (smail) 9/15/87
*/

/*
 * Here's something you've all been waiting for:  the AT&T public domain
 * source for getopt(3).  It is the code which was given out at the 1985
 * UNIFORUM conference in Dallas.  I obtained it by electronic mail
 * directly from AT&T.  The people there assure me that it is indeed
 * in the public domain.
 * 
 * There is no manual page.  That is because the one they gave out at
 * UNIFORUM was slightly different from the current System V Release 2
 * manual page.  The difference apparently involved a note about the
 * famous rules 5 and 6, recommending using white space between an option
 * and its first argument, and not grouping options that have arguments.
 * Getopt itself is currently lenient about both of these things White
 * space is allowed, but not mandatory, and the last option in a group can
 * have an argument.  That particular version of the man page evidently
 * has no official existence, and my source at AT&T did not send a copy.
 * The current SVR2 man page reflects the actual behavor of this getopt.
 * However, I am not about to post a copy of anything licensed by AT&T.
 */

/* This include is needed only to get "index" defined as "strchr" on Sys V. */
#include "defs.h"

/*LINTLIBRARY*/
#define NULL	0
#define EOF	(-1)
#define ERR(s, c)	if(opterr){\
	extern int write();\
	char errbuf[2];\
	errbuf[0] = c; errbuf[1] = '\n';\
	(void) write(2, argv[0], (unsigned)strlen(argv[0]));\
	(void) write(2, s, (unsigned)strlen(s));\
	(void) write(2, errbuf, 2);}

extern char *index();

int	opterr = 1;
int	optind = 1;
int	optopt;
char	*optarg;

int
getopt(argc, argv, opts)
int	argc;
char	**argv, *opts;
{
	static int sp = 1;
	register int c;
	register char *cp;

	if(sp == 1)
		if(optind >= argc ||
		   argv[optind][0] != '-' || argv[optind][1] == '\0')
			return(EOF);
		else if(strcmp(argv[optind], "--") == NULL) {
			optind++;
			return(EOF);
		}
	optopt = c = argv[optind][sp];
	if(c == ':' || (cp=index(opts, c)) == NULL) {
		ERR(": illegal option -- ", c);
		if(argv[optind][++sp] == '\0') {
			optind++;
			sp = 1;
		}
		return('?');
	}
	if(*++cp == ':') {
		if(argv[optind][sp+1] != '\0')
			optarg = &argv[optind++][sp+1];
		else if(++optind >= argc) {
			ERR(": option requires an argument -- ", c);
			sp = 1;
			return('?');
		} else
			optarg = argv[optind++];
		sp = 1;
	} else {
		if(argv[optind][++sp] == '\0') {
			sp = 1;
			optind++;
		}
		optarg = NULL;
	}
	return(c);
}
//E*O*F getopt.c//

echo x - getpath.c
cat > "getpath.c" << '//E*O*F getpath.c//'
#ifndef lint
static char 	*sccsid="@(#)getpath.c	2.5 (smail) 9/15/87";
#endif

# include	<stdio.h>
# include	<sys/types.h>
# include	<ctype.h>
# include	"defs.h"

extern enum edebug debug;	/* how verbose we are 		*/ 
extern char *pathfile;		/* location of path database	*/

/*
**
** getpath(): look up key in ascii sorted path database.
**
*/

getpath( key, path , cost)
char *key;		/* what we are looking for */
char *path;		/* where the path results go */
int *cost;		/* where the cost results go */
{
	long pos, middle, hi, lo;
	static long pathlength = 0;
	register char *s;
	int c;
	static FILE *file;
	int flag;

DEBUG("getpath: looking for '%s'\n", key);

	if(pathlength == 0) {	/* open file on first use */
		if((file = fopen(pathfile, "r")) == NULL) {
			(void) printf("can't access %s.\n", pathfile);
			pathlength = -1;
		} else {
			(void) fseek(file, 0L, 2);	/* find length */
			pathlength = ftell(file);
		}
	}
	if( pathlength == -1 )
		return( EX_OSFILE );

	lo = 0;
	hi = pathlength;
	(void) strcpy( path, key );
	(void) strcat( path, "\t" );
/*
** "Binary search routines are never written right the first time around."
** - Robert G. Sheldon.
*/
	for( ;; ) {
		pos = middle = ( hi+lo+1 )/2;
		(void) fseek(file, pos, 0);	/* find midpoint */
		if(pos != 0)
			while(((c = getc(file)) != EOF) && (c != '\n'))
				;	/* go to beginning of next line */
		if(c == EOF) {
			return(EX_NOHOST);
		}
		for( flag = 0, s = path; flag == 0; s++ ) { /* match??? */
			if( *s == '\0' ) {
				goto solved;
			}
			if((c = getc(file)) == EOF) {
				return(EX_NOHOST);
			}
			flag = lower(c) - lower(*s);
		} 
		if(lo >= middle) {		/* failure? */
			return(EX_NOHOST);
		}
		if((c != EOF) && (flag < 0)) {	/* close window */
			lo = middle;
		} else {
			hi = middle - 1;
		}
	}
/* 
** Now just copy the result.
*/
solved:
	while(((c  = getc(file)) != EOF) && (c != '\t') && (c != '\n')) {
		*path++ = c;
	}
	*path = '\0';
/*
** See if the next field on the line is numeric.
** If so, use it as the cost for the route.
*/
	if(c == '\t') {
		int tcost = -1;
		while(((c = getc(file)) != EOF) && isdigit(c)) {
			if(tcost < 0) tcost = 0;
			tcost *= 10;
			tcost += c - '0';
		}
		if(tcost >= 0) *cost = tcost;
	}
	return (EX_OK);
}
//E*O*F getpath.c//

echo x - headers.c
cat > "headers.c" << '//E*O*F headers.c//'
/*
**  message spooing, header and address parsing and completion
**  functions for smail/rmail
*/

#ifndef lint
static char 	*sccsid="@(#)headers.c	2.5 (smail) 9/15/87";
#endif

# include	<stdio.h>
# include	<sys/types.h>
# include	<time.h>
# include	<ctype.h>
# include	<pwd.h>
# include	"defs.h"

extern enum edebug debug;	/* how verbose we are 		*/ 
extern char hostname[];		/* */
extern char hostdomain[];	/* */
extern char *spoolfile;		/* file name of spooled message */
extern FILE *spoolfp;		/* file ptr  to spooled message */
extern int spoolmaster;		/* set if creator of spoolfile  */
extern time_t now;		/* time				*/
extern char nows[], arpanows[];	/* time strings			*/
extern struct tm *gmt, *loc;	/* time structs			*/
extern char *from_addr;		/* replacement fromaddr with -F */

static char toline[SMLBUF];
static char fromline[SMLBUF];
static char dateline[SMLBUF];
static char midline[SMLBUF];
static char *ieof = "NOTNULL";

struct reqheaders {
	char *name;
	char *field;
	char have;
};

static struct reqheaders reqtab[] = {
	"Message-Id:"	,	midline		,	'N'	,
	"Date:"		,	dateline	,	'N'	,
	"From:"		,	fromline	,	'N'	,
	"To:"		,	toline		,	'N'	,
	NULL 		,	NULL		,	'N'
};


/*
**
** parse(): parse <address> into <domain, user, form>.
**
** 	input		form
**	-----		----
**	user		LOCAL
**	domain!user	DOMAIN
**	user@domain	DOMAIN
**	@domain,address	LOCAL	(just for sendmail)
**	host!address	UUCP
**
*/

enum eform
parse(address, domain, user)
char *address;		/* input address 	*/
char *domain;		/* output domain 	*/
char *user;		/* output user 		*/
{
	int parts;
	char *partv[MAXPATH];				/* to crack address */

/*
**  If this is route address form @domain_a,@domain_b:user@domain_c, ...
*/
	if(*address == '@')
#ifdef SENDMAIL
/*
**  hand it to sendmail
*/
	{
		goto local;
	}
#else
/*
**  no sendmail, convert it into a bang path: domain_a!domain_b!domain_c!user
*/
	{
		char buf[SMLBUF], *p;
		char t_dom[SMLBUF], t_user[SMLBUF];

		(void) strcpy(buf, address+1);		/* elide leading '@' */

		for(p=buf; *p != '\0' ; p++) {	/* search for ',' or ':' */
			if(*p == ':') {		/* reached end of route */
				break;
			}
			if(*p == ',') {		/* elide ','s */
				(void) strcpy(p, p+1);
			}
			if(*p == '@') {		/* convert '@' to '!' */
				*p = '!';
			}
		}

		if(*p != ':') {	/* bad syntax - punt */
			goto local;
		}
		*p = '\0';

		if(parse(p+1, t_dom, t_user) != LOCAL) {
			(void) strcat(buf, "!");
			(void) strcat(buf, t_dom);
		}
		(void) strcat(buf, "!");
		(void) strcat(buf, t_user);

		/* munge the address (yuk)
		** it's OK to copy into 'address', because the machinations
		** above don't increase the string length of the address.
		*/

		(void) strcpy(address, buf);

		/* re-parse the address */
		return(parse(address, domain, user));
	}
#endif
/*
**  Try splitting at @.  If it works, this is user@domain, form DOMAIN.
**  Prefer the righthand @ in a@b@c.
*/
	if ((parts = ssplit(address, '@', partv)) >= 2) {
		(void) strcpy(domain, partv[parts-1]);
		(void) strncpy(user, partv[0], partv[parts-1]-partv[0]-1);
		user[partv[parts-1]-partv[0]-1] = '\0';
		return (DOMAIN);
	} 
/*
**  Try splitting at !. If it works, see if the piece before the ! has
**  a . in it (domain!user, form DOMAIN) or not (host!user, form UUCP).
*/
	if (ssplit(address, '!', partv) > 1) {
		(void) strcpy(user, partv[1]);
		(void) strncpy(domain, partv[0], partv[1]-partv[0]-1);
		domain[partv[1]-partv[0]-1] = '\0';

		if((parts = ssplit(domain, '.', partv)) < 2) {
			return(UUCP);
		}

		if(partv[parts-1][0] == '\0') {
			partv[parts-1][-1] = '\0'; /* strip trailing . */
		}
		return (DOMAIN);
	}
/* 
**  Done trying.  This must be just a user name, form LOCAL.
*/
local:
	(void) strcpy(user, address);
	(void) strcpy(domain, "");
	return(LOCAL);				/* user */
}

build(domain, user, form, result)
char *domain;
char *user;
enum eform form;
char *result;
{
	switch((int) form) {
	case LOCAL:
		(void) sprintf(result, "%s", user); 
		break;
	case UUCP:
		(void) sprintf(result, "%s!%s", domain, user);
		break;
	case DOMAIN:
		(void) sprintf(result, "%s@%s", user, domain);
		break;
	}
}

/*
**  ssplit(): split a line into array pointers.
**
**  Each pointer wordv[i] points to the first character after the i'th 
**  occurence of c in buf.  Note that each wordv[i] includes wordv[i+1].
**
*/

ssplit(buf, c, ptr)
register char *buf;		/* line to split up 		*/
char c;				/* character to split on	*/
char **ptr;			/* the resultant vector		*/
{
        int count = 0;
        int wasword = 0;

        for(; *buf; buf++) {
		if (!wasword) {
			count++;
			*ptr++ = buf;
		}
		wasword = (c != *buf);
        }
	if (!wasword) {
		count++;
		*ptr++ = buf;
	}
        *ptr = NULL;
        return(count);
}

/*
** Determine whether an address is a local address
*/

islocal(addr, domain, user)
char *addr, *domain, *user;
{
		enum eform form, parse();
		extern char hostuucp[];

		/*
		** parse the address
		*/

		form = parse(addr, domain, user);

		if((form == LOCAL)			/* user */
		||(strcmpic(domain, hostdomain) == 0)	/* user@hostdomain */
		||(strcmpic(domain, hostname)   == 0)	/* user@hostname */
#ifdef DOMGATE
		||(strcmpic(domain, &MYDOM[0]) == 0)	/* user@MYDOM w/ dot */
		||(strcmpic(domain, &MYDOM[1]) == 0)	/* user@MYDOM no dot */
#endif
		||(strcmpic(domain, hostuucp)   == 0)) {/* user@hostuucp */
			return(1);
		}
		return(0);
}

/*
** spool - message spooling module
**
** (1) get dates for headers, etc.
** (2) if the message is on the standard input (no '-f')
**     (a) create a temp file for spooling the message.
**     (b) collapse the From_ headers into a path.
**     (c) if the mail originated locally, then
**	     (i) establish default headers
**	    (ii) scan the message headers for required header fields
**	   (iii) add any required message headers that are absent
**     (d) copy rest of the message to the spool file
**     (e) close the spool file
** (3) open the spool file for reading
*/

void
spool(argc, argv)
int argc;
char **argv;
{
	static char *tmpf = "/tmp/rmXXXXXX";	/* temp file name */
	char *mktemp();
	char buf[SMLBUF];
	static char splbuf[SMLBUF];
	char from[SMLBUF], domain[SMLBUF], user[SMLBUF];
	void rline(), scanheaders(), compheaders();

	/*
	** if the mail has already been spooled by
	** a previous invocation of smail don't respool.
	** check the file name to prevent things like
	** rmail -f /etc/passwd badguy@dreadfuldomain
	*/

	if((spoolfile != NULL)
	&& (strncmp(spoolfile, tmpf, strlen(tmpf) - 6) != 0)) {
		error(EX_TEMPFAIL, "spool: bad file name '%s'\n", spoolfile);
	}

	/*
	** set dates in local, arpa, and gmt forms
	*/
	setdates();

	/*
	** If necessary, copy stdin to a temp file.
	*/

	if(spoolfile == NULL) {
		spoolfile = strcpy(splbuf, tmpf);
		(void) mktemp(spoolfile);

		if((spoolfp = fopen(spoolfile, "w")) == NULL) {
			error(EX_CANTCREAT, "can't create %s.\n", spoolfile);
		}

		spoolmaster = 1;

		/*
		** rline reads the standard input,
		** collapsing the From_ and >From_
		** lines into a single uucp path.
		** first non-from_ line is in buf[];
		*/

		rline(from, buf);

		/*
		** if the mail originated here, we parse the header
		** and add any required headers that are missing.
		*/

		if(islocal(from, domain, user) || (from_addr != NULL)) {
			/*
			** initialize default headers
			*/
			def_headers(argc, argv, from);

			/*
			** buf has first, non-from_  line
			*/
			scanheaders(buf);
			/*
			** buf has first, non-header line,
			*/

			compheaders();

			if(buf[0] != '\n') {
				(void) fputs("\n", spoolfp);
			}
		}

		/*
		** now, copy the rest of the letter into the spool file
		** terminate on either EOF or '^.$'
		*/

		while(ieof != NULL) {
			(void) fputs(buf, spoolfp);
			if((fgets(buf, SMLBUF, stdin) == NULL)
			|| (buf[0] == '.' && buf[1] == '\n')) {
				ieof = NULL;
			}
		}

		/*
		** close the spool file, and the standard input.
		*/

		(void) fclose(spoolfp);
		(void) fclose(stdin);	/* you don't see this too often! */
	}

	if((spoolfp = fopen(spoolfile, "r")) == NULL) {
		error(EX_TEMPFAIL, "can't open %s.\n", spoolfile);
	}
}

/*
**
**  rline(): collapse From_ and >From_ lines.
**
**  Same idea as the old rmail, but also turns user@domain to domain!user. 
**
*/

void
rline(from, retbuf)
char *from;
char *retbuf;
{
	int parts;			/* for cracking From_ lines ... */
	char *partv[16];		/* ... apart using ssplit() 	*/
	char user[SMLBUF];		/* for rewriting user@host	*/
	char domain[SMLBUF];		/* "   "         "          	*/
	char addr[SMLBUF];		/* "   "         "          	*/
	enum eform form, parse();	/* "   "         "          	*/
	extern build();			/* "   "         "          	*/
	char *c;
	int nhops, i;
	char buf[SMLBUF], tmp[SMLBUF], *hop[128], *e, *b;
	char *pwuid();

	if(spoolmaster == 0) return;

	buf[0] = from[0] = addr[0] = '\0';
/*
**  Read each line until we hit EOF or a line not beginning with "From "
**  or ">From " (called From_ lines), accumulating the new path in from
**  and stuffing the actual sending user (the user name on the last From_ 
**  line) in addr.
*/
	for(;;) {
		(void) strcpy(retbuf, buf);
		if(ieof == NULL) {
			break;
		}
		if((fgets(buf, sizeof(buf), stdin) == NULL)
		|| (buf[0] == '.' && buf[1] == '\n')) {
			ieof = NULL;
			break;
		}
		if (strncmp("From ", buf, 5) 
		    && strncmp(">From ", buf, 6)) {
			break;
		}
/*
**  Crack the line apart using ssplit.
*/
		if(c = index(buf, '\n')) {
			*c = '\0';
		}
		parts = ssplit(buf, ' ', partv);
/*
**  Tack host! onto the from argument if "remote from host" is present.
*/

		if((parts > 3)
		&& (strncmp("remote from ", partv[parts-3], 12) == 0)) {
			(void) strcat(from, partv[parts-1]);
			(void) strcat(from, "!");
		}
/*
**  Stuff user name into addr, overwriting the user name from previous 
**  From_ lines, since only the last one counts.  Then rewrite user@host 
**  into host!user, since @'s don't belong in the From_ argument.
*/
		if(parts < 2) {
			break;
		} else {
			char *x = partv[1];
			char *q = index(x, ' ');
			if(q != NULL) {
				*q = '\0';
			}
			(void) strcpy(addr, x);
		}

		(void) parse(addr, domain, user);
		if(*domain == '\0') {
			form = LOCAL;
		} else {
			form = UUCP;
		}

		build(domain, user, form, addr);
	}
/*
**  Now tack the user name onto the from argument.
*/
	(void) strcat(from, addr);
/*
**  If we still have no from argument, we have junk headers, but we try
**  to get the user's name using /etc/passwd.
*/

	if (from[0] == '\0') {
		char *login;
		if ((login = pwuid(getuid())) == NULL) {
			(void) strcpy(from, "nobody");	/* bad news */
		} else {
			(void) strcpy(from, login);
		}
	}

	/* split the from line on '!'s */
	nhops = ssplit(from, '!', hop);

	for(i = 0; i < (nhops - 1); i++) {
		b = hop[i];
		if(*b == '\0') {
			continue;
		}
		e = hop[i+1];
		e-- ;
		*e = '\0';	/* null terminate each path segment */
		e++;

#ifdef HIDDENHOSTS
/*
**  Strip hidden hosts:  anything.hostname.MYDOM -> hostname.MYDOM
*/
		for(p = b;(p = index(p, '.')) != NULL; p++) {
			if(strcmpic(hostdomain, p+1) == 0) {
				(void) strcpy(b, hostdomain);
				break;
			}
		}
#endif

/*
**  Strip useless MYDOM: hostname.MYDOM -> hostname
*/
		if(strcmpic(hop[i], hostdomain) == 0) {
			(void) strcpy(hop[i], hostname);
		}
	}

/*
**  Now strip out any redundant information in the From_ line
**  a!b!c!c!d	=> a!b!c!d
*/

	for(i = 0; i < (nhops - 2); i++) {
		b = hop[i];
		e = hop[i+1];
		if(strcmpic(b, e) == 0) {
			*b = '\0';
		}
	}
/*
**  Reconstruct the From_ line
*/
	tmp[0] = '\0';			/* empty the tmp buffer */

	for(i = 0; i < (nhops - 1); i++) {
		if((hop[i][0] == '\0')	/* deleted this hop */
		 ||((tmp[0] == '\0')	/* first hop == hostname */
		  &&(strcmpic(hop[i], hostname) == 0))) {
			continue;
		}
		(void) strcat(tmp, hop[i]);
		(void) strcat(tmp, "!");
	}
	(void) strcat(tmp, hop[i]);
	(void) strcpy(from, tmp);
	(void) strcpy(retbuf, buf);
	(void) fprintf(spoolfp, "%s\n", from);
}

void
scanheaders(buf)
char *buf;
{
	int inheader = 0;

	while(ieof != NULL) {
		if(buf[0] == '\n') {
			break; /* end of headers */
		}

		/*
		** header lines which begin with whitespace
		** are continuation lines
		*/
		if((inheader == 0)
		|| ((buf[0] != ' ' && buf[0] != '\t'))) {
			/* not a continuation line
			** check for header
			*/
			if(isheader(buf) == 0) {
				/*
				** not a header
				*/
				break;
			}
			inheader = 1;
			haveheaders(buf);
		}
		(void) fputs(buf, spoolfp);
		if((fgets(buf, SMLBUF, stdin) == NULL)
		|| (buf[0] == '.' && buf[1] == '\n')) {
			ieof = NULL;
		}
	}

	if(isheader(buf)) {
		buf[0] = '\0';
	}
}

/*
** complete headers - add any required headers that are not in the message
*/
void
compheaders()
{
	struct reqheaders *i;

	/*
	** look at the table of required headers and
	** add those that are missing to the spooled message.
	*/
	for(i = reqtab; i->name != NULL; i++) {
		if(i->have != 'Y') {
			(void) fprintf(spoolfp, "%s\n", i->field);
		}
	}
}

/*
** look at a string and determine
** whether or not it is a valid header.
*/
isheader(s)
char *s;
{
	char *p;

	/*
	** header field names must terminate with a colon
	** and may not be null.
	*/
	if(((p = index(s, ':')) == NULL) || (s == p)) {
		return(0);

	}
	/*
	** header field names must consist entirely of
	** printable ascii characters.
	*/
	while(s != p) {
		if((*s < '!') || (*s > '~')) {
			return(0);
		}
		s++;
	}
	/*
	** we hit the ':', so the field may be a header
	*/
	return(1);
}

/*
** compare the header field to those in the required header table.
** if it matches, then mark the required header as being present
** in the message.
*/
haveheaders(s)
char *s;
{
	struct reqheaders *i;

	for(i = reqtab; i->name != NULL; i++) {
		if(strncmpic(i->name, s, strlen(i->name)) == 0) {
			if((strncmpic("From:", s, 5) == 0)
			&& (from_addr != NULL)) {
				(void) sprintf(s, "From: %s\n", from_addr);
			}
			i->have = 'Y';
			break;
		}
	}
}

/*
** create default headers for the message.
*/
def_headers(argc, argv, from)
int argc;
char **argv;
char *from;
{
	def_to(argc, argv);	/* default To:		*/
	def_date();		/* default Date:	*/
	def_from(from);		/* default From: 	*/
	def_mid();		/* default Message-Id:	*/
}

/*
** default Date: in arpa format
*/
def_date()
{
	(void) strcpy(dateline, "Date: ");
	(void) strcat(dateline, arpanows);
}

/*
** default Message-Id
**  Message-Id: <yymmddhhmm.AAppppp@hostdomain>
**
**	yy	 year
**	mm	 month
**	dd	 day
**	hh	 hour
**	mm	 minute
**	ppppp	process-id
**
** date and time are set by GMT
*/
def_mid()
{
	(void) sprintf(midline, "Message-Id: <%02d%02d%02d%02d%02d.AA%05d@%s>",
		gmt->tm_year,
		gmt->tm_mon+1,
		gmt->tm_mday,
		gmt->tm_hour,
		gmt->tm_min,
		getpid(),
		hostdomain);
}

/*
** default From:
**  From: user@hostdomain (Full Name)
*/
def_from(from)
char *from;
{

	char *nameptr;
	char name[SMLBUF];
	char *getenv(), *login;
	char *pwfnam(), *pwuid();

	if (from_addr != NULL) {
		(void) sprintf(fromline, "From: %s", from_addr);
		return;
	}

	name[0] = '\0';
	if((nameptr = getenv("NAME")) != NULL) {
		(void) strcpy(name, nameptr);
	} else if((login = pwuid(getuid())) != NULL) {
		if((nameptr = pwfnam(login)) != NULL) {
			(void) strcpy(name, nameptr);
		}
	}
	if(name[0] != '\0') {
		(void) sprintf(fromline,
			"From: %s@%s (%s)", from, hostdomain, name);
	} else {
		(void) sprintf(fromline,
			"From: %s@%s", from, hostdomain);
	}
}

/*
** default To:
**  To: recip1, recip2, ...
**
** lines longer than 50 chars are continued on another line.
*/
def_to(argc, argv)
int argc;
char **argv;
{
	int i, n;
	char *bol;

	bol = toline;
	(void) strcpy(bol, "To: ");
	for(n = i = 0; i < argc; i++) {
		(void) strcat(bol, argv[i]);

		if((index(argv[i], '!') == NULL)
		&& (index(argv[i], '@') == NULL)) {
			(void) strcat(bol, "@");
			(void) strcat(bol, hostdomain);
		}
		if(i+1 < argc) {
			n = strlen(bol);
			if(n > 50) {
				(void) strcat(bol, ",\n\t");
				bol = bol + strlen(bol);
				*bol = '\0';
				n = 8;
			} else {
				(void) strcat(bol, ", ");
			}
		}
	}
}
//E*O*F headers.c//

exit 0