Sendmail source for the 3B1/7300 (part 2/8)

David H. Brierley dave at galaxia.Newport.RI.US
Sat Feb 25 12:27:50 AEST 1989


----- cut here and feed to /bin/sh -----
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 8)."
# Contents:  src/alias.c src/collect.c src/domain.c src/err.c
#   src/macro.c src/usersmtp.c
# Wrapped by dave at galaxia on Fri Feb 24 20:23:41 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/alias.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/alias.c'\"
else
echo shar: Extracting \"'src/alias.c'\" \(11559 characters\)
sed "s/^X//" >'src/alias.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X# include "sendmail.h"
X# include <pwd.h>
X# include <sys/stat.h>
X# include <signal.h>
X# include <errno.h>
X# ifdef FLOCK
X# include <sys/file.h>
X# endif FLOCK
X
X#ifndef lint
X# ifdef DBM
Xstatic char	SccsId[] = "@(#)alias.c	5.13 (Berkeley) 4/17/86	(with DBM)";
X# else DBM
Xstatic char	SccsId[] = "@(#)alias.c	5.13 (Berkeley) 4/17/86	(without DBM)";
X# endif DBM
X#endif not lint
X
X
X/*
X**  ALIAS -- Compute aliases.
X**
X**	Scans the alias file for an alias for the given address.
X**	If found, it arranges to deliver to the alias list instead.
X**	Uses libdbm database if -DDBM.
X**
X**	Parameters:
X**		a -- address to alias.
X**		sendq -- a pointer to the head of the send queue
X**			to put the aliases in.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Aliases found are expanded.
X**
X**	Notes:
X**		If NoAlias (the "-n" flag) is set, no aliasing is
X**			done.
X**
X**	Deficiencies:
X**		It should complain about names that are aliased to
X**			nothing.
X*/
X
X
X#ifdef DBM
Xtypedef struct
X{
X	char	*dptr;
X	int	dsize;
X} DATUM;
Xextern DATUM fetch();
X#endif DBM
X
Xalias(a, sendq)
X	register ADDRESS *a;
X	ADDRESS **sendq;
X{
X	register char *p;
X	extern char *aliaslookup();
X
X# ifdef DEBUG
X	if (tTd(27, 1))
X		printf("alias(%s)\n", a->q_paddr);
X# endif
X
X	/* don't realias already aliased names */
X	if (bitset(QDONTSEND, a->q_flags))
X		return;
X
X	CurEnv->e_to = a->q_paddr;
X
X	/*
X	**  Look up this name
X	*/
X
X	if (NoAlias)
X		p = NULL;
X	else
X		p = aliaslookup(a->q_user);
X	if (p == NULL)
X		return;
X
X	/*
X	**  Match on Alias.
X	**	Deliver to the target list.
X	*/
X
X# ifdef DEBUG
X	if (tTd(27, 1))
X		printf("%s (%s, %s) aliased to %s\n",
X		    a->q_paddr, a->q_host, a->q_user, p);
X# endif
X	message(Arpa_Info, "aliased to %s", p);
X	AliasLevel++;
X	sendtolist(p, a, sendq);
X	AliasLevel--;
X}
X/*
X**  ALIASLOOKUP -- look up a name in the alias file.
X**
X**	Parameters:
X**		name -- the name to look up.
X**
X**	Returns:
X**		the value of name.
X**		NULL if unknown.
X**
X**	Side Effects:
X**		none.
X**
X**	Warnings:
X**		The return value will be trashed across calls.
X*/
X
Xchar *
Xaliaslookup(name)
X	char *name;
X{
X# ifdef DBM
X	DATUM rhs, lhs;
X
X	/* create a key for fetch */
X	lhs.dptr = name;
X	lhs.dsize = strlen(name) + 1;
X	rhs = fetch(lhs);
X	return (rhs.dptr);
X# else DBM
X	register STAB *s;
X
X	s = stab(name, ST_ALIAS, ST_FIND);
X	if (s == NULL)
X		return (NULL);
X	return (s->s_alias);
X# endif DBM
X}
X/*
X**  INITALIASES -- initialize for aliasing
X**
X**	Very different depending on whether we are running DBM or not.
X**
X**	Parameters:
X**		aliasfile -- location of aliases.
X**		init -- if set and if DBM, initialize the DBM files.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		initializes aliases:
X**		if DBM:  opens the database.
X**		if ~DBM: reads the aliases into the symbol table.
X*/
X
X# define DBMMODE	0666
X
Xinitaliases(aliasfile, init)
X	char *aliasfile;
X	bool init;
X{
X#ifdef DBM
X	int atcnt;
X	time_t modtime;
X	bool automatic = FALSE;
X	char buf[MAXNAME];
X#endif DBM
X	struct stat stb;
X	static bool initialized = FALSE;
X
X	if (initialized)
X		return;
X	initialized = TRUE;
X
X	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
X	{
X		if (aliasfile != NULL && init)
X			syserr("Cannot open %s", aliasfile);
X		NoAlias = TRUE;
X		errno = 0;
X		return;
X	}
X
X# ifdef DBM
X	/*
X	**  Check to see that the alias file is complete.
X	**	If not, we will assume that someone died, and it is up
X	**	to us to rebuild it.
X	*/
X
X	if (!init)
X		dbminit(aliasfile);
X	atcnt = SafeAlias * 2;
X	if (atcnt > 0)
X	{
X		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
X		{
X			/*
X			**  Reinitialize alias file in case the new
X			**  one is mv'ed in instead of cp'ed in.
X			**
X			**	Only works with new DBM -- old one will
X			**	just consume file descriptors forever.
X			**	If you have a dbmclose() it can be
X			**	added before the sleep(30).
X			*/
X
X			sleep(30);
X# ifdef NDBM
X			dbminit(aliasfile);
X# endif NDBM
X		}
X	}
X	else
X		atcnt = 1;
X
X	/*
X	**  See if the DBM version of the file is out of date with
X	**  the text version.  If so, go into 'init' mode automatically.
X	**	This only happens if our effective userid owns the DBM
X	**	version or if the mode of the database is 666 -- this
X	**	is an attempt to avoid protection problems.  Note the
X	**	unpalatable hack to see if the stat succeeded.
X	*/
X
X	modtime = stb.st_mtime;
X	(void) strcpy(buf, aliasfile);
X	(void) strcat(buf, ".pag");
X	stb.st_ino = 0;
X	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
X	{
X		errno = 0;
X		if (AutoRebuild && stb.st_ino != 0 &&
X		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
X		{
X			init = TRUE;
X			automatic = TRUE;
X			message(Arpa_Info, "rebuilding alias database");
X#ifdef LOG
X			if (LogLevel >= 7)
X				syslog(LOG_INFO, "rebuilding alias database");
X#endif LOG
X		}
X		else
X		{
X#ifdef LOG
X			if (LogLevel >= 7)
X				syslog(LOG_INFO, "alias database out of date");
X#endif LOG
X			message(Arpa_Info, "Warning: alias database out of date");
X		}
X	}
X
X
X	/*
X	**  If necessary, load the DBM file.
X	**	If running without DBM, load the symbol table.
X	*/
X
X	if (init)
X	{
X#ifdef LOG
X		if (LogLevel >= 6)
X		{
X			extern char *username();
X
X			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
X				automatic ? "auto" : "", username());
X		}
X#endif LOG
X		readaliases(aliasfile, TRUE);
X	}
X# else DBM
X	readaliases(aliasfile, init);
X# endif DBM
X}
X/*
X**  READALIASES -- read and process the alias file.
X**
X**	This routine implements the part of initaliases that occurs
X**	when we are not going to use the DBM stuff.
X**
X**	Parameters:
X**		aliasfile -- the pathname of the alias file master.
X**		init -- if set, initialize the DBM stuff.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Reads aliasfile into the symbol table.
X**		Optionally, builds the .dir & .pag files.
X*/
X
Xstatic
Xreadaliases(aliasfile, init)
X	char *aliasfile;
X	bool init;
X{
X	register char *p;
X	char *rhs;
X	bool skipping;
X	int naliases, bytes, longest;
X	FILE *af;
X	int (*oldsigint)();
X	ADDRESS al, bl;
X	register STAB *s;
X	char line[BUFSIZ];
X
X	if ((af = fopen(aliasfile, "r")) == NULL)
X	{
X# ifdef DEBUG
X		if (tTd(27, 1))
X			printf("Can't open %s\n", aliasfile);
X# endif
X		errno = 0;
X		NoAlias++;
X		return;
X	}
X
X# ifdef DBM
X# ifdef FLOCK
X	/* see if someone else is rebuilding the alias file already */
X	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
X	{
X		/* yes, they are -- wait until done and then return */
X		message(Arpa_Info, "Alias file is already being rebuilt");
X		if (OpMode != MD_INITALIAS)
X		{
X			/* wait for other rebuild to complete */
X			(void) flock(fileno(af), LOCK_EX);
X		}
X		(void) fclose(af);
X		errno = 0;
X		return;
X	}
X# endif FLOCK
X
X	/*
X	**  If initializing, create the new DBM files.
X	*/
X
X	if (init)
X	{
X		oldsigint = signal(SIGINT, SIG_IGN);
X		(void) strcpy(line, aliasfile);
X		(void) strcat(line, ".dir");
X		if (close(creat(line, DBMMODE)) < 0)
X		{
X			syserr("cannot make %s", line);
X			(void) signal(SIGINT, oldsigint);
X			return;
X		}
X		(void) strcpy(line, aliasfile);
X		(void) strcat(line, ".pag");
X		if (close(creat(line, DBMMODE)) < 0)
X		{
X			syserr("cannot make %s", line);
X			(void) signal(SIGINT, oldsigint);
X			return;
X		}
X		dbminit(aliasfile);
X	}
X# endif DBM
X
X	/*
X	**  Read and interpret lines
X	*/
X
X	FileName = aliasfile;
X	LineNumber = 0;
X	naliases = bytes = longest = 0;
X	skipping = FALSE;
X	while (fgets(line, sizeof (line), af) != NULL)
X	{
X		int lhssize, rhssize;
X
X		LineNumber++;
X		p = index(line, '\n');
X		if (p != NULL)
X			*p = '\0';
X		switch (line[0])
X		{
X		  case '#':
X		  case '\0':
X			skipping = FALSE;
X			continue;
X
X		  case ' ':
X		  case '\t':
X			if (!skipping)
X				syserr("Non-continuation line starts with space");
X			skipping = TRUE;
X			continue;
X		}
X		skipping = FALSE;
X
X		/*
X		**  Process the LHS
X		**	Find the final colon, and parse the address.
X		**	It should resolve to a local name -- this will
X		**	be checked later (we want to optionally do
X		**	parsing of the RHS first to maximize error
X		**	detection).
X		*/
X
X		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
X			continue;
X		if (*p++ != ':')
X		{
X			syserr("missing colon");
X			continue;
X		}
X		if (parseaddr(line, &al, 1, ':') == NULL)
X		{
X			syserr("illegal alias name");
X			continue;
X		}
X		loweraddr(&al);
X
X		/*
X		**  Process the RHS.
X		**	'al' is the internal form of the LHS address.
X		**	'p' points to the text of the RHS.
X		*/
X
X		rhs = p;
X		for (;;)
X		{
X			register char c;
X
X			if (init && CheckAliases)
X			{
X				/* do parsing & compression of addresses */
X				while (*p != '\0')
X				{
X					extern char *DelimChar;
X
X					while (isspace(*p) || *p == ',')
X						p++;
X					if (*p == '\0')
X						break;
X					if (parseaddr(p, &bl, -1, ',') == NULL)
X						usrerr("%s... bad address", p);
X					p = DelimChar;
X				}
X			}
X			else
X			{
X				p = &p[strlen(p)];
X				if (p[-1] == '\n')
X					*--p = '\0';
X			}
X
X			/* see if there should be a continuation line */
X			c = fgetc(af);
X			if (!feof(af))
X				(void) ungetc(c, af);
X			if (c != ' ' && c != '\t')
X				break;
X
X			/* read continuation line */
X			if (fgets(p, sizeof line - (p - line), af) == NULL)
X				break;
X			LineNumber++;
X		}
X		if (al.q_mailer != LocalMailer)
X		{
X			syserr("cannot alias non-local names");
X			continue;
X		}
X
X		/*
X		**  Insert alias into symbol table or DBM file
X		*/
X
X		lhssize = strlen(al.q_user) + 1;
X		rhssize = strlen(rhs) + 1;
X
X# ifdef DBM
X		if (init)
X		{
X			DATUM key, content;
X
X			key.dsize = lhssize;
X			key.dptr = al.q_user;
X			content.dsize = rhssize;
X			content.dptr = rhs;
X			store(key, content);
X		}
X		else
X# endif DBM
X		{
X			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
X			s->s_alias = newstr(rhs);
X		}
X
X		/* statistics */
X		naliases++;
X		bytes += lhssize + rhssize;
X		if (rhssize > longest)
X			longest = rhssize;
X	}
X
X# ifdef DBM
X	if (init)
X	{
X		/* add the distinquished alias "@" */
X		DATUM key;
X
X		key.dsize = 2;
X		key.dptr = "@";
X		store(key, key);
X
X		/* restore the old signal */
X		(void) signal(SIGINT, oldsigint);
X	}
X# endif DBM
X
X	/* closing the alias file drops the lock */
X	(void) fclose(af);
X	CurEnv->e_to = NULL;
X	FileName = NULL;
X	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
X			naliases, longest, bytes);
X# ifdef LOG
X	if (LogLevel >= 8)
X		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
X			naliases, longest, bytes);
X# endif LOG
X}
X/*
X**  FORWARD -- Try to forward mail
X**
X**	This is similar but not identical to aliasing.
X**
X**	Parameters:
X**		user -- the name of the user who's mail we would like
X**			to forward to.  It must have been verified --
X**			i.e., the q_home field must have been filled
X**			in.
X**		sendq -- a pointer to the head of the send queue to
X**			put this user's aliases in.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		New names are added to send queues.
X*/
X
Xforward(user, sendq)
X	ADDRESS *user;
X	ADDRESS **sendq;
X{
X	char buf[60];
X	extern bool safefile();
X
X# ifdef DEBUG
X	if (tTd(27, 1))
X		printf("forward(%s)\n", user->q_paddr);
X# endif DEBUG
X
X	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
X		return;
X# ifdef DEBUG
X	if (user->q_home == NULL)
X		syserr("forward: no home");
X# endif DEBUG
X
X	/* good address -- look for .forward file in home */
X	define('z', user->q_home, CurEnv);
X	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
X	if (!safefile(buf, user->q_uid, S_IREAD))
X		return;
X
X	/* we do have an address to forward to -- do it */
X	include(buf, "forwarding", user, sendq);
X}
END_OF_FILE
if test 11559 -ne `wc -c <'src/alias.c'`; then
    echo shar: \"'src/alias.c'\" unpacked with wrong size!
