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

David H. Brierley dave at galaxia.Newport.RI.US
Sat Feb 25 12:28:16 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 3 (of 8)."
# Contents:  src/conf.c src/envelope.c src/recipient.c src/savemail.c
# Wrapped by dave at galaxia on Fri Feb 24 20:23:49 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/conf.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/conf.c'\"
else
echo shar: Extracting \"'src/conf.c'\" \(12327 characters\)
sed "s/^X//" >'src/conf.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[] = "@(#)conf.c	5.14 (Berkeley) 1/10/86";
X#endif not lint
X
X# include <pwd.h>
X# include <sys/ioctl.h>
X# ifdef sun
X# include <sys/param.h>
X# endif sun
X# include "sendmail.h"
X
X/*
X**  CONF.C -- Sendmail Configuration Tables.
X**
X**	Defines the configuration of this installation.
X**
X**	Compilation Flags:
X**		V6 -- running on a version 6 system.  This determines
X**			whether to define certain routines between
X**			the two systems.  If you are running a funny
X**			system, e.g., V6 with long tty names, this
X**			should be checked carefully.
X**		VMUNIX -- running on a Berkeley UNIX system.
X**
X**	Configuration Variables:
X**		HdrInfo -- a table describing well-known header fields.
X**			Each entry has the field name and some flags,
X**			which are described in sendmail.h.
X**
X**	Notes:
X**		I have tried to put almost all the reasonable
X**		configuration information into the configuration
X**		file read at runtime.  My intent is that anything
X**		here is a function of the version of UNIX you
X**		are running, or is really static -- for example
X**		the headers are a superset of widely used
X**		protocols.  If you find yourself playing with
X**		this file too much, you may be making a mistake!
X*/
X
X
X
X
X/*
X**  Header info table
X**	Final (null) entry contains the flags used for any other field.
X**
X**	Not all of these are actually handled specially by sendmail
X**	at this time.  They are included as placeholders, to let
X**	you know that "someday" I intend to have sendmail do
X**	something with them.
X*/
X
Xstruct hdrinfo	HdrInfo[] =
X{
X		/* originator fields, most to least significant  */
X	"resent-sender",	H_FROM|H_RESENT,
X	"resent-from",		H_FROM|H_RESENT,
X	"resent-reply-to",	H_FROM|H_RESENT,
X	"sender",		H_FROM,
X	"from",			H_FROM,
X	"reply-to",		H_FROM,
X	"full-name",		H_ACHECK,
X	"return-receipt-to",	H_FROM,
X	"errors-to",		H_FROM,
X		/* destination fields */
X	"to",			H_RCPT,
X	"resent-to",		H_RCPT|H_RESENT,
X	"cc",			H_RCPT,
X	"resent-cc",		H_RCPT|H_RESENT,
X	"bcc",			H_RCPT|H_ACHECK,
X	"resent-bcc",		H_RCPT|H_ACHECK|H_RESENT,
X		/* message identification and control */
X	"message-id",		0,
X	"resent-message-id",	H_RESENT,
X	"message",		H_EOH,
X	"text",			H_EOH,
X		/* date fields */
X	"date",			0,
X	"resent-date",		H_RESENT,
X		/* trace fields */
X	"received",		H_TRACE|H_FORCE,
X	"via",			H_TRACE|H_FORCE,
X	"mail-from",		H_TRACE|H_FORCE,
X
X	NULL,			0,
X};
X
X
X/*
X**  ARPANET error message numbers.
X*/
X
Xchar	Arpa_Info[] =		"050";	/* arbitrary info */
Xchar	Arpa_TSyserr[] =	"451";	/* some (transient) system error */
Xchar	Arpa_PSyserr[] =	"554";	/* some (permanent) system error */
Xchar	Arpa_Usrerr[] =		"554";	/* some (fatal) user error */
X
X
X
X/*
X**  Location of system files/databases/etc.
X*/
X
Xchar	*ConfFile =	"/usr/lib/sendmail.cf";	/* runtime configuration */
Xchar	*FreezeFile =	"/usr/lib/sendmail.fc";	/* frozen version of above */
X
X
X
X/*
X**  Miscellaneous stuff.
X*/
X
Xint	DtableSize =	50;		/* max open files; reset in 4.2bsd */
X/*
X**  SETDEFAULTS -- set default values
X**
X**	Because of the way freezing is done, these must be initialized
X**	using direct code.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Initializes a bunch of global variables to their
X**		default values.
X*/
X
Xsetdefaults()
X{
X	QueueLA = 8;
X	QueueFactor = 10000;
X	RefuseLA = 12;
X	SpaceSub = ' ';
X	WkRecipFact = 1000;
X	WkClassFact = 1800;
X	WkTimeFact = 9000;
X	FileMode = 0644;
X	DefUid = 1;
X	DefGid = 1;
X}
X
X# ifdef V6
X/*
X**  TTYNAME -- return name of terminal.
X**
X**	Parameters:
X**		fd -- file descriptor to check.
X**
X**	Returns:
X**		pointer to full path of tty.
X**		NULL if no tty.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xttyname(fd)
X	int fd;
X{
X	register char tn;
X	static char pathn[] = "/dev/ttyx";
X
X	/* compute the pathname of the controlling tty */
X	if ((tn = ttyn(fd)) == NULL)
X	{
X		errno = 0;
X		return (NULL);
X	}
X	pathn[8] = tn;
X	return (pathn);
X}
X/*
X**  FDOPEN -- Open a stdio file given an open file descriptor.
X**
X**	This is included here because it is standard in v7, but we
X**	need it in v6.
X**
X**	Algorithm:
X**		Open /dev/null to create a descriptor.
X**		Close that descriptor.
X**		Copy the existing fd into the descriptor.
X**
X**	Parameters:
X**		fd -- the open file descriptor.
X**		type -- "r", "w", or whatever.
X**
X**	Returns:
X**		The file descriptor it creates.
X**
X**	Side Effects:
X**		none
X**
X**	Called By:
X**		deliver
X**
X**	Notes:
X**		The mode of fd must match "type".
X*/
X
XFILE *
Xfdopen(fd, type)
X	int fd;
X	char *type;
X{
X	register FILE *f;
X
X	f = fopen("/dev/null", type);
X	(void) close(fileno(f));
X	fileno(f) = fd;
X	return (f);
X}
X/*
X**  INDEX -- Return pointer to character in string
X**
X**	For V7 compatibility.
X**
X**	Parameters:
X**		s -- a string to scan.
X**		c -- a character to look for.
X**
X**	Returns:
X**		If c is in s, returns the address of the first
X**			instance of c in s.
X**		NULL if c is not in s.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xindex(s, c)
X	register char *s;
X	register char c;
X{
X	while (*s != '\0')
X	{
X		if (*s++ == c)
X			return (--s);
X	}
X	return (NULL);
X}
X/*
X**  UMASK -- fake the umask system call.
X**
X**	Since V6 always acts like the umask is zero, we will just
X**	assume the same thing.
X*/
X
X/*ARGSUSED*/
Xumask(nmask)
X{
X	return (0);
X}
X
X
X/*
X**  GETRUID -- get real user id.
X*/
X
Xgetruid()
X{
X	return (getuid() & 0377);
X}
X
X
X/*
X**  GETRGID -- get real group id.
X*/
X
Xgetrgid()
X{
X	return (getgid() & 0377);
X}
X
X
X/*
X**  GETEUID -- get effective user id.
X*/
X
Xgeteuid()
X{
X	return ((getuid() >> 8) & 0377);
X}
X
X
X/*
X**  GETEGID -- get effective group id.
X*/
X
Xgetegid()
X{
X	return ((getgid() >> 8) & 0377);
X}
X
X# endif V6
X
X# ifndef V6
X
X/*
X**  GETRUID -- get real user id (V7)
X*/
X
Xgetruid()
X{
X	if (OpMode == MD_DAEMON)
X		return (RealUid);
X	else
X		return (getuid());
X}
X
X
X/*
X**  GETRGID -- get real group id (V7).
X*/
X
Xgetrgid()
X{
X	if (OpMode == MD_DAEMON)
X		return (RealGid);
X	else
X		return (getgid());
X}
X
X# endif V6
X/*
X**  USERNAME -- return the user id of the logged in user.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		The login name of the logged in user.
X**
X**	Side Effects:
X**		none.
X**
X**	Notes:
X**		The return value is statically allocated.
X*/
X
Xchar *
Xusername()
X{
X	static char *myname = NULL;
X	extern char *getlogin();
X	register struct passwd *pw;
X	extern struct passwd *getpwuid();
X
X	/* cache the result */
X	if (myname == NULL)
X	{
X		myname = getlogin();
X		if (myname == NULL || myname[0] == '\0')
X		{
X
X			pw = getpwuid(getruid());
X			if (pw != NULL)
X				myname = pw->pw_name;
X		}
X		else
X		{
X
X			pw = getpwnam(myname);
X			if(getuid() != pw->pw_uid)
X			{
X				pw = getpwuid(getuid());
X				if (pw != NULL)
X					myname = pw->pw_name;
X			}
X		}
X		if (myname == NULL || myname[0] == '\0')
X		{
X			syserr("Who are you?");
X			myname = "postmaster";
X		}
X	}
X
X	return (myname);
X}
X/*
X**  TTYPATH -- Get the path of the user's tty
X**
X**	Returns the pathname of the user's tty.  Returns NULL if
X**	the user is not logged in or if s/he has write permission
X**	denied.
X**
X**	Parameters:
X**		none
X**
X**	Returns:
X**		pathname of the user's tty.
X**		NULL if not logged in or write permission denied.
X**
X**	Side Effects:
X**		none.
X**
X**	WARNING:
X**		Return value is in a local buffer.
X**
X**	Called By:
X**		savemail
X*/
X
X# include <sys/stat.h>
X
Xchar *
Xttypath()
X{
X	struct stat stbuf;
X	register char *pathn;
X	extern char *ttyname();
X	extern char *getlogin();
X
X	/* compute the pathname of the controlling tty */
X	if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
X	    (pathn = ttyname(0)) == NULL)
X	{
X		errno = 0;
X		return (NULL);
X	}
X
X	/* see if we have write permission */
X	if (stat(pathn, &stbuf) < 0 || !bitset(02, stbuf.st_mode))
X	{
X		errno = 0;
X		return (NULL);
X	}
X
X	/* see if the user is logged in */
X	if (getlogin() == NULL)
X		return (NULL);
X
X	/* looks good */
X	return (pathn);
X}
X/*
X**  CHECKCOMPAT -- check for From and To person compatible.
X**
X**	This routine can be supplied on a per-installation basis
X**	to determine whether a person is allowed to send a message.
X**	This allows restriction of certain types of internet
X**	forwarding or registration of users.
X**
X**	If the hosts are found to be incompatible, an error
X**	message should be given using "usrerr" and FALSE should
X**	be returned.
X**
X**	'NoReturn' can be set to suppress the return-to-sender
X**	function; this should be done on huge messages.
X**
X**	Parameters:
X**		to -- the person being sent to.
X**
X**	Returns:
X**		TRUE -- ok to send.
X**		FALSE -- not ok.
X**
X**	Side Effects:
X**		none (unless you include the usrerr stuff)
X*/
X
Xbool
Xcheckcompat(to)
X	register ADDRESS *to;
X{
X# ifdef lint
X	if (to == NULL)
X		to++;
X# endif lint
X# ifdef EXAMPLE_CODE
X	/* this code is intended as an example only */
X	register STAB *s;
X
X	s = stab("arpa", ST_MAILER, ST_FIND);
X	if (s != NULL && CurEnv->e_from.q_mailer != LocalMailer &&
X	    to->q_mailer == s->s_mailer)
X	{
X		usrerr("No ARPA mail through this machine: see your system administration");
X		/* NoReturn = TRUE; to supress return copy */
X		return (FALSE);
X	}
X# endif EXAMPLE_CODE
X	return (TRUE);
X}
X/*
X**  HOLDSIGS -- arrange to hold all signals
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Arranges that signals are held.
X*/
X
Xholdsigs()
X{
X}
X/*
X**  RLSESIGS -- arrange to release all signals
X**
X**	This undoes the effect of holdsigs.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Arranges that signals are released.
X*/
X
Xrlsesigs()
X{
X}
X/*
X**  GETLA -- get the current load average
X**
X**	This code stolen from la.c.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		The current load average as an integer.
X**
X**	Side Effects:
X**		none.
X*/
X
X#ifdef VMUNIX
X
X#include <nlist.h>
X
Xstruct	nlist Nl[] =
X{
X	{ "_avenrun" },
X#define	X_AVENRUN	0
X	{ 0 },
X};
X
Xgetla()
X{
X	static int kmem = -1;
X# ifdef sun
X	long avenrun[3];
X# else
X	double avenrun[3];
X# endif
X	extern off_t lseek();
X
X	if (kmem < 0)
X	{
X		kmem = open("/dev/kmem", 0, 0);
X		if (kmem < 0)
X			return (-1);
X		(void) ioctl(kmem, (int) FIOCLEX, (char *) 0);
X		nlist("/vmunix", Nl);
X		if (Nl[0].n_type == 0)
X			return (-1);
X	}
X	if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 ||
X	    read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
X	{
X		/* thank you Ian */
X		return (-1);
X	}
X# ifdef sun
X	return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
X# else
X	return ((int) (avenrun[0] + 0.5));
X# endif
X}
X
X#else VMUNIX
X
Xgetla()
X{
X	return (0);
X}
X
X#endif VMUNIX
X/*
X**  SHOULDQUEUE -- should this message be queued or sent?
X**
X**	Compares the message cost to the load average to decide.
X**
X**	Parameters:
X**		pri -- the priority of the message in question.
X**
X**	Returns:
X**		TRUE -- if this message should be queued up for the
X**			time being.
X**		FALSE -- if the load is low enough to send this message.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xshouldqueue(pri)
X	long pri;
X{
X	int la;
X
X	la = getla();
X	if (la < QueueLA)
X		return (FALSE);
X	return (pri > (QueueFactor / (la - QueueLA + 1)));
X}
X/*
X**  SETPROCTITLE -- set process title for ps
X**
X**	Parameters:
X**		fmt -- a printf style format string.
X**		a, b, c -- possible parameters to fmt.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Clobbers argv of our main procedure so ps(1) will
X**		display the title.
X*/
X
X/*VARARGS1*/
Xsetproctitle(fmt, a, b, c)
X	char *fmt;
X{
X# ifdef SETPROCTITLE
X	register char *p;
X	register int i;
X	extern char **Argv;
X	extern char *LastArgv;
X	char buf[MAXLINE];
X
X	(void) sprintf(buf, fmt, a, b, c);
X
X	/* make ps print "(sendmail)" */
X	p = Argv[0];
X	*p++ = '-';
X
X	i = strlen(buf);
X	if (i > LastArgv - p - 2)
X	{
X		i = LastArgv - p - 2;
X		buf[i] = '\0';
X	}
X	(void) strcpy(p, buf);
X	p += i;
X	while (p < LastArgv)
X		*p++ = ' ';
X# endif SETPROCTITLE
X}
X/*
X**  REAPCHILD -- pick up the body of my child, lest it become a zombie
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Picks up extant zombies.
X*/
X
X# ifdef VMUNIX
X# include <sys/wait.h>
X# endif VMUNIX
X
Xreapchild()
X{
X# ifdef WNOHANG
X	union wait status;
X
X	while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0)
X		continue;
X# else WNOHANG
X	auto int status;
X
X	while (wait(&status) > 0)
X		continue;
X# endif WNOHANG
X}
END_OF_FILE
if test 12327 -ne `wc -c <'src/conf.c'`; then
    echo shar: \"'src/conf.c'\" unpacked with wrong size!
