recipient.c revision 64562
138032Speter/*
264562Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1438032Speter#ifndef lint
1564562Sgshapirostatic char id[] = "@(#)$Id: recipient.c,v 8.231.14.5 2000/06/27 20:15:46 gshapiro Exp $";
1664562Sgshapiro#endif /* ! lint */
1738032Speter
1864562Sgshapiro#include <sendmail.h>
1938032Speter
2064562Sgshapiro
2164562Sgshapirostatic void	includetimeout __P((void));
2264562Sgshapirostatic ADDRESS	*self_reference __P((ADDRESS *));
2364562Sgshapiro
2438032Speter/*
2538032Speter**  SENDTOLIST -- Designate a send list.
2638032Speter**
2738032Speter**	The parameter is a comma-separated list of people to send to.
2838032Speter**	This routine arranges to send to all of them.
2938032Speter**
3038032Speter**	Parameters:
3138032Speter**		list -- the send list.
3238032Speter**		ctladdr -- the address template for the person to
3338032Speter**			send to -- effective uid/gid are important.
3438032Speter**			This is typically the alias that caused this
3538032Speter**			expansion.
3638032Speter**		sendq -- a pointer to the head of a queue to put
3738032Speter**			these people into.
3838032Speter**		aliaslevel -- the current alias nesting depth -- to
3938032Speter**			diagnose loops.
4038032Speter**		e -- the envelope in which to add these recipients.
4138032Speter**
4238032Speter**	Returns:
4338032Speter**		The number of addresses actually on the list.
4438032Speter**
4538032Speter**	Side Effects:
4638032Speter**		none.
4738032Speter*/
4838032Speter
4938032Speter/* q_flags bits inherited from ctladdr */
5038032Speter#define QINHERITEDBITS	(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY)
5138032Speter
5238032Speterint
5338032Spetersendtolist(list, ctladdr, sendq, aliaslevel, e)
5438032Speter	char *list;
5538032Speter	ADDRESS *ctladdr;
5638032Speter	ADDRESS **sendq;
5738032Speter	int aliaslevel;
5838032Speter	register ENVELOPE *e;
5938032Speter{
6038032Speter	register char *p;
6138032Speter	register ADDRESS *al;	/* list of addresses to send to */
6238032Speter	char delimiter;		/* the address delimiter */
6338032Speter	int naddrs;
6438032Speter	int i;
6538032Speter	char *oldto = e->e_to;
6638032Speter	char *bufp;
6738032Speter	char buf[MAXNAME + 1];
6838032Speter
6938032Speter	if (list == NULL)
7038032Speter	{
7138032Speter		syserr("sendtolist: null list");
7238032Speter		return 0;
7338032Speter	}
7438032Speter
7538032Speter	if (tTd(25, 1))
7638032Speter	{
7764562Sgshapiro		dprintf("sendto: %s\n   ctladdr=", list);
7838032Speter		printaddr(ctladdr, FALSE);
7938032Speter	}
8038032Speter
8138032Speter	/* heuristic to determine old versus new style addresses */
8238032Speter	if (ctladdr == NULL &&
8338032Speter	    (strchr(list, ',') != NULL || strchr(list, ';') != NULL ||
8438032Speter	     strchr(list, '<') != NULL || strchr(list, '(') != NULL))
8538032Speter		e->e_flags &= ~EF_OLDSTYLE;
8638032Speter	delimiter = ' ';
8738032Speter	if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL)
8838032Speter		delimiter = ',';
8938032Speter
9038032Speter	al = NULL;
9138032Speter	naddrs = 0;
9238032Speter
9338032Speter	/* make sure we have enough space to copy the string */
9438032Speter	i = strlen(list) + 1;
9538032Speter	if (i <= sizeof buf)
9664562Sgshapiro	{
9738032Speter		bufp = buf;
9864562Sgshapiro		i = sizeof buf;
9964562Sgshapiro	}
10038032Speter	else
10138032Speter		bufp = xalloc(i);
10264562Sgshapiro	(void) strlcpy(bufp, denlstring(list, FALSE, TRUE), i);
10338032Speter
10464562Sgshapiro#if _FFR_ADDR_TYPE
10564562Sgshapiro	define(macid("{addr_type}", NULL), "e r", e);
10664562Sgshapiro#endif /* _FFR_ADDR_TYPE */
10738032Speter	for (p = bufp; *p != '\0'; )
10838032Speter	{
10938032Speter		auto char *delimptr;
11038032Speter		register ADDRESS *a;
11138032Speter
11238032Speter		/* parse the address */
11338032Speter		while ((isascii(*p) && isspace(*p)) || *p == ',')
11438032Speter			p++;
11538032Speter		a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e);
11638032Speter		p = delimptr;
11738032Speter		if (a == NULL)
11838032Speter			continue;
11938032Speter		a->q_next = al;
12038032Speter		a->q_alias = ctladdr;
12138032Speter
12238032Speter		/* arrange to inherit attributes from parent */
12338032Speter		if (ctladdr != NULL)
12438032Speter		{
12538032Speter			ADDRESS *b;
12638032Speter
12738032Speter			/* self reference test */
12838032Speter			if (sameaddr(ctladdr, a))
12938032Speter			{
13038032Speter				if (tTd(27, 5))
13138032Speter				{
13264562Sgshapiro					dprintf("sendtolist: QSELFREF ");
13338032Speter					printaddr(ctladdr, FALSE);
13438032Speter				}
13538032Speter				ctladdr->q_flags |= QSELFREF;
13638032Speter			}
13738032Speter
13838032Speter			/* check for address loops */
13964562Sgshapiro			b = self_reference(a);
14038032Speter			if (b != NULL)
14138032Speter			{
14238032Speter				b->q_flags |= QSELFREF;
14338032Speter				if (tTd(27, 5))
14438032Speter				{
14564562Sgshapiro					dprintf("sendtolist: QSELFREF ");
14638032Speter					printaddr(b, FALSE);
14738032Speter				}
14838032Speter				if (a != b)
14938032Speter				{
15038032Speter					if (tTd(27, 5))
15138032Speter					{
15264562Sgshapiro						dprintf("sendtolist: QS_DONTSEND ");
15338032Speter						printaddr(a, FALSE);
15438032Speter					}
15564562Sgshapiro					a->q_state = QS_DONTSEND;
15638032Speter					b->q_flags |= a->q_flags & QNOTREMOTE;
15738032Speter					continue;
15838032Speter				}
15938032Speter			}
16038032Speter
16138032Speter			/* full name */
16238032Speter			if (a->q_fullname == NULL)
16338032Speter				a->q_fullname = ctladdr->q_fullname;
16438032Speter
16538032Speter			/* various flag bits */
16638032Speter			a->q_flags &= ~QINHERITEDBITS;
16738032Speter			a->q_flags |= ctladdr->q_flags & QINHERITEDBITS;
16838032Speter
16938032Speter			/* original recipient information */
17038032Speter			a->q_orcpt = ctladdr->q_orcpt;
17138032Speter		}
17238032Speter
17338032Speter		al = a;
17438032Speter	}
17538032Speter
17638032Speter	/* arrange to send to everyone on the local send list */
17738032Speter	while (al != NULL)
17838032Speter	{
17938032Speter		register ADDRESS *a = al;
18038032Speter
18138032Speter		al = a->q_next;
18238032Speter		a = recipient(a, sendq, aliaslevel, e);
18338032Speter		naddrs++;
18438032Speter	}
18538032Speter
18638032Speter	e->e_to = oldto;
18738032Speter	if (bufp != buf)
18838032Speter		free(bufp);
18964562Sgshapiro#if _FFR_ADDR_TYPE
19064562Sgshapiro	define(macid("{addr_type}", NULL), NULL, e);
19164562Sgshapiro#endif /* _FFR_ADDR_TYPE */
19264562Sgshapiro	return naddrs;
19338032Speter}
19438032Speter/*
19564562Sgshapiro**  REMOVEFROMLIST -- Remove addresses from a send list.
19664562Sgshapiro**
19764562Sgshapiro**	The parameter is a comma-separated list of recipients to remove.
19864562Sgshapiro**	Note that it only deletes matching addresses.  If those addresses
19964562Sgshapiro**	have been expended already in the sendq, it won't mark the
20064562Sgshapiro**	expanded recipients as QS_REMOVED.
20164562Sgshapiro**
20264562Sgshapiro**	Parameters:
20364562Sgshapiro**		list -- the list to remove.
20464562Sgshapiro**		sendq -- a pointer to the head of a queue to remove
20564562Sgshapiro**			these addresses from.
20664562Sgshapiro**		e -- the envelope in which to remove these recipients.
20764562Sgshapiro**
20864562Sgshapiro**	Returns:
20964562Sgshapiro**		The number of addresses removed from the list.
21064562Sgshapiro**
21164562Sgshapiro*/
21264562Sgshapiro
21364562Sgshapiroint
21464562Sgshapiroremovefromlist(list, sendq, e)
21564562Sgshapiro	char *list;
21664562Sgshapiro	ADDRESS **sendq;
21764562Sgshapiro	ENVELOPE *e;
21864562Sgshapiro{
21964562Sgshapiro	char delimiter;		/* the address delimiter */
22064562Sgshapiro	int naddrs;
22164562Sgshapiro	int i;
22264562Sgshapiro	char *p;
22364562Sgshapiro	char *oldto = e->e_to;
22464562Sgshapiro	char *bufp;
22564562Sgshapiro	char buf[MAXNAME + 1];
22664562Sgshapiro
22764562Sgshapiro	if (list == NULL)
22864562Sgshapiro	{
22964562Sgshapiro		syserr("removefromlist: null list");
23064562Sgshapiro		return 0;
23164562Sgshapiro	}
23264562Sgshapiro
23364562Sgshapiro	if (tTd(25, 1))
23464562Sgshapiro		dprintf("removefromlist: %s\n", list);
23564562Sgshapiro
23664562Sgshapiro	/* heuristic to determine old versus new style addresses */
23764562Sgshapiro	if (strchr(list, ',') != NULL || strchr(list, ';') != NULL ||
23864562Sgshapiro	    strchr(list, '<') != NULL || strchr(list, '(') != NULL)
23964562Sgshapiro		e->e_flags &= ~EF_OLDSTYLE;
24064562Sgshapiro	delimiter = ' ';
24164562Sgshapiro	if (!bitset(EF_OLDSTYLE, e->e_flags))
24264562Sgshapiro		delimiter = ',';
24364562Sgshapiro
24464562Sgshapiro	naddrs = 0;
24564562Sgshapiro
24664562Sgshapiro	/* make sure we have enough space to copy the string */
24764562Sgshapiro	i = strlen(list) + 1;
24864562Sgshapiro	if (i <= sizeof buf)
24964562Sgshapiro	{
25064562Sgshapiro		bufp = buf;
25164562Sgshapiro		i = sizeof buf;
25264562Sgshapiro	}
25364562Sgshapiro	else
25464562Sgshapiro		bufp = xalloc(i);
25564562Sgshapiro	(void) strlcpy(bufp, denlstring(list, FALSE, TRUE), i);
25664562Sgshapiro
25764562Sgshapiro#if _FFR_ADDR_TYPE
25864562Sgshapiro	define(macid("{addr_type}", NULL), "e r", e);
25964562Sgshapiro#endif /* _FFR_ADDR_TYPE */
26064562Sgshapiro	for (p = bufp; *p != '\0'; )
26164562Sgshapiro	{
26264562Sgshapiro		ADDRESS a;		/* parsed address to be removed */
26364562Sgshapiro		ADDRESS *q;
26464562Sgshapiro		ADDRESS **pq;
26564562Sgshapiro		char *delimptr;
26664562Sgshapiro
26764562Sgshapiro		/* parse the address */
26864562Sgshapiro		while ((isascii(*p) && isspace(*p)) || *p == ',')
26964562Sgshapiro			p++;
27064562Sgshapiro		if (parseaddr(p, &a, RF_COPYALL,
27164562Sgshapiro			      delimiter, &delimptr, e) == NULL)
27264562Sgshapiro		{
27364562Sgshapiro			p = delimptr;
27464562Sgshapiro			continue;
27564562Sgshapiro		}
27664562Sgshapiro		p = delimptr;
27764562Sgshapiro		for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
27864562Sgshapiro		{
27964562Sgshapiro			if (!QS_IS_DEAD(q->q_state) &&
28064562Sgshapiro			    sameaddr(q, &a))
28164562Sgshapiro			{
28264562Sgshapiro				if (tTd(25, 5))
28364562Sgshapiro				{
28464562Sgshapiro					dprintf("removefromlist: QS_REMOVED ");
28564562Sgshapiro					printaddr(&a, FALSE);
28664562Sgshapiro				}
28764562Sgshapiro				q->q_state = QS_REMOVED;
28864562Sgshapiro				naddrs++;
28964562Sgshapiro				break;
29064562Sgshapiro			}
29164562Sgshapiro		}
29264562Sgshapiro	}
29364562Sgshapiro
29464562Sgshapiro	e->e_to = oldto;
29564562Sgshapiro	if (bufp != buf)
29664562Sgshapiro		free(bufp);
29764562Sgshapiro#if _FFR_ADDR_TYPE
29864562Sgshapiro	define(macid("{addr_type}", NULL), NULL, e);
29964562Sgshapiro#endif /* _FFR_ADDR_TYPE */
30064562Sgshapiro	return naddrs;
30164562Sgshapiro}
30264562Sgshapiro/*
30338032Speter**  RECIPIENT -- Designate a message recipient
30438032Speter**
30538032Speter**	Saves the named person for future mailing.
30638032Speter**
30738032Speter**	Parameters:
30838032Speter**		a -- the (preparsed) address header for the recipient.
30938032Speter**		sendq -- a pointer to the head of a queue to put the
31064562Sgshapiro**			recipient in.  Duplicate suppression is done
31138032Speter**			in this queue.
31238032Speter**		aliaslevel -- the current alias nesting depth.
31338032Speter**		e -- the current envelope.
31438032Speter**
31538032Speter**	Returns:
31638032Speter**		The actual address in the queue.  This will be "a" if
31738032Speter**		the address is not a duplicate, else the original address.
31838032Speter**
31938032Speter**	Side Effects:
32038032Speter**		none.
32138032Speter*/
32238032Speter
32338032SpeterADDRESS *
32438032Speterrecipient(a, sendq, aliaslevel, e)
32538032Speter	register ADDRESS *a;
32638032Speter	register ADDRESS **sendq;
32738032Speter	int aliaslevel;
32838032Speter	register ENVELOPE *e;
32938032Speter{
33038032Speter	register ADDRESS *q;
33138032Speter	ADDRESS **pq;
33238032Speter	register struct mailer *m;
33364562Sgshapiro	register char *p = NULL;
33438032Speter	bool quoted = FALSE;		/* set if the addr has a quote bit */
33538032Speter	int findusercount = 0;
33664562Sgshapiro	bool initialdontsend = QS_IS_DEAD(a->q_state);
33764562Sgshapiro	int i, buflen;
33838032Speter	char *buf;
33938032Speter	char buf0[MAXNAME + 1];		/* unquoted image of the user name */
34038032Speter
34138032Speter	e->e_to = a->q_paddr;
34238032Speter	m = a->q_mailer;
34338032Speter	errno = 0;
34438032Speter	if (aliaslevel == 0)
34538032Speter		a->q_flags |= QPRIMARY;
34638032Speter	if (tTd(26, 1))
34738032Speter	{
34864562Sgshapiro		dprintf("\nrecipient (%d): ", aliaslevel);
34938032Speter		printaddr(a, FALSE);
35038032Speter	}
35138032Speter
35238032Speter	/* if this is primary, add it to the original recipient list */
35338032Speter	if (a->q_alias == NULL)
35438032Speter	{
35538032Speter		if (e->e_origrcpt == NULL)
35638032Speter			e->e_origrcpt = a->q_paddr;
35738032Speter		else if (e->e_origrcpt != a->q_paddr)
35838032Speter			e->e_origrcpt = "";
35938032Speter	}
36038032Speter
36164562Sgshapiro#if _FFR_GEN_ORCPT
36264562Sgshapiro	/* set ORCPT DSN arg if not already set */
36364562Sgshapiro	if (a->q_orcpt == NULL)
36464562Sgshapiro	{
36564562Sgshapiro		for (q = a; q->q_alias != NULL; q = q->q_alias)
36664562Sgshapiro			continue;
36764562Sgshapiro
36864562Sgshapiro		/* check for an existing ORCPT */
36964562Sgshapiro		if (q->q_orcpt != NULL)
37064562Sgshapiro			a->q_orcpt = q->q_orcpt;
37164562Sgshapiro		else
37264562Sgshapiro		{
37364562Sgshapiro			/* make our own */
37464562Sgshapiro			bool b = FALSE;
37564562Sgshapiro			char *qp;
37664562Sgshapiro			char obuf[MAXLINE];
37764562Sgshapiro
37864562Sgshapiro			if (e->e_from.q_mailer != NULL)
37964562Sgshapiro				p = e->e_from.q_mailer->m_addrtype;
38064562Sgshapiro			if (p == NULL)
38164562Sgshapiro				p = "rfc822";
38264562Sgshapiro			(void) strlcpy(obuf, p, sizeof obuf);
38364562Sgshapiro			(void) strlcat(obuf, ";", sizeof obuf);
38464562Sgshapiro
38564562Sgshapiro			qp = q->q_paddr;
38664562Sgshapiro
38764562Sgshapiro			/* FFR: Needs to strip comments from stdin addrs */
38864562Sgshapiro
38964562Sgshapiro			/* strip brackets from address */
39064562Sgshapiro			if (*qp == '<')
39164562Sgshapiro			{
39264562Sgshapiro				b = qp[strlen(qp) - 1] == '>';
39364562Sgshapiro				if (b)
39464562Sgshapiro					qp[strlen(qp) - 1] = '\0';
39564562Sgshapiro				qp++;
39664562Sgshapiro			}
39764562Sgshapiro
39864562Sgshapiro			p = xtextify(denlstring(qp, TRUE, FALSE), NULL);
39964562Sgshapiro
40064562Sgshapiro			if (strlcat(obuf, p, sizeof obuf) >= sizeof obuf)
40164562Sgshapiro			{
40264562Sgshapiro				/* if too big, don't use it */
40364562Sgshapiro				obuf[0] = '\0';
40464562Sgshapiro			}
40564562Sgshapiro
40664562Sgshapiro			/* undo damage */
40764562Sgshapiro			if (b)
40864562Sgshapiro				qp[strlen(qp)] = '>';
40964562Sgshapiro
41064562Sgshapiro			if (obuf[0] != '\0')
41164562Sgshapiro				a->q_orcpt = newstr(obuf);
41264562Sgshapiro		}
41364562Sgshapiro	}
41464562Sgshapiro#endif /* _FFR_GEN_ORCPT */
41564562Sgshapiro
41638032Speter	/* break aliasing loops */
41738032Speter	if (aliaslevel > MaxAliasRecursion)
41838032Speter	{
41964562Sgshapiro		a->q_state = QS_BADADDR;
42038032Speter		a->q_status = "5.4.6";
42164562Sgshapiro		usrerrenh(a->q_status,
42264562Sgshapiro			  "554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
42364562Sgshapiro			  aliaslevel, MaxAliasRecursion);
42464562Sgshapiro		return a;
42538032Speter	}
42638032Speter
42738032Speter	/*
42838032Speter	**  Finish setting up address structure.
42938032Speter	*/
43038032Speter
43138032Speter	/* get unquoted user for file, program or user.name check */
43238032Speter	i = strlen(a->q_user);
43338032Speter	if (i >= sizeof buf0)
43464562Sgshapiro	{
43564562Sgshapiro		buflen = i + 1;
43664562Sgshapiro		buf = xalloc(buflen);
43764562Sgshapiro	}
43838032Speter	else
43964562Sgshapiro	{
44038032Speter		buf = buf0;
44164562Sgshapiro		buflen = sizeof buf0;
44264562Sgshapiro	}
44364562Sgshapiro	(void) strlcpy(buf, a->q_user, buflen);
44438032Speter	for (p = buf; *p != '\0' && !quoted; p++)
44538032Speter	{
44638032Speter		if (*p == '\\')
44738032Speter			quoted = TRUE;
44838032Speter	}
44938032Speter	stripquotes(buf);
45038032Speter
45138032Speter	/* check for direct mailing to restricted mailers */
45238032Speter	if (m == ProgMailer)
45338032Speter	{
45438032Speter		if (a->q_alias == NULL)
45538032Speter		{
45664562Sgshapiro			a->q_state = QS_BADADDR;
45738032Speter			a->q_status = "5.7.1";
45864562Sgshapiro			usrerrenh(a->q_status,
45964562Sgshapiro				  "550 Cannot mail directly to programs");
46038032Speter		}
46138032Speter		else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
46238032Speter		{
46364562Sgshapiro			a->q_state = QS_BADADDR;
46438032Speter			a->q_status = "5.7.1";
46538032Speter			if (a->q_alias->q_ruser == NULL)
46664562Sgshapiro				usrerrenh(a->q_status,
46764562Sgshapiro					  "550 UID %d is an unknown user: cannot mail to programs",
46864562Sgshapiro					  a->q_alias->q_uid);
46938032Speter			else
47064562Sgshapiro				usrerrenh(a->q_status,
47164562Sgshapiro					  "550 User %s@%s doesn't have a valid shell for mailing to programs",
47264562Sgshapiro					  a->q_alias->q_ruser, MyHostName);
47338032Speter		}
47438032Speter		else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
47538032Speter		{
47664562Sgshapiro			a->q_state = QS_BADADDR;
47738032Speter			a->q_status = "5.7.1";
47864562Sgshapiro			a->q_rstatus = newstr("550 Unsafe for mailing to programs");
47964562Sgshapiro			usrerrenh(a->q_status,
48064562Sgshapiro				  "550 Address %s is unsafe for mailing to programs",
48164562Sgshapiro				  a->q_alias->q_paddr);
48238032Speter		}
48338032Speter	}
48438032Speter
48538032Speter	/*
48638032Speter	**  Look up this person in the recipient list.
48738032Speter	**	If they are there already, return, otherwise continue.
48838032Speter	**	If the list is empty, just add it.  Notice the cute
48938032Speter	**	hack to make from addresses suppress things correctly:
49064562Sgshapiro	**	the QS_DUPLICATE state will be set in the send list.
49138032Speter	**	[Please note: the emphasis is on "hack."]
49238032Speter	*/
49338032Speter
49438032Speter	for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
49538032Speter	{
49638032Speter		if (sameaddr(q, a) &&
49738032Speter		    (bitset(QRCPTOK, q->q_flags) ||
49838032Speter		     !bitset(QPRIMARY, q->q_flags)))
49938032Speter		{
50038032Speter			if (tTd(26, 1))
50138032Speter			{
50264562Sgshapiro				dprintf("%s in sendq: ", a->q_paddr);
50338032Speter				printaddr(q, FALSE);
50438032Speter			}
50538032Speter			if (!bitset(QPRIMARY, q->q_flags))
50638032Speter			{
50764562Sgshapiro				if (!QS_IS_DEAD(a->q_state))
50838032Speter					message("duplicate suppressed");
50964562Sgshapiro				else
51064562Sgshapiro					q->q_state = QS_DUPLICATE;
51138032Speter				q->q_flags |= a->q_flags;
51238032Speter			}
51364562Sgshapiro			else if (bitset(QSELFREF, q->q_flags)
51464562Sgshapiro#if _FFR_MILTER
51564562Sgshapiro				 || q->q_state == QS_REMOVED
51664562Sgshapiro#endif /* _FFR_MILTER */
51764562Sgshapiro				 )
51864562Sgshapiro			{
51964562Sgshapiro#if _FFR_MILTER
52064562Sgshapiro				/*
52164562Sgshapiro				**  If an earlier milter removed the address,
52264562Sgshapiro				**  a later one can still add it back.
52364562Sgshapiro				*/
52464562Sgshapiro#endif /* _FFR_MILTER */
52564562Sgshapiro				q->q_state = a->q_state;
52664562Sgshapiro				q->q_flags |= a->q_flags;
52764562Sgshapiro			}
52838032Speter			a = q;
52938032Speter			goto done;
53038032Speter		}
53138032Speter	}
53238032Speter
53338032Speter	/* add address on list */
53442575Speter	if (pq != NULL)
53542575Speter	{
53642575Speter		*pq = a;
53742575Speter		a->q_next = NULL;
53842575Speter	}
53938032Speter
54038032Speter	/*
54138032Speter	**  Alias the name and handle special mailer types.
54238032Speter	*/
54338032Speter
54438032Speter  trylocaluser:
54538032Speter	if (tTd(29, 7))
54642575Speter	{
54764562Sgshapiro		dprintf("at trylocaluser: ");
54842575Speter		printaddr(a, FALSE);
54942575Speter	}
55038032Speter
55164562Sgshapiro	if (!QS_IS_OK(a->q_state))
55238032Speter		goto testselfdestruct;
55338032Speter
55438032Speter	if (m == InclMailer)
55538032Speter	{
55664562Sgshapiro		a->q_state = QS_INCLUDED;
55738032Speter		if (a->q_alias == NULL)
55838032Speter		{
55964562Sgshapiro			a->q_state = QS_BADADDR;
56038032Speter			a->q_status = "5.7.1";
56164562Sgshapiro			usrerrenh(a->q_status,
56264562Sgshapiro				  "550 Cannot mail directly to :include:s");
56338032Speter		}
56438032Speter		else
56538032Speter		{
56638032Speter			int ret;
56738032Speter
56838032Speter			message("including file %s", a->q_user);
56938032Speter			ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e);
57038032Speter			if (transienterror(ret))
57138032Speter			{
57238032Speter				if (LogLevel > 2)
57338032Speter					sm_syslog(LOG_ERR, e->e_id,
57464562Sgshapiro						  "include %s: transient error: %s",
57564562Sgshapiro						  shortenstring(a->q_user, MAXSHORTSTR),
57664562Sgshapiro						  errstring(ret));
57764562Sgshapiro				a->q_state = QS_QUEUEUP;
57864562Sgshapiro				usrerr("451 4.2.4 Cannot open %s: %s",
57938032Speter					shortenstring(a->q_user, MAXSHORTSTR),
58038032Speter					errstring(ret));
58138032Speter			}
58238032Speter			else if (ret != 0)
58338032Speter			{
58464562Sgshapiro				a->q_state = QS_BADADDR;
58538032Speter				a->q_status = "5.2.4";
58664562Sgshapiro				usrerrenh(a->q_status,
58764562Sgshapiro					  "550 Cannot open %s: %s",
58864562Sgshapiro					  shortenstring(a->q_user, MAXSHORTSTR),
58964562Sgshapiro					  errstring(ret));
59038032Speter			}
59138032Speter		}
59238032Speter	}
59338032Speter	else if (m == FileMailer)
59438032Speter	{
59538032Speter		/* check if writable or creatable */
59638032Speter		if (a->q_alias == NULL)
59738032Speter		{
59864562Sgshapiro			a->q_state = QS_BADADDR;
59938032Speter			a->q_status = "5.7.1";
60064562Sgshapiro			usrerrenh(a->q_status,
60164562Sgshapiro				  "550 Cannot mail directly to files");
60238032Speter		}
60338032Speter		else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
60438032Speter		{
60564562Sgshapiro			a->q_state = QS_BADADDR;
60638032Speter			a->q_status = "5.7.1";
60738032Speter			if (a->q_alias->q_ruser == NULL)
60864562Sgshapiro				usrerrenh(a->q_status,
60964562Sgshapiro					  "550 UID %d is an unknown user: cannot mail to files",
61064562Sgshapiro					  a->q_alias->q_uid);
61138032Speter			else
61264562Sgshapiro				usrerrenh(a->q_status,
61364562Sgshapiro					  "550 User %s@%s doesn't have a valid shell for mailing to files",
61464562Sgshapiro					  a->q_alias->q_ruser, MyHostName);
61538032Speter		}
61638032Speter		else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
61738032Speter		{
61864562Sgshapiro			a->q_state = QS_BADADDR;
61938032Speter			a->q_status = "5.7.1";
62064562Sgshapiro			a->q_rstatus = newstr("550 Unsafe for mailing to files");
62164562Sgshapiro			usrerrenh(a->q_status,
62264562Sgshapiro				  "550 Address %s is unsafe for mailing to files",
62364562Sgshapiro				  a->q_alias->q_paddr);
62438032Speter		}
62538032Speter	}
62638032Speter
62738032Speter	/* try aliasing */
62864562Sgshapiro	if (!quoted && QS_IS_OK(a->q_state) &&
62938032Speter	    bitnset(M_ALIASABLE, m->m_flags))
63038032Speter		alias(a, sendq, aliaslevel, e);
63138032Speter
63264562Sgshapiro#if USERDB
63338032Speter	/* if not aliased, look it up in the user database */
63464562Sgshapiro	if (!bitset(QNOTREMOTE, a->q_flags) &&
63564562Sgshapiro	    QS_IS_SENDABLE(a->q_state) &&
63638032Speter	    bitnset(M_CHECKUDB, m->m_flags))
63738032Speter	{
63838032Speter		if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL)
63938032Speter		{
64064562Sgshapiro			a->q_state = QS_QUEUEUP;
64138032Speter			if (e->e_message == NULL)
64238032Speter				e->e_message = newstr("Deferred: user database error");
64338032Speter			if (LogLevel > 8)
64438032Speter				sm_syslog(LOG_INFO, e->e_id,
64564562Sgshapiro					  "deferred: udbexpand: %s",
64664562Sgshapiro					  errstring(errno));
64738032Speter			message("queued (user database error): %s",
64838032Speter				errstring(errno));
64938032Speter			e->e_nrcpts++;
65038032Speter			goto testselfdestruct;
65138032Speter		}
65238032Speter	}
65364562Sgshapiro#endif /* USERDB */
65438032Speter
65538032Speter	/*
65638032Speter	**  If we have a level two config file, then pass the name through
65738032Speter	**  Ruleset 5 before sending it off.  Ruleset 5 has the right
65838032Speter	**  to send rewrite it to another mailer.  This gives us a hook
65938032Speter	**  after local aliasing has been done.
66038032Speter	*/
66138032Speter
66238032Speter	if (tTd(29, 5))
66338032Speter	{
66464562Sgshapiro		dprintf("recipient: testing local?  cl=%d, rr5=%lx\n\t",
66538032Speter			ConfigLevel, (u_long) RewriteRules[5]);
66638032Speter		printaddr(a, FALSE);
66738032Speter	}
66864562Sgshapiro	if (ConfigLevel >= 2 && RewriteRules[5] != NULL &&
66964562Sgshapiro	    bitnset(M_TRYRULESET5, m->m_flags) &&
67064562Sgshapiro	    !bitset(QNOTREMOTE, a->q_flags) &&
67164562Sgshapiro	    QS_IS_OK(a->q_state))
67238032Speter	{
67338032Speter		maplocaluser(a, sendq, aliaslevel + 1, e);
67438032Speter	}
67538032Speter
67638032Speter	/*
67738032Speter	**  If it didn't get rewritten to another mailer, go ahead
67838032Speter	**  and deliver it.
67938032Speter	*/
68038032Speter
68164562Sgshapiro	if (QS_IS_OK(a->q_state) &&
68238032Speter	    bitnset(M_HASPWENT, m->m_flags))
68338032Speter	{
68438032Speter		auto bool fuzzy;
68538032Speter		register struct passwd *pw;
68638032Speter
68738032Speter		/* warning -- finduser may trash buf */
68838032Speter		pw = finduser(buf, &fuzzy);
68938032Speter		if (pw == NULL || strlen(pw->pw_name) > MAXNAME)
69038032Speter		{
69164562Sgshapiro			{
69264562Sgshapiro				a->q_state = QS_BADADDR;
69364562Sgshapiro				a->q_status = "5.1.1";
69464562Sgshapiro				a->q_rstatus = newstr("550 5.1.1 User unknown");
69564562Sgshapiro				giveresponse(EX_NOUSER, a->q_status, m, NULL,
69664562Sgshapiro					     a->q_alias, (time_t) 0, e);
69764562Sgshapiro			}
69838032Speter		}
69938032Speter		else
70038032Speter		{
70138032Speter			char nbuf[MAXNAME + 1];
70238032Speter
70338032Speter			if (fuzzy)
70438032Speter			{
70538032Speter				/* name was a fuzzy match */
70638032Speter				a->q_user = newstr(pw->pw_name);
70738032Speter				if (findusercount++ > 3)
70838032Speter				{
70964562Sgshapiro					a->q_state = QS_BADADDR;
71038032Speter					a->q_status = "5.4.6";
71164562Sgshapiro					usrerrenh(a->q_status,
71264562Sgshapiro						  "554 aliasing/forwarding loop for %s broken",
71364562Sgshapiro						  pw->pw_name);
71438032Speter					goto done;
71538032Speter				}
71638032Speter
71738032Speter				/* see if it aliases */
71864562Sgshapiro				(void) strlcpy(buf, pw->pw_name, buflen);
71938032Speter				goto trylocaluser;
72038032Speter			}
72138032Speter			if (strcmp(pw->pw_dir, "/") == 0)
72238032Speter				a->q_home = "";
72338032Speter			else
72438032Speter				a->q_home = newstr(pw->pw_dir);
72538032Speter			a->q_uid = pw->pw_uid;
72638032Speter			a->q_gid = pw->pw_gid;
72738032Speter			a->q_ruser = newstr(pw->pw_name);
72838032Speter			a->q_flags |= QGOODUID;
72938032Speter			buildfname(pw->pw_gecos, pw->pw_name, nbuf, sizeof nbuf);
73038032Speter			if (nbuf[0] != '\0')
73138032Speter				a->q_fullname = newstr(nbuf);
73238032Speter			if (!usershellok(pw->pw_name, pw->pw_shell))
73338032Speter			{
73438032Speter				a->q_flags |= QBOGUSSHELL;
73538032Speter			}
73638032Speter			if (bitset(EF_VRFYONLY, e->e_flags))
73738032Speter			{
73838032Speter				/* don't do any more now */
73964562Sgshapiro				a->q_state = QS_VERIFIED;
74038032Speter			}
74138032Speter			else if (!quoted)
74238032Speter				forward(a, sendq, aliaslevel, e);
74338032Speter		}
74438032Speter	}
74564562Sgshapiro	if (!QS_IS_DEAD(a->q_state))
74638032Speter		e->e_nrcpts++;
74738032Speter
74838032Speter  testselfdestruct:
74938032Speter	a->q_flags |= QTHISPASS;
75038032Speter	if (tTd(26, 8))
75138032Speter	{
75264562Sgshapiro		dprintf("testselfdestruct: ");
75338032Speter		printaddr(a, FALSE);
75438032Speter		if (tTd(26, 10))
75538032Speter		{
75664562Sgshapiro			dprintf("SENDQ:\n");
75738032Speter			printaddr(*sendq, TRUE);
75864562Sgshapiro			dprintf("----\n");
75938032Speter		}
76038032Speter	}
76138032Speter	if (a->q_alias == NULL && a != &e->e_from &&
76264562Sgshapiro	    QS_IS_DEAD(a->q_state))
76338032Speter	{
76438032Speter		for (q = *sendq; q != NULL; q = q->q_next)
76538032Speter		{
76664562Sgshapiro			if (!QS_IS_DEAD(q->q_state))
76738032Speter				break;
76838032Speter		}
76938032Speter		if (q == NULL)
77038032Speter		{
77164562Sgshapiro			a->q_state = QS_BADADDR;
77238032Speter			a->q_status = "5.4.6";
77364562Sgshapiro			usrerrenh(a->q_status,
77464562Sgshapiro				  "554 aliasing/forwarding loop broken");
77538032Speter		}
77638032Speter	}
77738032Speter
77838032Speter  done:
77938032Speter	a->q_flags |= QTHISPASS;
78038032Speter	if (buf != buf0)
78138032Speter		free(buf);
78238032Speter
78338032Speter	/*
78438032Speter	**  If we are at the top level, check to see if this has
78538032Speter	**  expanded to exactly one address.  If so, it can inherit
78638032Speter	**  the primaryness of the address.
78738032Speter	**
78838032Speter	**  While we're at it, clear the QTHISPASS bits.
78938032Speter	*/
79038032Speter
79138032Speter	if (aliaslevel == 0)
79238032Speter	{
79338032Speter		int nrcpts = 0;
79438032Speter		ADDRESS *only = NULL;
79538032Speter
79638032Speter		for (q = *sendq; q != NULL; q = q->q_next)
79738032Speter		{
79838032Speter			if (bitset(QTHISPASS, q->q_flags) &&
79964562Sgshapiro			    QS_IS_SENDABLE(q->q_state))
80038032Speter			{
80138032Speter				nrcpts++;
80238032Speter				only = q;
80338032Speter			}
80438032Speter			q->q_flags &= ~QTHISPASS;
80538032Speter		}
80638032Speter		if (nrcpts == 1)
80738032Speter		{
80838032Speter			/* check to see if this actually got a new owner */
80938032Speter			q = only;
81038032Speter			while ((q = q->q_alias) != NULL)
81138032Speter			{
81238032Speter				if (q->q_owner != NULL)
81338032Speter					break;
81438032Speter			}
81538032Speter			if (q == NULL)
81638032Speter				only->q_flags |= QPRIMARY;
81738032Speter		}
81838032Speter		else if (!initialdontsend && nrcpts > 0)
81938032Speter		{
82038032Speter			/* arrange for return receipt */
82138032Speter			e->e_flags |= EF_SENDRECEIPT;
82238032Speter			a->q_flags |= QEXPANDED;
82364562Sgshapiro			if (e->e_xfp != NULL &&
82464562Sgshapiro			    bitset(QPINGONSUCCESS, a->q_flags))
82538032Speter				fprintf(e->e_xfp,
82638032Speter					"%s... expanded to multiple addresses\n",
82738032Speter					a->q_paddr);
82838032Speter		}
82938032Speter	}
83038032Speter	a->q_flags |= QRCPTOK;
83164562Sgshapiro	return a;
83238032Speter}
83338032Speter/*
83438032Speter**  FINDUSER -- find the password entry for a user.
83538032Speter**
83638032Speter**	This looks a lot like getpwnam, except that it may want to
83738032Speter**	do some fancier pattern matching in /etc/passwd.
83838032Speter**
83938032Speter**	This routine contains most of the time of many sendmail runs.
84038032Speter**	It deserves to be optimized.
84138032Speter**
84238032Speter**	Parameters:
84338032Speter**		name -- the name to match against.
84438032Speter**		fuzzyp -- an outarg that is set to TRUE if this entry
84538032Speter**			was found using the fuzzy matching algorithm;
84638032Speter**			set to FALSE otherwise.
84738032Speter**
84838032Speter**	Returns:
84938032Speter**		A pointer to a pw struct.
85038032Speter**		NULL if name is unknown or ambiguous.
85138032Speter**
85238032Speter**	Side Effects:
85338032Speter**		may modify name.
85438032Speter*/
85538032Speter
85638032Speterstruct passwd *
85738032Speterfinduser(name, fuzzyp)
85838032Speter	char *name;
85938032Speter	bool *fuzzyp;
86038032Speter{
86138032Speter	register struct passwd *pw;
86238032Speter	register char *p;
86338032Speter	bool tryagain;
86438032Speter
86538032Speter	if (tTd(29, 4))
86664562Sgshapiro		dprintf("finduser(%s): ", name);
86738032Speter
86838032Speter	*fuzzyp = FALSE;
86938032Speter
87038032Speter#ifdef HESIOD
87138032Speter	/* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
87238032Speter	for (p = name; *p != '\0'; p++)
87338032Speter		if (!isascii(*p) || !isdigit(*p))
87438032Speter			break;
87538032Speter	if (*p == '\0')
87638032Speter	{
87738032Speter		if (tTd(29, 4))
87864562Sgshapiro			dprintf("failed (numeric input)\n");
87938032Speter		return NULL;
88038032Speter	}
88164562Sgshapiro#endif /* HESIOD */
88238032Speter
88338032Speter	/* look up this login name using fast path */
88438032Speter	if ((pw = sm_getpwnam(name)) != NULL)
88538032Speter	{
88638032Speter		if (tTd(29, 4))
88764562Sgshapiro			dprintf("found (non-fuzzy)\n");
88864562Sgshapiro		return pw;
88938032Speter	}
89038032Speter
89138032Speter	/* try mapping it to lower case */
89238032Speter	tryagain = FALSE;
89338032Speter	for (p = name; *p != '\0'; p++)
89438032Speter	{
89538032Speter		if (isascii(*p) && isupper(*p))
89638032Speter		{
89738032Speter			*p = tolower(*p);
89838032Speter			tryagain = TRUE;
89938032Speter		}
90038032Speter	}
90138032Speter	if (tryagain && (pw = sm_getpwnam(name)) != NULL)
90238032Speter	{
90338032Speter		if (tTd(29, 4))
90464562Sgshapiro			dprintf("found (lower case)\n");
90538032Speter		*fuzzyp = TRUE;
90638032Speter		return pw;
90738032Speter	}
90838032Speter
90938032Speter#if MATCHGECOS
91038032Speter	/* see if fuzzy matching allowed */
91138032Speter	if (!MatchGecos)
91238032Speter	{
91338032Speter		if (tTd(29, 4))
91464562Sgshapiro			dprintf("not found (fuzzy disabled)\n");
91538032Speter		return NULL;
91638032Speter	}
91738032Speter
91838032Speter	/* search for a matching full name instead */
91938032Speter	for (p = name; *p != '\0'; p++)
92038032Speter	{
92138032Speter		if (*p == (SpaceSub & 0177) || *p == '_')
92238032Speter			*p = ' ';
92338032Speter	}
92438032Speter	(void) setpwent();
92538032Speter	while ((pw = getpwent()) != NULL)
92638032Speter	{
92738032Speter		char buf[MAXNAME + 1];
92838032Speter
92938032Speter# if 0
93038032Speter		if (strcasecmp(pw->pw_name, name) == 0)
93138032Speter		{
93238032Speter			if (tTd(29, 4))
93364562Sgshapiro				dprintf("found (case wrapped)\n");
93438032Speter			break;
93538032Speter		}
93664562Sgshapiro# endif /* 0 */
93738032Speter
93838032Speter		buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf);
93964562Sgshapiro		if (strchr(buf, ' ') != NULL && strcasecmp(buf, name) == 0)
94038032Speter		{
94138032Speter			if (tTd(29, 4))
94264562Sgshapiro				dprintf("fuzzy matches %s\n", pw->pw_name);
94338032Speter			message("sending to login name %s", pw->pw_name);
94438032Speter			break;
94538032Speter		}
94638032Speter	}
94738032Speter	if (pw != NULL)
94838032Speter		*fuzzyp = TRUE;
94938032Speter	else if (tTd(29, 4))
95064562Sgshapiro		dprintf("no fuzzy match found\n");
95138032Speter# if DEC_OSF_BROKEN_GETPWENT	/* DEC OSF/1 3.2 or earlier */
95238032Speter	endpwent();
95364562Sgshapiro# endif /* DEC_OSF_BROKEN_GETPWENT */
95438032Speter	return pw;
95564562Sgshapiro#else /* MATCHGECOS */
95638032Speter	if (tTd(29, 4))
95764562Sgshapiro		dprintf("not found (fuzzy disabled)\n");
95838032Speter	return NULL;
95964562Sgshapiro#endif /* MATCHGECOS */
96038032Speter}
96138032Speter/*
96238032Speter**  WRITABLE -- predicate returning if the file is writable.
96338032Speter**
96438032Speter**	This routine must duplicate the algorithm in sys/fio.c.
96538032Speter**	Unfortunately, we cannot use the access call since we
96638032Speter**	won't necessarily be the real uid when we try to
96738032Speter**	actually open the file.
96838032Speter**
96938032Speter**	Notice that ANY file with ANY execute bit is automatically
97038032Speter**	not writable.  This is also enforced by mailfile.
97138032Speter**
97238032Speter**	Parameters:
97338032Speter**		filename -- the file name to check.
97438032Speter**		ctladdr -- the controlling address for this file.
97538032Speter**		flags -- SFF_* flags to control the function.
97638032Speter**
97738032Speter**	Returns:
97838032Speter**		TRUE -- if we will be able to write this file.
97938032Speter**		FALSE -- if we cannot write this file.
98038032Speter**
98138032Speter**	Side Effects:
98238032Speter**		none.
98338032Speter*/
98438032Speter
98538032Speterbool
98638032Speterwritable(filename, ctladdr, flags)
98738032Speter	char *filename;
98838032Speter	ADDRESS *ctladdr;
98964562Sgshapiro	long flags;
99038032Speter{
99164562Sgshapiro	uid_t euid = 0;
99264562Sgshapiro	gid_t egid = 0;
99364562Sgshapiro	char *user = NULL;
99438032Speter
99538032Speter	if (tTd(44, 5))
99664562Sgshapiro		dprintf("writable(%s, 0x%lx)\n", filename, flags);
99738032Speter
99838032Speter	/*
99938032Speter	**  File does exist -- check that it is writable.
100038032Speter	*/
100138032Speter
100238032Speter	if (geteuid() != 0)
100338032Speter	{
100438032Speter		euid = geteuid();
100538032Speter		egid = getegid();
100664562Sgshapiro		user = NULL;
100738032Speter	}
100838032Speter	else if (ctladdr != NULL)
100938032Speter	{
101038032Speter		euid = ctladdr->q_uid;
101138032Speter		egid = ctladdr->q_gid;
101264562Sgshapiro		user = ctladdr->q_user;
101338032Speter	}
101438032Speter	else if (bitset(SFF_RUNASREALUID, flags))
101538032Speter	{
101638032Speter		euid = RealUid;
101738032Speter		egid = RealGid;
101864562Sgshapiro		user = RealUserName;
101938032Speter	}
102038032Speter	else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags))
102138032Speter	{
102238032Speter		euid = FileMailer->m_uid;
102338032Speter		egid = FileMailer->m_gid;
102464562Sgshapiro		user = NULL;
102538032Speter	}
102638032Speter	else
102738032Speter	{
102838032Speter		euid = egid = 0;
102964562Sgshapiro		user = NULL;
103038032Speter	}
103138032Speter	if (!bitset(SFF_ROOTOK, flags))
103238032Speter	{
103338032Speter		if (euid == 0)
103438032Speter		{
103538032Speter			euid = DefUid;
103664562Sgshapiro			user = DefUser;
103738032Speter		}
103838032Speter		if (egid == 0)
103938032Speter			egid = DefGid;
104038032Speter	}
104138032Speter	if (geteuid() == 0 &&
104238032Speter	    (ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags)))
104338032Speter		flags |= SFF_SETUIDOK;
104438032Speter
104564562Sgshapiro	if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
104638032Speter		flags |= SFF_NOSLINK;
104764562Sgshapiro	if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
104838032Speter		flags |= SFF_NOHLINK;
104938032Speter
105064562Sgshapiro	errno = safefile(filename, euid, egid, user, flags, S_IWRITE, NULL);
105138032Speter	return errno == 0;
105238032Speter}
105338032Speter/*
105438032Speter**  INCLUDE -- handle :include: specification.
105538032Speter**
105638032Speter**	Parameters:
105738032Speter**		fname -- filename to include.
105838032Speter**		forwarding -- if TRUE, we are reading a .forward file.
105938032Speter**			if FALSE, it's a :include: file.
106038032Speter**		ctladdr -- address template to use to fill in these
106138032Speter**			addresses -- effective user/group id are
106238032Speter**			the important things.
106338032Speter**		sendq -- a pointer to the head of the send queue
106438032Speter**			to put these addresses in.
106538032Speter**		aliaslevel -- the alias nesting depth.
106638032Speter**		e -- the current envelope.
106738032Speter**
106838032Speter**	Returns:
106938032Speter**		open error status
107038032Speter**
107138032Speter**	Side Effects:
107238032Speter**		reads the :include: file and sends to everyone
107338032Speter**		listed in that file.
107438032Speter**
107538032Speter**	Security Note:
107638032Speter**		If you have restricted chown (that is, you can't
107738032Speter**		give a file away), it is reasonable to allow programs
107838032Speter**		and files called from this :include: file to be to be
107938032Speter**		run as the owner of the :include: file.  This is bogus
108038032Speter**		if there is any chance of someone giving away a file.
108138032Speter**		We assume that pre-POSIX systems can give away files.
108238032Speter**
108338032Speter**		There is an additional restriction that if you
108438032Speter**		forward to a :include: file, it will not take on
108538032Speter**		the ownership of the :include: file.  This may not
108638032Speter**		be necessary, but shouldn't hurt.
108738032Speter*/
108838032Speter
108938032Speterstatic jmp_buf	CtxIncludeTimeout;
109038032Speter
109138032Speterint
109238032Speterinclude(fname, forwarding, ctladdr, sendq, aliaslevel, e)
109338032Speter	char *fname;
109438032Speter	bool forwarding;
109538032Speter	ADDRESS *ctladdr;
109638032Speter	ADDRESS **sendq;
109738032Speter	int aliaslevel;
109838032Speter	ENVELOPE *e;
109938032Speter{
110038032Speter	FILE *volatile fp = NULL;
110138032Speter	char *oldto = e->e_to;
110238032Speter	char *oldfilename = FileName;
110338032Speter	int oldlinenumber = LineNumber;
110438032Speter	register EVENT *ev = NULL;
110538032Speter	int nincludes;
110638032Speter	int mode;
110764562Sgshapiro	volatile bool maxreached = FALSE;
110838032Speter	register ADDRESS *ca;
110964562Sgshapiro	volatile uid_t saveduid;
111064562Sgshapiro	volatile gid_t savedgid;
111164562Sgshapiro	volatile uid_t uid;
111264562Sgshapiro	volatile gid_t gid;
111364562Sgshapiro	char *volatile user;
111438032Speter	int rval = 0;
111564562Sgshapiro	volatile long sfflags = SFF_REGONLY;
111638032Speter	register char *p;
111738032Speter	bool safechown = FALSE;
111838032Speter	volatile bool safedir = FALSE;
111938032Speter	struct stat st;
112038032Speter	char buf[MAXLINE];
112138032Speter
112238032Speter	if (tTd(27, 2))
112364562Sgshapiro		dprintf("include(%s)\n", fname);
112438032Speter	if (tTd(27, 4))
112564562Sgshapiro		dprintf("   ruid=%d euid=%d\n",
112664562Sgshapiro			(int) getuid(), (int) geteuid());
112738032Speter	if (tTd(27, 14))
112838032Speter	{
112964562Sgshapiro		dprintf("ctladdr ");
113038032Speter		printaddr(ctladdr, FALSE);
113138032Speter	}
113238032Speter
113338032Speter	if (tTd(27, 9))
113464562Sgshapiro		dprintf("include: old uid = %d/%d\n",
113564562Sgshapiro			(int) getuid(), (int) geteuid());
113638032Speter
113738032Speter	if (forwarding)
113842575Speter		sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK;
113938032Speter
114064562Sgshapiro	/*
114164562Sgshapiro	**  If RunAsUser set, won't be able to run programs as user
114264562Sgshapiro	**  so mark them as unsafe unless the administrator knows better.
114364562Sgshapiro	*/
114464562Sgshapiro
114564562Sgshapiro	if ((geteuid() != 0 || RunAsUid != 0) &&
114664562Sgshapiro	    !bitnset(DBS_NONROOTSAFEADDR, DontBlameSendmail))
114764562Sgshapiro	{
114864562Sgshapiro		if (tTd(27, 4))
114964562Sgshapiro			dprintf("include: not safe (euid=%d, RunAsUid=%d)\n",
115064562Sgshapiro				(int) geteuid(), (int) RunAsUid);
115164562Sgshapiro		ctladdr->q_flags |= QUNSAFEADDR;
115264562Sgshapiro	}
115364562Sgshapiro
115438032Speter	ca = getctladdr(ctladdr);
115564562Sgshapiro	if (ca == NULL ||
115664562Sgshapiro	    (ca->q_uid == DefUid && ca->q_gid == 0))
115738032Speter	{
115838032Speter		uid = DefUid;
115938032Speter		gid = DefGid;
116064562Sgshapiro		user = DefUser;
116138032Speter	}
116238032Speter	else
116338032Speter	{
116438032Speter		uid = ca->q_uid;
116538032Speter		gid = ca->q_gid;
116664562Sgshapiro		user = ca->q_user;
116738032Speter	}
116864562Sgshapiro#if MAILER_SETUID_METHOD != USE_SETUID
116938032Speter	saveduid = geteuid();
117038032Speter	savedgid = getegid();
117138032Speter	if (saveduid == 0)
117238032Speter	{
117338032Speter		if (!DontInitGroups)
117438032Speter		{
117564562Sgshapiro			if (initgroups(user, gid) == -1)
117664562Sgshapiro			{
117764562Sgshapiro				rval = EAGAIN;
117838032Speter				syserr("include: initgroups(%s, %d) failed",
117964562Sgshapiro					user, gid);
118064562Sgshapiro				goto resetuid;
118164562Sgshapiro			}
118238032Speter		}
118338032Speter		else
118438032Speter		{
118538032Speter			GIDSET_T gidset[1];
118638032Speter
118738032Speter			gidset[0] = gid;
118838032Speter			if (setgroups(1, gidset) == -1)
118964562Sgshapiro			{
119064562Sgshapiro				rval = EAGAIN;
119138032Speter				syserr("include: setgroups() failed");
119264562Sgshapiro				goto resetuid;
119364562Sgshapiro			}
119438032Speter		}
119538032Speter
119638032Speter		if (gid != 0 && setgid(gid) < -1)
119764562Sgshapiro		{
119864562Sgshapiro			rval = EAGAIN;
119938032Speter			syserr("setgid(%d) failure", gid);
120064562Sgshapiro			goto resetuid;
120164562Sgshapiro		}
120238032Speter		if (uid != 0)
120338032Speter		{
120464562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID
120538032Speter			if (seteuid(uid) < 0)
120664562Sgshapiro			{
120764562Sgshapiro				rval = EAGAIN;
120838032Speter				syserr("seteuid(%d) failure (real=%d, eff=%d)",
120938032Speter					uid, getuid(), geteuid());
121064562Sgshapiro				goto resetuid;
121164562Sgshapiro			}
121264562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */
121364562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID
121438032Speter			if (setreuid(0, uid) < 0)
121564562Sgshapiro			{
121664562Sgshapiro				rval = EAGAIN;
121738032Speter				syserr("setreuid(0, %d) failure (real=%d, eff=%d)",
121838032Speter					uid, getuid(), geteuid());
121964562Sgshapiro				goto resetuid;
122064562Sgshapiro			}
122164562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */
122238032Speter		}
122338032Speter	}
122464562Sgshapiro#endif /* MAILER_SETUID_METHOD != USE_SETUID */
122538032Speter
122638032Speter	if (tTd(27, 9))
122764562Sgshapiro		dprintf("include: new uid = %d/%d\n",
122864562Sgshapiro			(int) getuid(), (int) geteuid());
122938032Speter
123038032Speter	/*
123138032Speter	**  If home directory is remote mounted but server is down,
123238032Speter	**  this can hang or give errors; use a timeout to avoid this
123338032Speter	*/
123438032Speter
123538032Speter	if (setjmp(CtxIncludeTimeout) != 0)
123638032Speter	{
123764562Sgshapiro		ctladdr->q_state = QS_QUEUEUP;
123838032Speter		errno = 0;
123938032Speter
124038032Speter		/* return pseudo-error code */
124138032Speter		rval = E_SM_OPENTIMEOUT;
124238032Speter		goto resetuid;
124338032Speter	}
124438032Speter	if (TimeOuts.to_fileopen > 0)
124538032Speter		ev = setevent(TimeOuts.to_fileopen, includetimeout, 0);
124638032Speter	else
124738032Speter		ev = NULL;
124838032Speter
124964562Sgshapiro
125038032Speter	/* check for writable parent directory */
125138032Speter	p = strrchr(fname, '/');
125238032Speter	if (p != NULL)
125338032Speter	{
125438032Speter		int ret;
125538032Speter
125638032Speter		*p = '\0';
125764562Sgshapiro		ret = safedirpath(fname, uid, gid, user,
125864562Sgshapiro				  sfflags|SFF_SAFEDIRPATH, 0, 0);
125938032Speter		if (ret == 0)
126038032Speter		{
126138032Speter			/* in safe directory: relax chown & link rules */
126238032Speter			safedir = TRUE;
126338032Speter			sfflags |= SFF_NOPATHCHECK;
126438032Speter		}
126538032Speter		else
126638032Speter		{
126764562Sgshapiro			if (bitnset((forwarding ?
126864562Sgshapiro				     DBS_FORWARDFILEINUNSAFEDIRPATH :
126964562Sgshapiro				     DBS_INCLUDEFILEINUNSAFEDIRPATH),
127064562Sgshapiro				    DontBlameSendmail))
127138032Speter				sfflags |= SFF_NOPATHCHECK;
127264562Sgshapiro			else if (bitnset((forwarding ?
127364562Sgshapiro					  DBS_FORWARDFILEINGROUPWRITABLEDIRPATH :
127464562Sgshapiro					  DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH),
127564562Sgshapiro					 DontBlameSendmail) &&
127638032Speter				 ret == E_SM_GWDIR)
127738032Speter			{
127864562Sgshapiro				setbitn(DBS_GROUPWRITABLEDIRPATHSAFE,
127964562Sgshapiro					DontBlameSendmail);
128064562Sgshapiro				ret = safedirpath(fname, uid, gid, user,
128164562Sgshapiro						  sfflags|SFF_SAFEDIRPATH,
128264562Sgshapiro						  0, 0);
128364562Sgshapiro				clrbitn(DBS_GROUPWRITABLEDIRPATHSAFE,
128464562Sgshapiro					DontBlameSendmail);
128538032Speter				if (ret == 0)
128638032Speter					sfflags |= SFF_NOPATHCHECK;
128738032Speter				else
128838032Speter					sfflags |= SFF_SAFEDIRPATH;
128938032Speter			}
129038032Speter			else
129138032Speter				sfflags |= SFF_SAFEDIRPATH;
129238032Speter			if (ret > E_PSEUDOBASE &&
129364562Sgshapiro			    !bitnset((forwarding ?
129464562Sgshapiro				      DBS_FORWARDFILEINUNSAFEDIRPATHSAFE :
129564562Sgshapiro				      DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE),
129664562Sgshapiro				     DontBlameSendmail))
129738032Speter			{
129838032Speter				if (LogLevel >= 12)
129938032Speter					sm_syslog(LOG_INFO, e->e_id,
130038032Speter						  "%s: unsafe directory path, marked unsafe",
130138032Speter						  shortenstring(fname, MAXSHORTSTR));
130238032Speter				ctladdr->q_flags |= QUNSAFEADDR;
130338032Speter			}
130438032Speter		}
130538032Speter		*p = '/';
130638032Speter	}
130738032Speter
130838032Speter	/* allow links only in unwritable directories */
130938032Speter	if (!safedir &&
131064562Sgshapiro	    !bitnset((forwarding ?
131164562Sgshapiro		      DBS_LINKEDFORWARDFILEINWRITABLEDIR :
131264562Sgshapiro		      DBS_LINKEDINCLUDEFILEINWRITABLEDIR),
131364562Sgshapiro		     DontBlameSendmail))
131438032Speter		sfflags |= SFF_NOLINK;
131538032Speter
131664562Sgshapiro	rval = safefile(fname, uid, gid, user, sfflags, S_IREAD, &st);
131738032Speter	if (rval != 0)
131838032Speter	{
131938032Speter		/* don't use this :include: file */
132038032Speter		if (tTd(27, 4))
132164562Sgshapiro			dprintf("include: not safe (uid=%d): %s\n",
132238032Speter				(int) uid, errstring(rval));
132338032Speter	}
132438032Speter	else if ((fp = fopen(fname, "r")) == NULL)
132538032Speter	{
132638032Speter		rval = errno;
132738032Speter		if (tTd(27, 4))
132864562Sgshapiro			dprintf("include: open: %s\n", errstring(rval));
132938032Speter	}
133038032Speter	else if (filechanged(fname, fileno(fp), &st))
133138032Speter	{
133238032Speter		rval = E_SM_FILECHANGE;
133338032Speter		if (tTd(27, 4))
133464562Sgshapiro			dprintf("include: file changed after open\n");
133538032Speter	}
133638032Speter	if (ev != NULL)
133738032Speter		clrevent(ev);
133838032Speter
133938032Speterresetuid:
134038032Speter
134138032Speter#if HASSETREUID || USESETEUID
134238032Speter	if (saveduid == 0)
134338032Speter	{
134438032Speter		if (uid != 0)
134538032Speter		{
134638032Speter# if USESETEUID
134738032Speter			if (seteuid(0) < 0)
134864562Sgshapiro				syserr("!seteuid(0) failure (real=%d, eff=%d)",
134938032Speter					getuid(), geteuid());
135064562Sgshapiro# else /* USESETEUID */
135138032Speter			if (setreuid(-1, 0) < 0)
135264562Sgshapiro				syserr("!setreuid(-1, 0) failure (real=%d, eff=%d)",
135338032Speter					getuid(), geteuid());
135438032Speter			if (setreuid(RealUid, 0) < 0)
135564562Sgshapiro				syserr("!setreuid(%d, 0) failure (real=%d, eff=%d)",
135638032Speter					RealUid, getuid(), geteuid());
135764562Sgshapiro# endif /* USESETEUID */
135838032Speter		}
135964562Sgshapiro		if (setgid(savedgid) < 0)
136064562Sgshapiro			syserr("!setgid(%d) failure (real=%d eff=%d)",
136164562Sgshapiro			       savedgid, getgid(), getegid());
136238032Speter	}
136364562Sgshapiro#endif /* HASSETREUID || USESETEUID */
136438032Speter
136538032Speter	if (tTd(27, 9))
136664562Sgshapiro		dprintf("include: reset uid = %d/%d\n",
136764562Sgshapiro			(int) getuid(), (int) geteuid());
136838032Speter
136938032Speter	if (rval == E_SM_OPENTIMEOUT)
137064562Sgshapiro		usrerr("451 4.4.1 open timeout on %s", fname);
137138032Speter
137238032Speter	if (fp == NULL)
137338032Speter		return rval;
137438032Speter
137538032Speter	if (fstat(fileno(fp), &st) < 0)
137638032Speter	{
137738032Speter		rval = errno;
137838032Speter		syserr("Cannot fstat %s!", fname);
137964562Sgshapiro		(void) fclose(fp);
138038032Speter		return rval;
138138032Speter	}
138238032Speter
138338032Speter	/* if path was writable, check to avoid file giveaway tricks */
138438032Speter	safechown = chownsafe(fileno(fp), safedir);
138538032Speter	if (tTd(27, 6))
138664562Sgshapiro		dprintf("include: parent of %s is %s, chown is %ssafe\n",
138738032Speter			fname,
138838032Speter			safedir ? "safe" : "dangerous",
138938032Speter			safechown ? "" : "un");
139038032Speter
139164562Sgshapiro	/* if no controlling user or coming from an alias delivery */
139264562Sgshapiro	if (safechown &&
139364562Sgshapiro	    (ca == NULL ||
139464562Sgshapiro	     (ca->q_uid == DefUid && ca->q_gid == 0)))
139538032Speter	{
139638032Speter		ctladdr->q_uid = st.st_uid;
139738032Speter		ctladdr->q_gid = st.st_gid;
139838032Speter		ctladdr->q_flags |= QGOODUID;
139938032Speter	}
140038032Speter	if (ca != NULL && ca->q_uid == st.st_uid)
140138032Speter	{
140238032Speter		/* optimization -- avoid getpwuid if we already have info */
140338032Speter		ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL;
140438032Speter		ctladdr->q_ruser = ca->q_ruser;
140538032Speter	}
140638032Speter	else if (!forwarding)
140738032Speter	{
140838032Speter		register struct passwd *pw;
140938032Speter
141038032Speter		pw = sm_getpwuid(st.st_uid);
141138032Speter		if (pw == NULL)
141264562Sgshapiro		{
141364562Sgshapiro			ctladdr->q_uid = st.st_uid;
141438032Speter			ctladdr->q_flags |= QBOGUSSHELL;
141564562Sgshapiro		}
141638032Speter		else
141738032Speter		{
141838032Speter			char *sh;
141938032Speter
142038032Speter			ctladdr->q_ruser = newstr(pw->pw_name);
142138032Speter			if (safechown)
142238032Speter				sh = pw->pw_shell;
142338032Speter			else
142438032Speter				sh = "/SENDMAIL/ANY/SHELL/";
142538032Speter			if (!usershellok(pw->pw_name, sh))
142638032Speter			{
142738032Speter				if (LogLevel >= 12)
142838032Speter					sm_syslog(LOG_INFO, e->e_id,
142964562Sgshapiro						  "%s: user %s has bad shell %s, marked %s",
143064562Sgshapiro						  shortenstring(fname, MAXSHORTSTR),
143164562Sgshapiro						  pw->pw_name, sh,
143264562Sgshapiro						  safechown ? "bogus" : "unsafe");
143338032Speter				if (safechown)
143438032Speter					ctladdr->q_flags |= QBOGUSSHELL;
143538032Speter				else
143638032Speter					ctladdr->q_flags |= QUNSAFEADDR;
143738032Speter			}
143838032Speter		}
143938032Speter	}
144038032Speter
144138032Speter	if (bitset(EF_VRFYONLY, e->e_flags))
144238032Speter	{
144338032Speter		/* don't do any more now */
144464562Sgshapiro		ctladdr->q_state = QS_VERIFIED;
144538032Speter		e->e_nrcpts++;
144664562Sgshapiro		(void) fclose(fp);
144738032Speter		return rval;
144838032Speter	}
144938032Speter
145038032Speter	/*
145142575Speter	**  Check to see if some bad guy can write this file
145238032Speter	**
145338032Speter	**	Group write checking could be more clever, e.g.,
145438032Speter	**	guessing as to which groups are actually safe ("sys"
145538032Speter	**	may be; "user" probably is not).
145638032Speter	*/
145738032Speter
145838032Speter	mode = S_IWOTH;
145964562Sgshapiro	if (!bitnset((forwarding ?
146064562Sgshapiro		      DBS_GROUPWRITABLEFORWARDFILESAFE :
146164562Sgshapiro		      DBS_GROUPWRITABLEINCLUDEFILESAFE),
146264562Sgshapiro		     DontBlameSendmail))
146338032Speter		mode |= S_IWGRP;
146438032Speter
146538032Speter	if (bitset(mode, st.st_mode))
146638032Speter	{
146738032Speter		if (tTd(27, 6))
146864562Sgshapiro			dprintf("include: %s is %s writable, marked unsafe\n",
146938032Speter				shortenstring(fname, MAXSHORTSTR),
147038032Speter				bitset(S_IWOTH, st.st_mode) ? "world" : "group");
147138032Speter		if (LogLevel >= 12)
147238032Speter			sm_syslog(LOG_INFO, e->e_id,
147364562Sgshapiro				  "%s: %s writable %s file, marked unsafe",
147464562Sgshapiro				  shortenstring(fname, MAXSHORTSTR),
147564562Sgshapiro				  bitset(S_IWOTH, st.st_mode) ? "world" : "group",
147664562Sgshapiro				  forwarding ? "forward" : ":include:");
147738032Speter		ctladdr->q_flags |= QUNSAFEADDR;
147838032Speter	}
147938032Speter
148038032Speter	/* read the file -- each line is a comma-separated list. */
148138032Speter	FileName = fname;
148238032Speter	LineNumber = 0;
148338032Speter	ctladdr->q_flags &= ~QSELFREF;
148438032Speter	nincludes = 0;
148564562Sgshapiro	while (fgets(buf, sizeof buf, fp) != NULL && !maxreached)
148638032Speter	{
148764562Sgshapiro		fixcrlf(buf, TRUE);
148838032Speter		LineNumber++;
148938032Speter		if (buf[0] == '#' || buf[0] == '\0')
149038032Speter			continue;
149138032Speter
149238032Speter		/* <sp>#@# introduces a comment anywhere */
149338032Speter		/* for Japanese character sets */
149438032Speter		for (p = buf; (p = strchr(++p, '#')) != NULL; )
149538032Speter		{
149638032Speter			if (p[1] == '@' && p[2] == '#' &&
149738032Speter			    isascii(p[-1]) && isspace(p[-1]) &&
149838032Speter			    (p[3] == '\0' || (isascii(p[3]) && isspace(p[3]))))
149938032Speter			{
150038032Speter				p[-1] = '\0';
150138032Speter				break;
150238032Speter			}
150338032Speter		}
150438032Speter		if (buf[0] == '\0')
150538032Speter			continue;
150638032Speter
150738032Speter		e->e_to = NULL;
150838032Speter		message("%s to %s",
150938032Speter			forwarding ? "forwarding" : "sending", buf);
151064562Sgshapiro		if (forwarding && LogLevel > 10)
151138032Speter			sm_syslog(LOG_INFO, e->e_id,
151264562Sgshapiro				  "forward %.200s => %s",
151364562Sgshapiro				  oldto, shortenstring(buf, MAXSHORTSTR));
151438032Speter
151538032Speter		nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e);
151664562Sgshapiro
151764562Sgshapiro		if (forwarding &&
151864562Sgshapiro		    MaxForwardEntries > 0 &&
151964562Sgshapiro		    nincludes >= MaxForwardEntries)
152064562Sgshapiro		{
152164562Sgshapiro			/* just stop reading and processing further entries */
152264562Sgshapiro			/* additional: (?)
152364562Sgshapiro			ctladdr->q_state = QS_DONTSEND;
152464562Sgshapiro			**/
152564562Sgshapiro			syserr("Attempt to forward to more then %d addresses (in %s)!",
152664562Sgshapiro				MaxForwardEntries,fname);
152764562Sgshapiro			maxreached = TRUE;
152864562Sgshapiro		}
152938032Speter	}
153038032Speter
153138032Speter	if (ferror(fp) && tTd(27, 3))
153264562Sgshapiro		dprintf("include: read error: %s\n", errstring(errno));
153338032Speter	if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags))
153438032Speter	{
153538032Speter		if (tTd(27, 5))
153638032Speter		{
153764562Sgshapiro			dprintf("include: QS_DONTSEND ");
153838032Speter			printaddr(ctladdr, FALSE);
153938032Speter		}
154064562Sgshapiro		ctladdr->q_state = QS_DONTSEND;
154138032Speter	}
154238032Speter
154364562Sgshapiro	(void) fclose(fp);
154438032Speter	FileName = oldfilename;
154538032Speter	LineNumber = oldlinenumber;
154638032Speter	e->e_to = oldto;
154738032Speter	return rval;
154838032Speter}
154938032Speter
155038032Speterstatic void
155138032Speterincludetimeout()
155238032Speter{
155338032Speter	longjmp(CtxIncludeTimeout, 1);
155438032Speter}
155538032Speter/*
155638032Speter**  SENDTOARGV -- send to an argument vector.
155738032Speter**
155838032Speter**	Parameters:
155938032Speter**		argv -- argument vector to send to.
156038032Speter**		e -- the current envelope.
156138032Speter**
156238032Speter**	Returns:
156338032Speter**		none.
156438032Speter**
156538032Speter**	Side Effects:
156638032Speter**		puts all addresses on the argument vector onto the
156738032Speter**			send queue.
156838032Speter*/
156938032Speter
157038032Spetervoid
157138032Spetersendtoargv(argv, e)
157238032Speter	register char **argv;
157338032Speter	register ENVELOPE *e;
157438032Speter{
157538032Speter	register char *p;
157638032Speter
157738032Speter	while ((p = *argv++) != NULL)
157838032Speter	{
157938032Speter		(void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e);
158038032Speter	}
158138032Speter}
158238032Speter/*
158338032Speter**  GETCTLADDR -- get controlling address from an address header.
158438032Speter**
158538032Speter**	If none, get one corresponding to the effective userid.
158638032Speter**
158738032Speter**	Parameters:
158838032Speter**		a -- the address to find the controller of.
158938032Speter**
159038032Speter**	Returns:
159138032Speter**		the controlling address.
159238032Speter**
159338032Speter**	Side Effects:
159438032Speter**		none.
159538032Speter*/
159638032Speter
159738032SpeterADDRESS *
159838032Spetergetctladdr(a)
159938032Speter	register ADDRESS *a;
160038032Speter{
160138032Speter	while (a != NULL && !bitset(QGOODUID, a->q_flags))
160238032Speter		a = a->q_alias;
160364562Sgshapiro	return a;
160438032Speter}
160538032Speter/*
160638032Speter**  SELF_REFERENCE -- check to see if an address references itself
160738032Speter**
160838032Speter**	The check is done through a chain of aliases.  If it is part of
160938032Speter**	a loop, break the loop at the "best" address, that is, the one
161038032Speter**	that exists as a real user.
161138032Speter**
161238032Speter**	This is to handle the case of:
161338032Speter**		awc:		Andrew.Chang
161438032Speter**		Andrew.Chang:	awc@mail.server
161538032Speter**	which is a problem only on mail.server.
161638032Speter**
161738032Speter**	Parameters:
161838032Speter**		a -- the address to check.
161938032Speter**
162038032Speter**	Returns:
162138032Speter**		The address that should be retained.
162238032Speter*/
162338032Speter
162464562Sgshapirostatic ADDRESS *
162564562Sgshapiroself_reference(a)
162638032Speter	ADDRESS *a;
162738032Speter{
162838032Speter	ADDRESS *b;		/* top entry in self ref loop */
162938032Speter	ADDRESS *c;		/* entry that point to a real mail box */
163038032Speter
163138032Speter	if (tTd(27, 1))
163264562Sgshapiro		dprintf("self_reference(%s)\n", a->q_paddr);
163338032Speter
163438032Speter	for (b = a->q_alias; b != NULL; b = b->q_alias)
163538032Speter	{
163638032Speter		if (sameaddr(a, b))
163738032Speter			break;
163838032Speter	}
163938032Speter
164038032Speter	if (b == NULL)
164138032Speter	{
164238032Speter		if (tTd(27, 1))
164364562Sgshapiro			dprintf("\t... no self ref\n");
164438032Speter		return NULL;
164538032Speter	}
164638032Speter
164738032Speter	/*
164838032Speter	**  Pick the first address that resolved to a real mail box
164938032Speter	**  i.e has a pw entry.  The returned value will be marked
165038032Speter	**  QSELFREF in recipient(), which in turn will disable alias()
165164562Sgshapiro	**  from marking it as QS_IS_DEAD(), which mean it will be used
165238032Speter	**  as a deliverable address.
165338032Speter	**
165438032Speter	**  The 2 key thing to note here are:
165538032Speter	**	1) we are in a recursive call sequence:
165638032Speter	**		alias->sentolist->recipient->alias
165738032Speter	**	2) normally, when we return back to alias(), the address
165864562Sgshapiro	**	   will be marked QS_EXPANDED, since alias() assumes the
165938032Speter	**	   expanded form will be used instead of the current address.
166038032Speter	**	   This behaviour is turned off if the address is marked
166138032Speter	**	   QSELFREF We set QSELFREF when we return to recipient().
166238032Speter	*/
166338032Speter
166438032Speter	c = a;
166538032Speter	while (c != NULL)
166638032Speter	{
166743730Speter		if (tTd(27, 10))
166864562Sgshapiro			dprintf("  %s", c->q_user);
166938032Speter		if (bitnset(M_HASPWENT, c->q_mailer->m_flags))
167038032Speter		{
167138032Speter			if (tTd(27, 2))
167264562Sgshapiro				dprintf("\t... getpwnam(%s)... ", c->q_user);
167338032Speter			if (sm_getpwnam(c->q_user) != NULL)
167438032Speter			{
167538032Speter				if (tTd(27, 2))
167664562Sgshapiro					dprintf("found\n");
167738032Speter
167838032Speter				/* ought to cache results here */
167938032Speter				if (sameaddr(b, c))
168038032Speter					return b;
168138032Speter				else
168238032Speter					return c;
168338032Speter			}
168438032Speter			if (tTd(27, 2))
168564562Sgshapiro				dprintf("failed\n");
168638032Speter		}
168743730Speter		else
168843730Speter		{
168943730Speter			/* if local delivery, compare usernames */
169043730Speter			if (bitnset(M_LOCALMAILER, c->q_mailer->m_flags) &&
169143730Speter			    b->q_mailer == c->q_mailer)
169243730Speter			{
169343730Speter				if (tTd(27, 2))
169464562Sgshapiro					dprintf("\t... local match (%s)\n",
169564562Sgshapiro						c->q_user);
169643730Speter				if (sameaddr(b, c))
169743730Speter					return b;
169843730Speter				else
169943730Speter					return c;
170043730Speter			}
170143730Speter		}
170243730Speter		if (tTd(27, 10))
170364562Sgshapiro			dprintf("\n");
170438032Speter		c = c->q_alias;
170538032Speter	}
170638032Speter
170738032Speter	if (tTd(27, 1))
170864562Sgshapiro		dprintf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
170938032Speter
171038032Speter	return NULL;
171138032Speter}
1712