fi
# end of 'src/alias.c'
fi
if test -f 'src/collect.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/collect.c'\"
else
echo shar: Extracting \"'src/collect.c'\" \(6760 characters\)
sed "s/^X//" >'src/collect.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)collect.c	5.2 (Berkeley) 6/8/85";
X#endif not lint
X
X# include <errno.h>
X# include "sendmail.h"
X
X/*
X**  COLLECT -- read & parse message header & make temp file.
X**
X**	Creates a temporary file name and copies the standard
X**	input to that file.  Leading UNIX-style "From" lines are
X**	stripped off (after important information is extracted).
X**
X**	Parameters:
X**		sayok -- if set, give an ARPANET style message
X**			to say we are ready to collect input.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Temp file is created and filled.
X**		The from person may be set.
X*/
X
Xcollect(sayok)
X	bool sayok;
X{
X	register FILE *tf;
X	char buf[MAXFIELD+2];
X	register char *p;
X	extern char *hvalue();
X
X	/*
X	**  Create the temp file name and create the file.
X	*/
X
X	CurEnv->e_df = newstr(queuename(CurEnv, 'd'));
X	if ((tf = dfopen(CurEnv->e_df, "w")) == NULL)
X	{
X		syserr("Cannot create %s", CurEnv->e_df);
X		NoReturn = TRUE;
X		finis();
X	}
X	(void) chmod(CurEnv->e_df, FileMode);
X
X	/*
X	**  Tell ARPANET to go ahead.
X	*/
X
X	if (sayok)
X		message("354", "Enter mail, end with \".\" on a line by itself");
X
X	/*
X	**  Try to read a UNIX-style From line
X	*/
X
X	(void) sfgets(buf, sizeof buf, InChannel);
X	fixcrlf(buf, FALSE);
X# ifndef NOTUNIX
X	if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
X	{
X		eatfrom(buf);
X		(void) sfgets(buf, sizeof buf, InChannel);
X		fixcrlf(buf, FALSE);
X	}
X# endif NOTUNIX
X
X	/*
X	**  Copy InChannel to temp file & do message editing.
X	**	To keep certain mailers from getting confused,
X	**	and to keep the output clean, lines that look
X	**	like UNIX "From" lines are deleted in the header.
X	*/
X
X	do
X	{
X		int c;
X		extern bool isheader();
X
X		/* drop out on error */
X		if (ferror(InChannel))
X			break;
X
X		/* if the line is too long, throw the rest away */
X		if (index(buf, '\n') == NULL)
X		{
X			while ((c = getc(InChannel)) != '\n' && c != EOF)
X				continue;
X			/* give an error? */
X		}
X
X		fixcrlf(buf, TRUE);
X
X		/* see if the header is over */
X		if (!isheader(buf))
X			break;
X
X		/* get the rest of this field */
X		while ((c = getc(InChannel)) == ' ' || c == '\t')
X		{
X			p = &buf[strlen(buf)];
X			*p++ = '\n';
X			*p++ = c;
X			if (sfgets(p, MAXFIELD - (p - buf), InChannel) == NULL)
X				break;
X			fixcrlf(p, TRUE);
X		}
X		if (!feof(InChannel) && !ferror(InChannel))
X			(void) ungetc(c, InChannel);
X
X		CurEnv->e_msgsize += strlen(buf);
X
X		/*
X		**  Snarf header away.
X		*/
X
X		if (bitset(H_EOH, chompheader(buf, FALSE)))
X			break;
X	} while (sfgets(buf, MAXFIELD, InChannel) != NULL);
X
X# ifdef DEBUG
X	if (tTd(30, 1))
X		printf("EOH\n");
X# endif DEBUG
X
X	/* throw away a blank line */
X	if (buf[0] == '\0')
X		(void) sfgets(buf, MAXFIELD, InChannel);
X
X	/*
X	**  Collect the body of the message.
X	*/
X
X	do
X	{
X		register char *bp = buf;
X
X		fixcrlf(buf, TRUE);
X
X		/* check for end-of-message */
X		if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
X			break;
X
X		/* check for transparent dot */
X		if (OpMode == MD_SMTP && !IgnrDot && bp[0] == '.' && bp[1] == '.')
X			bp++;
X
X		/*
X		**  Figure message length, output the line to the temp
X		**  file, and insert a newline if missing.
X		*/
X
X		CurEnv->e_msgsize += strlen(bp) + 1;
X		fputs(bp, tf);
X		fputs("\n", tf);
X		if (ferror(tf))
X			tferror(tf);
X	} while (sfgets(buf, MAXFIELD, InChannel) != NULL);
X	if (fflush(tf) != 0)
X		tferror(tf);
X	(void) fclose(tf);
X
X	/* An EOF when running SMTP is an error */
X	if ((feof(InChannel) || ferror(InChannel)) && OpMode == MD_SMTP)
X	{
X		syserr("collect: unexpected close, from=%s", CurEnv->e_from.q_paddr);
X
X		/* don't return an error indication */
X		CurEnv->e_to = NULL;
X		CurEnv->e_flags &= ~EF_FATALERRS;
X
X		/* and don't try to deliver the partial message either */
X		finis();
X	}
X
X	/*
X	**  Find out some information from the headers.
X	**	Examples are who is the from person & the date.
X	*/
X
X	eatheader(CurEnv);
X
X	/*
X	**  Add an Apparently-To: line if we have no recipient lines.
X	*/
X
X	if (hvalue("to") == NULL && hvalue("cc") == NULL &&
X	    hvalue("bcc") == NULL && hvalue("apparently-to") == NULL)
X	{
X		register ADDRESS *q;
X
X		/* create an Apparently-To: field */
X		/*    that or reject the message.... */
X		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
X		{
X			if (q->q_alias != NULL)
X				continue;
X# ifdef DEBUG
X			if (tTd(30, 3))
X				printf("Adding Apparently-To: %s\n", q->q_paddr);
X# endif DEBUG
X			addheader("apparently-to", q->q_paddr, CurEnv);
X		}
X	}
X
X	if ((CurEnv->e_dfp = fopen(CurEnv->e_df, "r")) == NULL)
X		syserr("Cannot reopen %s", CurEnv->e_df);
X}
X/*
X**  TFERROR -- signal error on writing the temporary file.
X**
X**	Parameters:
X**		tf -- the file pointer for the temporary file.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Gives an error message.
X**		Arranges for following output to go elsewhere.
X*/
X
Xtferror(tf)
X	FILE *tf;
X{
X	if (errno == ENOSPC)
X	{
X		(void) freopen(CurEnv->e_df, "w", tf);
X		fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
X		usrerr("452 Out of disk space for temp file");
X	}
X	else
X		syserr("collect: Cannot write %s", CurEnv->e_df);
X	(void) freopen("/dev/null", "w", tf);
X}
X/*
X**  EATFROM -- chew up a UNIX style from line and process
X**
X**	This does indeed make some assumptions about the format
X**	of UNIX messages.
X**
X**	Parameters:
X**		fm -- the from line.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		extracts what information it can from the header,
X**		such as the date.
X*/
X
X# ifndef NOTUNIX
X
Xchar	*DowList[] =
X{
X	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
X};
X
Xchar	*MonthList[] =
X{
X	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
X	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X	NULL
X};
X
Xeatfrom(fm)
X	char *fm;
X{
X	register char *p;
X	register char **dt;
X
X# ifdef DEBUG
X	if (tTd(30, 2))
X		printf("eatfrom(%s)\n", fm);
X# endif DEBUG
X
X	/* find the date part */
X	p = fm;
X	while (*p != '\0')
X	{
X		/* skip a word */
X		while (*p != '\0' && *p != ' ')
X			p++;
X		while (*p == ' ')
X			p++;
X		if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':')
X			continue;
X
X		/* we have a possible date */
X		for (dt = DowList; *dt != NULL; dt++)
X			if (strncmp(*dt, p, 3) == 0)
X				break;
X		if (*dt == NULL)
X			continue;
X
X		for (dt = MonthList; *dt != NULL; dt++)
X			if (strncmp(*dt, &p[4], 3) == 0)
X				break;
X		if (*dt != NULL)
X			break;
X	}
X
X	if (*p != NULL)
X	{
X		char *q;
X		extern char *arpadate();
X
X		/* we have found a date */
X		q = xalloc(25);
X		(void) strncpy(q, p, 25);
X		q[24] = '\0';
X		define('d', q, CurEnv);
X		q = arpadate(q);
X		define('a', newstr(q), CurEnv);
X	}
X}
X
X# endif NOTUNIX
END_OF_FILE
if test 6760 -ne `wc -c <'src/collect.c'`; then
    echo shar: \"'src/collect.c'\" unpacked with wrong size!