fi
# end of 'src/conf.c'
fi
if test -f 'src/envelope.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/envelope.c'\"
else
echo shar: Extracting \"'src/envelope.c'\" \(12701 characters\)
sed "s/^X//" >'src/envelope.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[] = "@(#)envelope.c	5.12 (Berkeley) 12/17/85";
X#endif not lint
X
X#include <pwd.h>
X#include "sendmail.h"
X#ifdef	UNIXPC
X#include <time.h>
X#else
X#include <sys/time.h>
X#endif
X#include <sys/stat.h>
X
X/*
X**  NEWENVELOPE -- allocate a new envelope
X**
X**	Supports inheritance.
X**
X**	Parameters:
X**		e -- the new envelope to fill in.
X**
X**	Returns:
X**		e.
X**
X**	Side Effects:
X**		none.
X*/
X
XENVELOPE *
Xnewenvelope(e)
X	register ENVELOPE *e;
X{
X	register ENVELOPE *parent;
X	extern putheader(), putbody();
X	extern ENVELOPE BlankEnvelope;
X
X	parent = CurEnv;
X	if (e == CurEnv)
X		parent = e->e_parent;
X	clearenvelope(e, TRUE);
X	if (e == CurEnv)
X		bcopy((char *) &NullAddress, (char *) &e->e_from, sizeof e->e_from);
X	else
X		bcopy((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from);
X	e->e_parent = parent;
X	e->e_ctime = curtime();
X	e->e_msgpriority = parent->e_msgsize;
X	e->e_puthdr = putheader;
X	e->e_putbody = putbody;
X	if (CurEnv->e_xfp != NULL)
X		(void) fflush(CurEnv->e_xfp);
X
X	return (e);
X}
X/*
X**  DROPENVELOPE -- deallocate an envelope.
X**
X**	Parameters:
X**		e -- the envelope to deallocate.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		housekeeping necessary to dispose of an envelope.
X**		Unlocks this queue file.
X*/
X
Xdropenvelope(e)
X	register ENVELOPE *e;
X{
X	bool queueit = FALSE;
X	register ADDRESS *q;
X
X#ifdef DEBUG
X	if (tTd(50, 1))
X	{
X		printf("dropenvelope %x id=", e);
X		xputs(e->e_id);
X		printf(" flags=%o\n", e->e_flags);
X	}
X#endif DEBUG
X#ifdef LOG
X	if (LogLevel > 10)
X		syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=%o, pid=%d",
X				  e->e_id == NULL ? "(none)" : e->e_id,
X				  e->e_flags, getpid());
X#endif LOG
X
X	/* we must have an id to remove disk files */
X	if (e->e_id == NULL)
X		return;
X
X	/*
X	**  Extract state information from dregs of send list.
X	*/
X
X	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
X	{
X		if (bitset(QQUEUEUP, q->q_flags))
X			queueit = TRUE;
X	}
X
X	/*
X	**  Send back return receipts as requested.
X	*/
X
X	if (e->e_receiptto != NULL && bitset(EF_SENDRECEIPT, e->e_flags))
X	{
X		auto ADDRESS *rlist = NULL;
X
X		sendtolist(CurEnv->e_receiptto, (ADDRESS *) NULL, &rlist);
X		(void) returntosender("Return receipt", rlist, FALSE);
X	}
X
X	/*
X	**  Arrange to send error messages if there are fatal errors.
X	*/
X
X	if (bitset(EF_FATALERRS|EF_TIMEOUT, e->e_flags) && ErrorMode != EM_QUIET)
X		savemail(e);
X
X	/*
X	**  Instantiate or deinstantiate the queue.
X	*/
X
X	if ((!queueit && !bitset(EF_KEEPQUEUE, e->e_flags)) ||
X	    bitset(EF_CLRQUEUE, e->e_flags))
X	{
X		if (e->e_df != NULL)
X			xunlink(e->e_df);
X		xunlink(queuename(e, 'q'));
X	}
X	else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
X	{
X#ifdef QUEUE
X		queueup(e, FALSE, FALSE);
X#else QUEUE
X		syserr("dropenvelope: queueup");
X#endif QUEUE
X	}
X
X	/* now unlock the job */
X	closexscript(e);
X	unlockqueue(e);
X
X	/* make sure that this envelope is marked unused */
X	e->e_id = e->e_df = NULL;
X	if (e->e_dfp != NULL)
X		(void) fclose(e->e_dfp);
X	e->e_dfp = NULL;
X}
X/*
X**  CLEARENVELOPE -- clear an envelope without unlocking
X**
X**	This is normally used by a child process to get a clean
X**	envelope without disturbing the parent.
X**
X**	Parameters:
X**		e -- the envelope to clear.
X**		fullclear - if set, the current envelope is total
X**			garbage and should be ignored; otherwise,
X**			release any resources it may indicate.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Closes files associated with the envelope.
X**		Marks the envelope as unallocated.
X*/
X
Xclearenvelope(e, fullclear)
X	register ENVELOPE *e;
X	bool fullclear;
X{
X	register HDR *bh;
X	register HDR **nhp;
X	extern ENVELOPE BlankEnvelope;
X
X	if (!fullclear)
X	{
X		/* clear out any file information */
X		if (e->e_xfp != NULL)
X			(void) fclose(e->e_xfp);
X		if (e->e_dfp != NULL)
X			(void) fclose(e->e_dfp);
X	}
X
X	/* now clear out the data */
X	STRUCTCOPY(BlankEnvelope, *e);
X	bh = BlankEnvelope.e_header;
X	nhp = &e->e_header;
X	while (bh != NULL)
X	{
X		*nhp = (HDR *) xalloc(sizeof *bh);
X		bcopy((char *) bh, (char *) *nhp, sizeof *bh);
X		bh = bh->h_link;
X		nhp = &(*nhp)->h_link;
X	}
X}
X/*
X**  INITSYS -- initialize instantiation of system
X**
X**	In Daemon mode, this is done in the child.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Initializes the system macros, some global variables,
X**		etc.  In particular, the current time in various
X**		forms is set.
X*/
X
Xinitsys()
X{
X	static char cbuf[5];			/* holds hop count */
X	static char pbuf[10];			/* holds pid */
X#ifdef TTYNAME
X	static char ybuf[10];			/* holds tty id */
X	register char *p;
X#endif TTYNAME
X	extern char *ttyname();
X	extern char *macvalue();
X	extern char Version[];
X
X	/*
X	**  Give this envelope a reality.
X	**	I.e., an id, a transcript, and a creation time.
X	*/
X
X	openxscript(CurEnv);
X	CurEnv->e_ctime = curtime();
X
X	/*
X	**  Set OutChannel to something useful if stdout isn't it.
X	**	This arranges that any extra stuff the mailer produces
X	**	gets sent back to the user on error (because it is
X	**	tucked away in the transcript).
X	*/
X
X	if (OpMode == MD_DAEMON && QueueRun)
X		OutChannel = CurEnv->e_xfp;
X
X	/*
X	**  Set up some basic system macros.
X	*/
X
X	/* process id */
X	(void) sprintf(pbuf, "%d", getpid());
X	define('p', pbuf, CurEnv);
X
X	/* hop count */
X	(void) sprintf(cbuf, "%d", CurEnv->e_hopcount);
X	define('c', cbuf, CurEnv);
X
X	/* time as integer, unix time, arpa time */
X	settime();
X
X#ifdef TTYNAME
X	/* tty name */
X	if (macvalue('y', CurEnv) == NULL)
X	{
X		p = ttyname(2);
X		if (p != NULL)
X		{
X			if (rindex(p, '/') != NULL)
X				p = rindex(p, '/') + 1;
X			(void) strcpy(ybuf, p);
X			define('y', ybuf, CurEnv);
X		}
X	}
X#endif TTYNAME
X}
X/*
X**  SETTIME -- set the current time.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Sets the various time macros -- $a, $b, $d, $t.
X*/
X
Xsettime()
X{
X	register char *p;
X	auto time_t now;
X	static char tbuf[20];			/* holds "current" time */
X	static char dbuf[30];			/* holds ctime(tbuf) */
X	register struct tm *tm;
X	extern char *arpadate();
X	extern struct tm *gmtime();
X	extern char *macvalue();
X
X	now = curtime();
X	tm = gmtime(&now);
X	(void) sprintf(tbuf, "%02d%02d%02d%02d%02d", tm->tm_year, tm->tm_mon+1,
X			tm->tm_mday, tm->tm_hour, tm->tm_min);
X	define('t', tbuf, CurEnv);
X	(void) strcpy(dbuf, ctime(&now));
X	*index(dbuf, '\n') = '\0';
X	if (macvalue('d', CurEnv) == NULL)
X		define('d', dbuf, CurEnv);
X	p = newstr(arpadate(dbuf));
X	if (macvalue('a', CurEnv) == NULL)
X		define('a', p, CurEnv);
X	define('b', p, CurEnv);
X}
X/*
X**  OPENXSCRIPT -- Open transcript file
X**
X**	Creates a transcript file for possible eventual mailing or
X**	sending back.
X**
X**	Parameters:
X**		e -- the envelope to create the transcript in/for.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Creates the transcript file.
X*/
X
Xopenxscript(e)
X	register ENVELOPE *e;
X{
X	register char *p;
X
X# ifdef LOG
X	if (LogLevel > 19)
X		syslog(LOG_DEBUG, "%s: openx%s", e->e_id, e->e_xfp == NULL ? "" : " (no)");
X# endif LOG
X	if (e->e_xfp != NULL)
X		return;
X	p = queuename(e, 'x');
X	e->e_xfp = fopen(p, "w");
X	if (e->e_xfp == NULL)
X		syserr("Can't create %s", p);
X	else
X		(void) chmod(p, 0644);
X}
X/*
X**  CLOSEXSCRIPT -- close the transcript file.
X**
X**	Parameters:
X**		e -- the envelope containing the transcript to close.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xclosexscript(e)
X	register ENVELOPE *e;
X{
X	if (e->e_xfp == NULL)
X		return;
X	(void) fclose(e->e_xfp);
X	e->e_xfp = NULL;
X}
X/*
X**  SETSENDER -- set the person who this message is from
X**
X**	Under certain circumstances allow the user to say who
X**	s/he is (using -f or -r).  These are:
X**	1.  The user's uid is zero (root).
X**	2.  The user's login name is in an approved list (typically
X**	    from a network server).
X**	3.  The address the user is trying to claim has a
X**	    "!" character in it (since #2 doesn't do it for
X**	    us if we are dialing out for UUCP).
X**	A better check to replace #3 would be if the
X**	effective uid is "UUCP" -- this would require me
X**	to rewrite getpwent to "grab" uucp as it went by,
X**	make getname more nasty, do another passwd file
X**	scan, or compile the UID of "UUCP" into the code,
X**	all of which are reprehensible.
X**
X**	Assuming all of these fail, we figure out something
X**	ourselves.
X**
X**	Parameters:
X**		from -- the person we would like to believe this message
X**			is from, as specified on the command line.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		sets sendmail's notion of who the from person is.
X*/
X
Xsetsender(from)
X	char *from;
X{
X	register char **pvp;
X	char *realname = NULL;
X	register struct passwd *pw;
X	char buf[MAXNAME];
X	char pvpbuf[PSBUFSIZE];
X	extern struct passwd *getpwnam();
X	extern char *macvalue();
X	extern char **prescan();
X	extern bool safefile();
X	extern char *FullName;
X
X# ifdef DEBUG
X	if (tTd(45, 1))
X		printf("setsender(%s)\n", from == NULL ? "" : from);
X# endif DEBUG
X
X	/*
X	**  Figure out the real user executing us.
X	**	Username can return errno != 0 on non-errors.
X	*/
X
X	if (QueueRun || OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
X		realname = from;
X	if (realname == NULL || realname[0] == '\0')
X	{
X		extern char *username();
X
X		realname = username();
X	}
X
X	/*
X	**  Determine if this real person is allowed to alias themselves.
X	*/
X
X	if (from != NULL)
X	{
X		extern bool trusteduser();
X
X		if (!trusteduser(realname) &&
X# ifdef DEBUG
X		    (!tTd(1, 9) || getuid() != geteuid()) &&
X# endif DEBUG
X		    index(from, '!') == NULL && getuid() != 0)
X		{
X			/* network sends -r regardless (why why why?) */
X			/* syserr("%s, you cannot use the -f flag", realname); */
X			from = NULL;
X		}
X	}
X
X	SuprErrs = TRUE;
X	if (from == NULL || parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL)
X	{
X		/* log garbage addresses for traceback */
X		if (from != NULL)
X		{
X# ifdef LOG
X			if (LogLevel >= 1)
X				syslog(LOG_ERR, "Unparseable user %s wants to be %s",
X						realname, from);
X# endif LOG
X		}
X		from = newstr(realname);
X		if (parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL &&
X		    parseaddr("postmaster", &CurEnv->e_from, 1, '\0') == NULL)
X		{
X			syserr("setsender: can't even parse postmaster!");
X		}
X	}
X	else
X		FromFlag = TRUE;
X	CurEnv->e_from.q_flags |= QDONTSEND;
X	loweraddr(&CurEnv->e_from);
X	SuprErrs = FALSE;
X
X	if (CurEnv->e_from.q_mailer == LocalMailer &&
X	    (pw = getpwnam(CurEnv->e_from.q_user)) != NULL)
X	{
X		/*
X		**  Process passwd file entry.
X		*/
X
X
X		/* extract home directory */
X		CurEnv->e_from.q_home = newstr(pw->pw_dir);
X		define('z', CurEnv->e_from.q_home, CurEnv);
X
X		/* extract user and group id */
X		CurEnv->e_from.q_uid = pw->pw_uid;
X		CurEnv->e_from.q_gid = pw->pw_gid;
X
X		/* if the user has given fullname already, don't redefine */
X		if (FullName == NULL)
X			FullName = macvalue('x', CurEnv);
X		if (FullName != NULL && FullName[0] == '\0')
X			FullName = NULL;
X
X		/* extract full name from passwd file */
X		if (FullName == NULL && pw->pw_gecos != NULL &&
X		    strcmp(pw->pw_name, CurEnv->e_from.q_user) == 0)
X		{
X			buildfname(pw->pw_gecos, CurEnv->e_from.q_user, buf);
X			if (buf[0] != '\0')
X				FullName = newstr(buf);
X		}
X		if (FullName != NULL)
X			define('x', FullName, CurEnv);
X	}
X	else
X	{
X#ifndef V6
X		if (CurEnv->e_from.q_home == NULL)
X			CurEnv->e_from.q_home = getenv("HOME");
X#endif V6
X		CurEnv->e_from.q_uid = getuid();
X		CurEnv->e_from.q_gid = getgid();
X	}
X
X	if (CurEnv->e_from.q_uid != 0)
X	{
X		DefUid = CurEnv->e_from.q_uid;
X		DefGid = CurEnv->e_from.q_gid;
X	}
X
X	/*
X	**  Rewrite the from person to dispose of possible implicit
X	**	links in the net.
X	*/
X
X	pvp = prescan(from, '\0', pvpbuf);
X	if (pvp == NULL)
X	{
X		syserr("cannot prescan from (%s)", from);
X		finis();
X	}
X	rewrite(pvp, 3);
X	rewrite(pvp, 1);
X	rewrite(pvp, 4);
X	cataddr(pvp, buf, sizeof buf);
X	define('f', newstr(buf), CurEnv);
X
X	/* save the domain spec if this mailer wants it */
X	if (CurEnv->e_from.q_mailer != NULL &&
X	    bitnset(M_CANONICAL, CurEnv->e_from.q_mailer->m_flags))
X	{
X		extern char **copyplist();
X
X		while (*pvp != NULL && strcmp(*pvp, "@") != 0)
X			pvp++;
X		if (*pvp != NULL)
X			CurEnv->e_fromdomain = copyplist(pvp, TRUE);
X	}
X}
X/*
X**  TRUSTEDUSER -- tell us if this user is to be trusted.
X**
X**	Parameters:
X**		user -- the user to be checked.
X**
X**	Returns:
X**		TRUE if the user is in an approved list.
X**		FALSE otherwise.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xtrusteduser(user)
X	char *user;
X{
X	register char **ulist;
X	extern char *TrustedUsers[];
X
X	for (ulist = TrustedUsers; *ulist != NULL; ulist++)
X		if (strcmp(*ulist, user) == 0)
X			return (TRUE);
X	return (FALSE);
X}
END_OF_FILE
if test 12701 -ne `wc -c <'src/envelope.c'`; then
    echo shar: \"'src/envelope.c'\" unpacked with wrong size!
fi
# end of 'src/envelope.c'
fi
if test -f 'src/recipient.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/recipient.c'\"
else
echo shar: Extracting \"'src/recipient.c'\" \(12741 characters\)
sed "s/^X//" >'src/recipient.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[] = "@(#)recipient.c	5.9 (Berkeley) 10/23/86";
X#endif not lint
X
X# include <pwd.h>
X# include "sendmail.h"
X# include <sys/stat.h>
X
X/*
X**  SENDTOLIST -- Designate a send list.
X**
X**	The parameter is a comma-separated list of people to send to.
X**	This routine arranges to send to all of them.
X**
X**	Parameters:
X**		list -- the send list.
X**		ctladdr -- the address template for the person to
X**			send to -- effective uid/gid are important.
X**			This is typically the alias that caused this
X**			expansion.
X**		sendq -- a pointer to the head of a queue to put
X**			these people into.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		none.
X*/
X
X# define MAXRCRSN	10
X
Xsendtolist(list, ctladdr, sendq)
X	char *list;
X	ADDRESS *ctladdr;
X	ADDRESS **sendq;
X{
X	register char *p;
X	register ADDRESS *al;	/* list of addresses to send to */
X	bool firstone;		/* set on first address sent */
X	bool selfref;		/* set if this list includes ctladdr */
X	char delimiter;		/* the address delimiter */
X
X# ifdef DEBUG
X	if (tTd(25, 1))
X	{
X		printf("sendto: %s\n   ctladdr=", list);
X		printaddr(ctladdr, FALSE);
X	}
X# endif DEBUG
X
X	/* heuristic to determine old versus new style addresses */
X	if (ctladdr == NULL &&
X	    (index(list, ',') != NULL || index(list, ';') != NULL ||
X	     index(list, '<') != NULL || index(list, '(') != NULL))
X		CurEnv->e_flags &= ~EF_OLDSTYLE;
X	delimiter = ' ';
X	if (!bitset(EF_OLDSTYLE, CurEnv->e_flags) || ctladdr != NULL)
X		delimiter = ',';
X
X	firstone = TRUE;
X	selfref = FALSE;
X	al = NULL;
X
X	for (p = list; *p != '\0'; )
X	{
X		register ADDRESS *a;
X		extern char *DelimChar;		/* defined in prescan */
X
X		/* parse the address */
X		while (isspace(*p) || *p == ',')
X			p++;
X		a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter);
X		p = DelimChar;
X		if (a == NULL)
X			continue;
X		a->q_next = al;
X		a->q_alias = ctladdr;
X
X		/* see if this should be marked as a primary address */
X		if (ctladdr == NULL ||
X		    (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags)))
X			a->q_flags |= QPRIMARY;
X
X		/* put on send queue or suppress self-reference */
X		if (ctladdr != NULL && sameaddr(ctladdr, a))
X			selfref = TRUE;
X		else
X			al = a;
X		firstone = FALSE;
X	}
X
X	/* if this alias doesn't include itself, delete ctladdr */
X	if (!selfref && ctladdr != NULL)
X		ctladdr->q_flags |= QDONTSEND;
X
X	/* arrange to send to everyone on the local send list */
X	while (al != NULL)
X	{
X		register ADDRESS *a = al;
X		extern ADDRESS *recipient();
X
X		al = a->q_next;
X		a = recipient(a, sendq);
X
X		/* arrange to inherit full name */
X		if (a->q_fullname == NULL && ctladdr != NULL)
X			a->q_fullname = ctladdr->q_fullname;
X	}
X
X	CurEnv->e_to = NULL;
X}
X/*
X**  RECIPIENT -- Designate a message recipient
X**
X**	Saves the named person for future mailing.
X**
X**	Parameters:
X**		a -- the (preparsed) address header for the recipient.
X**		sendq -- a pointer to the head of a queue to put the
X**			recipient in.  Duplicate supression is done
X**			in this queue.
X**
X**	Returns:
X**		The actual address in the queue.  This will be "a" if
X**		the address is not a duplicate, else the original address.
X**
X**	Side Effects:
X**		none.
X*/
X
XADDRESS *
Xrecipient(a, sendq)
X	register ADDRESS *a;
X	register ADDRESS **sendq;
X{
X	register ADDRESS *q;
X	ADDRESS **pq;
X	register struct mailer *m;
X	register char *p;
X	bool quoted = FALSE;		/* set if the addr has a quote bit */
X	char buf[MAXNAME];		/* unquoted image of the user name */
X	extern ADDRESS *getctladdr();
X	extern bool safefile();
X
X	CurEnv->e_to = a->q_paddr;
X	m = a->q_mailer;
X	errno = 0;
X# ifdef DEBUG
X	if (tTd(26, 1))
X	{
X		printf("\nrecipient: ");
X		printaddr(a, FALSE);
X	}
X# endif DEBUG
X
X	/* break aliasing loops */
X	if (AliasLevel > MAXRCRSN)
X	{
X		usrerr("aliasing/forwarding loop broken");
X		return (a);
X	}
X
X	/*
X	**  Finish setting up address structure.
X	*/
X
X	/* set the queue timeout */
X	a->q_timeout = TimeOut;
X
X	/* map user & host to lower case if requested on non-aliases */
X	if (a->q_alias == NULL)
X		loweraddr(a);
X
X	/* get unquoted user for file, program or user.name check */
X	(void) strcpy(buf, a->q_user);
X	for (p = buf; *p != '\0' && !quoted; p++)
X	{
X		if (!isascii(*p) && (*p & 0377) != (SpaceSub & 0377))
X			quoted = TRUE;
X	}
X	stripquotes(buf, TRUE);
X
X	/* do sickly crude mapping for program mailing, etc. */
X	if (m == LocalMailer && buf[0] == '|')
X	{
X		a->q_mailer = m = ProgMailer;
X		a->q_user++;
X		if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X		{
X			a->q_flags |= QDONTSEND|QBADADDR;
X			usrerr("Cannot mail directly to programs");
X		}
X	}
X
X	/*
X	**  Look up this person in the recipient list.
X	**	If they are there already, return, otherwise continue.
X	**	If the list is empty, just add it.  Notice the cute
X	**	hack to make from addresses suppress things correctly:
X	**	the QDONTSEND bit will be set in the send list.
X	**	[Please note: the emphasis is on "hack."]
X	*/
X
X	for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
X	{
X		if (!ForceMail && sameaddr(q, a))
X		{
X# ifdef DEBUG
X			if (tTd(26, 1))
X			{
X				printf("%s in sendq: ", a->q_paddr);
X				printaddr(q, FALSE);
X			}
X# endif DEBUG
X			if (!bitset(QDONTSEND, a->q_flags))
X				message(Arpa_Info, "duplicate suppressed");
X			if (!bitset(QPRIMARY, q->q_flags))
X				q->q_flags |= a->q_flags;
X			return (q);
X		}
X	}
X
X	/* add address on list */
X	*pq = a;
X	a->q_next = NULL;
X	CurEnv->e_nrcpts++;
X
X	/*
X	**  Alias the name and handle :include: specs.
X	*/
X
X	if (m == LocalMailer && !bitset(QDONTSEND, a->q_flags))
X	{
X		if (strncmp(a->q_user, ":include:", 9) == 0)
X		{
X			a->q_flags |= QDONTSEND;
X			if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X			{
X				a->q_flags |= QBADADDR;
X				usrerr("Cannot mail directly to :include:s");
X			}
X			else
X			{
X				message(Arpa_Info, "including file %s", &a->q_user[9]);
X				include(&a->q_user[9], " sending", a, sendq);
X			}
X		}
X		else
X			alias(a, sendq);
X	}
X
X	/*
X	**  If the user is local and still being sent, verify that
X	**  the address is good.  If it is, try to forward.
X	**  If the address is already good, we have a forwarding
X	**  loop.  This can be broken by just sending directly to
X	**  the user (which is probably correct anyway).
X	*/
X
X	if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer)
X	{
X		struct stat stb;
X		extern bool writable();
X
X		/* see if this is to a file */
X		if (buf[0] == '/')
X		{
X			p = rindex(buf, '/');
X			/* check if writable or creatable */
X			if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X			{
X				a->q_flags |= QDONTSEND|QBADADDR;
X				usrerr("Cannot mail directly to files");
X			}
X			else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) :
X			    (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC)))
X			{
X				a->q_flags |= QBADADDR;
X				giveresponse(EX_CANTCREAT, m, CurEnv);
X			}
X		}
X		else
X		{
X			register struct passwd *pw;
X			extern struct passwd *finduser();
X
X			/* warning -- finduser may trash buf */
X			pw = finduser(buf);
X			if (pw == NULL)
X			{
X				a->q_flags |= QBADADDR;
X				giveresponse(EX_NOUSER, m, CurEnv);
X			}
X			else
X			{
X				char nbuf[MAXNAME];
X
X				if (strcmp(a->q_user, pw->pw_name) != 0)
X				{
X					a->q_user = newstr(pw->pw_name);
X					(void) strcpy(buf, pw->pw_name);
X				}
X				a->q_home = newstr(pw->pw_dir);
X				a->q_uid = pw->pw_uid;
X				a->q_gid = pw->pw_gid;
X				a->q_flags |= QGOODUID;
X				buildfname(pw->pw_gecos, pw->pw_name, nbuf);
X				if (nbuf[0] != '\0')
X					a->q_fullname = newstr(nbuf);
X				if (!quoted)
X					forward(a, sendq);
X			}
X		}
X	}
X	return (a);
X}
X/*
X**  FINDUSER -- find the password entry for a user.
X**
X**	This looks a lot like getpwnam, except that it may want to
X**	do some fancier pattern matching in /etc/passwd.
X**
X**	This routine contains most of the time of many sendmail runs.
X**	It deserves to be optimized.
X**
X**	Parameters:
X**		name -- the name to match against.
X**
X**	Returns:
X**		A pointer to a pw struct.
X**		NULL if name is unknown or ambiguous.
X**
X**	Side Effects:
X**		may modify name.
X*/
X
Xstruct passwd *
Xfinduser(name)
X	char *name;
X{
X	register struct passwd *pw;
X	register char *p;
X	extern struct passwd *getpwent();
X	extern struct passwd *getpwnam();
X
X	/* map upper => lower case */
X	for (p = name; *p != '\0'; p++)
X	{
X		if (isascii(*p) && isupper(*p))
X			*p = tolower(*p);
X	}
X
X	/* look up this login name using fast path */
X	if ((pw = getpwnam(name)) != NULL)
X		return (pw);
X
X	/* search for a matching full name instead */
X	for (p = name; *p != '\0'; p++)
X	{
X		if (*p == (SpaceSub & 0177) || *p == '_')
X			*p = ' ';
X	}
X	(void) setpwent();
X	while ((pw = getpwent()) != NULL)
X	{
X		char buf[MAXNAME];
X		extern bool sameword();
X
X		buildfname(pw->pw_gecos, pw->pw_name, buf);
X		if (index(buf, ' ') != NULL && sameword(buf, name))
X		{
X			message(Arpa_Info, "sending to login name %s", pw->pw_name);
X			return (pw);
X		}
X	}
X	return (NULL);
X}
X/*
X**  WRITABLE -- predicate returning if the file is writable.
X**
X**	This routine must duplicate the algorithm in sys/fio.c.
X**	Unfortunately, we cannot use the access call since we
X**	won't necessarily be the real uid when we try to
X**	actually open the file.
X**
X**	Notice that ANY file with ANY execute bit is automatically
X**	not writable.  This is also enforced by mailfile.
X**
X**	Parameters:
X**		s -- pointer to a stat struct for the file.
X**
X**	Returns:
X**		TRUE -- if we will be able to write this file.
X**		FALSE -- if we cannot write this file.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xwritable(s)
X	register struct stat *s;
X{
X	int euid, egid;
X	int bits;
X
X	if (bitset(0111, s->st_mode))
X		return (FALSE);
X	euid = getruid();
X	egid = getrgid();
X	if (geteuid() == 0)
X	{
X		if (bitset(S_ISUID, s->st_mode))
X			euid = s->st_uid;
X		if (bitset(S_ISGID, s->st_mode))
X			egid = s->st_gid;
X	}
X
X	if (euid == 0)
X		return (TRUE);
X	bits = S_IWRITE;
X	if (euid != s->st_uid)
X	{
X		bits >>= 3;
X		if (egid != s->st_gid)
X			bits >>= 3;
X	}
X	return ((s->st_mode & bits) != 0);
X}
X/*
X**  INCLUDE -- handle :include: specification.
X**
X**	Parameters:
X**		fname -- filename to include.
X**		msg -- message to print in verbose mode.
X**		ctladdr -- address template to use to fill in these
X**			addresses -- effective user/group id are
X**			the important things.
X**		sendq -- a pointer to the head of the send queue
X**			to put these addresses in.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		reads the :include: file and sends to everyone
X**		listed in that file.
X*/
X
Xinclude(fname, msg, ctladdr, sendq)
X	char *fname;
X	char *msg;
X	ADDRESS *ctladdr;
X	ADDRESS **sendq;
X{
X	char buf[MAXLINE];
X	register FILE *fp;
X	char *oldto = CurEnv->e_to;
X	char *oldfilename = FileName;
X	int oldlinenumber = LineNumber;
X
X	fp = fopen(fname, "r");
X	if (fp == NULL)
X	{
X		usrerr("Cannot open %s", fname);
X		return;
X	}
X	if (getctladdr(ctladdr) == NULL)
X	{
X		struct stat st;
X
X		if (fstat(fileno(fp), &st) < 0)
X			syserr("Cannot fstat %s!", fname);
X		ctladdr->q_uid = st.st_uid;
X		ctladdr->q_gid = st.st_gid;
X		ctladdr->q_flags |= QGOODUID;
X	}
X
X	/* read the file -- each line is a comma-separated list. */
X	FileName = fname;
X	LineNumber = 0;
X	while (fgets(buf, sizeof buf, fp) != NULL)
X	{
X		register char *p = index(buf, '\n');
X
X		if (p != NULL)
X			*p = '\0';
X		if (buf[0] == '\0')
X			continue;
X		CurEnv->e_to = oldto;
X		message(Arpa_Info, "%s to %s", msg, buf);
X		AliasLevel++;
X		sendtolist(buf, ctladdr, sendq);
X		AliasLevel--;
X	}
X
X	(void) fclose(fp);
X	FileName = oldfilename;
X	LineNumber = oldlinenumber;
X}
X/*
X**  SENDTOARGV -- send to an argument vector.
X**
X**	Parameters:
X**		argv -- argument vector to send to.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		puts all addresses on the argument vector onto the
X**			send queue.
X*/
X
Xsendtoargv(argv)
X	register char **argv;
X{
X	register char *p;
X	extern bool sameword();
X
X	while ((p = *argv++) != NULL)
X	{
X		if (argv[0] != NULL && argv[1] != NULL && sameword(argv[0], "at"))
X		{
X			char nbuf[MAXNAME];
X
X			if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf)
X				usrerr("address overflow");
X			else
X			{
X				(void) strcpy(nbuf, p);
X				(void) strcat(nbuf, "@");
X				(void) strcat(nbuf, argv[1]);
X				p = newstr(nbuf);
X				argv += 2;
X			}
X		}
X		sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
X	}
X}
X/*
X**  GETCTLADDR -- get controlling address from an address header.
X**
X**	If none, get one corresponding to the effective userid.
X**
X**	Parameters:
X**		a -- the address to find the controller of.
X**
X**	Returns:
X**		the controlling address.
X**
X**	Side Effects:
X**		none.
X*/
X
XADDRESS *
Xgetctladdr(a)
X	register ADDRESS *a;
X{
X	while (a != NULL && !bitset(QGOODUID, a->q_flags))
X		a = a->q_alias;
X	return (a);
X}
END_OF_FILE
if test 12741 -ne `wc -c <'src/recipient.c'`; then
    echo shar: \"'src/recipient.c'\" unpacked with wrong size!
