DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T d

⟦299930548⟧ TextFile

    Length: 50880 (0xc6c0)
    Types: TextFile
    Names: »deliver.c,v«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦bfebc70e2⟧ »EurOpenD3/mail/sendmail-5.65b+IDA-1.4.3.tar.Z« 
        └─⟦f9e35cd84⟧ 
            └─⟦this⟧ »sendmail/src/RCS/deliver.c,v« 

TextFile

head	5.38;
branch	5.38.0;
access;
symbols
	UICSO:5.38.0
	VANILLA:5.38;
locks; strict;
comment	@ * @;


5.38
date	90.06.20.08.35.40;	author paul;	state Exp;
branches
	5.38.0.1;
next	;

5.38.0.1
date	90.06.20.09.42.49;	author paul;	state Exp;
branches;
next	5.38.0.2;

5.38.0.2
date	90.06.21.13.47.24;	author paul;	state Exp;
branches;
next	5.38.0.3;

5.38.0.3
date	90.06.25.09.20.43;	author paul;	state Exp;
branches;
next	5.38.0.4;

5.38.0.4
date	90.07.09.10.06.16;	author paul;	state Exp;
branches;
next	5.38.0.5;

5.38.0.5
date	90.08.02.12.44.33;	author paul;	state Exp;
branches;
next	5.38.0.6;

5.38.0.6
date	90.09.22.18.40.25;	author paul;	state Exp;
branches;
next	5.38.0.7;

5.38.0.7
date	90.09.25.09.45.07;	author paul;	state Exp;
branches;
next	5.38.0.8;

5.38.0.8
date	90.10.13.18.18.14;	author paul;	state Exp;
branches;
next	5.38.0.9;

5.38.0.9
date	90.11.01.17.14.47;	author paul;	state Exp;
branches;
next	5.38.0.10;

5.38.0.10
date	90.11.19.16.16.28;	author paul;	state Exp;
branches;
next	5.38.0.11;

5.38.0.11
date	90.11.24.02.47.45;	author paul;	state Exp;
branches;
next	5.38.0.12;

5.38.0.12
date	90.11.26.20.39.58;	author paul;	state Exp;
branches;
next	5.38.0.13;

5.38.0.13
date	90.12.30.19.27.12;	author paul;	state Exp;
branches;
next	5.38.0.14;

5.38.0.14
date	91.01.19.19.26.02;	author paul;	state Exp;
branches;
next	5.38.0.15;

5.38.0.15
date	91.02.15.16.59.22;	author paul;	state Exp;
branches;
next	5.38.0.16;

5.38.0.16
date	91.02.17.04.04.34;	author paul;	state Exp;
branches;
next	5.38.0.17;

5.38.0.17
date	91.03.04.21.48.23;	author paul;	state Exp;
branches;
next	5.38.0.18;

5.38.0.18
date	91.03.06.13.58.47;	author paul;	state Exp;
branches;
next	;


desc
@@


5.38
log
@5.64 Berkeley release
@
text
@/*
 * Copyright (c) 1983 Eric P. Allman
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted provided
 * that: (1) source distributions retain this entire copyright notice and
 * comment, and (2) distributions including binaries display the following
 * acknowledgement:  ``This product includes software developed by the
 * University of California, Berkeley and its contributors'' in the
 * documentation or other materials provided with the distribution and in
 * all advertising materials mentioning features or use of this software.
 * Neither the name of the University nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef lint
static char sccsid[] = "@@(#)deliver.c	5.38 (Berkeley) 6/1/90";
#endif /* not lint */

#include "sendmail.h"
#include <sys/signal.h>
#include <sys/stat.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#ifdef NAMED_BIND
#include <arpa/nameser.h>
#include <resolv.h>
#endif

/*
**  DELIVER -- Deliver a message to a list of addresses.
**
**	This routine delivers to everyone on the same host as the
**	user on the head of the list.  It is clever about mailers
**	that don't handle multiple users.  It is NOT guaranteed
**	that it will deliver to all these addresses however -- so
**	deliver should be called once for each address on the
**	list.
**
**	Parameters:
**		e -- the envelope to deliver.
**		firstto -- head of the address list to deliver to.
**
**	Returns:
**		zero -- successfully delivered.
**		else -- some failure, see ExitStat for more info.
**
**	Side Effects:
**		The standard input is passed off to someone.
*/