fi
# end of 'src/collect.c'
fi
if test -f 'src/domain.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/domain.c'\"
else
echo shar: Extracting \"'src/domain.c'\" \(6431 characters\)
sed "s/^X//" >'src/domain.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1986  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1986 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#include "sendmail.h"
X
X#ifndef MXDOMAIN
X#ifndef lint
Xstatic char	SccsId[] = "@(#)domain.c	5.7 (Berkeley) 12/12/87 (no MXDOMAIN)";
X#endif not lint
X#else MXDOMAIN
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)domain.c	5.7 (Berkeley) 12/12/87";
X#endif not lint
X
X# include <sys/param.h>
X# include <arpa/nameser.h>
X# include <resolv.h>
X# include <netdb.h>
X
Xtypedef union {
X	HEADER qb1;
X	char qb2[PACKETSZ];
X} querybuf;
X
Xstatic char	hostbuf[BUFSIZ];
Xint		h_errno;
Xextern u_short	getshort();
X
Xgetmxrr(host, mxhosts, maxmx, localhost)
X	char *host, **mxhosts;
X	int maxmx;
X	char *localhost;
X{
X
X	HEADER *hp;
X	char *eom, *bp, *cp;
X	querybuf buf, answer;
X	int n, n1, i, j, nmx, ancount, qdcount, buflen;
X	int seenlocal;
X	u_short prefer[BUFSIZ];
X	u_short pref, localpref, type, class;
X
X	n = res_mkquery(QUERY, host, C_IN, T_MX, (char *)NULL, 0, NULL,
X		(char *)&buf, sizeof(buf));
X	if (n < 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("res_mkquery failed\n");
X#endif
X		h_errno = NO_RECOVERY;
X		return(-2);
X	}
X	n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer));
X	if (n < 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("res_send failed\n");
X#endif
X		h_errno = TRY_AGAIN;
X		return (-1);
X	}
X	eom = (char *)&answer + n;
X	/*
X	 * find first satisfactory answer
X	 */
X	hp = (HEADER *) &answer;
X	ancount = ntohs(hp->ancount);
X	qdcount = ntohs(hp->qdcount);
X	if (hp->rcode != NOERROR || ancount == 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("rcode = %d, ancount=%d\n", hp->rcode, ancount);
X#endif
X		switch (hp->rcode) {
X			case NXDOMAIN:
X				/* Check if it's an authoritive answer */
X				if (hp->aa) {
X					h_errno = HOST_NOT_FOUND;
X					return(-3);
X				} else {
X					h_errno = TRY_AGAIN;
X					return(-1);
X				}
X			case SERVFAIL:
X				h_errno = TRY_AGAIN;
X				return(-1);
X#ifdef OLDJEEVES
X			/*
X			 * Jeeves (TOPS-20 server) still does not
X			 * support MX records.  For the time being,
X			 * we must accept FORMERRs as the same as
X			 * NOERROR.
X			 */
X			case FORMERR:
X#endif OLDJEEVES
X			case NOERROR:
X				(void) strcpy(hostbuf, host);
X				mxhosts[0] = hostbuf;
X				return(1);
X#ifndef OLDJEEVES
X			case FORMERR:
X#endif OLDJEEVES
X			case NOTIMP:
X			case REFUSED:
X				h_errno = NO_RECOVERY;
X				return(-2);
X		}
X		return (-1);
X	}
X	bp = hostbuf;
X	nmx = 0;
X	seenlocal = 0;
X	buflen = sizeof(hostbuf);
X	cp = (char *)&answer + sizeof(HEADER);
X	if (qdcount) {
X		cp += dn_skipname(cp, eom) + QFIXEDSZ;
X		while (--qdcount > 0)
X			cp += dn_skipname(cp, eom) + QFIXEDSZ;
X	}
X	while (--ancount >= 0 && cp < eom && nmx < maxmx) {
X		if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0)
X			break;
X		cp += n;
X		type = getshort(cp);
X 		cp += sizeof(u_short);
X		/*
X		class = getshort(cp);
X		*/
X 		cp += sizeof(u_short) + sizeof(u_long);
X		n = getshort(cp);
X		cp += sizeof(u_short);
X		if (type != T_MX)  {
X#ifdef DEBUG
X			if (tTd(8, 1) || _res.options & RES_DEBUG)
X				printf("unexpected answer type %d, size %d\n",
X					type, n);
X#endif
X			cp += n;
X			continue;
X		}
X		pref = getshort(cp);
X		cp += sizeof(u_short);
X		if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0)
X			break;
X		cp += n;
X		if (sameword(bp, localhost))
X		{
X			seenlocal = 1;
X			localpref = pref;
X			continue;
X		}
X		prefer[nmx] = pref;
X		mxhosts[nmx++] = bp;
X		n1 = strlen(bp)+1;
X		bp += n1;
X		buflen -= n1;
X	}
X	if (nmx == 0) {
X		(void) strcpy(hostbuf, host);
X		mxhosts[0] = hostbuf;
X		return(1);
X	}
X	/* sort the records */
X	for (i = 0; i < nmx; i++) {
X		for (j = i + 1; j < nmx; j++) {
X			if (prefer[i] > prefer[j]) {
X				int temp;
X				char *temp1;
X
X				temp = prefer[i];
X				prefer[i] = prefer[j];
X				prefer[j] = temp;
X				temp1 = mxhosts[i];
X				mxhosts[i] = mxhosts[j];
X				mxhosts[j] = temp1;
X			}
X		}
X		if (seenlocal && (prefer[i] >= localpref))
X		{
X			nmx = i;
X			/*
X			 * We are the first MX, might as well try delivering
X			 * since nobody is supposed to have more info.
X			 */
X			if (nmx == 0)
X			{
X				(void) strcpy(hostbuf, host);
X				mxhosts[0] = hostbuf;
X				return(1);
X			}
X			break;
X		}
X	}
X	return(nmx);
X}
X
X
Xgetcanonname(host, hbsize)
X	char *host;
X	int hbsize;
X{
X
X	HEADER *hp;
X	char *eom, *cp;
X	querybuf buf, answer;
X	int n, ancount, qdcount;
X	u_short type;
X	char nbuf[BUFSIZ];
X	int first;
X
X	n = res_mkquery(QUERY, host, C_IN, T_ANY, (char *)NULL, 0, NULL,
X		(char *)&buf, sizeof(buf));
X	if (n < 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("res_mkquery failed\n");
X#endif
X		h_errno = NO_RECOVERY;
X		return;
X	}
X	n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer));
X	if (n < 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("res_send failed\n");
X#endif
X		h_errno = TRY_AGAIN;
X		return;
X	}
X	eom = (char *)&answer + n;
X	/*
X	 * find first satisfactory answer
X	 */
X	hp = (HEADER *) &answer;
X	ancount = ntohs(hp->ancount);
X	qdcount = ntohs(hp->qdcount);
X	/*
X	 * We don't care about errors here, only if we got an answer
X	 */
X	if (ancount == 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("rcode = %d, ancount=%d\n", hp->rcode, ancount);
X#endif
X		return;
X	}
X	cp = (char *)&answer + sizeof(HEADER);
X	if (qdcount) {
X		cp += dn_skipname(cp, eom) + QFIXEDSZ;
X		while (--qdcount > 0)
X			cp += dn_skipname(cp, eom) + QFIXEDSZ;
X	}
X	first = 1;
X	while (--ancount >= 0 && cp < eom) {
X		if ((n = dn_expand((char *)&answer, eom, cp, nbuf,
X		    sizeof(nbuf))) < 0)
X			break;
X		if (first) {
X			(void)strncpy(host, nbuf, hbsize);
X			host[hbsize - 1] = '\0';
X			first = 0;
X		}
X		cp += n;
X		type = getshort(cp);
X 		cp += sizeof(u_short);
X 		cp += sizeof(u_short) + sizeof(u_long);
X		n = getshort(cp);
X		cp += sizeof(u_short);
X		if (type == T_CNAME)  {
X			/*
X			 * Assume that only one cname will be found.  More
X			 * than one is undefined.
X			 */
X			if ((n = dn_expand((char *)&answer, eom, cp, nbuf,
X			    sizeof(nbuf))) < 0)
X				break;
X			(void)strncpy(host, nbuf, hbsize);
X			host[hbsize - 1] = '\0';
X			getcanonname(host, hbsize);
X			break;
X		}
X		cp += n;
X	}
X	return;
X}
X
Xu_short
Xgetshort(msgp)
X	char *msgp;
X{
X	register u_char *p = (u_char *) msgp;
X	register int u;
X
X	u = *p++ << 8;
X	return ((u_short)(u | *p));
X}
X
X#endif MXDOMAIN
END_OF_FILE
if test 6431 -ne `wc -c <'src/domain.c'`; then
    echo shar: \"'src/domain.c'\" unpacked with wrong size!