fi
# end of 'src/recipient.c'
fi
if test -f 'src/savemail.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/savemail.c'\"
else
echo shar: Extracting \"'src/savemail.c'\" \(11670 characters\)
sed "s/^X//" >'src/savemail.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[] = "@(#)savemail.c	5.7 (Berkeley) 12/7/85";
X#endif not lint
X
X# include <pwd.h>
X# include "sendmail.h"
X
X/*
X**  SAVEMAIL -- Save mail on error
X**
X**	If mailing back errors, mail it back to the originator
X**	together with an error message; otherwise, just put it in
X**	dead.letter in the user's home directory (if he exists on
X**	this machine).
X**
X**	Parameters:
X**		e -- the envelope containing the message in error.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Saves the letter, by writing or mailing it back to the
X**		sender, or by putting it in dead.letter in her home
X**		directory.
X*/
X
X/* defines for state machine */
X# define ESM_REPORT	0	/* report to sender's terminal */
X# define ESM_MAIL	1	/* mail back to sender */
X# define ESM_QUIET	2	/* messages have already been returned */
X# define ESM_DEADLETTER	3	/* save in ~/dead.letter */
X# define ESM_POSTMASTER	4	/* return to postmaster */
X# define ESM_USRTMP	5	/* save in /usr/tmp/dead.letter */
X# define ESM_PANIC	6	/* leave the locked queue/transcript files */
X# define ESM_DONE	7	/* the message is successfully delivered */
X
X
Xsavemail(e)
X	register ENVELOPE *e;
X{
X	register struct passwd *pw;
X	register FILE *fp;
X	int state;
X	auto ADDRESS *q;
X	char buf[MAXLINE+1];
X	extern struct passwd *getpwnam();
X	register char *p;
X	extern char *ttypath();
X	typedef int (*fnptr)();
X
X# ifdef DEBUG
X	if (tTd(6, 1))
X		printf("\nsavemail, ErrorMode = %c\n", ErrorMode);
X# endif DEBUG
X
X	if (bitset(EF_RESPONSE, e->e_flags))
X		return;
X	if (e->e_class < 0)
X	{
X		message(Arpa_Info, "Dumping junk mail");
X		return;
X	}
X	ForceMail = TRUE;
X	e->e_flags &= ~EF_FATALERRS;
X
X	/*
X	**  In the unhappy event we don't know who to return the mail
X	**  to, make someone up.
X	*/
X
X	if (e->e_from.q_paddr == NULL)
X	{
X		if (parseaddr("root", &e->e_from, 0, '\0') == NULL)
X		{
X			syserr("Cannot parse root!");
X			ExitStat = EX_SOFTWARE;
X			finis();
X		}
X	}
X	e->e_to = NULL;
X
X	/*
X	**  Basic state machine.
X	**
X	**	This machine runs through the following states:
X	**
X	**	ESM_QUIET	Errors have already been printed iff the
X	**			sender is local.
X	**	ESM_REPORT	Report directly to the sender's terminal.
X	**	ESM_MAIL	Mail response to the sender.
X	**	ESM_DEADLETTER	Save response in ~/dead.letter.
X	**	ESM_POSTMASTER	Mail response to the postmaster.
X	**	ESM_PANIC	Save response anywhere possible.
X	*/
X
X	/* determine starting state */
X	switch (ErrorMode)
X	{
X	  case EM_WRITE:
X		state = ESM_REPORT;
X		break;
X
X	  case EM_BERKNET:
X		/* mail back, but return o.k. exit status */
X		ExitStat = EX_OK;
X
X		/* fall through.... */
X
X	  case EM_MAIL:
X		state = ESM_MAIL;
X		break;
X
X	  case EM_PRINT:
X	  case '\0':
X		state = ESM_QUIET;
X		break;
X
X	  case EM_QUIET:
X		/* no need to return anything at all */
X		return;
X
X	  default:
X		syserr("savemail: ErrorMode x%x\n");
X		state = ESM_MAIL;
X		break;
X	}
X
X	while (state != ESM_DONE)
X	{
X# ifdef DEBUG
X		if (tTd(6, 5))
X			printf("  state %d\n", state);
X# endif DEBUG
X
X		switch (state)
X		{
X		  case ESM_QUIET:
X			if (e->e_from.q_mailer == LocalMailer)
X				state = ESM_DEADLETTER;
X			else
X				state = ESM_MAIL;
X			break;
X
X		  case ESM_REPORT:
X
X			/*
X			**  If the user is still logged in on the same terminal,
X			**  then write the error messages back to hir (sic).
X			*/
X
X			p = ttypath();
X			if (p == NULL || freopen(p, "w", stdout) == NULL)
X			{
X				state = ESM_MAIL;
X				break;
X			}
X
X			expand("\001n", buf, &buf[sizeof buf - 1], e);
X			printf("\r\nMessage from %s...\r\n", buf);
X			printf("Errors occurred while sending mail.\r\n");
X			if (e->e_xfp != NULL)
X			{
X				(void) fflush(e->e_xfp);
X				fp = fopen(queuename(e, 'x'), "r");
X			}
X			else
X				fp = NULL;
X			if (fp == NULL)
X			{
X				syserr("Cannot open %s", queuename(e, 'x'));
X				printf("Transcript of session is unavailable.\r\n");
X			}
X			else
X			{
X				printf("Transcript follows:\r\n");
X				while (fgets(buf, sizeof buf, fp) != NULL &&
X				       !ferror(stdout))
X					fputs(buf, stdout);
X				(void) fclose(fp);
X			}
X			printf("Original message will be saved in dead.letter.\r\n");
X			if (ferror(stdout))
X				(void) syserr("savemail: stdout: write err");
X			state = ESM_DEADLETTER;
X			break;
X
X		  case ESM_MAIL:
X		  case ESM_POSTMASTER:
X			/*
X			**  If mailing back, do it.
X			**	Throw away all further output.  Don't alias,
X			**	since this could cause loops, e.g., if joe
X			**	mails to joe at x, and for some reason the network
X			**	for @x is down, then the response gets sent to
X			**	joe at x, which gives a response, etc.  Also force
X			**	the mail to be delivered even if a version of
X			**	it has already been sent to the sender.
X			*/
X
X			if (state == ESM_MAIL)
X			{
X				if (e->e_errorqueue == NULL)
X					sendtolist(e->e_from.q_paddr,
X						(ADDRESS *) NULL,
X						&e->e_errorqueue);
X
X				/* deliver a cc: to the postmaster if desired */
X				if (PostMasterCopy != NULL)
X					sendtolist(PostMasterCopy,
X						(ADDRESS *) NULL,
X						&e->e_errorqueue);
X				q = e->e_errorqueue;
X			}
X			else
X			{
X				if (parseaddr("postmaster", q, 0, '\0') == NULL)
X				{
X					syserr("cannot parse postmaster!");
X					ExitStat = EX_SOFTWARE;
X					state = ESM_USRTMP;
X					break;
X				}
X			}
X			if (returntosender(e->e_message != NULL ? e->e_message :
X					   "Unable to deliver mail",
X					   q, TRUE) == 0)
X			{
X				state = ESM_DONE;
X				break;
X			}
X
X			state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP;
X			break;
X
X		  case ESM_DEADLETTER:
X			/*
X			**  Save the message in dead.letter.
X			**	If we weren't mailing back, and the user is
X			**	local, we should save the message in
X			**	~/dead.letter so that the poor person doesn't
X			**	have to type it over again -- and we all know
X			**	what poor typists UNIX users are.
X			*/
X
X			p = NULL;
X			if (e->e_from.q_mailer == LocalMailer)
X			{
X				if (e->e_from.q_home != NULL)
X					p = e->e_from.q_home;
X				else if ((pw = getpwnam(e->e_from.q_user)) != NULL)
X					p = pw->pw_dir;
X			}
X			if (p == NULL)
X			{
X				syserr("Can't return mail to %s", e->e_from.q_paddr);
X				state = ESM_MAIL;
X				break;
X			}
X			if (e->e_dfp != NULL)
X			{
X				auto ADDRESS *q;
X				bool oldverb = Verbose;
X
X				/* we have a home directory; open dead.letter */
X				define('z', p, e);
X				expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e);
X				Verbose = TRUE;
X				message(Arpa_Info, "Saving message in %s", buf);
X				Verbose = oldverb;
X				e->e_to = buf;
X				q = NULL;
X				sendtolist(buf, (ADDRESS *) NULL, &q);
X				if (deliver(e, q) == 0)
X					state = ESM_DONE;
X				else
X					state = ESM_MAIL;
X			}
X			else
X			{
X				/* no data file -- try mailing back */
X				state = ESM_MAIL;
X			}
X			break;
X
X		  case ESM_USRTMP:
X			/*
X			**  Log the mail in /usr/tmp/dead.letter.
X			*/
X
X			fp = dfopen("/usr/tmp/dead.letter", "a");
X			if (fp == NULL)
X			{
X				state = ESM_PANIC;
X				break;
X			}
X
X			putfromline(fp, ProgMailer);
X			(*e->e_puthdr)(fp, ProgMailer, e);
X			putline("\n", fp, ProgMailer);
X			(*e->e_putbody)(fp, ProgMailer, e);
X			putline("\n", fp, ProgMailer);
X			(void) fflush(fp);
X			state = ferror(fp) ? ESM_PANIC : ESM_DONE;
X			(void) fclose(fp);
X			break;
X
X		  default:
X			syserr("savemail: unknown state %d", state);
X
X			/* fall through ... */
X
X		  case ESM_PANIC:
X			syserr("savemail: HELP!!!!");
X# ifdef LOG
X			if (LogLevel >= 1)
X				syslog(LOG_ALERT, "savemail: HELP!!!!");
X# endif LOG
X
X			/* leave the locked queue & transcript files around */
X			exit(EX_SOFTWARE);
X		}
X	}
X}
X/*
X**  RETURNTOSENDER -- return a message to the sender with an error.
X**
X**	Parameters:
X**		msg -- the explanatory message.
X**		returnq -- the queue of people to send the message to.
X**		sendbody -- if TRUE, also send back the body of the
X**			message; otherwise just send the header.
X**
X**	Returns:
X**		zero -- if everything went ok.
X**		else -- some error.
X**
X**	Side Effects:
X**		Returns the current message to the sender via
X**		mail.
X*/
X
Xstatic bool	SendBody;
X
X#define MAXRETURNS	6	/* max depth of returning messages */
X
Xreturntosender(msg, returnq, sendbody)
X	char *msg;
X	ADDRESS *returnq;
X	bool sendbody;
X{
X	char buf[MAXNAME];
X	extern putheader(), errbody();
X	register ENVELOPE *ee;
X	extern ENVELOPE *newenvelope();
X	ENVELOPE errenvelope;
X	static int returndepth;
X	register ADDRESS *q;
X
X# ifdef DEBUG
X	if (tTd(6, 1))
X	{
X		printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
X		       msg, returndepth, CurEnv);
X		printf("\treturnq=");
X		printaddr(returnq, TRUE);
X	}
X# endif DEBUG
X
X	if (++returndepth >= MAXRETURNS)
X	{
X		if (returndepth != MAXRETURNS)
X			syserr("returntosender: infinite recursion on %s", returnq->q_paddr);
X		/* don't "unrecurse" and fake a clean exit */
X		/* returndepth--; */
X		return (0);
X	}
X
X	SendBody = sendbody;
X	define('g', "\001f", CurEnv);
X	ee = newenvelope(&errenvelope);
X	define('a', "\001b", ee);
X	ee->e_puthdr = putheader;
X	ee->e_putbody = errbody;
X	ee->e_flags |= EF_RESPONSE;
X	ee->e_sendqueue = returnq;
X	openxscript(ee);
X	for (q = returnq; q != NULL; q = q->q_next)
X	{
X		if (q->q_alias == NULL)
X			addheader("to", q->q_paddr, ee);
X	}
X
X	(void) sprintf(buf, "Returned mail: %s", msg);
X	addheader("subject", buf, ee);
X
X	/* fake up an address header for the from person */
X	expand("\001n", buf, &buf[sizeof buf - 1], CurEnv);
X	if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL)
X	{
X		syserr("Can't parse myself!");
X		ExitStat = EX_SOFTWARE;
X		returndepth--;
X		return (-1);
X	}
X	loweraddr(&ee->e_from);
X
X	/* push state into submessage */
X	CurEnv = ee;
X	define('f', "\001n", ee);
X	define('x', "Mail Delivery Subsystem", ee);
X	eatheader(ee);
X
X	/* actually deliver the error message */
X	sendall(ee, SM_DEFAULT);
X
X	/* restore state */
X	dropenvelope(ee);
X	CurEnv = CurEnv->e_parent;
X	returndepth--;
X
X	/* should check for delivery errors here */
X	return (0);
X}
X/*
X**  ERRBODY -- output the body of an error message.
X**
X**	Typically this is a copy of the transcript plus a copy of the
X**	original offending message.
X**
X**	Parameters:
X**		fp -- the output file.
X**		m -- the mailer to output to.
X**		e -- the envelope we are working in.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Outputs the body of an error message.
X*/
X
Xerrbody(fp, m, e)
X	register FILE *fp;
X	register struct mailer *m;
X	register ENVELOPE *e;
X{
X	register FILE *xfile;
X	char buf[MAXLINE];
X	char *p;
X
X	/*
X	**  Output transcript of errors
X	*/
X
X	(void) fflush(stdout);
X	p = queuename(e->e_parent, 'x');
X	if ((xfile = fopen(p, "r")) == NULL)
X	{
X		syserr("Cannot open %s", p);
X		fprintf(fp, "  ----- Transcript of session is unavailable -----\n");
X	}
X	else
X	{
X		fprintf(fp, "   ----- Transcript of session follows -----\n");
X		if (e->e_xfp != NULL)
X			(void) fflush(e->e_xfp);
X		while (fgets(buf, sizeof buf, xfile) != NULL)
X			putline(buf, fp, m);
X		(void) fclose(xfile);
X	}
X	errno = 0;
X
X	/*
X	**  Output text of original message
X	*/
X
X	if (NoReturn)
X		fprintf(fp, "\n   ----- Return message suppressed -----\n\n");
X	else if (e->e_parent->e_dfp != NULL)
X	{
X		if (SendBody)
X		{
X			putline("\n", fp, m);
X			putline("   ----- Unsent message follows -----\n", fp, m);
X			(void) fflush(fp);
X			putheader(fp, m, e->e_parent);
X			putline("\n", fp, m);
X			putbody(fp, m, e->e_parent);
X		}
X		else
X		{
X			putline("\n", fp, m);
X			putline("  ----- Message header follows -----\n", fp, m);
X			(void) fflush(fp);
X			putheader(fp, m, e->e_parent);
X		}
X	}
X	else
X	{
X		putline("\n", fp, m);
X		putline("  ----- No message was collected -----\n", fp, m);
X		putline("\n", fp, m);
X	}
X
X	/*
X	**  Cleanup and exit
X	*/
X
X	if (errno != 0)
X		syserr("errbody: I/O error");
X}
END_OF_FILE
if test 11670 -ne `wc -c <'src/savemail.c'`; then
    echo shar: \"'src/savemail.c'\" unpacked with wrong size!
fi
# end of 'src/savemail.c'
fi
echo shar: End of archive 3 \(of 8\).
cp /dev/null ark3isdone
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