deliver(e, firstto)
	register ENVELOPE *e;
	ADDRESS *firstto;
{
	char *host;			/* host being sent to */
	char *user;			/* user being sent to */
	char **pvp;
	register char **mvp;
	register char *p;
	register MAILER *m;		/* mailer for this recipient */
	ADDRESS *ctladdr;
	register ADDRESS *to = firstto;
	bool clever = FALSE;		/* running user smtp to this mailer */
	ADDRESS *tochain = NULL;	/* chain of users in this mailer call */
	int rcode;		/* response code */
	char *pv[MAXPV+1];
	char tobuf[MAXLINE-50];		/* text line of to people */
	char buf[MAXNAME];
	char tfrombuf[MAXNAME];		/* translated from person */
	extern bool checkcompat();
	extern ADDRESS *getctladdr();
	extern char *remotename();

	errno = 0;
	if (bitset(QDONTSEND, to->q_flags))
		return (0);

#ifdef NAMED_BIND
	/* unless interactive, try twice, over a minute */
	if (OpMode == MD_DAEMON || OpMode == MD_SMTP) {
		_res.retrans = 30;
		_res.retry = 2;
	}
#endif 

	m = to->q_mailer;
	host = to->q_host;

	if (tTd(10, 1))
		printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
			m->m_mno, host, to->q_user);

	/*
	**  If this mailer is expensive, and if we don't want to make
	**  connections now, just mark these addresses and return.
	**	This is useful if we want to batch connections to
	**	reduce load.  This will cause the messages to be
	**	queued up, and a daemon will come along to send the
	**	messages later.
	**		This should be on a per-mailer basis.
	*/

	if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) &&
	    !Verbose)
	{
		for (; to != NULL; to = to->q_next)
		{
			if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m)
				continue;
			to->q_flags |= QQUEUEUP|QDONTSEND;
			e->e_to = to->q_paddr;
			message(Arpa_Info, "queued");
			if (LogLevel > 4)
				logdelivery("queued");
		}
		e->e_to = NULL;
		return (0);
	}

	/*
	**  Do initial argv setup.
	**	Insert the mailer name.  Notice that $x expansion is
	**	NOT done on the mailer name.  Then, if the mailer has
	**	a picky -f flag, we insert it as appropriate.  This
	**	code does not check for 'pv' overflow; this places a
	**	manifest lower limit of 4 for MAXPV.
	**		The from address rewrite is expected to make
	**		the address relative to the other end.
	*/

	/* rewrite from address, using rewriting rules */
	expand("\001f", buf, &buf[sizeof buf - 1], e);
	(void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE));

	define('g', tfrombuf, e);		/* translated sender address */
	define('h', host, e);			/* to host */
	Errors = 0;
	pvp = pv;
	*pvp++ = m->m_argv[0];

	/* insert -f or -r flag as appropriate */
	if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags)))
	{
		if (bitnset(M_FOPT, m->m_flags))
			*pvp++ = "-f";
		else
			*pvp++ = "-r";
		expand("\001g", buf, &buf[sizeof buf - 1], e);
		*pvp++ = newstr(buf);
	}

	/*
	**  Append the other fixed parts of the argv.  These run
	**  up to the first entry containing "$u".  There can only
	**  be one of these, and there are only a few more slots
	**  in the pv after it.
	*/

	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
	{
		while ((p = index(p, '\001')) != NULL)
			if (*++p == 'u')
				break;
		if (p != NULL)
			break;

		/* this entry is safe -- go ahead and process it */
		expand(*mvp, buf, &buf[sizeof buf - 1], e);
		*pvp++ = newstr(buf);
		if (pvp >= &pv[MAXPV - 3])
		{
			syserr("Too many parameters to %s before $u", pv[0]);
			return (-1);
		}
	}

	/*
	**  If we have no substitution for the user name in the argument
	**  list, we know that we must supply the names otherwise -- and
	**  SMTP is the answer!!
	*/

	if (*mvp == NULL)
	{
		/* running SMTP */
# ifdef SMTP
		clever = TRUE;
		*pvp = NULL;
# else SMTP
		/* oops!  we don't implement SMTP */
		syserr("SMTP style mailer");
		return (EX_SOFTWARE);
# endif SMTP
	}

	/*
	**  At this point *mvp points to the argument with $u.  We
	**  run through our address list and append all the addresses
	**  we can.  If we run out of space, do not fret!  We can
	**  always send another copy later.
	*/

	tobuf[0] = '\0';
	e->e_to = tobuf;
	ctladdr = NULL;
	for (; to != NULL; to = to->q_next)
	{
		/* avoid sending multiple recipients to dumb mailers */
		if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
			break;

		/* if already sent or not for this host, don't send */
		if (bitset(QDONTSEND, to->q_flags) ||
		    strcmp(to->q_host, host) != 0 ||
		    to->q_mailer != firstto->q_mailer)
			continue;

		/* avoid overflowing tobuf */
		if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2))
			break;

		if (tTd(10, 1))
		{
			printf("\nsend to ");
			printaddr(to, FALSE);
		}

		/* compute effective uid/gid when sending */
		if (to->q_mailer == ProgMailer)
			ctladdr = getctladdr(to);

		user = to->q_user;
		e->e_to = to->q_paddr;
		to->q_flags |= QDONTSEND;

		/*
		**  Check to see that these people are allowed to
		**  talk to each other.
		*/

		if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
		{
			NoReturn = TRUE;
			usrerr("Message is too large; %ld bytes max", m->m_maxsize);
			giveresponse(EX_UNAVAILABLE, m, e);
			continue;
		}
		if (!checkcompat(to))
		{
			giveresponse(EX_UNAVAILABLE, m, e);
			continue;
		}

		/*
		**  Strip quote bits from names if the mailer is dumb
		**	about them.
		*/

		if (bitnset(M_STRIPQ, m->m_flags))
		{
			stripquotes(user, TRUE);
			stripquotes(host, TRUE);
		}
		else
		{
			stripquotes(user, FALSE);
			stripquotes(host, FALSE);
		}

		/* hack attack -- delivermail compatibility */
		if (m == ProgMailer && *user == '|')
			user++;

		/*
		**  If an error message has already been given, don't
		**	bother to send to this address.
		**
		**	>>>>>>>>>> This clause assumes that the local mailer
		**	>> NOTE >> cannot do any further aliasing; that
		**	>>>>>>>>>> function is subsumed by sendmail.
		*/

		if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
			continue;

		/* save statistics.... */
		markstats(e, to);

		/*
		**  See if this user name is "special".
		**	If the user name has a slash in it, assume that this
		**	is a file -- send it off without further ado.  Note
		**	that this type of addresses is not processed along
		**	with the others, so we fudge on the To person.
		*/

		if (m == LocalMailer)
		{
			if (user[0] == '/')
			{
				rcode = mailfile(user, getctladdr(to));
				giveresponse(rcode, m, e);
				continue;
			}
		}

		/*
		**  Address is verified -- add this user to mailer
		**  argv, and add it to the print list of recipients.
		*/

		/* link together the chain of recipients */
		to->q_tchain = tochain;
		tochain = to;

		/* create list of users for error messages */
		(void) strcat(tobuf, ",");
		(void) strcat(tobuf, to->q_paddr);
		define('u', user, e);		/* to user */
		define('z', to->q_home, e);	/* user's home */

		/*
		**  Expand out this user into argument list.
		*/

		if (!clever)
		{
			expand(*mvp, buf, &buf[sizeof buf - 1], e);
			*pvp++ = newstr(buf);
			if (pvp >= &pv[MAXPV - 2])
			{
				/* allow some space for trailing parms */
				break;
			}
		}
	}

	/* see if any addresses still exist */
	if (tobuf[0] == '\0')
	{
		define('g', (char *) NULL, e);
		return (0);
	}

	/* print out messages as full list */
	e->e_to = tobuf + 1;

	/*
	**  Fill out any parameters after the $u parameter.
	*/

	while (!clever && *++mvp != NULL)
	{
		expand(*mvp, buf, &buf[sizeof buf - 1], e);
		*pvp++ = newstr(buf);
		if (pvp >= &pv[MAXPV])
			syserr("deliver: pv overflow after $u for %s", pv[0]);
	}
	*pvp++ = NULL;

	/*
	**  Call the mailer.
	**	The argument vector gets built, pipes
	**	are created as necessary, and we fork & exec as
	**	appropriate.
	**	If we are running SMTP, we just need to clean up.
	*/

	if (ctladdr == NULL)
		ctladdr = &e->e_from;
#ifdef NAMED_BIND
	_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);		/* XXX */
#endif
#ifdef SMTP
	if (clever)
	{
		rcode = EX_OK;
#ifdef NAMED_BIND
		if (host[0] && host[0] != '[')
		{
			expand("\001w", buf, &buf[sizeof(buf) - 1], e);
			Nmx = getmxrr(host, MxHosts, buf, &rcode);
		}
		else
#endif
		{
			Nmx = 1;
			MxHosts[0] = host;
		}
		if (Nmx >= 0)
		{
			message(Arpa_Info, "Connecting to %s (%s)...",
			    MxHosts[0], m->m_name);
			if ((rcode = smtpinit(m, pv)) == EX_OK) {
				register char *t = tobuf;
				register int i;

				/* send the recipient list */
				tobuf[0] = '\0';
				for (to = tochain; to; to = to->q_tchain) {
					e->e_to = to->q_paddr;
					if ((i = smtprcpt(to, m)) != EX_OK) {
						markfailure(e, to, i);
						giveresponse(i, m, e);
					}
					else {
						*t++ = ',';
						for (p = to->q_paddr; *p; *t++ = *p++);
					}
				}

				/* now send the data */
				if (tobuf[0] == '\0')
					e->e_to = NULL;
				else {
					e->e_to = tobuf + 1;
					rcode = smtpdata(m, e);
				}

				/* now close the connection */
				smtpquit(m);
			}
		}
	}
	else
#endif /* SMTP */
	{
		message(Arpa_Info, "Connecting to %s (%s)...", host, m->m_name);
		rcode = sendoff(e, m, pv, ctladdr);
	}
#ifdef NAMED_BIND
	_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
#endif

	/*
	**  Do final status disposal.
	**	We check for something in tobuf for the SMTP case.
	**	If we got a temporary failure, arrange to queue the
	**		addressees.
	*/

	if (tobuf[0] != '\0')
		giveresponse(rcode, m, e);
	if (rcode != EX_OK)
		for (to = tochain; to != NULL; to = to->q_tchain)
			markfailure(e, to, rcode);

	errno = 0;
	define('g', (char *) NULL, e);
	return (rcode);
}
\f

/*
**  MARKFAILURE -- mark a failure on a specific address.
**
**	Parameters:
**		e -- the envelope we are sending.
**		q -- the address to mark.
**		rcode -- the code signifying the particular failure.
**
**	Returns:
**		none.
**
**	Side Effects:
**		marks the address (and possibly the envelope) with the
**			failure so that an error will be returned or
**			the message will be queued, as appropriate.
*/

