122710Sdist /*
268839Seric  * Copyright (c) 1983, 1995 Eric P. Allman
363589Sbostic  * Copyright (c) 1988, 1993
463589Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822710Sdist 
922710Sdist #ifndef lint
10*69544Seric static char sccsid[] = "@(#)recipient.c	8.84 (Berkeley) 05/18/95";
1133731Sbostic #endif /* not lint */
1222710Sdist 
1358332Seric # include "sendmail.h"
144174Seric 
154174Seric /*
169622Seric **  SENDTOLIST -- Designate a send list.
174174Seric **
184174Seric **	The parameter is a comma-separated list of people to send to.
194174Seric **	This routine arranges to send to all of them.
204174Seric **
214174Seric **	Parameters:
224174Seric **		list -- the send list.
234399Seric **		ctladdr -- the address template for the person to
244399Seric **			send to -- effective uid/gid are important.
255006Seric **			This is typically the alias that caused this
265006Seric **			expansion.
275006Seric **		sendq -- a pointer to the head of a queue to put
285006Seric **			these people into.
2968481Seric **		aliaslevel -- the current alias nesting depth -- to
3068481Seric **			diagnose loops.
3158247Seric **		e -- the envelope in which to add these recipients.
324174Seric **
334174Seric **	Returns:
3458082Seric **		The number of addresses actually on the list.
354174Seric **
364174Seric **	Side Effects:
374174Seric **		none.
384174Seric */
394174Seric 
4068481Seric #define MAXRCRSN	10	/* maximum levels of alias recursion */
414174Seric 
4268481Seric /* q_flags bits inherited from ctladdr */
4368595Seric #define QINHERITEDBITS	(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY)
4468481Seric 
4568481Seric int
4668481Seric sendtolist(list, ctladdr, sendq, aliaslevel, e)
474174Seric 	char *list;
484399Seric 	ADDRESS *ctladdr;
495198Seric 	ADDRESS **sendq;
5068481Seric 	int aliaslevel;
5155012Seric 	register ENVELOPE *e;
524174Seric {
534174Seric 	register char *p;
548223Seric 	register ADDRESS *al;	/* list of addresses to send to */
554423Seric 	bool firstone;		/* set on first address sent */
5611446Seric 	char delimiter;		/* the address delimiter */
5758082Seric 	int naddrs;
5868392Seric 	int i;
5963847Seric 	char *oldto = e->e_to;
6068392Seric 	char *bufp;
6168271Seric 	char buf[MAXNAME + 1];
624174Seric 
6364131Seric 	if (list == NULL)
6464131Seric 	{
6564131Seric 		syserr("sendtolist: null list");
6664131Seric 		return 0;
6764131Seric 	}
6864131Seric 
697676Seric 	if (tTd(25, 1))
704444Seric 	{
714444Seric 		printf("sendto: %s\n   ctladdr=", list);
724444Seric 		printaddr(ctladdr, FALSE);
734444Seric 	}
744324Seric 
758223Seric 	/* heuristic to determine old versus new style addresses */
768230Seric 	if (ctladdr == NULL &&
7756795Seric 	    (strchr(list, ',') != NULL || strchr(list, ';') != NULL ||
7856795Seric 	     strchr(list, '<') != NULL || strchr(list, '(') != NULL))
7955012Seric 		e->e_flags &= ~EF_OLDSTYLE;
8011446Seric 	delimiter = ' ';
8155012Seric 	if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL)
8211446Seric 		delimiter = ',';
838223Seric 
844423Seric 	firstone = TRUE;
854324Seric 	al = NULL;
8658082Seric 	naddrs = 0;
878223Seric 
8868392Seric 	/* make sure we have enough space to copy the string */
8968392Seric 	i = strlen(list) + 1;
9068392Seric 	if (i <= sizeof buf)
9168271Seric 		bufp = buf;
9268392Seric 	else
9368392Seric 		bufp = xalloc(i);
9468478Seric 	strcpy(bufp, denlstring(list, FALSE, TRUE));
9568271Seric 
9668271Seric 	for (p = bufp; *p != '\0'; )
9768271Seric 	{
9858333Seric 		auto char *delimptr;
998081Seric 		register ADDRESS *a;
1004319Seric 
1018081Seric 		/* parse the address */
10258050Seric 		while ((isascii(*p) && isspace(*p)) || *p == ',')
1034174Seric 			p++;
10464284Seric 		a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e);
10558333Seric 		p = delimptr;
1069297Seric 		if (a == NULL)
1074174Seric 			continue;
1084324Seric 		a->q_next = al;
1094399Seric 		a->q_alias = ctladdr;
1104444Seric 
11168481Seric 		/* arrange to inherit attributes from parent */
11268481Seric 		if (ctladdr != NULL)
11368481Seric 		{
114*69544Seric 			ADDRESS *b;
115*69544Seric 			extern ADDRESS *self_reference();
116*69544Seric 
117*69544Seric #if 0
11868481Seric 			/* self reference test */
11968481Seric 			if (sameaddr(ctladdr, a))
12068481Seric 				ctladdr->q_flags |= QSELFREF;
121*69544Seric #endif
12268481Seric 
123*69544Seric 			/* check for address loops */
124*69544Seric 			b = self_reference(a, e);
125*69544Seric 			if (b != NULL)
126*69544Seric 			{
127*69544Seric 				b->q_flags |= QSELFREF;
128*69544Seric 				if (a != b)
129*69544Seric 				{
130*69544Seric 					a->q_flags |= QDONTSEND;
131*69544Seric 					continue;
132*69544Seric 				}
133*69544Seric 			}
134*69544Seric 
13568481Seric 			/* full name */
13668481Seric 			if (a->q_fullname == NULL)
13768481Seric 				a->q_fullname = ctladdr->q_fullname;
13868481Seric 
13968481Seric 			/* various flag bits */
14068481Seric 			a->q_flags &= ~QINHERITEDBITS;
14168481Seric 			a->q_flags |= ctladdr->q_flags & QINHERITEDBITS;
14268481Seric 
14368481Seric 			/* original recipient information */
14468481Seric 			a->q_orcpt = ctladdr->q_orcpt;
14568481Seric 		}
14668481Seric 
14757731Seric 		al = a;
1484423Seric 		firstone = FALSE;
1494324Seric 	}
1504324Seric 
1514324Seric 	/* arrange to send to everyone on the local send list */
1524324Seric 	while (al != NULL)
1534324Seric 	{
1544324Seric 		register ADDRESS *a = al;
1554324Seric 
1564324Seric 		al = a->q_next;
15768481Seric 		a = recipient(a, sendq, aliaslevel, e);
15858082Seric 		naddrs++;
1594174Seric 	}
1604324Seric 
16163847Seric 	e->e_to = oldto;
16268392Seric 	if (bufp != buf)
16368392Seric 		free(bufp);
16458082Seric 	return (naddrs);
1654174Seric }
1664174Seric /*
1674174Seric **  RECIPIENT -- Designate a message recipient
1684174Seric **
1694174Seric **	Saves the named person for future mailing.
1704174Seric **
1714174Seric **	Parameters:
1724174Seric **		a -- the (preparsed) address header for the recipient.
1735006Seric **		sendq -- a pointer to the head of a queue to put the
1745006Seric **			recipient in.  Duplicate supression is done
1755006Seric **			in this queue.
17668481Seric **		aliaslevel -- the current alias nesting depth.
17757731Seric **		e -- the current envelope.
1784174Seric **
1794174Seric **	Returns:
18012613Seric **		The actual address in the queue.  This will be "a" if
18112613Seric **		the address is not a duplicate, else the original address.
1824174Seric **
1834174Seric **	Side Effects:
1844174Seric **		none.
1854174Seric */
1864174Seric 
18712613Seric ADDRESS *
18868481Seric recipient(a, sendq, aliaslevel, e)
1894174Seric 	register ADDRESS *a;
1905006Seric 	register ADDRESS **sendq;
19168481Seric 	int aliaslevel;
19255012Seric 	register ENVELOPE *e;
1934174Seric {
1944174Seric 	register ADDRESS *q;
1954319Seric 	ADDRESS **pq;
1964174Seric 	register struct mailer *m;
1979210Seric 	register char *p;
1989210Seric 	bool quoted = FALSE;		/* set if the addr has a quote bit */
19953735Seric 	int findusercount = 0;
20068603Seric 	bool initialdontsend = bitset(QDONTSEND, a->q_flags);
20168481Seric 	int i;
20268481Seric 	char *buf;
20368528Seric 	char buf0[MAXNAME + 1];		/* unquoted image of the user name */
20458247Seric 	extern int safefile();
2054174Seric 
20655012Seric 	e->e_to = a->q_paddr;
2074600Seric 	m = a->q_mailer;
2084174Seric 	errno = 0;
20968603Seric 	if (aliaslevel == 0)
21068603Seric 		a->q_flags |= QPRIMARY;
2117676Seric 	if (tTd(26, 1))
2124444Seric 	{
21368603Seric 		printf("\nrecipient (%d): ", aliaslevel);
2144444Seric 		printaddr(a, FALSE);
2154444Seric 	}
2164174Seric 
21764146Seric 	/* if this is primary, add it to the original recipient list */
21864146Seric 	if (a->q_alias == NULL)
21964146Seric 	{
22064146Seric 		if (e->e_origrcpt == NULL)
22164146Seric 			e->e_origrcpt = a->q_paddr;
22264146Seric 		else if (e->e_origrcpt != a->q_paddr)
22364146Seric 			e->e_origrcpt = "";
22464146Seric 	}
22564146Seric 
2264174Seric 	/* break aliasing loops */
22768481Seric 	if (aliaslevel > MAXRCRSN)
2284174Seric 	{
22968857Seric 		a->q_status = "5.4.6";
23068481Seric 		usrerr("554 aliasing/forwarding loop broken (%d aliases deep; %d max",
23168481Seric 			aliaslevel, MAXRCRSN);
23212613Seric 		return (a);
2334174Seric 	}
2344174Seric 
2354174Seric 	/*
2364627Seric 	**  Finish setting up address structure.
2374174Seric 	*/
2384174Seric 
23916160Seric 	/* get unquoted user for file, program or user.name check */
24068481Seric 	i = strlen(a->q_user);
24168528Seric 	if (i >= sizeof buf0)
24268481Seric 		buf = xalloc(i + 1);
24368481Seric 	else
24468481Seric 		buf = buf0;
2459210Seric 	(void) strcpy(buf, a->q_user);
2469210Seric 	for (p = buf; *p != '\0' && !quoted; p++)
2479210Seric 	{
24854993Seric 		if (*p == '\\')
2499210Seric 			quoted = TRUE;
2509210Seric 	}
25154983Seric 	stripquotes(buf);
2529210Seric 
25357402Seric 	/* check for direct mailing to restricted mailers */
25465496Seric 	if (m == ProgMailer)
2554174Seric 	{
25665496Seric 		if (a->q_alias == NULL)
25765496Seric 		{
25865496Seric 			a->q_flags |= QBADADDR;
25968857Seric 			a->q_status = "5.7.1";
26065496Seric 			usrerr("550 Cannot mail directly to programs");
26165496Seric 		}
26265496Seric 		else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
26365496Seric 		{
26465496Seric 			a->q_flags |= QBADADDR;
26568857Seric 			a->q_status = "5.7.1";
26665496Seric 			usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs",
26765496Seric 				a->q_alias->q_ruser, MyHostName);
26865496Seric 		}
26965496Seric 		else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
27065496Seric 		{
27165496Seric 			a->q_flags |= QBADADDR;
27268857Seric 			a->q_status = "5.7.1";
27365496Seric 			usrerr("550 Address %s is unsafe for mailing to programs",
27465496Seric 				a->q_alias->q_paddr);
27565496Seric 		}
2764174Seric 	}
2774174Seric 
2784174Seric 	/*
2794419Seric 	**  Look up this person in the recipient list.
2804419Seric 	**	If they are there already, return, otherwise continue.
2814419Seric 	**	If the list is empty, just add it.  Notice the cute
2824419Seric 	**	hack to make from addresses suppress things correctly:
2834419Seric 	**	the QDONTSEND bit will be set in the send list.
2844419Seric 	**	[Please note: the emphasis is on "hack."]
2854174Seric 	*/
2864174Seric 
2875006Seric 	for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
2884174Seric 	{
28958294Seric 		if (sameaddr(q, a))
2904174Seric 		{
2917676Seric 			if (tTd(26, 1))
2924444Seric 			{
2934444Seric 				printf("%s in sendq: ", a->q_paddr);
2944444Seric 				printaddr(q, FALSE);
2954444Seric 			}
29665593Seric 			if (!bitset(QPRIMARY, q->q_flags))
29758065Seric 			{
29865593Seric 				if (!bitset(QDONTSEND, a->q_flags))
29958151Seric 					message("duplicate suppressed");
30065593Seric 				q->q_flags |= a->q_flags;
30165593Seric 			}
30265593Seric 			else if (bitset(QSELFREF, q->q_flags))
30365579Seric 				q->q_flags |= a->q_flags & ~QDONTSEND;
30463847Seric 			a = q;
30568481Seric 			goto done;
3064174Seric 		}
3074319Seric 	}
3084174Seric 
3094319Seric 	/* add address on list */
31058884Seric 	*pq = a;
31158884Seric 	a->q_next = NULL;
3124174Seric 
3134174Seric 	/*
31457402Seric 	**  Alias the name and handle special mailer types.
3154174Seric 	*/
3164174Seric 
31753735Seric   trylocaluser:
31855354Seric 	if (tTd(29, 7))
31955354Seric 		printf("at trylocaluser %s\n", a->q_user);
32055354Seric 
32158680Seric 	if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
32263847Seric 		goto testselfdestruct;
32357402Seric 
32457402Seric 	if (m == InclMailer)
3254174Seric 	{
32657402Seric 		a->q_flags |= QDONTSEND;
32764761Seric 		if (a->q_alias == NULL)
3284174Seric 		{
32958680Seric 			a->q_flags |= QBADADDR;
33068857Seric 			a->q_status = "5.7.1";
33158151Seric 			usrerr("550 Cannot mail directly to :include:s");
3324174Seric 		}
3334174Seric 		else
33450556Seric 		{
33559563Seric 			int ret;
33658247Seric 
33758151Seric 			message("including file %s", a->q_user);
33868481Seric 			ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e);
33959563Seric 			if (transienterror(ret))
34059563Seric 			{
34159563Seric #ifdef LOG
34259563Seric 				if (LogLevel > 2)
34366239Seric 					syslog(LOG_ERR, "%s: include %s: transient error: %s",
34466284Seric 						e->e_id == NULL ? "NOQUEUE" : e->e_id,
34566284Seric 						a->q_user, errstring(ret));
34659563Seric #endif
34763853Seric 				a->q_flags |= QQUEUEUP;
34865215Seric 				a->q_flags &= ~QDONTSEND;
34959563Seric 				usrerr("451 Cannot open %s: %s",
35059563Seric 					a->q_user, errstring(ret));
35159563Seric 			}
35259563Seric 			else if (ret != 0)
35359563Seric 			{
35463938Seric 				a->q_flags |= QBADADDR;
35568857Seric 				a->q_status = "5.2.4";
35659563Seric 				usrerr("550 Cannot open %s: %s",
35759563Seric 					a->q_user, errstring(ret));
35859563Seric 			}
35950556Seric 		}
3604174Seric 	}
36157642Seric 	else if (m == FileMailer)
3624174Seric 	{
3634329Seric 		extern bool writable();
3644174Seric 
36551317Seric 		/* check if writable or creatable */
36664761Seric 		if (a->q_alias == NULL)
3674174Seric 		{
36858680Seric 			a->q_flags |= QBADADDR;
36968857Seric 			a->q_status = "5.7.1";
37058151Seric 			usrerr("550 Cannot mail directly to files");
3714174Seric 		}
37265496Seric 		else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
37365496Seric 		{
37465496Seric 			a->q_flags |= QBADADDR;
37568857Seric 			a->q_status = "5.7.1";
37665496Seric 			usrerr("550 User %s@%s doesn't have a valid shell for mailing to files",
37765496Seric 				a->q_alias->q_ruser, MyHostName);
37865496Seric 		}
37965496Seric 		else if (bitset(QUNSAFEADDR, a->q_alias->q_flags))
38065496Seric 		{
38165496Seric 			a->q_flags |= QBADADDR;
38268857Seric 			a->q_status = "5.7.1";
38365496Seric 			usrerr("550 Address %s is unsafe for mailing to files",
38465496Seric 				a->q_alias->q_paddr);
38565496Seric 		}
38668494Seric 		else if (!writable(buf, getctladdr(a), SFF_CREAT))
38751317Seric 		{
38858680Seric 			a->q_flags |= QBADADDR;
38968481Seric 			giveresponse(EX_CANTCREAT, m, NULL, a->q_alias,
39068481Seric 				     (time_t) 0, e);
39151317Seric 		}
39251317Seric 	}
39351317Seric 
39457402Seric 	/* try aliasing */
39568481Seric 	if (!bitset(QDONTSEND, a->q_flags) && bitnset(M_ALIASABLE, m->m_flags))
39668481Seric 		alias(a, sendq, aliaslevel, e);
39757402Seric 
39857402Seric # ifdef USERDB
39957402Seric 	/* if not aliased, look it up in the user database */
40068481Seric 	if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) &&
40168481Seric 	    bitnset(M_CHECKUDB, m->m_flags))
40257402Seric 	{
40357402Seric 		extern int udbexpand();
40457402Seric 
40568481Seric 		if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL)
40657402Seric 		{
40763853Seric 			a->q_flags |= QQUEUEUP;
40857402Seric 			if (e->e_message == NULL)
40957402Seric 				e->e_message = newstr("Deferred: user database error");
41057402Seric # ifdef LOG
41158020Seric 			if (LogLevel > 8)
41259623Seric 				syslog(LOG_INFO, "%s: deferred: udbexpand: %s",
41366284Seric 					e->e_id == NULL ? "NOQUEUE" : e->e_id,
41466284Seric 					errstring(errno));
41557402Seric # endif
41659615Seric 			message("queued (user database error): %s",
41759615Seric 				errstring(errno));
41857642Seric 			e->e_nrcpts++;
41963847Seric 			goto testselfdestruct;
42057402Seric 		}
42157402Seric 	}
42257402Seric # endif
42357402Seric 
42451317Seric 	/*
42551317Seric 	**  If we have a level two config file, then pass the name through
42651317Seric 	**  Ruleset 5 before sending it off.  Ruleset 5 has the right
42751317Seric 	**  to send rewrite it to another mailer.  This gives us a hook
42851317Seric 	**  after local aliasing has been done.
42951317Seric 	*/
43051317Seric 
43151317Seric 	if (tTd(29, 5))
43251317Seric 	{
43351317Seric 		printf("recipient: testing local?  cl=%d, rr5=%x\n\t",
43451317Seric 			ConfigLevel, RewriteRules[5]);
43551317Seric 		printaddr(a, FALSE);
43651317Seric 	}
43768481Seric 	if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
43868481Seric 	    ConfigLevel >= 2 && RewriteRules[5] != NULL &&
43968481Seric 	    bitnset(M_TRYRULESET5, m->m_flags))
44051317Seric 	{
44168603Seric 		maplocaluser(a, sendq, aliaslevel + 1, e);
44251317Seric 	}
44351317Seric 
44451317Seric 	/*
44551317Seric 	**  If it didn't get rewritten to another mailer, go ahead
44651317Seric 	**  and deliver it.
44751317Seric 	*/
44851317Seric 
44968481Seric 	if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
45068481Seric 	    bitnset(M_HASPWENT, m->m_flags))
45151317Seric 	{
45255354Seric 		auto bool fuzzy;
45351317Seric 		register struct passwd *pw;
45451317Seric 		extern struct passwd *finduser();
45551317Seric 
45651317Seric 		/* warning -- finduser may trash buf */
45755354Seric 		pw = finduser(buf, &fuzzy);
45851317Seric 		if (pw == NULL)
45951317Seric 		{
46058680Seric 			a->q_flags |= QBADADDR;
46168857Seric 			a->q_status = "5.1.1";
46268481Seric 			giveresponse(EX_NOUSER, m, NULL, a->q_alias,
46368481Seric 				     (time_t) 0, e);
46451317Seric 		}
4654174Seric 		else
4664174Seric 		{
46768528Seric 			char nbuf[MAXNAME + 1];
4684373Seric 
46955354Seric 			if (fuzzy)
4704174Seric 			{
47153735Seric 				/* name was a fuzzy match */
47251317Seric 				a->q_user = newstr(pw->pw_name);
47353735Seric 				if (findusercount++ > 3)
47453735Seric 				{
47558680Seric 					a->q_flags |= QBADADDR;
47668857Seric 					a->q_status = "5.4.6";
47758151Seric 					usrerr("554 aliasing/forwarding loop for %s broken",
47853735Seric 						pw->pw_name);
47968481Seric 					goto done;
48053735Seric 				}
48153735Seric 
48253735Seric 				/* see if it aliases */
48351317Seric 				(void) strcpy(buf, pw->pw_name);
48453735Seric 				goto trylocaluser;
4854174Seric 			}
48665822Seric 			if (strcmp(pw->pw_dir, "/") == 0)
48765822Seric 				a->q_home = "";
48865822Seric 			else
48965822Seric 				a->q_home = newstr(pw->pw_dir);
49051317Seric 			a->q_uid = pw->pw_uid;
49151317Seric 			a->q_gid = pw->pw_gid;
49259083Seric 			a->q_ruser = newstr(pw->pw_name);
49351317Seric 			a->q_flags |= QGOODUID;
49451317Seric 			buildfname(pw->pw_gecos, pw->pw_name, nbuf);
49551317Seric 			if (nbuf[0] != '\0')
49651317Seric 				a->q_fullname = newstr(nbuf);
49765211Seric 			if (pw->pw_shell != NULL && pw->pw_shell[0] != '\0' &&
49865211Seric 			    !usershellok(pw->pw_shell))
49965206Seric 			{
50065211Seric 				a->q_flags |= QBOGUSSHELL;
50165206Seric 			}
50251317Seric 			if (!quoted)
50368481Seric 				forward(a, sendq, aliaslevel, e);
5044174Seric 		}
5054174Seric 	}
50657642Seric 	if (!bitset(QDONTSEND, a->q_flags))
50757642Seric 		e->e_nrcpts++;
50863847Seric 
50963847Seric   testselfdestruct:
51068603Seric 	a->q_flags |= QTHISPASS;
51163978Seric 	if (tTd(26, 8))
51263847Seric 	{
51363978Seric 		printf("testselfdestruct: ");
51468603Seric 		printaddr(a, FALSE);
51568603Seric 		if (tTd(26, 10))
51668603Seric 		{
51768603Seric 			printf("SENDQ:\n");
51868603Seric 			printaddr(*sendq, TRUE);
51968603Seric 			printf("----\n");
52068603Seric 		}
52163978Seric 	}
52263978Seric 	if (a->q_alias == NULL && a != &e->e_from &&
52363978Seric 	    bitset(QDONTSEND, a->q_flags))
52463978Seric 	{
52568603Seric 		for (q = *sendq; q != NULL; q = q->q_next)
52668603Seric 		{
52768603Seric 			if (!bitset(QDONTSEND|QBADADDR, q->q_flags) &&
52868603Seric 			    bitset(QTHISPASS, q->q_flags))
52968603Seric 				break;
53068603Seric 		}
53163978Seric 		if (q == NULL)
53263847Seric 		{
53363847Seric 			a->q_flags |= QBADADDR;
53468858Seric 			a->q_status = "5.4.6";
53563847Seric 			usrerr("554 aliasing/forwarding loop broken");
53663847Seric 		}
53763847Seric 	}
53868481Seric 
53968481Seric   done:
54068603Seric 	a->q_flags |= QTHISPASS;
54168481Seric 	if (buf != buf0)
54268481Seric 		free(buf);
54368603Seric 
54468603Seric 	/*
54568603Seric 	**  If we are at the top level, check to see if this has
54668603Seric 	**  expanded to exactly one address.  If so, it can inherit
54768603Seric 	**  the primaryness of the address.
54868603Seric 	**
54968603Seric 	**  While we're at it, clear the QTHISPASS bits.
55068603Seric 	*/
55168603Seric 
55268603Seric 	if (aliaslevel == 0)
55368603Seric 	{
55468603Seric 		int nrcpts = 0;
55568603Seric 		ADDRESS *only;
55668603Seric 
55768603Seric 		for (q = *sendq; q != NULL; q = q->q_next)
55868603Seric 		{
55968603Seric 			if (bitset(QTHISPASS, q->q_flags) &&
56068603Seric 			    !bitset(QDONTSEND|QBADADDR, q->q_flags))
56168603Seric 			{
56268603Seric 				nrcpts++;
56368603Seric 				only = q;
56468603Seric 			}
56568603Seric 			q->q_flags &= ~QTHISPASS;
56668603Seric 		}
56768603Seric 		if (nrcpts == 1)
56868603Seric 		{
56968867Seric 			/* check to see if this actually got a new owner */
57068867Seric 			q = only;
57168867Seric 			while ((q = q->q_alias) != NULL)
57268867Seric 			{
57368867Seric 				if (q->q_owner != NULL)
57468867Seric 					break;
57568867Seric 			}
57668867Seric 			if (q == NULL)
57768867Seric 				only->q_flags |= QPRIMARY;
57868867Seric 		}
57968867Seric 		else if (!initialdontsend && nrcpts > 0)
58068867Seric 		{
58168603Seric 			/* arrange for return receipt */
58268603Seric 			e->e_flags |= EF_SENDRECEIPT;
58368867Seric 			a->q_flags |= QEXPANDED;
58468603Seric 			if (e->e_xfp != NULL)
58568603Seric 				fprintf(e->e_xfp,
58668603Seric 					"%s... expanded to multiple addresses\n",
58768603Seric 					a->q_paddr);
58868603Seric 		}
58968603Seric 	}
59068603Seric 
59112613Seric 	return (a);
5924174Seric }
5934174Seric /*
5944373Seric **  FINDUSER -- find the password entry for a user.
5954373Seric **
5964373Seric **	This looks a lot like getpwnam, except that it may want to
5974373Seric **	do some fancier pattern matching in /etc/passwd.
5984373Seric **
5999379Seric **	This routine contains most of the time of many sendmail runs.
6009379Seric **	It deserves to be optimized.
6019379Seric **
6024373Seric **	Parameters:
6034373Seric **		name -- the name to match against.
60455354Seric **		fuzzyp -- an outarg that is set to TRUE if this entry
60555354Seric **			was found using the fuzzy matching algorithm;
60655354Seric **			set to FALSE otherwise.
6074373Seric **
6084373Seric **	Returns:
6094373Seric **		A pointer to a pw struct.
6104373Seric **		NULL if name is unknown or ambiguous.
6114373Seric **
6124373Seric **	Side Effects:
6134407Seric **		may modify name.
6144373Seric */
6154373Seric 
6164373Seric struct passwd *
61755354Seric finduser(name, fuzzyp)
6184373Seric 	char *name;
61955354Seric 	bool *fuzzyp;
6204373Seric {
6214376Seric 	register struct passwd *pw;
6224407Seric 	register char *p;
6234373Seric 
62455354Seric 	if (tTd(29, 4))
62555354Seric 		printf("finduser(%s): ", name);
62655354Seric 
62755354Seric 	*fuzzyp = FALSE;
6284407Seric 
62968481Seric #ifdef HESIOD
63064673Seric 	/* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */
63164673Seric 	for (p = name; *p != '\0'; p++)
63264673Seric 		if (!isascii(*p) || !isdigit(*p))
63364673Seric 			break;
63464673Seric 	if (*p == '\0')
63564673Seric 	{
63664673Seric 		if (tTd(29, 4))
63764673Seric 			printf("failed (numeric input)\n");
63864673Seric 		return NULL;
63964673Seric 	}
64068481Seric #endif
64164673Seric 
64225777Seric 	/* look up this login name using fast path */
64368693Seric 	if ((pw = sm_getpwnam(name)) != NULL)
64455354Seric 	{
64555354Seric 		if (tTd(29, 4))
64655354Seric 			printf("found (non-fuzzy)\n");
64712634Seric 		return (pw);
64855354Seric 	}
64912634Seric 
65053735Seric #ifdef MATCHGECOS
65153735Seric 	/* see if fuzzy matching allowed */
65253735Seric 	if (!MatchGecos)
65355354Seric 	{
65455354Seric 		if (tTd(29, 4))
65555354Seric 			printf("not found (fuzzy disabled)\n");
65653735Seric 		return NULL;
65755354Seric 	}
65853735Seric 
65912634Seric 	/* search for a matching full name instead */
66025777Seric 	for (p = name; *p != '\0'; p++)
66125777Seric 	{
66225777Seric 		if (*p == (SpaceSub & 0177) || *p == '_')
66325777Seric 			*p = ' ';
66425777Seric 	}
66523107Seric 	(void) setpwent();
6664376Seric 	while ((pw = getpwent()) != NULL)
6674376Seric 	{
66868528Seric 		char buf[MAXNAME + 1];
6694376Seric 
6704998Seric 		buildfname(pw->pw_gecos, pw->pw_name, buf);
67156795Seric 		if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name))
6724381Seric 		{
67355354Seric 			if (tTd(29, 4))
67455354Seric 				printf("fuzzy matches %s\n", pw->pw_name);
67558151Seric 			message("sending to login name %s", pw->pw_name);
67655354Seric 			*fuzzyp = TRUE;
6774376Seric 			return (pw);
6784377Seric 		}
6794376Seric 	}
68055354Seric 	if (tTd(29, 4))
68155354Seric 		printf("no fuzzy match found\n");
68259015Seric #else
68359015Seric 	if (tTd(29, 4))
68459015Seric 		printf("not found (fuzzy disabled)\n");
68559015Seric #endif
6864376Seric 	return (NULL);
6874373Seric }
6884373Seric /*
6894329Seric **  WRITABLE -- predicate returning if the file is writable.
6904329Seric **
6914329Seric **	This routine must duplicate the algorithm in sys/fio.c.
6924329Seric **	Unfortunately, we cannot use the access call since we
6934329Seric **	won't necessarily be the real uid when we try to
6944329Seric **	actually open the file.
6954329Seric **
6964329Seric **	Notice that ANY file with ANY execute bit is automatically
6974329Seric **	not writable.  This is also enforced by mailfile.
6984329Seric **
6994329Seric **	Parameters:
70065064Seric **		filename -- the file name to check.
70165112Seric **		ctladdr -- the controlling address for this file.
70265064Seric **		flags -- SFF_* flags to control the function.
7034329Seric **
7044329Seric **	Returns:
7054329Seric **		TRUE -- if we will be able to write this file.
7064329Seric **		FALSE -- if we cannot write this file.
7074329Seric **
7084329Seric **	Side Effects:
7094329Seric **		none.
7104329Seric */
7114329Seric 
7124329Seric bool
71365112Seric writable(filename, ctladdr, flags)
71464819Seric 	char *filename;
71565112Seric 	ADDRESS *ctladdr;
71665064Seric 	int flags;
7174329Seric {
71855372Seric 	uid_t euid;
71955372Seric 	gid_t egid;
7204329Seric 	int bits;
72164944Seric 	register char *p;
72264944Seric 	char *uname;
7234329Seric 
72464819Seric 	if (tTd(29, 5))
72568802Seric 		printf("writable(%s, 0x%x)\n", filename, flags);
72664944Seric 
72765225Seric #ifdef SUID_ROOT_FILES_OK
72865225Seric 	/* really ought to be passed down -- and not a good idea */
72965225Seric 	flags |= SFF_ROOTOK;
73065225Seric #endif
73165225Seric 
73264944Seric 	/*
73364944Seric 	**  File does exist -- check that it is writable.
73464944Seric 	*/
73564944Seric 
73665112Seric 	if (ctladdr != NULL && geteuid() == 0)
73764944Seric 	{
73865112Seric 		euid = ctladdr->q_uid;
73965112Seric 		egid = ctladdr->q_gid;
74065112Seric 		uname = ctladdr->q_user;
74164944Seric 	}
74268802Seric 	else if (bitset(SFF_RUNASREALUID, flags))
74365112Seric 	{
74468494Seric 		extern char RealUserName[];
74568494Seric 
74665112Seric 		euid = RealUid;
74765112Seric 		egid = RealGid;
74865112Seric 		uname = RealUserName;
74965112Seric 	}
75068481Seric 	else if (FileMailer != NULL)
75168481Seric 	{
75268481Seric 		euid = FileMailer->m_uid;
75368481Seric 		egid = FileMailer->m_gid;
75468481Seric 	}
75568481Seric 	else
75668481Seric 	{
75768481Seric 		euid = egid = 0;
75868481Seric 	}
75965138Seric 	if (euid == 0)
76065138Seric 	{
76165138Seric 		euid = DefUid;
76265138Seric 		uname = DefUser;
76365138Seric 	}
76465138Seric 	if (egid == 0)
76565138Seric 		egid = DefGid;
7664329Seric 	if (geteuid() == 0)
76768494Seric 		flags |= SFF_SETUIDOK;
7684329Seric 
76968494Seric 	errno = safefile(filename, euid, egid, uname, flags, S_IWRITE, NULL);
77065067Seric 	return errno == 0;
7714329Seric }
7724329Seric /*
7734174Seric **  INCLUDE -- handle :include: specification.
7744174Seric **
7754174Seric **	Parameters:
7764174Seric **		fname -- filename to include.
77753037Seric **		forwarding -- if TRUE, we are reading a .forward file.
77853037Seric **			if FALSE, it's a :include: file.
7794399Seric **		ctladdr -- address template to use to fill in these
7804399Seric **			addresses -- effective user/group id are
7814399Seric **			the important things.
7825006Seric **		sendq -- a pointer to the head of the send queue
7835006Seric **			to put these addresses in.
78468481Seric **		aliaslevel -- the alias nesting depth.
78568481Seric **		e -- the current envelope.
7864174Seric **
7874174Seric **	Returns:
78857136Seric **		open error status
7894174Seric **
7904174Seric **	Side Effects:
7914174Seric **		reads the :include: file and sends to everyone
7924174Seric **		listed in that file.
79365909Seric **
79465909Seric **	Security Note:
79565909Seric **		If you have restricted chown (that is, you can't
79665909Seric **		give a file away), it is reasonable to allow programs
79765909Seric **		and files called from this :include: file to be to be
79865909Seric **		run as the owner of the :include: file.  This is bogus
79965909Seric **		if there is any chance of someone giving away a file.
80065909Seric **		We assume that pre-POSIX systems can give away files.
80165909Seric **
80265909Seric **		There is an additional restriction that if you
80365909Seric **		forward to a :include: file, it will not take on
80465909Seric **		the ownership of the :include: file.  This may not
80565909Seric **		be necessary, but shouldn't hurt.
8064174Seric */
8074174Seric 
80853037Seric static jmp_buf	CtxIncludeTimeout;
80968481Seric static void	includetimeout();
81053037Seric 
81157136Seric int
81268481Seric include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
8134174Seric 	char *fname;
81453037Seric 	bool forwarding;
8154399Seric 	ADDRESS *ctladdr;
8165006Seric 	ADDRESS **sendq;
81768481Seric 	int aliaslevel;
81855012Seric 	ENVELOPE *e;
8194174Seric {
82068481Seric 	FILE *fp = NULL;
82155012Seric 	char *oldto = e->e_to;
8229379Seric 	char *oldfilename = FileName;
8239379Seric 	int oldlinenumber = LineNumber;
82453037Seric 	register EVENT *ev = NULL;
82558082Seric 	int nincludes;
82664325Seric 	register ADDRESS *ca;
82764325Seric 	uid_t saveduid, uid;
82864325Seric 	gid_t savedgid, gid;
82964083Seric 	char *uname;
83064325Seric 	int rval = 0;
83168513Seric 	int sfflags = SFF_REGONLY;
83265496Seric 	struct stat st;
83365948Seric 	char buf[MAXLINE];
83465909Seric #ifdef _POSIX_CHOWN_RESTRICTED
83565948Seric # if _POSIX_CHOWN_RESTRICTED == -1
83665948Seric #  define safechown	FALSE
83765948Seric # else
83865948Seric #  define safechown	TRUE
83965948Seric # endif
84065948Seric #else
84165948Seric # ifdef _PC_CHOWN_RESTRICTED
84265909Seric 	bool safechown;
84365948Seric # else
84465948Seric #  ifdef BSD
84565948Seric #   define safechown	TRUE
84665948Seric #  else
84765948Seric #   define safechown	FALSE
84865948Seric #  endif
84965948Seric # endif
85065909Seric #endif
85165948Seric 	extern bool chownsafe();
8524174Seric 
85357186Seric 	if (tTd(27, 2))
85457186Seric 		printf("include(%s)\n", fname);
85563902Seric 	if (tTd(27, 4))
85663902Seric 		printf("   ruid=%d euid=%d\n", getuid(), geteuid());
85763581Seric 	if (tTd(27, 14))
85863581Seric 	{
85963581Seric 		printf("ctladdr ");
86063581Seric 		printaddr(ctladdr, FALSE);
86163581Seric 	}
86257186Seric 
86364325Seric 	if (tTd(27, 9))
86464325Seric 		printf("include: old uid = %d/%d\n", getuid(), geteuid());
86553037Seric 
86668513Seric 	if (forwarding)
86769306Seric 		sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOSLINK;
86868513Seric 
86963581Seric 	ca = getctladdr(ctladdr);
87063581Seric 	if (ca == NULL)
87164083Seric 	{
87264846Seric 		uid = DefUid;
87364846Seric 		gid = DefGid;
87464846Seric 		uname = DefUser;
87564083Seric 	}
87663581Seric 	else
87764083Seric 	{
87863581Seric 		uid = ca->q_uid;
87964083Seric 		gid = ca->q_gid;
88064083Seric 		uname = ca->q_user;
88168481Seric 	}
88264325Seric #ifdef HASSETREUID
88368481Seric 	saveduid = geteuid();
88468481Seric 	savedgid = getegid();
88568481Seric 	if (saveduid == 0)
88668481Seric 	{
88768481Seric 		initgroups(uname, gid);
88868481Seric 		if (uid != 0)
88964325Seric 		{
89068481Seric 			if (setreuid(0, uid) < 0)
89168481Seric 				syserr("setreuid(0, %d) failure (real=%d, eff=%d)",
89268481Seric 					uid, getuid(), geteuid());
89368481Seric 			else
89468481Seric 				sfflags |= SFF_NOPATHCHECK;
89564325Seric 		}
89668481Seric 	}
897*69544Seric #endif
89863581Seric 
89964325Seric 	if (tTd(27, 9))
90064325Seric 		printf("include: new uid = %d/%d\n", getuid(), geteuid());
90164325Seric 
90264325Seric 	/*
90364325Seric 	**  If home directory is remote mounted but server is down,
90464325Seric 	**  this can hang or give errors; use a timeout to avoid this
90564325Seric 	*/
90664325Seric 
90753037Seric 	if (setjmp(CtxIncludeTimeout) != 0)
90853037Seric 	{
90963853Seric 		ctladdr->q_flags |= QQUEUEUP;
91053037Seric 		errno = 0;
91163993Seric 
91263993Seric 		/* return pseudo-error code */
91364325Seric 		rval = EOPENTIMEOUT;
91464325Seric 		goto resetuid;
91553037Seric 	}
91668481Seric 	if (TimeOuts.to_fileopen > 0)
91768481Seric 		ev = setevent(TimeOuts.to_fileopen, includetimeout, 0);
91868481Seric 	else
91968481Seric 		ev = NULL;
92053037Seric 
92163581Seric 	/* the input file must be marked safe */
92268494Seric 	rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, NULL);
92364329Seric 	if (rval != 0)
92453037Seric 	{
92564325Seric 		/* don't use this :include: file */
92657186Seric 		if (tTd(27, 4))
92758247Seric 			printf("include: not safe (uid=%d): %s\n",
92864329Seric 				uid, errstring(rval));
92953037Seric 	}
93065496Seric 	else
9314174Seric 	{
93265496Seric 		fp = fopen(fname, "r");
93365496Seric 		if (fp == NULL)
93458061Seric 		{
93564329Seric 			rval = errno;
93665496Seric 			if (tTd(27, 4))
93765496Seric 				printf("include: open: %s\n", errstring(rval));
93858061Seric 		}
9394406Seric 	}
94068481Seric 	if (ev != NULL)
94168481Seric 		clrevent(ev);
94253037Seric 
94364570Seric resetuid:
94464570Seric 
94564570Seric #ifdef HASSETREUID
94664570Seric 	if (saveduid == 0)
94764570Seric 	{
94864570Seric 		if (uid != 0)
94968481Seric 		{
95068481Seric 			if (setreuid(-1, 0) < 0)
95168481Seric 				syserr("setreuid(-1, 0) failure (real=%d, eff=%d)",
95268481Seric 					getuid(), geteuid());
95368481Seric 			if (setreuid(RealUid, 0) < 0)
95464570Seric 				syserr("setreuid(%d, 0) failure (real=%d, eff=%d)",
95564570Seric 					RealUid, getuid(), geteuid());
95668481Seric 		}
95764570Seric 		setgid(savedgid);
95864570Seric 	}
95964570Seric #endif
96064570Seric 
96164570Seric 	if (tTd(27, 9))
96264570Seric 		printf("include: reset uid = %d/%d\n", getuid(), geteuid());
96364570Seric 
96465593Seric 	if (rval == EOPENTIMEOUT)
96565593Seric 		usrerr("451 open timeout on %s", fname);
96665593Seric 
96764570Seric 	if (fp == NULL)
96864570Seric 		return rval;
96964570Seric 
97065496Seric 	if (fstat(fileno(fp), &st) < 0)
97165496Seric 	{
97265496Seric 		rval = errno;
97365496Seric 		syserr("Cannot fstat %s!", fname);
97465496Seric 		return rval;
97565496Seric 	}
97665496Seric 
97765948Seric #ifndef safechown
97865948Seric 	safechown = chownsafe(fileno(fp));
97965948Seric #endif
98065909Seric 	if (ca == NULL && safechown)
98165496Seric 	{
98265496Seric 		ctladdr->q_uid = st.st_uid;
98365496Seric 		ctladdr->q_gid = st.st_gid;
98465496Seric 		ctladdr->q_flags |= QGOODUID;
98565496Seric 	}
98665496Seric 	if (ca != NULL && ca->q_uid == st.st_uid)
98765496Seric 	{
98865496Seric 		/* optimization -- avoid getpwuid if we already have info */
98965496Seric 		ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL;
99065496Seric 		ctladdr->q_ruser = ca->q_ruser;
99165496Seric 	}
99265496Seric 	else
99365496Seric 	{
99465496Seric 		register struct passwd *pw;
99565496Seric 
99668693Seric 		pw = sm_getpwuid(st.st_uid);
99768481Seric 		if (pw == NULL)
99868481Seric 			ctladdr->q_flags |= QBOGUSSHELL;
99968481Seric 		else
100068478Seric 		{
100168481Seric 			char *sh;
100268481Seric 
100368478Seric 			ctladdr->q_ruser = newstr(pw->pw_name);
100468478Seric 			if (safechown)
100568478Seric 				sh = pw->pw_shell;
100665909Seric 			else
100768481Seric 				sh = "/SENDMAIL/ANY/SHELL/";
100868481Seric 			if (!usershellok(sh))
100968481Seric 			{
101068481Seric 				if (safechown)
101168481Seric 					ctladdr->q_flags |= QBOGUSSHELL;
101268481Seric 				else
101368481Seric 					ctladdr->q_flags |= QUNSAFEADDR;
101468481Seric 			}
101565496Seric 		}
101665496Seric 	}
101765496Seric 
101858092Seric 	if (bitset(EF_VRFYONLY, e->e_flags))
101958092Seric 	{
102058092Seric 		/* don't do any more now */
102158868Seric 		ctladdr->q_flags |= QVERIFIED;
102258884Seric 		e->e_nrcpts++;
102358680Seric 		xfclose(fp, "include", fname);
102464570Seric 		return rval;
102558092Seric 	}
102658092Seric 
102765496Seric 	/*
102865496Seric 	** Check to see if some bad guy can write this file
102965496Seric 	**
103065496Seric 	**	This should really do something clever with group
103165496Seric 	**	permissions; currently we just view world writable
103265496Seric 	**	as unsafe.  Also, we don't check for writable
103365496Seric 	**	directories in the path.  We've got to leave
103465496Seric 	**	something for the local sysad to do.
103565496Seric 	*/
103665496Seric 
103765496Seric 	if (bitset(S_IWOTH, st.st_mode))
103865496Seric 		ctladdr->q_flags |= QUNSAFEADDR;
103965496Seric 
10404174Seric 	/* read the file -- each line is a comma-separated list. */
10419379Seric 	FileName = fname;
10429379Seric 	LineNumber = 0;
104358082Seric 	ctladdr->q_flags &= ~QSELFREF;
104458082Seric 	nincludes = 0;
10454174Seric 	while (fgets(buf, sizeof buf, fp) != NULL)
10464174Seric 	{
104756795Seric 		register char *p = strchr(buf, '\n');
10484174Seric 
104940963Sbostic 		LineNumber++;
10504174Seric 		if (p != NULL)
10514174Seric 			*p = '\0';
105257186Seric 		if (buf[0] == '#' || buf[0] == '\0')
105357139Seric 			continue;
105468787Seric 
105568787Seric 		/* <sp>#@# introduces a comment anywhere */
105668787Seric 		/* for Japanese character sets */
105768787Seric 		for (p = buf; (p = strchr(++p, '#')) != NULL; )
105868787Seric 		{
105968787Seric 			if (p[1] == '@' && p[2] == '#' &&
106068787Seric 			    isascii(p[-1]) && isspace(p[-1]) &&
106168787Seric 			    isascii(p[3]) && isspace(p[3]))
106268787Seric 			{
106368787Seric 				p[-1] = '\0';
106468787Seric 				break;
106568787Seric 			}
106668787Seric 		}
106768787Seric 		if (buf[0] == '\0')
106868787Seric 			continue;
106968787Seric 
107058008Seric 		e->e_to = NULL;
107158151Seric 		message("%s to %s",
107253037Seric 			forwarding ? "forwarding" : "sending", buf);
107357977Seric #ifdef LOG
107458020Seric 		if (forwarding && LogLevel > 9)
107557977Seric 			syslog(LOG_INFO, "%s: forward %s => %s",
107666284Seric 				e->e_id == NULL ? "NOQUEUE" : e->e_id,
107766284Seric 				oldto, buf);
107857977Seric #endif
107957977Seric 
108068481Seric 		nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e);
10814174Seric 	}
108263902Seric 
108363902Seric 	if (ferror(fp) && tTd(27, 3))
108463902Seric 		printf("include: read error: %s\n", errstring(errno));
108558082Seric 	if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags))
108658065Seric 	{
108758065Seric 		if (tTd(27, 5))
108858065Seric 		{
108958065Seric 			printf("include: QDONTSEND ");
109058065Seric 			printaddr(ctladdr, FALSE);
109158065Seric 		}
109258065Seric 		ctladdr->q_flags |= QDONTSEND;
109358065Seric 	}
10944174Seric 
109558680Seric 	(void) xfclose(fp, "include", fname);
10969379Seric 	FileName = oldfilename;
10979379Seric 	LineNumber = oldlinenumber;
109863847Seric 	e->e_to = oldto;
109964325Seric 	return rval;
11004174Seric }
110153037Seric 
110268481Seric static void
110353037Seric includetimeout()
110453037Seric {
110553037Seric 	longjmp(CtxIncludeTimeout, 1);
110653037Seric }
11074324Seric /*
11084324Seric **  SENDTOARGV -- send to an argument vector.
11094324Seric **
11104324Seric **	Parameters:
11114324Seric **		argv -- argument vector to send to.
111258247Seric **		e -- the current envelope.
11134324Seric **
11144324Seric **	Returns:
11154324Seric **		none.
11164324Seric **
11174324Seric **	Side Effects:
11184324Seric **		puts all addresses on the argument vector onto the
11194324Seric **			send queue.
11204324Seric */
11214324Seric 
112255012Seric sendtoargv(argv, e)
11234324Seric 	register char **argv;
112455012Seric 	register ENVELOPE *e;
11254324Seric {
11264324Seric 	register char *p;
11274324Seric 
11284324Seric 	while ((p = *argv++) != NULL)
11294324Seric 	{
113068481Seric 		(void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e);
11314324Seric 	}
11324324Seric }
11334399Seric /*
11344399Seric **  GETCTLADDR -- get controlling address from an address header.
11354399Seric **
11364399Seric **	If none, get one corresponding to the effective userid.
11374399Seric **
11384399Seric **	Parameters:
11394399Seric **		a -- the address to find the controller of.
11404399Seric **
11414399Seric **	Returns:
11424399Seric **		the controlling address.
11434399Seric **
11444399Seric **	Side Effects:
11454399Seric **		none.
11464399Seric */
11474399Seric 
11484399Seric ADDRESS *
11494399Seric getctladdr(a)
11504399Seric 	register ADDRESS *a;
11514399Seric {
11524404Seric 	while (a != NULL && !bitset(QGOODUID, a->q_flags))
11534399Seric 		a = a->q_alias;
11544399Seric 	return (a);
11554399Seric }
1156*69544Seric /*
1157*69544Seric **  SELF_REFERENCE -- check to see if an address references itself
1158*69544Seric **
1159*69544Seric **	The check is done through a chain of aliases.  If it is part of
1160*69544Seric **	a loop, break the loop at the "best" address, that is, the one
1161*69544Seric **	that exists as a real user.
1162*69544Seric **
1163*69544Seric **	This is to handle the case of:
1164*69544Seric **		Andrew.Chang:	awc
1165*69544Seric **		awc:		Andrew.Chang@mail.server.
1166*69544Seric **	which is a problem only on mail.server.
1167*69544Seric **
1168*69544Seric **	Parameters:
1169*69544Seric **		a -- the address to check.
1170*69544Seric **		e -- the current envelope.
1171*69544Seric **
1172*69544Seric **	Returns:
1173*69544Seric **		The address that should be retained.
1174*69544Seric */
1175*69544Seric 
1176*69544Seric ADDRESS *
1177*69544Seric self_reference(a, e)
1178*69544Seric 	ADDRESS *a;
1179*69544Seric 	ENVELOPE *e;
1180*69544Seric {
1181*69544Seric 	ADDRESS *b;		/* top entry in self ref loop */
1182*69544Seric 	ADDRESS *c;		/* entry that point to a real mail box */
1183*69544Seric 
1184*69544Seric 	if (tTd(27, 1))
1185*69544Seric 		printf("self_reference(%s)\n", a->q_paddr);
1186*69544Seric 
1187*69544Seric 	for (b = a->q_alias; b != NULL; b = b->q_alias)
1188*69544Seric 	{
1189*69544Seric 		if (sameaddr(a, b))
1190*69544Seric 			break;
1191*69544Seric 	}
1192*69544Seric 
1193*69544Seric 	if (b == NULL)
1194*69544Seric 	{
1195*69544Seric 		if (tTd(27, 1))
1196*69544Seric 			printf("\t... no self ref\n");
1197*69544Seric 		return NULL;
1198*69544Seric 	}
1199*69544Seric 
1200*69544Seric 	/*
1201*69544Seric 	**  Pick the first address that resolved to a real mail box
1202*69544Seric 	**  i.e has a pw entry.  The returned value will be marked
1203*69544Seric 	**  QSELFREF in recipient(), which in turn will disable alias()
1204*69544Seric 	**  from marking it QDONTSEND, which mean it will be used
1205*69544Seric 	**  as a deliverable address.
1206*69544Seric 	**
1207*69544Seric 	**  The 2 key thing to note here are:
1208*69544Seric 	**	1) we are in a recursive call sequence:
1209*69544Seric 	**		alias->sentolist->recipient->alias
1210*69544Seric 	**	2) normally, when we return back to alias(), the address
1211*69544Seric 	**	   will be marked QDONTSEND, since alias() assumes the
1212*69544Seric 	**	   expanded form will be used instead of the current address.
1213*69544Seric 	**	   This behaviour is turned off if the address is marked
1214*69544Seric 	**	   QSELFREF We set QSELFREF when we return to recipient().
1215*69544Seric 	*/
1216*69544Seric 
1217*69544Seric 	c = a;
1218*69544Seric 	while (c != NULL)
1219*69544Seric 	{
1220*69544Seric 		if (bitnset(M_HASPWENT, c->q_mailer->m_flags))
1221*69544Seric 		{
1222*69544Seric 			if (tTd(27, 2))
1223*69544Seric 				printf("\t... getpwnam(%s)... ", c->q_user);
1224*69544Seric 			if (sm_getpwnam(c->q_user) != NULL)
1225*69544Seric 			{
1226*69544Seric 				if (tTd(27, 2))
1227*69544Seric 					printf("found\n");
1228*69544Seric 
1229*69544Seric 				/* ought to cache results here */
1230*69544Seric 				return c;
1231*69544Seric 			}
1232*69544Seric 			if (tTd(27, 2))
1233*69544Seric 				printf("failed\n");
1234*69544Seric 		}
1235*69544Seric 		c = c->q_alias;
1236*69544Seric 	}
1237*69544Seric 
1238*69544Seric 	if (tTd(27, 1))
1239*69544Seric 		printf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
1240*69544Seric 
1241*69544Seric 	return NULL;
1242*69544Seric }
1243