fi
# end of 'src/domain.c'
fi
if test -f 'src/err.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/err.c'\"
else
echo shar: Extracting \"'src/err.c'\" \(7229 characters\)
sed "s/^X//" >'src/err.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)err.c	5.7 (Berkeley) 11/22/85";
X#endif not lint
X
X# include "sendmail.h"
X# include <errno.h>
X
X#ifdef	SMTP
X# include <netdb.h>
X#endif
X
X/*
X**  SYSERR -- Print error message.
X**
X**	Prints an error message via printf to the diagnostic
X**	output.  If LOG is defined, it logs it also.
X**
X**	Parameters:
X**		f -- the format string
X**		a, b, c, d, e -- parameters
X**
X**	Returns:
X**		none
X**		Through TopFrame if QuickAbort is set.
X**
X**	Side Effects:
X**		increments Errors.
X**		sets ExitStat.
X*/
X
X# ifdef lint
Xint	sys_nerr;
Xchar	*sys_errlist[];
X# endif lint
Xchar	MsgBuf[BUFSIZ*2];	/* text of most recent message */
X
X/*VARARGS1*/
Xsyserr(fmt, a, b, c, d, e)
X	char *fmt;
X{
X	register char *p;
X	int olderrno = errno;
X	extern char Arpa_PSyserr[];
X	extern char Arpa_TSyserr[];
X
X	/* format and output the error message */
X	if (olderrno == 0)
X		p = Arpa_PSyserr;
X	else
X		p = Arpa_TSyserr;
X	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, a, b, c, d, e);
X	puterrmsg(MsgBuf);
X
X	/* determine exit status if not already set */
X	if (ExitStat == EX_OK)
X	{
X		if (olderrno == 0)
X			ExitStat = EX_SOFTWARE;
X		else
X			ExitStat = EX_OSERR;
X	}
X
X# ifdef LOG
X	if (LogLevel > 0)
X		syslog(LOG_CRIT, "%s: SYSERR: %s",
X			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
X			&MsgBuf[4]);
X# endif LOG
X	errno = 0;
X	if (QuickAbort)
X		longjmp(TopFrame, 2);
X}
X/*
X**  USRERR -- Signal user error.
X**
X**	This is much like syserr except it is for user errors.
X**
X**	Parameters:
X**		fmt, a, b, c, d -- printf strings
X**
X**	Returns:
X**		none
X**		Through TopFrame if QuickAbort is set.
X**
X**	Side Effects:
X**		increments Errors.
X*/
X
X/*VARARGS1*/
Xusrerr(fmt, a, b, c, d, e)
X	char *fmt;
X{
X	extern char SuprErrs;
X	extern char Arpa_Usrerr[];
X	extern int errno;
X
X	if (SuprErrs)
X		return;
X
X	fmtmsg(MsgBuf, CurEnv->e_to, Arpa_Usrerr, errno, fmt, a, b, c, d, e);
X	puterrmsg(MsgBuf);
X
X	if (QuickAbort)
X		longjmp(TopFrame, 1);
X}
X/*
X**  MESSAGE -- print message (not necessarily an error)
X**
X**	Parameters:
X**		num -- the default ARPANET error number (in ascii)
X**		msg -- the message (printf fmt) -- if it begins
X**			with a digit, this number overrides num.
X**		a, b, c, d, e -- printf arguments
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		none.
X*/
X
X/*VARARGS2*/
Xmessage(num, msg, a, b, c, d, e)
X	register char *num;
X	register char *msg;
X{
X	errno = 0;
X	fmtmsg(MsgBuf, CurEnv->e_to, num, 0, msg, a, b, c, d, e);
X	putmsg(MsgBuf, FALSE);
X}
X/*
X**  NMESSAGE -- print message (not necessarily an error)
X**
X**	Just like "message" except it never puts the to... tag on.
X**
X**	Parameters:
X**		num -- the default ARPANET error number (in ascii)
X**		msg -- the message (printf fmt) -- if it begins
X**			with three digits, this number overrides num.
X**		a, b, c, d, e -- printf arguments
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		none.
X*/
X
X/*VARARGS2*/
Xnmessage(num, msg, a, b, c, d, e)
X	register char *num;
X	register char *msg;
X{
X	errno = 0;
X	fmtmsg(MsgBuf, (char *) NULL, num, 0, msg, a, b, c, d, e);
X	putmsg(MsgBuf, FALSE);
X}
X/*
X**  PUTMSG -- output error message to transcript and channel
X**
X**	Parameters:
X**		msg -- message to output (in SMTP format).
X**		holdmsg -- if TRUE, don't output a copy of the message to
X**			our output channel.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Outputs msg to the transcript.
X**		If appropriate, outputs it to the channel.
X**		Deletes SMTP reply code number as appropriate.
X*/
X
Xputmsg(msg, holdmsg)
X	char *msg;
X	bool holdmsg;
X{
X	/* output to transcript if serious */
X	if (CurEnv->e_xfp != NULL && (msg[0] == '4' || msg[0] == '5'))
X		fprintf(CurEnv->e_xfp, "%s\n", msg);
X
X	/* output to channel if appropriate */
X	if (!holdmsg && (Verbose || msg[0] != '0'))
X	{
X		(void) fflush(stdout);
X		if (OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
X			fprintf(OutChannel, "%s\r\n", msg);
X		else
X			fprintf(OutChannel, "%s\n", &msg[4]);
X		(void) fflush(OutChannel);
X	}
X}
X/*
X**  PUTERRMSG -- like putmsg, but does special processing for error messages
X**
X**	Parameters:
X**		msg -- the message to output.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Sets the fatal error bit in the envelope as appropriate.
X*/
X
Xputerrmsg(msg)
X	char *msg;
X{
X	/* output the message as usual */
X	putmsg(msg, HoldErrs);
X
X	/* signal the error */
X	Errors++;
X	if (msg[0] == '5')
X		CurEnv->e_flags |= EF_FATALERRS;
X}
X/*
X**  FMTMSG -- format a message into buffer.
X**
X**	Parameters:
X**		eb -- error buffer to get result.
X**		to -- the recipient tag for this message.
X**		num -- arpanet error number.
X**		en -- the error number to display.
X**		fmt -- format of string.
X**		a, b, c, d, e -- arguments.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
X/*VARARGS5*/
Xstatic
Xfmtmsg(eb, to, num, eno, fmt, a, b, c, d, e)
X	register char *eb;
X	char *to;
X	char *num;
X	int eno;
X	char *fmt;
X{
X	char del;
X
X	/* output the reply code */
X	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
X	{
X		num = fmt;
X		fmt += 4;
X	}
X	if (num[3] == '-')
X		del = '-';
X	else
X		del = ' ';
X	(void) sprintf(eb, "%3.3s%c", num, del);
X	eb += 4;
X
X	/* output the file name and line number */
X	if (FileName != NULL)
X	{
X		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
X		eb += strlen(eb);
X	}
X
X	/* output the "to" person */
X	if (to != NULL && to[0] != '\0')
X	{
X		(void) sprintf(eb, "%s... ", to);
X		while (*eb != '\0')
X			*eb++ &= 0177;
X	}
X
X	/* output the message */
X	(void) sprintf(eb, fmt, a, b, c, d, e);
X	while (*eb != '\0')
X		*eb++ &= 0177;
X
X	/* output the error code, if any */
X	if (eno != 0)
X	{
X		extern char *errstring();
X
X		(void) sprintf(eb, ": %s", errstring(eno));
X		eb += strlen(eb);
X	}
X}
X/*
X**  ERRSTRING -- return string description of error code
X**
X**	Parameters:
X**		errno -- the error number to translate
X**
X**	Returns:
X**		A string description of errno.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xerrstring(errno)
X	int errno;
X{
X	extern char *sys_errlist[];
X	extern int sys_nerr;
X	static char buf[100];
X# ifdef SMTP
X	extern char *SmtpPhase;
X# endif SMTP
X
X# ifdef DAEMON
X# ifdef VMUNIX
X	/*
X	**  Handle special network error codes.
X	**
X	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
X	*/
X
X	switch (errno)
X	{
X	  case ETIMEDOUT:
X	  case ECONNRESET:
X		(void) strcpy(buf, sys_errlist[errno]);
X		if (SmtpPhase != NULL)
X		{
X			(void) strcat(buf, " during ");
X			(void) strcat(buf, SmtpPhase);
X		}
X		if (CurHostName != NULL)
X		{
X			(void) strcat(buf, " with ");
X			(void) strcat(buf, CurHostName);
X		}
X		return (buf);
X
X	  case EHOSTDOWN:
X		if (CurHostName == NULL)
X			break;
X		(void) sprintf(buf, "Host %s is down", CurHostName);
X		return (buf);
X
X	  case ECONNREFUSED:
X		if (CurHostName == NULL)
X			break;
X		(void) sprintf(buf, "Connection refused by %s", CurHostName);
X		return (buf);
X
X	  case (TRY_AGAIN+MAX_ERRNO):
X		(void) sprintf(buf, "Host Name Lookup Failure");
X		return (buf);
X	}
X# endif VMUNIX
X# endif DAEMON
X
X	if (errno > 0 && errno < sys_nerr)
X		return (sys_errlist[errno]);
X
X	(void) sprintf(buf, "Error %d", errno);
X	return (buf);
X}
END_OF_FILE
if test 7229 -ne `wc -c <'src/err.c'`; then
    echo shar: \"'src/err.c'\" unpacked with wrong size!