markfailure(e, q, rcode)
	register ENVELOPE *e;
	register ADDRESS *q;
	int rcode;
{
	if (rcode == EX_OK)
		return;
	else if (rcode != EX_TEMPFAIL && rcode != EX_IOERR && rcode != EX_OSERR)
		q->q_flags |= QBADADDR;
	else if (curtime() > e->e_ctime + TimeOut)
	{
		extern char *pintvl();
		char buf[MAXLINE];

		if (!bitset(EF_TIMEOUT, e->e_flags))
		{
			(void) sprintf(buf, "Cannot send message for %s",
				pintvl(TimeOut, FALSE));
			if (e->e_message != NULL)
				free(e->e_message);
			e->e_message = newstr(buf);
			message(Arpa_Info, buf);
		}
		q->q_flags |= QBADADDR;
		e->e_flags |= EF_TIMEOUT;
	}
	else
		q->q_flags |= QQUEUEUP;
}
\f

/*
**  DOFORK -- do a fork, retrying a couple of times on failure.
**
**	This MUST be a macro, since after a vfork we are running
**	two processes on the same stack!!!
**
**	Parameters:
**		none.
**
**	Returns:
**		From a macro???  You've got to be kidding!
**
**	Side Effects:
**		Modifies the ==> LOCAL <== variable 'pid', leaving:
**			pid of child in parent, zero in child.
**			-1 on unrecoverable error.
**
**	Notes:
**		I'm awfully sorry this looks so awful.  That's
**		vfork for you.....
*/

# define NFORKTRIES	5
# ifdef VMUNIX
# define XFORK	vfork
# else VMUNIX
# define XFORK	fork
# endif VMUNIX

# define DOFORK(fORKfN) \
{\
	register int i;\
\
	for (i = NFORKTRIES; --i >= 0; )\
	{\
		pid = fORKfN();\
		if (pid >= 0)\
			break;\
		if (i > 0)\
			sleep((unsigned) NFORKTRIES - i);\
	}\
}
\f

/*
**  DOFORK -- simple fork interface to DOFORK.
**
**	Parameters:
**		none.
**
**	Returns:
**		pid of child in parent.
**		zero in child.
**		-1 on error.
**
**	Side Effects:
**		returns twice, once in parent and once in child.
*/

dofork()
{
	register int pid;

	DOFORK(fork);
	return (pid);
}
\f

/*
**  SENDOFF -- send off call to mailer & collect response.
**
**	Parameters:
**		e -- the envelope to mail.
**		m -- mailer descriptor.
**		pvp -- parameter vector to send to it.
**		ctladdr -- an address pointer controlling the
**			user/groupid etc. of the mailer.
**
**	Returns:
**		exit status of mailer.
**
**	Side Effects:
**		none.
*/
static
sendoff(e, m, pvp, ctladdr)
	register ENVELOPE *e;
	MAILER *m;
	char **pvp;
	ADDRESS *ctladdr;
{
	auto FILE *mfile;
	auto FILE *rfile;
	register int i;
	int pid;

	/*
	**  Create connection to mailer.
	*/

	pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile);
	if (pid < 0)
		return (-1);

	/*
	**  Format and send message.
	*/

	putfromline(mfile, m);
	(*e->e_puthdr)(mfile, m, e);
	putline("\n", mfile, m);
	(*e->e_putbody)(mfile, m, e);
	(void) fclose(mfile);
	if (rfile != NULL)
		(void) fclose(rfile);

	i = endmailer(pid, pvp[0]);

	/* arrange a return receipt if requested */
	if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
	{
		e->e_flags |= EF_SENDRECEIPT;
		/* do we want to send back more info? */
	}

	return (i);
}
\f

/*
**  ENDMAILER -- Wait for mailer to terminate.
**
**	We should never get fatal errors (e.g., segmentation
**	violation), so we report those specially.  For other
**	errors, we choose a status message (into statmsg),
**	and if it represents an error, we print it.
**
**	Parameters:
**		pid -- pid of mailer.
**		name -- name of mailer (for error messages).
**
**	Returns:
**		exit code of mailer.
**
**	Side Effects:
**		none.
*/

endmailer(pid, name)
	int pid;
	char *name;
{
	int st;

	/* in the IPC case there is nothing to wait for */
	if (pid == 0)
		return (EX_OK);

	/* wait for the mailer process to die and collect status */
	st = waitfor(pid);
	if (st == -1)
	{
		syserr("endmailer %s: wait", name);
		return (EX_SOFTWARE);
	}

	/* see if it died a horrid death */
	if ((st & 0377) != 0)
	{
		syserr("mailer %s died with signal %o", name, st);
		ExitStat = EX_TEMPFAIL;
		return (EX_TEMPFAIL);
	}

	/* normal death -- return status */
	st = (st >> 8) & 0377;
	return (st);
}
\f

/*
**  OPENMAILER -- open connection to mailer.
**
**	Parameters:
**		m -- mailer descriptor.
**		pvp -- parameter vector to pass to mailer.
**		ctladdr -- controlling address for user.
**		clever -- create a full duplex connection.
**		pmfile -- pointer to mfile (to mailer) connection.
**		prfile -- pointer to rfile (from mailer) connection.
**
**	Returns:
**		pid of mailer ( > 0 ).
**		-1 on error.
**		zero on an IPC connection.
**
**	Side Effects:
**		creates a mailer in a subprocess.
*/

openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
	MAILER *m;
	char **pvp;
	ADDRESS *ctladdr;
	bool clever;
	FILE **pmfile;
	FILE **prfile;
{
	int pid;
	int mpvect[2];
	int rpvect[2];
	FILE *mfile = NULL;
	FILE *rfile = NULL;
	extern FILE *fdopen();

	if (tTd(11, 1))
	{
		printf("openmailer:");
		printav(pvp);
	}
	errno = 0;

	CurHostName = m->m_mailer;

	/*
	**  Deal with the special case of mail handled through an IPC
	**  connection.
	**	In this case we don't actually fork.  We must be
	**	running SMTP for this to work.  We will return a
	**	zero pid to indicate that we are running IPC.
	**  We also handle a debug version that just talks to stdin/out.
	*/

	/* check for Local Person Communication -- not for mortals!!! */
	if (strcmp(m->m_mailer, "[LPC]") == 0)
	{
		*pmfile = stdout;
		*prfile = stdin;
		return (0);
	}

	if (strcmp(m->m_mailer, "[IPC]") == 0)
	{
#ifdef HOSTINFO
		register STAB *st;
		extern STAB *stab();
#endif HOSTINFO
#ifdef DAEMON
		register int i, j;
		register u_short port;

		CurHostName = pvp[1];
		if (!clever)
			syserr("non-clever IPC");
		if (pvp[2] != NULL)
			port = atoi(pvp[2]);
		else
			port = 0;
		for (j = 0; j < Nmx; j++)
		{
			CurHostName = MxHosts[j];
#ifdef HOSTINFO
		/* see if we have already determined that this host is fried */
			st = stab(MxHosts[j], ST_HOST, ST_FIND);
			if (st == NULL || st->s_host.ho_exitstat == EX_OK) {
				if (j > 1)
					message(Arpa_Info,
					    "Connecting to %s (%s)...",
					    MxHosts[j], m->m_name);
				i = makeconnection(MxHosts[j], port, pmfile, prfile);
			}
			else
			{
				i = st->s_host.ho_exitstat;
				errno = st->s_host.ho_errno;
			}
#else HOSTINFO
			i = makeconnection(MxHosts[j], port, pmfile, prfile);
#endif HOSTINFO
			if (i != EX_OK)
			{
#ifdef HOSTINFO
				/* enter status of this host */
				if (st == NULL)
					st = stab(MxHosts[j], ST_HOST, ST_ENTER);
				st->s_host.ho_exitstat = i;
				st->s_host.ho_errno = errno;
#endif HOSTINFO
				ExitStat = i;
				continue;
			}
			else
				return (0);
		}
		return (-1);
#else DAEMON
		syserr("openmailer: no IPC");
		return (-1);
#endif DAEMON
	}

	/* create a pipe to shove the mail through */
	if (pipe(mpvect) < 0)
	{
		syserr("openmailer: pipe (to mailer)");
		return (-1);
	}

#ifdef SMTP
	/* if this mailer speaks smtp, create a return pipe */
	if (clever && pipe(rpvect) < 0)
	{
		syserr("openmailer: pipe (from mailer)");
		(void) close(mpvect[0]);
		(void) close(mpvect[1]);
		return (-1);
	}
#endif SMTP

	/*
	**  Actually fork the mailer process.
	**	DOFORK is clever about retrying.
	**
	**	Dispose of SIGCHLD signal catchers that may be laying
	**	around so that endmail will get it.
	*/

	if (CurEnv->e_xfp != NULL)
		(void) fflush(CurEnv->e_xfp);		/* for debugging */
	(void) fflush(stdout);
# ifdef SIGCHLD
	(void) signal(SIGCHLD, SIG_DFL);
# endif SIGCHLD
	DOFORK(XFORK);
	/* pid is set by DOFORK */
	if (pid < 0)
	{
		/* failure */
		syserr("openmailer: cannot fork");
		(void) close(mpvect[0]);
		(void) close(mpvect[1]);
#ifdef SMTP
		if (clever)
		{
			(void) close(rpvect[0]);
			(void) close(rpvect[1]);
		}
#endif SMTP
		return (-1);
	}
	else if (pid == 0)
	{
		int i;
		extern int DtableSize;

		/* child -- set up input & exec mailer */
		/* make diagnostic output be standard output */
		(void) signal(SIGINT, SIG_IGN);
		(void) signal(SIGHUP, SIG_IGN);
		(void) signal(SIGTERM, SIG_DFL);

		/* arrange to filter standard & diag output of command */
		if (clever)
		{
			(void) close(rpvect[0]);
			(void) close(1);
			(void) dup(rpvect[1]);
			(void) close(rpvect[1]);
		}
		else if (OpMode == MD_SMTP || HoldErrs)
		{
			/* put mailer output in transcript */
			(void) close(1);
			(void) dup(fileno(CurEnv->e_xfp));
		}
		(void) close(2);
		(void) dup(1);

		/* arrange to get standard input */
		(void) close(mpvect[1]);
		(void) close(0);
		if (dup(mpvect[0]) < 0)
		{
			syserr("Cannot dup to zero!");
			_exit(EX_OSERR);
		}
		(void) close(mpvect[0]);
		if (!bitnset(M_RESTR, m->m_flags))
		{
			if (ctladdr == NULL || ctladdr->q_uid == 0)
			{
				(void) setgid(DefGid);
				(void) initgroups(DefUser, DefGid);
				(void) setuid(DefUid);
			}
			else
			{
				(void) setgid(ctladdr->q_gid);
				(void) initgroups(ctladdr->q_ruser?
					ctladdr->q_ruser: ctladdr->q_user,
					ctladdr->q_gid);
				(void) setuid(ctladdr->q_uid);
			}
		}

		/* arrange for all the files to be closed */
		for (i = 3; i < DtableSize; i++) {
			register int j;
			if ((j = fcntl(i, F_GETFD, 0)) != -1)
				(void)fcntl(i, F_SETFD, j|1);
		}

		/* try to execute the mailer */
		execve(m->m_mailer, pvp, UserEnviron);
		syserr("Cannot exec %s", m->m_mailer);
		if (m == LocalMailer || errno == EIO || errno == EAGAIN ||
		    errno == ENOMEM || errno == EPROCLIM)
			_exit(EX_TEMPFAIL);
		else
			_exit(EX_UNAVAILABLE);
	}

	/*
	**  Set up return value.
	*/

	(void) close(mpvect[0]);
	mfile = fdopen(mpvect[1], "w");
	if (clever)
	{
		(void) close(rpvect[1]);
		rfile = fdopen(rpvect[0], "r");
	} else
		rfile = NULL;

	*pmfile = mfile;
	*prfile = rfile;

	return (pid);
}
\f

/*
**  GIVERESPONSE -- Interpret an error response from a mailer
**
**	Parameters:
**		stat -- the status code from the mailer (high byte
**			only; core dumps must have been taken care of
**			already).
**		m -- the mailer descriptor for this mailer.
**
**	Returns:
**		none.
**
**	Side Effects:
**		Errors may be incremented.
**		ExitStat may be set.
*/

giveresponse(stat, m, e)
	int stat;
	register MAILER *m;
	ENVELOPE *e;
{
	register char *statmsg;
	extern char *SysExMsg[];
	register int i;
	extern int N_SysEx;
#ifdef NAMED_BIND
	extern int h_errno;
#endif
	char buf[MAXLINE];

#ifdef lint
	if (m == NULL)
		return;
#endif lint

	/*
	**  Compute status message from code.
	*/

	i = stat - EX__BASE;
	if (stat == 0)
		statmsg = "250 Sent";
	else if (i < 0 || i > N_SysEx)
	{
		(void) sprintf(buf, "554 unknown mailer error %d", stat);
		stat = EX_UNAVAILABLE;
		statmsg = buf;
	}
	else if (stat == EX_TEMPFAIL)
	{
		(void) strcpy(buf, SysExMsg[i]);
#ifdef NAMED_BIND
		if (h_errno == TRY_AGAIN)
		{
			extern char *errstring();

			statmsg = errstring(h_errno+MAX_ERRNO);
		}
		else
#endif
		{
			if (errno != 0)
			{
				extern char *errstring();

				statmsg = errstring(errno);
			}
			else
			{
#ifdef SMTP
				extern char SmtpError[];

				statmsg = SmtpError;
#else SMTP
				statmsg = NULL;
#endif SMTP
			}
		}
		if (statmsg != NULL && statmsg[0] != '\0')
		{
			(void) strcat(buf, ": ");
			(void) strcat(buf, statmsg);
		}
		statmsg = buf;
	}
	else
	{
		statmsg = SysExMsg[i];
	}

	/*
	**  Print the message as appropriate
	*/

	if (stat == EX_OK || stat == EX_TEMPFAIL)
		message(Arpa_Info, &statmsg[4]);
	else
	{
		Errors++;
		usrerr(statmsg);
	}

	/*
	**  Final cleanup.
	**	Log a record of the transaction.  Compute the new
	**	ExitStat -- if we already had an error, stick with
	**	that.
	*/

	if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
		logdelivery(&statmsg[4]);

	if (stat != EX_TEMPFAIL)
		setstat(stat);
	if (stat != EX_OK)
	{
		if (e->e_message != NULL)
			free(e->e_message);
		e->e_message = newstr(&statmsg[4]);
	}
	errno = 0;
#ifdef NAMED_BIND
	h_errno = 0;
#endif
}
\f

/*
**  LOGDELIVERY -- log the delivery in the system log
**
**	Parameters:
**		stat -- the message to print for the status
**
**	Returns:
**		none
**
**	Side Effects:
**		none
*/

logdelivery(stat)
	char *stat;
{
	extern char *pintvl();

# ifdef LOG
	syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id,
	       CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);
# endif LOG
}
\f

/*
**  PUTFROMLINE -- output a UNIX-style from line (or whatever)
**
**	This can be made an arbitrary message separator by changing $l
**
**	One of the ugliest hacks seen by human eyes is contained herein:
**	UUCP wants those stupid "remote from <host>" lines.  Why oh why
**	does a well-meaning programmer such as myself have to deal with
**	this kind of antique garbage????
**
**	Parameters:
**		fp -- the file to output to.
**		m -- the mailer describing this entry.
**
**	Returns:
**		none
**
**	Side Effects:
**		outputs some text to fp.
*/

putfromline(fp, m)
	register FILE *fp;
	register MAILER *m;
{
	char *template = "\001l\n";
	char buf[MAXLINE];

	if (bitnset(M_NHDR, m->m_flags))
		return;

# ifdef UGLYUUCP
	if (bitnset(M_UGLYUUCP, m->m_flags))
	{
		char *bang;
		char xbuf[MAXLINE];

		expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
		bang = index(buf, '!');
		if (bang == NULL)
			syserr("No ! in UUCP! (%s)", buf);
		else
		{
			*bang++ = '\0';
			(void) sprintf(xbuf, "From %s  \001d remote from %s\n", bang, buf);
			template = xbuf;
		}
	}
# endif UGLYUUCP
	expand(template, buf, &buf[sizeof buf - 1], CurEnv);
	putline(buf, fp, m);
}
\f

/*
**  PUTBODY -- put the body of a message.
**
**	Parameters:
**		fp -- file to output onto.
**		m -- a mailer descriptor to control output format.
**		e -- the envelope to put out.
**
**	Returns:
**		none.
**
**	Side Effects:
**		The message is written onto fp.
*/

putbody(fp, m, e)
	FILE *fp;
	MAILER *m;
	register ENVELOPE *e;
{
	char buf[MAXLINE];

	/*
	**  Output the body of the message
	*/

	if (e->e_dfp == NULL)
	{
		if (e->e_df != NULL)
		{
			e->e_dfp = fopen(e->e_df, "r");
			if (e->e_dfp == NULL)
				syserr("putbody: Cannot open %s for %s from %s",
				e->e_df, e->e_to, e->e_from);
		}
		else
			putline("<<< No Message Collected >>>", fp, m);
	}
	if (e->e_dfp != NULL)
	{
		rewind(e->e_dfp);
		while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
		{
			if (buf[0] == 'F' && bitnset(M_ESCFROM, m->m_flags) &&
			    strncmp(buf, "From ", 5) == 0)
				(void) putc('>', fp);
			putline(buf, fp, m);
		}

		if (ferror(e->e_dfp))
		{
			syserr("putbody: read error");
			ExitStat = EX_IOERR;
		}
	}

	(void) fflush(fp);
	if (ferror(fp) && errno != EPIPE)
	{
		syserr("putbody: write error");
		ExitStat = EX_IOERR;
	}
	errno = 0;
}
\f

/*
**  MAILFILE -- Send a message to a file.
**
**	If the file has the setuid/setgid bits set, but NO execute
**	bits, sendmail will try to become the owner of that file
**	rather than the real user.  Obviously, this only works if
**	sendmail runs as root.
**
**	This could be done as a subordinate mailer, except that it
**	is used implicitly to save messages in ~/dead.letter.  We
**	view this as being sufficiently important as to include it
**	here.  For example, if the system is dying, we shouldn't have
**	to create another process plus some pipes to save the message.
**
**	Parameters:
**		filename -- the name of the file to send to.
**		ctladdr -- the controlling address header -- includes
**			the userid/groupid to be when sending.
**
**	Returns:
**		The exit code associated with the operation.
**
**	Side Effects:
**		none.
*/

mailfile(filename, ctladdr)
	char *filename;
	ADDRESS *ctladdr;
{
	register FILE *f;
	register int pid;
	ENVELOPE *e = CurEnv;

	/*
	**  Fork so we can change permissions here.
	**	Note that we MUST use fork, not vfork, because of
	**	the complications of calling subroutines, etc.
	*/

	DOFORK(fork);

	if (pid < 0)
		return (EX_OSERR);
	else if (pid == 0)
	{
		/* child -- actually write to file */
		struct stat stb;

		(void) signal(SIGINT, SIG_DFL);
		(void) signal(SIGHUP, SIG_DFL);
		(void) signal(SIGTERM, SIG_DFL);
		(void) umask(OldUmask);
		if (stat(filename, &stb) < 0)
		{
			errno = 0;
			stb.st_mode = 0666;
		}
		if (bitset(0111, stb.st_mode))
			exit(EX_CANTCREAT);
		if (ctladdr == NULL)
			ctladdr = &e->e_from;
		/* we have to open the dfile BEFORE setuid */
		if (e->e_dfp == NULL &&  e->e_df != NULL)
		{
			e->e_dfp = fopen(e->e_df, "r");
			if (e->e_dfp == NULL) {
				syserr("mailfile: Cannot open %s for %s from %s",
				e->e_df, e->e_to, e->e_from);
			}
		}

		if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0)
		{
			if (ctladdr->q_uid == 0) {
				(void) setgid(DefGid);
				(void) initgroups(DefUser, DefGid);
			} else {
				(void) setgid(ctladdr->q_gid);
				(void) initgroups(ctladdr->q_ruser?
					ctladdr->q_ruser: ctladdr->q_user,
					ctladdr->q_gid);
			}
		}
		if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0)
		{
			if (ctladdr->q_uid == 0)
				(void) setuid(DefUid);
			else
				(void) setuid(ctladdr->q_uid);
		}
		f = dfopen(filename, "a");
		if (f == NULL)
			exit(EX_CANTCREAT);

		putfromline(f, ProgMailer);
		(*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
		putline("\n", f, ProgMailer);
		(*CurEnv->e_putbody)(f, ProgMailer, CurEnv);
		putline("\n", f, ProgMailer);
		(void) fclose(f);
		(void) fflush(stdout);

		/* reset ISUID & ISGID bits for paranoid systems */
		(void) chmod(filename, (int) stb.st_mode);
		exit(EX_OK);
		/*NOTREACHED*/
	}
	else
	{
		/* parent -- wait for exit status */
		int st;

		st = waitfor(pid);
		if ((st & 0377) != 0)
			return (EX_UNAVAILABLE);
		else
			return ((st >> 8) & 0377);
		/*NOTREACHED*/
	}
}
\f

/*
**  SENDALL -- actually send all the messages.
**
**	Parameters:
**		e -- the envelope to send.
**		mode -- the delivery mode to use.  If SM_DEFAULT, use
**			the current SendMode.
**
**	Returns:
**		none.
**
**	Side Effects:
**		Scans the send lists and sends everything it finds.
**		Delivers any appropriate error messages.
**		If we are running in a non-interactive mode, takes the
**			appropriate action.
*/