fi
# end of 'src/err.c'
fi
if test -f 'src/macro.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/macro.c'\"
else
echo shar: Extracting \"'src/macro.c'\" \(4832 characters\)
sed "s/^X//" >'src/macro.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)macro.c	5.3 (Berkeley) 9/19/85";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X**  EXPAND -- macro expand a string using $x escapes.
X**
X**	Parameters:
X**		s -- the string to expand.
X**		buf -- the place to put the expansion.
X**		buflim -- the buffer limit, i.e., the address
X**			of the last usable position in buf.
X**		e -- envelope in which to work.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xexpand(s, buf, buflim, e)
X	register char *s;
X	register char *buf;
X	char *buflim;
X	register ENVELOPE *e;
X{
X	register char *xp;
X	register char *q;
X	bool skipping;		/* set if conditionally skipping output */
X	bool recurse = FALSE;	/* set if recursion required */
X	int i;
X	char xbuf[BUFSIZ];
X	extern char *macvalue();
X
X# ifdef DEBUG
X	if (tTd(35, 24))
X	{
X		printf("expand(");
X		xputs(s);
X		printf(")\n");
X	}
X# endif DEBUG
X
X	skipping = FALSE;
X	if (s == NULL)
X		s = "";
X	for (xp = xbuf; *s != '\0'; s++)
X	{
X		char c;
X
X		/*
X		**  Check for non-ordinary (special?) character.
X		**	'q' will be the interpolated quantity.
X		*/
X
X		q = NULL;
X		c = *s;
X		switch (c)
X		{
X		  case CONDIF:		/* see if var set */
X			c = *++s;
X			skipping = macvalue(c, e) == NULL;
X			continue;
X
X		  case CONDELSE:	/* change state of skipping */
X			skipping = !skipping;
X			continue;
X
X		  case CONDFI:		/* stop skipping */
X			skipping = FALSE;
X			continue;
X
X		  case '\001':		/* macro interpolation */
X			c = *++s;
X			q = macvalue(c & 0177, e);
X			if (q == NULL)
X				continue;
X			break;
X		}
X
X		/*
X		**  Interpolate q or output one character
X		*/
X
X		if (skipping || xp >= &xbuf[sizeof xbuf])
X			continue;
X		if (q == NULL)
X			*xp++ = c;
X		else
X		{
X			/* copy to end of q or max space remaining in buf */
X			while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1])
X			{
X				if (iscntrl(c) && !isspace(c))
X					recurse = TRUE;
X				*xp++ = c;
X			}
X		}
X	}
X	*xp = '\0';
X
X# ifdef DEBUG
X	if (tTd(35, 24))
X	{
X		printf("expand ==> ");
X		xputs(xbuf);
X		printf("\n");
X	}
X# endif DEBUG
X
X	/* recurse as appropriate */
X	if (recurse)
X	{
X		expand(xbuf, buf, buflim, e);
X		return;
X	}
X
X	/* copy results out */
X	i = buflim - buf - 1;
X	if (i > xp - xbuf)
X		i = xp - xbuf;
X	bcopy(xbuf, buf, i);
X	buf[i] = '\0';
X}
X/*
X**  DEFINE -- define a macro.
X**
X**	this would be better done using a #define macro.
X**
X**	Parameters:
X**		n -- the macro name.
X**		v -- the macro value.
X**		e -- the envelope to store the definition in.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		e->e_macro[n] is defined.
X**
X**	Notes:
X**		There is one macro for each ASCII character,
X**		although they are not all used.  The currently
X**		defined macros are:
X**
X**		$a   date in ARPANET format (preferring the Date: line
X**		     of the message)
X**		$b   the current date (as opposed to the date as found
X**		     the message) in ARPANET format
X**		$c   hop count
X**		$d   (current) date in UNIX (ctime) format
X**		$e   the SMTP entry message+
X**		$f   raw from address
X**		$g   translated from address
X**		$h   to host
X**		$i   queue id
X**		$j   official SMTP hostname, used in messages+
X**		$l   UNIX-style from line+
X**		$n   name of sendmail ("MAILER-DAEMON" on local
X**		     net typically)+
X**		$o   delimiters ("operators") for address tokens+
X**		$p   my process id in decimal
X**		$q   the string that becomes an address -- this is
X**		     normally used to combine $g & $x.
X**		$r   protocol used to talk to sender
X**		$s   sender's host name
X**		$t   the current time in seconds since 1/1/1970
X**		$u   to user
X**		$v   version number of sendmail
X**		$w   our host name (if it can be determined)
X**		$x   signature (full name) of from person
X**		$y   the tty id of our terminal
X**		$z   home directory of to person
X**
X**		Macros marked with + must be defined in the
X**		configuration file and are used internally, but
X**		are not set.
X**
X**		There are also some macros that can be used
X**		arbitrarily to make the configuration file
X**		cleaner.  In general all upper-case letters
X**		are available.
X*/
X
Xdefine(n, v, e)
X	char n;
X	char *v;
X	register ENVELOPE *e;
X{
X# ifdef DEBUG
X	if (tTd(35, 9))
X	{
X		printf("define(%c as ", n);
X		xputs(v);
X		printf(")\n");
X	}
X# endif DEBUG
X	e->e_macro[n & 0177] = v;
X}
X/*
X**  MACVALUE -- return uninterpreted value of a macro.
X**
X**	Parameters:
X**		n -- the name of the macro.
X**
X**	Returns:
X**		The value of n.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xmacvalue(n, e)
X	char n;
X	register ENVELOPE *e;
X{
X	n &= 0177;
X	while (e != NULL)
X	{
X		register char *p = e->e_macro[n];
X
X		if (p != NULL)
X			return (p);
X		e = e->e_parent;
X	}
X	return (NULL);
X}
END_OF_FILE
if test 4832 -ne `wc -c <'src/macro.c'`; then
    echo shar: \"'src/macro.c'\" unpacked with wrong size!
fi
# end of 'src/macro.c'
fi
if test -f 'src/usersmtp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/usersmtp.c'\"
else
echo shar: Extracting \"'src/usersmtp.c'\" \(10561 characters\)
sed "s/^X//" >'src/usersmtp.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X
X# include <sysexits.h>
X# include <errno.h>
X# include "sendmail.h"
X
X# ifndef SMTP
X# ifndef lint
Xstatic char	SccsId[] = "@(#)usersmtp.c	5.8 (Berkeley) 12/17/86	(no SMTP)";
X# endif not lint
X# else SMTP
X
X# ifndef lint
Xstatic char	SccsId[] = "@(#)usersmtp.c	5.8 (Berkeley) 12/17/86";
X# endif not lint
X
X
X
X/*
X**  USERSMTP -- run SMTP protocol from the user end.
X**
X**	This protocol is described in RFC821.
X*/
X
X#define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
X#define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
X#define SMTPCLOSING	421			/* "Service Shutting Down" */
X
Xchar	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
Xchar	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
Xchar	SmtpError[MAXLINE] = "";	/* save failure error messages */
XFILE	*SmtpOut;			/* output file */
XFILE	*SmtpIn;			/* input file */
Xint	SmtpPid;			/* pid of mailer */
X
X/* following represents the state of the SMTP connection */
Xint	SmtpState;			/* connection state, see below */
X
X#define SMTP_CLOSED	0		/* connection is closed */
X#define SMTP_OPEN	1		/* connection is open for business */
X#define SMTP_SSD	2		/* service shutting down */
X/*
X**  SMTPINIT -- initialize SMTP.
X**
X**	Opens the connection and sends the initial protocol.
X**
X**	Parameters:
X**		m -- mailer to create connection to.
X**		pvp -- pointer to parameter vector to pass to
X**			the mailer.
X**
X**	Returns:
X**		appropriate exit status -- EX_OK on success.
X**		If not EX_OK, it should close the connection.
X**
X**	Side Effects:
X**		creates connection and sends initial protocol.
X*/
X
Xjmp_buf	CtxGreeting;
X
Xsmtpinit(m, pvp)
X	struct mailer *m;
X	char **pvp;
X{
X	register int r;
X	EVENT *gte;
X	char buf[MAXNAME];
X	extern greettimeout();
X
X	/*
X	**  Open the connection to the mailer.
X	*/
X
X#ifdef DEBUG
X	if (SmtpState == SMTP_OPEN)
X		syserr("smtpinit: already open");
X#endif DEBUG
X
X	SmtpIn = SmtpOut = NULL;
X	SmtpState = SMTP_CLOSED;
X	SmtpError[0] = '\0';
X	SmtpPhase = "user open";
X	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
X	if (SmtpPid < 0)
X	{
X# ifdef DEBUG
X		if (tTd(18, 1))
X			printf("smtpinit: cannot open %s: stat %d errno %d\n",
X			   pvp[0], ExitStat, errno);
X# endif DEBUG
X		if (CurEnv->e_xfp != NULL)
X		{
X			register char *p;
X			extern char *errstring();
X			extern char *statstring();
X
X			if (errno == 0)
X			{
X				p = statstring(ExitStat);
X				fprintf(CurEnv->e_xfp,
X					"%.3s %s.%s... %s\n",
X					p, pvp[1], m->m_name, p);
X			}
X			else
X			{
X				fprintf(CurEnv->e_xfp,
X					"421 %s.%s... Deferred: %s\n",
X					pvp[1], m->m_name, errstring(errno));
X			}
X		}
X		return (ExitStat);
X	}
X	SmtpState = SMTP_OPEN;
X
X	/*
X	**  Get the greeting message.
X	**	This should appear spontaneously.  Give it five minutes to
X	**	happen.
X	*/
X
X	if (setjmp(CtxGreeting) != 0)
X		goto tempfail;
X	gte = setevent((time_t) 300, greettimeout, 0);
X	SmtpPhase = "greeting wait";
X	r = reply(m);
X	clrevent(gte);
X	if (r < 0 || REPLYTYPE(r) != 2)
X		goto tempfail;
X
X	/*
X	**  Send the HELO command.
X	**	My mother taught me to always introduce myself.
X	*/
X
X	smtpmessage("HELO %s", m, MyHostName);
X	SmtpPhase = "HELO wait";
X	r = reply(m);
X	if (r < 0)
X		goto tempfail;
X	else if (REPLYTYPE(r) == 5)
X		goto unavailable;
X	else if (REPLYTYPE(r) != 2)
X		goto tempfail;
X
X	/*
X	**  If this is expected to be another sendmail, send some internal
X	**  commands.
X	*/
X
X	if (bitnset(M_INTERNAL, m->m_flags))
X	{
X		/* tell it to be verbose */
X		smtpmessage("VERB", m);
X		r = reply(m);
X		if (r < 0)
X			goto tempfail;
X
X		/* tell it we will be sending one transaction only */
X		smtpmessage("ONEX", m);
X		r = reply(m);
X		if (r < 0)
X			goto tempfail;
X	}
X
X	/*
X	**  Send the MAIL command.
X	**	Designates the sender.
X	*/
X
X	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
X	if (CurEnv->e_from.q_mailer == LocalMailer ||
X	    !bitnset(M_FROMPATH, m->m_flags))
X	{
X		smtpmessage("MAIL From:<%s>", m, buf);
X	}
X	else
X	{
X		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
X			buf[0] == '@' ? ',' : ':', buf);
X	}
X	SmtpPhase = "MAIL wait";
X	r = reply(m);
X	if (r < 0 || REPLYTYPE(r) == 4)
X		goto tempfail;
X	else if (r == 250)
X		return (EX_OK);
X	else if (r == 552)
X		goto unavailable;
X
X	/* protocol error -- close up */
X	smtpquit(m);
X	return (EX_PROTOCOL);
X
X	/* signal a temporary failure */
X  tempfail:
X	smtpquit(m);
X	return (EX_TEMPFAIL);
X
X	/* signal service unavailable */
X  unavailable:
X	smtpquit(m);
X	return (EX_UNAVAILABLE);
X}
X
X
Xstatic
Xgreettimeout()
X{
X	/* timeout reading the greeting message */
X	longjmp(CtxGreeting, 1);
X}
X/*
X**  SMTPRCPT -- designate recipient.
X**
X**	Parameters:
X**		to -- address of recipient.
X**		m -- the mailer we are sending to.
X**
X**	Returns:
X**		exit status corresponding to recipient status.
X**
X**	Side Effects:
X**		Sends the mail via SMTP.
X*/
X
Xsmtprcpt(to, m)
X	ADDRESS *to;
X	register MAILER *m;
X{
X	register int r;
X	extern char *remotename();
X
X	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
X
X	SmtpPhase = "RCPT wait";
X	r = reply(m);
X	if (r < 0 || REPLYTYPE(r) == 4)
X		return (EX_TEMPFAIL);
X	else if (REPLYTYPE(r) == 2)
X		return (EX_OK);
X	else if (r == 550 || r == 551 || r == 553)
X		return (EX_NOUSER);
X	else if (r == 552 || r == 554)
X		return (EX_UNAVAILABLE);
X	return (EX_PROTOCOL);
X}
X/*
X**  SMTPDATA -- send the data and clean up the transaction.
X**
X**	Parameters:
X**		m -- mailer being sent to.
X**		e -- the envelope for this message.
X**
X**	Returns:
X**		exit status corresponding to DATA command.
X**
X**	Side Effects:
X**		none.
X*/
X
Xsmtpdata(m, e)
X	struct mailer *m;
X	register ENVELOPE *e;
X{
X	register int r;
X
X	/*
X	**  Send the data.
X	**	First send the command and check that it is ok.
X	**	Then send the data.
X	**	Follow it up with a dot to terminate.
X	**	Finally get the results of the transaction.
X	*/
X
X	/* send the command and check ok to proceed */
X	smtpmessage("DATA", m);
X	SmtpPhase = "DATA wait";
X	r = reply(m);
X	if (r < 0 || REPLYTYPE(r) == 4)
X		return (EX_TEMPFAIL);
X	else if (r == 554)
X		return (EX_UNAVAILABLE);
X	else if (r != 354)
X		return (EX_PROTOCOL);
X
X	/* now output the actual message */
X	(*e->e_puthdr)(SmtpOut, m, CurEnv);
X	putline("\n", SmtpOut, m);
X	(*e->e_putbody)(SmtpOut, m, CurEnv);
X
X	/* terminate the message */
X	fprintf(SmtpOut, ".%s", m->m_eol);
X	if (Verbose && !HoldErrs)
X		nmessage(Arpa_Info, ">>> .");
X
X	/* check for the results of the transaction */
X	SmtpPhase = "result wait";
X	r = reply(m);
X	if (r < 0 || REPLYTYPE(r) == 4)
X		return (EX_TEMPFAIL);
X	else if (r == 250)
X		return (EX_OK);
X	else if (r == 552 || r == 554)
X		return (EX_UNAVAILABLE);
X	return (EX_PROTOCOL);
X}
X/*
X**  SMTPQUIT -- close the SMTP connection.
X**
X**	Parameters:
X**		m -- a pointer to the mailer.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		sends the final protocol and closes the connection.
X*/
X
Xsmtpquit(m)
X	register MAILER *m;
X{
X	int i;
X
X	/* if the connection is already closed, don't bother */
X	if (SmtpIn == NULL)
X		return;
X
X	/* send the quit message if not a forced quit */
X	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
X	{
X		smtpmessage("QUIT", m);
X		(void) reply(m);
X		if (SmtpState == SMTP_CLOSED)
X			return;
X	}
X
X	/* now actually close the connection */
X	(void) fclose(SmtpIn);
X	(void) fclose(SmtpOut);
X	SmtpIn = SmtpOut = NULL;
X	SmtpState = SMTP_CLOSED;
X
X	/* and pick up the zombie */
X	i = endmailer(SmtpPid, m->m_argv[0]);
X	if (i != EX_OK)
X		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
X}
X/*
X**  REPLY -- read arpanet reply
X**
X**	Parameters:
X**		m -- the mailer we are reading the reply from.
X**
X**	Returns:
X**		reply code it reads.
X**
X**	Side Effects:
X**		flushes the mail file.
X*/
X
Xreply(m)
X	MAILER *m;
X{
X	(void) fflush(SmtpOut);
X
X	if (tTd(18, 1))
X		printf("reply\n");
X
X	/*
X	**  Read the input line, being careful not to hang.
X	*/
X
X	for (;;)
X	{
X		register int r;
X		register char *p;
X
X		/* actually do the read */
X		if (CurEnv->e_xfp != NULL)
X			(void) fflush(CurEnv->e_xfp);	/* for debugging */
X
X		/* if we are in the process of closing just give the code */
X		if (SmtpState == SMTP_CLOSED)
X			return (SMTPCLOSING);
X
X		/* get the line from the other side */
X		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
X		if (p == NULL)
X		{
X			extern char MsgBuf[];		/* err.c */
X			extern char Arpa_TSyserr[];	/* conf.c */
X
X			/* if the remote end closed early, fake an error */
X			if (errno == 0)
X# ifdef ECONNRESET
X				errno = ECONNRESET;
X# else ECONNRESET
X				errno = EPIPE;
X# endif ECONNRESET
X
X			message(Arpa_TSyserr, "reply: read error");
X# ifdef DEBUG
X			/* if debugging, pause so we can see state */
X			if (tTd(18, 100))
X				pause();
X# endif DEBUG
X# ifdef LOG
X			syslog(LOG_ERR, "%s", &MsgBuf[4]);
X# endif LOG
X			SmtpState = SMTP_CLOSED;
X			smtpquit(m);
X			return (-1);
X		}
X		fixcrlf(SmtpReplyBuffer, TRUE);
X
X		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
X		{
X			/* serious error -- log the previous command */
X			if (SmtpMsgBuffer[0] != '\0')
X				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
X			SmtpMsgBuffer[0] = '\0';
X
X			/* now log the message as from the other side */
X			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
X		}
X
X		/* display the input for verbose mode */
X		if (Verbose && !HoldErrs)
X			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
X
X		/* if continuation is required, we can go on */
X		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
X			continue;
X
X		/* decode the reply code */
X		r = atoi(SmtpReplyBuffer);
X
X		/* extra semantics: 0xx codes are "informational" */
X		if (r < 100)
X			continue;
X
X		/* reply code 421 is "Service Shutting Down" */
X		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
X		{
X			/* send the quit protocol */
X			SmtpState = SMTP_SSD;
X			smtpquit(m);
X		}
X
X		/* save temporary failure messages for posterity */
X		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
X			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
X
X		return (r);
X	}
X}
X/*
X**  SMTPMESSAGE -- send message to server
X**
X**	Parameters:
X**		f -- format
X**		m -- the mailer to control formatting.
X**		a, b, c -- parameters
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		writes message to SmtpOut.
X*/
X
X/*VARARGS1*/
Xsmtpmessage(f, m, a, b, c)
X	char *f;
X	MAILER *m;
X{
X	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
X	if (tTd(18, 1) || (Verbose && !HoldErrs))
X		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
X	if (SmtpOut != NULL)
X		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
X}
X
X# endif SMTP
END_OF_FILE
if test 10561 -ne `wc -c <'src/usersmtp.c'`; then
    echo shar: \"'src/usersmtp.c'\" unpacked with wrong size!
fi
# end of 'src/usersmtp.c'
fi
echo shar: End of archive 2 \(of 8\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave at galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb at rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb



More information about the Unix-pc.sources mailing list