sendall(e, mode)
	ENVELOPE *e;
	char mode;
{
	register ADDRESS *q;
	bool oldverbose;
	int pid;
	FILE *lockfp = NULL, *queueup();

	/* determine actual delivery mode */
	if (mode == SM_DEFAULT)
	{
		extern bool shouldqueue();

		if (shouldqueue(e->e_msgpriority))
			mode = SM_QUEUE;
		else
			mode = SendMode;
	}

	if (tTd(13, 1))
	{
		printf("\nSENDALL: mode %c, sendqueue:\n", mode);
		printaddr(e->e_sendqueue, TRUE);
	}

	/*
	**  Do any preprocessing necessary for the mode we are running.
	**	Check to make sure the hop count is reasonable.
	**	Delete sends to the sender in mailing lists.
	*/

	CurEnv = e;

	if (e->e_hopcount > MAXHOP)
	{
		errno = 0;
		syserr("sendall: too many hops %d (%d max): from %s, to %s",
			e->e_hopcount, MAXHOP, e->e_from, e->e_to);
		return;
	}

	if (!MeToo)
	{
		extern ADDRESS *recipient();

		e->e_from.q_flags |= QDONTSEND;
		(void) recipient(&e->e_from, &e->e_sendqueue);
	}

# ifdef QUEUE
	if ((mode == SM_QUEUE || mode == SM_FORK ||
	     (mode != SM_VERIFY && SuperSafe)) &&
	    !bitset(EF_INQUEUE, e->e_flags))
		lockfp = queueup(e, TRUE, mode == SM_QUEUE);
#endif QUEUE

	oldverbose = Verbose;
	switch (mode)
	{
	  case SM_VERIFY:
		Verbose = TRUE;
		break;

	  case SM_QUEUE:
		e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
		return;

	  case SM_FORK:
		if (e->e_xfp != NULL)
			(void) fflush(e->e_xfp);
		pid = fork();
		if (pid < 0)
		{
			mode = SM_DELIVER;
			break;
		}
		else if (pid > 0)
		{
			/* be sure we leave the temp files to our child */
			e->e_id = e->e_df = NULL;
			if (lockfp != NULL)
				(void) fclose(lockfp);
			return;
		}

		/* double fork to avoid zombies */
		if (fork() > 0)
			exit(EX_OK);

		/* be sure we are immune from the terminal */
		disconnect(FALSE);

		break;
	}

	/*
	**  Run through the list and send everything.
	*/

	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
	{
		if (mode == SM_VERIFY)
		{
			e->e_to = q->q_paddr;
			if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
				message(Arpa_Info, "deliverable");
		}
		else
			(void) deliver(e, q);
	}
	Verbose = oldverbose;

	/*
	**  Now run through and check for errors.
	*/

	if (mode == SM_VERIFY) {
		if (lockfp != NULL)
			(void) fclose(lockfp);
		return;
	}

	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
	{
		register ADDRESS *qq;

		if (tTd(13, 3))
		{
			printf("Checking ");
			printaddr(q, FALSE);
		}

		/* only send errors if the message failed */
		if (!bitset(QBADADDR, q->q_flags))
			continue;

		/* we have an address that failed -- find the parent */
		for (qq = q; qq != NULL; qq = qq->q_alias)
		{
			char obuf[MAXNAME + 6];
			extern char *aliaslookup();

			/* we can only have owners for local addresses */
			if (!bitnset(M_LOCAL, qq->q_mailer->m_flags))
				continue;

			/* see if the owner list exists */
			(void) strcpy(obuf, "owner-");
			if (strncmp(qq->q_user, "owner-", 6) == 0)
				(void) strcat(obuf, "owner");
			else
				(void) strcat(obuf, qq->q_user);
			makelower(obuf);
			if (aliaslookup(obuf) == NULL)
				continue;

			if (tTd(13, 4))
				printf("Errors to %s\n", obuf);

			/* owner list exists -- add it to the error queue */
			sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
			ErrorMode = EM_MAIL;
			break;
		}

		/* if we did not find an owner, send to the sender */
		if (qq == NULL && bitset(QBADADDR, q->q_flags))
			sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue);
	}

	/* this removes the lock on the file */
	if (lockfp != NULL)
		(void) fclose(lockfp);

	if (mode == SM_FORK)
		finis();
}
@


5.38.0.1
log
@IDA patches
@
text
@d34 1
a34 1
#endif  /* NAMED_BIND */
a36 53
**  Status error messages
*/
#define MAXENDERR	(sizeof(Enderr) / sizeof(*Enderr))
char *Enderr[] = {
	"IMPOSSIBLE",
	/* SIGHUP */	"hangup",
	/* SIGINT */	"interrupt",
	/* SIGQUIT */	"quit",
	/* SIGILL */	"illegal instruction",
	/* SIGTRAP */	"trace trap",
	/* SIGIOT */	"IOT instruction",
	/* SIGEMT */	"EMT instruction",
	/* SIGFPE */	"floating point exception",
	/* SIGKILL */	"kill",
	/* SIGBUS */	"bus error",
	/* SIGSEGV */	"segmentation violation",
	/* SIGSYS */	"bad argument to system call",
	/* SIGPIPE */	"write on a pipe with no one to read it",
	/* SIGALRM */	"alarm clock",
	/* SIGTERM */	"software termination signal",
	/* SIGURG */	"urgent condition present on socket",
	/* SIGSTOP */	"stop",
	/* SIGTSTP */	"stop signal generated from keyboard",
	/* SIGCONT */	"continue after stop",
	/* SIGCHLD */	"child status has changed",
	/* SIGTTIN */	"background read attempted from control terminal",
	/* SIGTTOU */	"background write attempted to control terminal",
	/* SIGIO */	"I/O is possible on a descriptor",
	/* SIGXCPU */	"cpu time limit exceeded",
	/* SIGXFSZ */	"file size limit exceeded",
	/* SIGVTALRM */	"virtual time alarm",
	/* SIGPROF */	"profiling timer alarm",
	/* SIGWINCH */	"window changed",
	/* SIGLOST */	"resource lost",
	/* SIGUSR1 */	"user-defined signal 1",
	/* SIGUSR2 */	"user-defined signal 2"
};

#ifdef	NAMED_BIND
/*
**  Name server error messages
*/
#define MAXH_ERR		(sizeof(H_Errmsg) / sizeof(*H_Errmsg))
char *H_Errmsg[] = {
	/* XXX */		"[Unknown error]",
	/* HOST_NOT_FOUND */	"Authoritative answer from name server",
	/* TRY_AGAIN */		"Non-authoritiatve answer or name server failure",
	/* NO_RECOVERY */	"Non recoverable name server error",
	/* NO_DATA */		"Valid name but no data [address]"
};
#endif	/* NAMED_BIND */

/*
d91 1
a91 1
#endif  /* NAMED_BIND */
d140 1
a140 1
	(void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE, FALSE));
d196 1
a196 1
# else /* !SMTP */
d200 1
a200 1
# endif /* SMTP */
d379 2
a380 2
	/* _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
#endif  /* NAMED_BIND */
d392 1
a392 1
#endif  /* NAMED_BIND */
d413 1
a413 2
					else
					{
d422 1
a422 2
				else
				{
d439 2
a440 2
	/* _res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
#endif  /* NAMED_BIND */
d530 1
a530 1
# else /* !VMUNIX */
d532 1
a532 1
# endif /* VMUNIX */
d668 1
a668 3
		syserr("%s died because of %s (%d)--requeueing message",
		    name, ((st >= 0) && (st < MAXENDERR)) ?
		    Enderr[st] : "unknown error code", st);
d743 1
a743 1
#endif /* HOSTINFO */
d773 1
a773 1
#else /* !HOSTINFO */
d775 1
a775 1
#endif /* HOSTINFO */
a777 16
				/*
				 * Consider the case of multiple MX entries
				 * for a given host where the last entry refers
				 * to non-existent host.  On occasions when
				 * none of the hosts are reachable, the mail
				 * will bounce if the last ExitStat is
				 * EX_NOHOST.  Handle this by resetting i to
				 * EX_TEMPFAIL if it's not the primary MX entry
				 * and it's the last MX entry.  -pbp
				 */
#ifdef LOG
				if (i == EX_NOHOST)
					syslog(LOG_WARNING, "Found non-existent host %s in MX records for %s", CurHostName, pvp[1]);
#endif /* LOG */
				if (j != 0 && j == (Nmx - 1))
					i = EX_TEMPFAIL;
d784 1
a784 1
#endif /* HOSTINFO */
d792 1
a792 1
#else /* !DAEMON */
d795 1
a795 1
#endif /* DAEMON */
d814 1
a814 1
#endif /* SMTP */
d829 1
a829 1
# endif /* SIGCHLD */
d844 1
a844 1
#endif /* SMTP */
d965 1
a965 1
#endif	/* NAMED_BIND */
d971 1
a971 1
#endif /* lint */
d997 1
a997 1
#endif	/* NAMED_BIND */
d1011 1
a1011 1
#else /* !SMTP */
d1013 1
a1013 1
#endif /* SMTP */
a1035 2
		extern char Arpa_Usrerr[];

d1037 1
a1037 7
#ifdef	NAMED_BIND
		if (stat == EX_NOHOST && h_errno != 0)
			usrerr("%s (%s)", statmsg,
				H_Errmsg[h_errno > MAXH_ERR ? 0 : h_errno]);
		else
#endif	/* NAMED_BIND */
			usrerr(statmsg);
d1061 1
a1061 1
#endif	/* NAMED_BIND */
d1084 1
a1084 1
# endif /* LOG */
d1111 1
a1111 4
	extern char *macvalue();
	char *oldg = macvalue('g', CurEnv);
	char template[MAXLINE];
	char newg[MAXLINE];
a1113 2
	strcpy(template, "\001l\n");

a1116 12
	/* construct path through us if needed */
	if (bitnset(M_FROMPATH, m->m_flags)) {
		char myname[MAXLINE];

		expand("\001k", myname, &myname[sizeof myname - 1], CurEnv);
		if (index(oldg, '!') == NULL
		    || strncmp(oldg, myname, strlen(myname)) != 0) {
			sprintf(newg, "%s!%s", myname, oldg);
			define('g', newg, CurEnv);
		}
	}

d1121 1
d1126 1
a1126 2
			syserr("No `!' in UUCP envelope \"from\" address! (%s)",
			    buf);
d1130 2
a1131 2
			(void) sprintf(template,
			    "From %s  \001d remote from %s\n", bang, buf);
d1134 1
a1134 1
# endif /* UGLYUUCP */
a1136 4

	/* redefine old from address */
	if (bitnset(M_FROMPATH, m->m_flags))
		define('g', oldg, CurEnv);
d1396 1
a1396 1
#endif /* QUEUE */
@


5.38.0.2
log
@Date: Wed, 20 Jun 90 15:39:37 -0400 (EDT)
From: Craig_Everhart@@transarc.com
To: paul@@uxc.cso.uiuc.edu (Paul Pomes - UofIllinois CSO)
Subject: Re: Sendmail V5.64 + IDA enhancements available for anon-FTP

Here's a sendmail patch for you that I've been sending around.  I've
been running it for years, and it was only when Peter Neumann had such
trouble with RISKS duplications that I decided to try to propagate it.

The problem addressed by the fix is that if sendmail delivers to a long
address list (in a single request) but crashes in the middle, it has no
record of the successful deliveries that it has made so far, so when it
eventually gets around to retrying that request, it re-delivers to all
the addresses to which it had been able to deliver earlier.  Not only
does this generate duplicate mail, but the re-delivery process also
requires additional queue processing time, so that it might continue to
take a long time even to attempt delivery to the last address in the
list.

The solution that this patch builds is simple and not very expensive:
after every successful mail delivery, the qf* file is rewritten,
omitting the recipient to whom delivery was successful.  The qf* file
gets shorter even as the request is processed.  A crash in the middle of
processing the list will result in duplicate delivery to at most one
host.  It doesn't require that much time to rewrite the qf* file;
certainly less time than the delivery itself takes.

The only real disadvantage I can imagine is that my sendmail sources
have diverged in many ways from a UCB version of about 1986. 
Nonetheless, it shouldn't be difficult to install this change.

As you can see, this change works by adding an additional flag parameter
to queueup(), then calling queueup() (from sendall()) with this
parameter set sometimes.  The queueup() flag parameter controls which
addresses are rewritten, looking at different flags.

So here's the patch.  I hope that you find it useful.  I look forward to
your reply.

		Thanks,
		Craig Everhart
@
text
@d1448 1
a1448 1
	bool oldverbose, DoingMore;
d1497 1
a1497 1
		lockfp = queueup(e, TRUE, mode == SM_QUEUE, FALSE);
a1542 1
	DoingMore = FALSE;
d1551 2
a1552 10
		else if (!bitset(QDONTSEND, q->q_flags))
		{
			int estat;
			if (DoingMore)
				lockfp = queueup(e, TRUE, FALSE, TRUE);
			DoingMore = FALSE;
			estat = deliver(e, q);	/* Queueup if any delivered */
			if (estat == EX_OK)
				DoingMore = TRUE;
		}
@


5.38.0.3
log
@Patches for HP-UX from Andy Linton <root@@comp.vuw.ac.nz>.  Thanks Andy!
@
text
@d985 2
a986 6
                if (m == LocalMailer || errno == EIO || errno == EAGAIN ||
#if !defined(hpux)
                    errno == EPROCLIM ||
#endif /* hpux */
                    errno == ENOMEM)

@


5.38.0.4
log
@Un-do queue file re-writing.
@
text
@d1452 1
a1452 1
	bool oldverbose;
d1501 1
a1501 1
		lockfp = queueup(e, TRUE, mode == SM_QUEUE);
d1547 1
d1556 10
a1565 2
		else
			(void) deliver(e, q);
@


5.38.0.5
log
@Change comparison for a local MX record to use $j instead of $w.
Suggested by Neil Rickert, NIU.
@
text
@d441 1
a441 2
			/* Compare MX records against $j and not $w */
			expand("\001j", buf, &buf[sizeof(buf) - 1], e);
@


5.38.0.6
log
@First revisions to support HEAD and MULT extensions to SMTP for DEC's
mail11v3 program.
@
text
@d432 1
a432 1
	_res.options &= ~(RES_DEFNAMES | RES_DNSRCH);	/* XXX */
d455 1
a455 6
#ifdef MAIL11V3
			if ((rcode = smtpinit(m, pv, e)) == EX_OK)
#else /* ! MAIL11V3 */
			if ((rcode = smtpinit(m, pv)) == EX_OK)
#endif /* MAIL11V3 */
			{
d461 1
a461 2
				for (to = tochain; to; to = to->q_tchain)
				{
d463 1
a463 2
					if ((i = smtprcpt(to, m)) != EX_OK)
					{
a480 27
#ifdef MAIL11V3
					SmtpPhase = "result wait";
					setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
					if (rcode == EX_OK)
					{
						if (!SmtpManyStatus)
							rcode = smtpstat(m);
						else for (tobuf[0] = '\0',
						    to = tochain; to != NULL;
						    to = to->q_tchain) {
							int j;

							e->e_to = to->q_paddr;
							j = smtpstat(m);
							if (j != EX_OK)
							{
								markfailure(e, to, j);
								giveresponse(j, m, e);
							}
							else
							{
								(void) strcat(tobuf, ",");
								(void) strcat(tobuf, to->q_paddr);
							}
						}
					}
#endif /* MAIL11V3 */
d495 1
a495 1
	_res.options |= RES_DEFNAMES | RES_DNSRCH;	/* XXX */
@


5.38.0.7
log
@Suppress call to getmxrr() for the case of a non-IPC mailer as well as for
domain literals.
@
text
@d439 1
a439 5
		/*
		** Don't do MX lookups on domain literals or for non-IPC
		** mailers.
		*/ 
		if (host[0] && host[0] != '[' && *m->m_mailer != '/')
@


5.38.0.8
log
@Deleted newlines from putline calls (Bruce Lilly), added Convex share scheduler
support (Jon Roma).
@
text
@d25 1
a26 1
#include "sendmail.h"
d32 2
a33 2
# include <arpa/nameser.h>
# include <resolv.h>
a34 3
#if defined(__convex__) && defined(SHARE)
# include <shares.h>
#endif /* __convex__ && SHARE */
a132 1
	static sendoff();
d658 1
a658 1
	register int pid = 0;
d705 1
a705 1
	putline("", mfile, m);
d801 1
a801 1
	int pid = 0;
a999 4
#if defined(__convex__) && defined(SHARE)
				if (setupshares(DefShareUid, syserr))
					syserr("Can't install shares!\n");
#endif /* __convex__ && SHARE */
a1005 4
#if defined(__convex__) && defined(SHARE)
				if (setupshares(DefShareUid, syserr))
					syserr("Can't install shares!\n");
#endif /* __convex__ && SHARE */
d1025 3
a1027 3
#if defined(EPROCLIM)
							errno == EPROCLIM ||
# endif /* EPROCLIM */
d1378 1
a1378 1
	register int pid = 0;
d1444 1
a1444 1
		putline("", f, ProgMailer);
d1446 1
a1446 1
		putline("", f, ProgMailer);
@


5.38.0.9
log
@Don't pass constant strings to putline().
@
text
@d1253 1
a1253 1
	(void) strcpy(template, "\001l\n");
d1331 1
a1331 4
		{
			(void) strcpy(buf, "<<< No Message Collected >>>");
			putline(buf, fp, m);
		}
@


5.38.0.10
log
@Fixed lockf()/fcntl() emulation of flock() across fork() calls.  In the
case of emulated flock(), the lock is released prior to the fork() and
re-locked afterwards.  If the re-lock fails, which should be rare, do
nothing and return.  Andy Linton (andy.linton@@comp.vuw.ac.nz), provided
the X-Open Portability Guide #3 changes (#ifdef XPG3) plus a fix to
limit the size of syslog() arguments.
@
text
@d82 1
a82 1
# define MAXH_ERR		(sizeof(H_Errmsg) / sizeof(*H_Errmsg))
d250 1
a250 1
#ifdef SMTP
d253 1
a253 1
#else /* !SMTP */
d257 1
a257 1
#endif /* SMTP */
d442 1
a442 1
# ifdef NAMED_BIND
d454 1
a454 1
# endif  /* NAMED_BIND */
d463 1
a463 1
# ifdef MAIL11V3
d465 1
a465 1
# else /* ! MAIL11V3 */
d467 1
a467 1
# endif /* MAIL11V3 */
d496 1
a496 1
# ifdef MAIL11V3
d522 1
a522 1
# endif /* MAIL11V3 */
d625 2
a626 2
#define NFORKTRIES	5
#ifdef VMUNIX
d628 1
a628 1
#else /* !VMUNIX */
d630 1
a630 1
#endif /* VMUNIX */
d632 1
a632 1
#define DOFORK(fORKfN) \
d858 1
a858 1
# ifdef HOSTINFO
d873 1
a873 1
# else /* !HOSTINFO */
d875 1
a875 1
# endif /* HOSTINFO */
d888 1
a888 1
# ifdef LOG
d891 1
a891 1
# endif /* LOG */
d894 1
a894 1
# ifdef HOSTINFO
d900 1
a900 1
# endif /* HOSTINFO */
d943 1
a943 1
#ifdef SIGCHLD
d945 1
a945 1
#endif /* SIGCHLD */
d966 1
d1027 1
a1027 5
#if defined(XPG3)
                for (i = (int) sysconf (_SC_OPEN_MAX); i > 2; --i) {
#else
                for (i = getdtablesize(); i > 2; --i) {
#endif /* XPG3 */
d1039 1
a1039 1
#endif /* EPROCLIM */
a1215 2
# define TBUFLEN 128
        char tbuf[TBUFLEN];
a1216 9
	/*
	** If there's a very long To: line the buffer in syslog() on HPUX
	** (and others?) isn't large enough.  Truncate long To: lines since
	** they are likely to be of little use anyway.
	*/
	if (strlen (CurEnv->e_to) >= TBUFLEN)
		sprintf(tbuf, "%.*s ...", TBUFLEN-5, CurEnv->e_to);
	else
		(void) strcpy(tbuf, CurEnv->e_to);
d1219 1
a1219 1
	       tbuf, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);
d1270 1
a1270 1
#ifdef UGLYUUCP
d1287 1
a1287 1
#endif /* UGLYUUCP */
d1551 1
a1551 1
#ifdef QUEUE
a1571 10

#if defined(FCNTL_FLOCK) || defined(LOCKF_FLOCK)
		/*
		** lockf()/fcntl() emulation of flock() breaks down here as
		** locks are not inherited across fork().  release lock so
		** child can re-lock.
		**/
 		if (lockfp != NULL)
 			(void) flock(fileno(lockfp), LOCK_UN);
#endif /* (FCNTL_FLOCK) || (LOCKF_FLOCK) */
a1589 12

#if defined(FCNTL_FLOCK) || defined(LOCKF_FLOCK) 
		/*
		** re-lock tf file in child (again for flock() as lockf())
		*/
		if (lockfp != NULL && flock(fileno(lockfp), LOCK_EX|LOCK_NB) <0)
		{
			/* someone else got in before us */
			(void) fclose(lockfp);
			return;
		}
#endif /* (FCNTL_FLOCK) || (LOCKF_FLOCK) */
@


5.38.0.11
log
@Fixed forward declaration of sendoff().
@
text
@a112 1
int sendoff();
d136 1
@


5.38.0.12
log
@Deleted un-needed initialization.
@
text
@d808 2
a809 1
	FILE *mfile, *rfile;
@


5.38.0.13
log
@Replaced logdelivery() with version from Piet Beertema that folds rather
than truncates long lines.
@
text
@d1218 2
a1219 3
# ifdef LOG
#  define LOGSPLIT 900
	register char *p, *q;
d1222 3
a1224 2
	** Split up long To: lines, since the buffer in
	** syslog() on various systems isn't large enough.
d1226 5
a1230 13
	p = CurEnv->e_to;
	while (strlen(p) >= LOGSPLIT)
	{
		if ((q = index(p + LOGSPLIT, ',')) != NULL)
		{
			syslog(LOG_INFO, "%s: to=%.*s(cont'd), delay=%s, stat=%s",
				CurEnv->e_id, q - p + 1, p,
				pintvl(curtime() - CurEnv->e_ctime, TRUE),
				stat);
				p = q + 1;
		} else
			break;
	}
d1232 1
a1232 1
		p, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);
@


5.38.0.14
log
@Deleted #include <sys/types.h> as it's already included via sendmail.h from
useful.h.  #include "sendmail.h" relocated to top of #include list.
@
text
@d25 1
a26 1
#include <sys/signal.h>
@


5.38.0.15
log
@Fixed declaration of setupshares() for convex.  Fixed test for printing
message when connecting to a non-primary MX host.
@
text
@a36 2
extern void syserr();
extern setupshares(int, void (*func)());
d861 1
a861 1
				if (j > 0)
d1004 1
a1004 1
					syserr("Can't install shares!");
d1014 1
a1014 1
					syserr("Can't install shares!");
@


5.38.0.16
log
@Added static keywork to declaration for mailfile() and markfailure().
@
text
@a138 1
	static markfailure(), mailfile();
a575 1
static
a1410 1
static
@


5.38.0.17
log
@ANSIfied.
@
text
@d37 2
a38 1
int setupshares(int, void (*func)());
a40 10
#ifdef __STDC__
static mailfile(char *, ADDRESS *);
static void markfailure(ENVELOPE *, ADDRESS *, int);
static sendoff(ENVELOPE *, MAILER *, char **, ADDRESS *);
#else /* !__STDC__ */
static mailfile();
static void markfailure();
static sendoff();
#endif /* __STDC__ */

d115 1
d136 4
d577 1
a577 1
static void
d589 1
d646 1
a646 1
			Xsleep((unsigned) NFORKTRIES - i);\
d751 1
a751 1
	const char *name;
d813 1
d845 1
a950 2
	if (pid > 0 && tTd(4, 2))
		printf("openmailer: forking (pid = %d)\n", pid);
a1087 1
void
d1125 3
d1129 1
d1134 3
d1138 1
a1217 1
void
d1219 1
a1219 1
	const char *stat;
d1221 1
a1267 1
void
d1272 1
a1334 1
void
a1429 2
	if (pid > 0 && tTd(4, 2))
		printf("mailfile: forking (pid = %d)\n", pid);
a1526 1
void
d1534 1
a1534 1
	FILE *lockfp = NULL;
d1539 2
d1571 2
a1608 2
		if (pid > 0 && tTd(4, 2))
			printf("sendall: forking (pid = %d)\n", pid);
d1690 1
@


5.38.0.18
log
@In sendall(), remember to close lockfp if the message is queued.  From
Kannan Varadhan .
@
text
@a1588 2
		if (lockfp != NULL)
			(void) fclose(lockfp);
@