125a99e2eSeric # include <signal.h>
254aa2b0fSeric # include <errno.h>
3b20b3270Seric # include "sendmail.h"
4c77d1c25Seric # include <sys/stat.h>
525a99e2eSeric 
6*57fc6f17Seric SCCSID(@(#)deliver.c	3.147		02/02/83);
7259cace7Seric 
825a99e2eSeric /*
913bbc08cSeric **  DELIVER -- Deliver a message to a list of addresses.
1013bbc08cSeric **
1113bbc08cSeric **	This routine delivers to everyone on the same host as the
1213bbc08cSeric **	user on the head of the list.  It is clever about mailers
1313bbc08cSeric **	that don't handle multiple users.  It is NOT guaranteed
1413bbc08cSeric **	that it will deliver to all these addresses however -- so
1513bbc08cSeric **	deliver should be called once for each address on the
1613bbc08cSeric **	list.
1725a99e2eSeric **
1825a99e2eSeric **	Parameters:
19588cad61Seric **		e -- the envelope to deliver.
20c77d1c25Seric **		firstto -- head of the address list to deliver to.
2125a99e2eSeric **
2225a99e2eSeric **	Returns:
2325a99e2eSeric **		zero -- successfully delivered.
2425a99e2eSeric **		else -- some failure, see ExitStat for more info.
2525a99e2eSeric **
2625a99e2eSeric **	Side Effects:
2725a99e2eSeric **		The standard input is passed off to someone.
2825a99e2eSeric */
2925a99e2eSeric 
30588cad61Seric deliver(e, firstto)
31588cad61Seric 	register ENVELOPE *e;
32c77d1c25Seric 	ADDRESS *firstto;
3325a99e2eSeric {
3478442df3Seric 	char *host;			/* host being sent to */
3578442df3Seric 	char *user;			/* user being sent to */
3625a99e2eSeric 	char **pvp;
375dfc646bSeric 	register char **mvp;
3825a99e2eSeric 	register char *p;
39588cad61Seric 	register MAILER *m;		/* mailer for this recipient */
406259796dSeric 	ADDRESS *ctladdr;
41c77d1c25Seric 	register ADDRESS *to = firstto;
42c579ef51Seric 	bool clever = FALSE;		/* running user smtp to this mailer */
43772e6e50Seric 	ADDRESS *tochain = NULL;	/* chain of users in this mailer call */
445826d9d3Seric 	register int rcode;		/* response code */
45ee6bf8dfSeric 	char *pv[MAXPV+1];
46ee6bf8dfSeric 	char tobuf[MAXLINE-50];		/* text line of to people */
47ee6bf8dfSeric 	char buf[MAXNAME];
48ee6bf8dfSeric 	char tfrombuf[MAXNAME];		/* translated from person */
49ee6bf8dfSeric 	extern bool checkcompat();
50ee6bf8dfSeric 	extern ADDRESS *getctladdr();
51ee6bf8dfSeric 	extern char *remotename();
5225a99e2eSeric 
5335490626Seric 	errno = 0;
54da2935e1Seric 	if (bitset(QDONTSEND, to->q_flags))
555dfc646bSeric 		return (0);
5625a99e2eSeric 
5751552439Seric 	m = to->q_mailer;
5851552439Seric 	host = to->q_host;
5951552439Seric 
6025a99e2eSeric # ifdef DEBUG
616ef48975Seric 	if (tTd(10, 1))
625dfc646bSeric 		printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
6351552439Seric 			m->m_mno, host, to->q_user);
6425a99e2eSeric # endif DEBUG
65f3dbc832Seric 
66f3dbc832Seric 	/*
67f3dbc832Seric 	**  If this mailer is expensive, and if we don't want to make
68f3dbc832Seric 	**  connections now, just mark these addresses and return.
69f3dbc832Seric 	**	This is useful if we want to batch connections to
70f3dbc832Seric 	**	reduce load.  This will cause the messages to be
71f3dbc832Seric 	**	queued up, and a daemon will come along to send the
72f3dbc832Seric 	**	messages later.
73f3dbc832Seric 	**		This should be on a per-mailer basis.
74f3dbc832Seric 	*/
75f3dbc832Seric 
76*57fc6f17Seric 	if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) &&
77317680d6Seric 	    !Verbose)
78f3dbc832Seric 	{
79f3dbc832Seric 		for (; to != NULL; to = to->q_next)
80f4560e80Seric 		{
81f4560e80Seric 			if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m)
82f4560e80Seric 				continue;
83f3dbc832Seric 			to->q_flags |= QQUEUEUP|QDONTSEND;
84588cad61Seric 			e->e_to = to->q_paddr;
85eb238f8cSeric 			message(Arpa_Info, "queued");
86eb238f8cSeric 			if (LogLevel > 4)
87eb238f8cSeric 				logdelivery("queued");
88f4560e80Seric 		}
89588cad61Seric 		e->e_to = NULL;
90f3dbc832Seric 		return (0);
91f3dbc832Seric 	}
92f3dbc832Seric 
9325a99e2eSeric 	/*
945dfc646bSeric 	**  Do initial argv setup.
955dfc646bSeric 	**	Insert the mailer name.  Notice that $x expansion is
965dfc646bSeric 	**	NOT done on the mailer name.  Then, if the mailer has
975dfc646bSeric 	**	a picky -f flag, we insert it as appropriate.  This
985dfc646bSeric 	**	code does not check for 'pv' overflow; this places a
995dfc646bSeric 	**	manifest lower limit of 4 for MAXPV.
1003bea8136Seric 	**		The from address rewrite is expected to make
1013bea8136Seric 	**		the address relative to the other end.
1025dfc646bSeric 	*/
1035dfc646bSeric 
10478442df3Seric 	/* rewrite from address, using rewriting rules */
105588cad61Seric 	expand("$f", buf, &buf[sizeof buf - 1], e);
106ee6bf8dfSeric 	(void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE));
10778442df3Seric 
108588cad61Seric 	define('g', tfrombuf, e);		/* translated sender address */
109588cad61Seric 	define('h', host, e);			/* to host */
1105dfc646bSeric 	Errors = 0;
1115dfc646bSeric 	pvp = pv;
1125dfc646bSeric 	*pvp++ = m->m_argv[0];
1135dfc646bSeric 
1145dfc646bSeric 	/* insert -f or -r flag as appropriate */
115*57fc6f17Seric 	if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags)))
1165dfc646bSeric 	{
117*57fc6f17Seric 		if (bitnset(M_FOPT, m->m_flags))
1185dfc646bSeric 			*pvp++ = "-f";
1195dfc646bSeric 		else
1205dfc646bSeric 			*pvp++ = "-r";
121588cad61Seric 		expand("$g", buf, &buf[sizeof buf - 1], e);
1225dfc646bSeric 		*pvp++ = newstr(buf);
1235dfc646bSeric 	}
1245dfc646bSeric 
1255dfc646bSeric 	/*
1265dfc646bSeric 	**  Append the other fixed parts of the argv.  These run
1275dfc646bSeric 	**  up to the first entry containing "$u".  There can only
1285dfc646bSeric 	**  be one of these, and there are only a few more slots
1295dfc646bSeric 	**  in the pv after it.
1305dfc646bSeric 	*/
1315dfc646bSeric 
1325dfc646bSeric 	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
1335dfc646bSeric 	{
1345dfc646bSeric 		while ((p = index(p, '$')) != NULL)
1355dfc646bSeric 			if (*++p == 'u')
1365dfc646bSeric 				break;
1375dfc646bSeric 		if (p != NULL)
1385dfc646bSeric 			break;
1395dfc646bSeric 
1405dfc646bSeric 		/* this entry is safe -- go ahead and process it */
141588cad61Seric 		expand(*mvp, buf, &buf[sizeof buf - 1], e);
1425dfc646bSeric 		*pvp++ = newstr(buf);
1435dfc646bSeric 		if (pvp >= &pv[MAXPV - 3])
1445dfc646bSeric 		{
1455dfc646bSeric 			syserr("Too many parameters to %s before $u", pv[0]);
1465dfc646bSeric 			return (-1);
1475dfc646bSeric 		}
1485dfc646bSeric 	}
149c579ef51Seric 
15033db8731Seric 	/*
15133db8731Seric 	**  If we have no substitution for the user name in the argument
15233db8731Seric 	**  list, we know that we must supply the names otherwise -- and
15333db8731Seric 	**  SMTP is the answer!!
15433db8731Seric 	*/
15533db8731Seric 
1565dfc646bSeric 	if (*mvp == NULL)
157c579ef51Seric 	{
158c579ef51Seric 		/* running SMTP */
1592c7e1b8dSeric # ifdef SMTP
160c579ef51Seric 		clever = TRUE;
161c579ef51Seric 		*pvp = NULL;
1622c7e1b8dSeric # else SMTP
16333db8731Seric 		/* oops!  we don't implement SMTP */
1642c7e1b8dSeric 		syserr("SMTP style mailer");
1652c7e1b8dSeric 		return (EX_SOFTWARE);
1662c7e1b8dSeric # endif SMTP
167c579ef51Seric 	}
1685dfc646bSeric 
1695dfc646bSeric 	/*
1705dfc646bSeric 	**  At this point *mvp points to the argument with $u.  We
1715dfc646bSeric 	**  run through our address list and append all the addresses
1725dfc646bSeric 	**  we can.  If we run out of space, do not fret!  We can
1735dfc646bSeric 	**  always send another copy later.
1745dfc646bSeric 	*/
1755dfc646bSeric 
1765dfc646bSeric 	tobuf[0] = '\0';
177588cad61Seric 	e->e_to = tobuf;
1786259796dSeric 	ctladdr = NULL;
1795dfc646bSeric 	for (; to != NULL; to = to->q_next)
1805dfc646bSeric 	{
1815dfc646bSeric 		/* avoid sending multiple recipients to dumb mailers */
182*57fc6f17Seric 		if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
1835dfc646bSeric 			break;
1845dfc646bSeric 
1855dfc646bSeric 		/* if already sent or not for this host, don't send */
186da2935e1Seric 		if (bitset(QDONTSEND, to->q_flags) ||
187da2935e1Seric 		    strcmp(to->q_host, host) != 0 ||
188da2935e1Seric 		    to->q_mailer != firstto->q_mailer)
1895dfc646bSeric 			continue;
1906259796dSeric 
1914b22ea87Seric 		/* avoid overflowing tobuf */
192588cad61Seric 		if (sizeof tobuf - (strlen(to->q_paddr) + strlen(tobuf) + 2) < 0)
1934b22ea87Seric 			break;
1944b22ea87Seric 
195772e6e50Seric # ifdef DEBUG
1966ef48975Seric 		if (tTd(10, 1))
197772e6e50Seric 		{
198772e6e50Seric 			printf("\nsend to ");
199772e6e50Seric 			printaddr(to, FALSE);
200772e6e50Seric 		}
201772e6e50Seric # endif DEBUG
202772e6e50Seric 
2036259796dSeric 		/* compute effective uid/gid when sending */
2047da1035fSeric 		if (to->q_mailer == ProgMailer)
2056259796dSeric 			ctladdr = getctladdr(to);
2066259796dSeric 
2075dfc646bSeric 		user = to->q_user;
208588cad61Seric 		e->e_to = to->q_paddr;
2095dfc646bSeric 		to->q_flags |= QDONTSEND;
2105dfc646bSeric 
2115dfc646bSeric 		/*
2125dfc646bSeric 		**  Check to see that these people are allowed to
2135dfc646bSeric 		**  talk to each other.
2142a6e0786Seric 		*/
2152a6e0786Seric 
2162a6e0786Seric 		if (!checkcompat(to))
2175dfc646bSeric 		{
218198d9be0Seric 			giveresponse(EX_UNAVAILABLE, m, e);
2195dfc646bSeric 			continue;
2205dfc646bSeric 		}
2212a6e0786Seric 
2222a6e0786Seric 		/*
2239ec9501bSeric 		**  Strip quote bits from names if the mailer is dumb
2249ec9501bSeric 		**	about them.
22525a99e2eSeric 		*/
22625a99e2eSeric 
227*57fc6f17Seric 		if (bitnset(M_STRIPQ, m->m_flags))
22825a99e2eSeric 		{
2299ec9501bSeric 			stripquotes(user, TRUE);
2309ec9501bSeric 			stripquotes(host, TRUE);
2319ec9501bSeric 		}
2329ec9501bSeric 		else
2339ec9501bSeric 		{
2349ec9501bSeric 			stripquotes(user, FALSE);
2359ec9501bSeric 			stripquotes(host, FALSE);
23625a99e2eSeric 		}
23725a99e2eSeric 
238cdb828c5Seric 		/* hack attack -- delivermail compatibility */
239cdb828c5Seric 		if (m == ProgMailer && *user == '|')
240cdb828c5Seric 			user++;
241cdb828c5Seric 
24225a99e2eSeric 		/*
2433efaed6eSeric 		**  If an error message has already been given, don't
2443efaed6eSeric 		**	bother to send to this address.
2453efaed6eSeric 		**
2463efaed6eSeric 		**	>>>>>>>>>> This clause assumes that the local mailer
2473efaed6eSeric 		**	>> NOTE >> cannot do any further aliasing; that
2483efaed6eSeric 		**	>>>>>>>>>> function is subsumed by sendmail.
2493efaed6eSeric 		*/
2503efaed6eSeric 
2516cae517dSeric 		if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
2523efaed6eSeric 			continue;
2533efaed6eSeric 
254f2fec898Seric 		/* save statistics.... */
255588cad61Seric 		markstats(e, to);
256f2fec898Seric 
2573efaed6eSeric 		/*
25825a99e2eSeric 		**  See if this user name is "special".
25925a99e2eSeric 		**	If the user name has a slash in it, assume that this
26051552439Seric 		**	is a file -- send it off without further ado.  Note
26151552439Seric 		**	that this type of addresses is not processed along
26251552439Seric 		**	with the others, so we fudge on the To person.
26325a99e2eSeric 		*/
26425a99e2eSeric 
2657da1035fSeric 		if (m == LocalMailer)
26625a99e2eSeric 		{
267a49f24c0Seric 			if (user[0] == '/')
26825a99e2eSeric 			{
2695826d9d3Seric 				rcode = mailfile(user, getctladdr(to));
270198d9be0Seric 				giveresponse(rcode, m, e);
2715dfc646bSeric 				continue;
27225a99e2eSeric 			}
27325a99e2eSeric 		}
27425a99e2eSeric 
27513bbc08cSeric 		/*
27613bbc08cSeric 		**  Address is verified -- add this user to mailer
27713bbc08cSeric 		**  argv, and add it to the print list of recipients.
27813bbc08cSeric 		*/
27913bbc08cSeric 
280508daeccSeric 		/* link together the chain of recipients */
281508daeccSeric 		to->q_tchain = tochain;
282508daeccSeric 		tochain = to;
283508daeccSeric 
2845dfc646bSeric 		/* create list of users for error messages */
285db8841e9Seric 		(void) strcat(tobuf, ",");
286db8841e9Seric 		(void) strcat(tobuf, to->q_paddr);
287588cad61Seric 		define('u', user, e);		/* to user */
288588cad61Seric 		define('z', to->q_home, e);	/* user's home */
2895dfc646bSeric 
290c579ef51Seric 		/*
291508daeccSeric 		**  Expand out this user into argument list.
292c579ef51Seric 		*/
293c579ef51Seric 
294508daeccSeric 		if (!clever)
295c579ef51Seric 		{
296588cad61Seric 			expand(*mvp, buf, &buf[sizeof buf - 1], e);
2975dfc646bSeric 			*pvp++ = newstr(buf);
2985dfc646bSeric 			if (pvp >= &pv[MAXPV - 2])
2995dfc646bSeric 			{
3005dfc646bSeric 				/* allow some space for trailing parms */
3015dfc646bSeric 				break;
3025dfc646bSeric 			}
3035dfc646bSeric 		}
304c579ef51Seric 	}
3055dfc646bSeric 
306145b49b1Seric 	/* see if any addresses still exist */
307145b49b1Seric 	if (tobuf[0] == '\0')
308c579ef51Seric 	{
309588cad61Seric 		define('g', (char *) NULL, e);
310145b49b1Seric 		return (0);
311c579ef51Seric 	}
312145b49b1Seric 
3135dfc646bSeric 	/* print out messages as full list */
31463780dbdSeric 	e->e_to = tobuf + 1;
3155dfc646bSeric 
3165dfc646bSeric 	/*
3175dfc646bSeric 	**  Fill out any parameters after the $u parameter.
3185dfc646bSeric 	*/
3195dfc646bSeric 
320c579ef51Seric 	while (!clever && *++mvp != NULL)
3215dfc646bSeric 	{
322588cad61Seric 		expand(*mvp, buf, &buf[sizeof buf - 1], e);
3235dfc646bSeric 		*pvp++ = newstr(buf);
3245dfc646bSeric 		if (pvp >= &pv[MAXPV])
3255dfc646bSeric 			syserr("deliver: pv overflow after $u for %s", pv[0]);
3265dfc646bSeric 	}
3275dfc646bSeric 	*pvp++ = NULL;
3285dfc646bSeric 
32925a99e2eSeric 	/*
33025a99e2eSeric 	**  Call the mailer.
3316328bdf7Seric 	**	The argument vector gets built, pipes
33225a99e2eSeric 	**	are created as necessary, and we fork & exec as
3336328bdf7Seric 	**	appropriate.
334c579ef51Seric 	**	If we are running SMTP, we just need to clean up.
33525a99e2eSeric 	*/
33625a99e2eSeric 
337588cad61Seric 	message(Arpa_Info, "Connecting to %s.%s...", host, m->m_name);
338588cad61Seric 
3396259796dSeric 	if (ctladdr == NULL)
340588cad61Seric 		ctladdr = &e->e_from;
3412c7e1b8dSeric # ifdef SMTP
342c579ef51Seric 	if (clever)
343c579ef51Seric 	{
344588cad61Seric 		/* send the initial SMTP protocol */
34577b52738Seric 		rcode = smtpinit(m, pv);
346588cad61Seric 
34763780dbdSeric 		if (rcode == EX_OK)
34863780dbdSeric 		{
349588cad61Seric 			/* send the recipient list */
35063780dbdSeric 			tobuf[0] = '\0';
351588cad61Seric 			for (to = tochain; to != NULL; to = to->q_tchain)
352588cad61Seric 			{
353588cad61Seric 				int i;
354588cad61Seric 
35563780dbdSeric 				e->e_to = to->q_paddr;
35677b52738Seric 				i = smtprcpt(to, m);
357588cad61Seric 				if (i != EX_OK)
358588cad61Seric 				{
35983b7ddc9Seric 					markfailure(e, to, i);
360198d9be0Seric 					giveresponse(i, m, e);
36163780dbdSeric 				}
36263780dbdSeric 				else
36363780dbdSeric 				{
36463780dbdSeric 					strcat(tobuf, ",");
36563780dbdSeric 					strcat(tobuf, to->q_paddr);
366588cad61Seric 				}
367588cad61Seric 			}
368588cad61Seric 
36963780dbdSeric 			/* now send the data */
37063780dbdSeric 			if (tobuf[0] == '\0')
37163780dbdSeric 				e->e_to = NULL;
37263780dbdSeric 			else
37363780dbdSeric 			{
37463780dbdSeric 				e->e_to = tobuf + 1;
37577b52738Seric 				rcode = smtpdata(m, e);
37663780dbdSeric 			}
37763780dbdSeric 
37863780dbdSeric 			/* now close the connection */
37977b52738Seric 			smtpquit(pv[0], m);
38063780dbdSeric 		}
381c579ef51Seric 	}
382c579ef51Seric 	else
3832c7e1b8dSeric # endif SMTP
38477b52738Seric 		rcode = sendoff(e, m, pv, ctladdr);
3855dfc646bSeric 
386c77d1c25Seric 	/*
38763780dbdSeric 	**  Do final status disposal.
38863780dbdSeric 	**	We check for something in tobuf for the SMTP case.
389c77d1c25Seric 	**	If we got a temporary failure, arrange to queue the
390c77d1c25Seric 	**		addressees.
391c77d1c25Seric 	*/
392c77d1c25Seric 
39363780dbdSeric 	if (tobuf[0] != '\0')
394198d9be0Seric 		giveresponse(rcode, m, e);
39563780dbdSeric 	if (rcode != EX_OK)
396c77d1c25Seric 	{
397772e6e50Seric 		for (to = tochain; to != NULL; to = to->q_tchain)
39883b7ddc9Seric 			markfailure(e, to, rcode);
399c77d1c25Seric 	}
400c77d1c25Seric 
40135490626Seric 	errno = 0;
402588cad61Seric 	define('g', (char *) NULL, e);
4035826d9d3Seric 	return (rcode);
40425a99e2eSeric }
4055dfc646bSeric /*
40683b7ddc9Seric **  MARKFAILURE -- mark a failure on a specific address.
40783b7ddc9Seric **
40883b7ddc9Seric **	Parameters:
40983b7ddc9Seric **		e -- the envelope we are sending.
41083b7ddc9Seric **		q -- the address to mark.
41183b7ddc9Seric **		rcode -- the code signifying the particular failure.
41283b7ddc9Seric **
41383b7ddc9Seric **	Returns:
41483b7ddc9Seric **		none.
41583b7ddc9Seric **
41683b7ddc9Seric **	Side Effects:
41783b7ddc9Seric **		marks the address (and possibly the envelope) with the
41883b7ddc9Seric **			failure so that an error will be returned or
41983b7ddc9Seric **			the message will be queued, as appropriate.
42083b7ddc9Seric */
42183b7ddc9Seric 
42283b7ddc9Seric markfailure(e, q, rcode)
42383b7ddc9Seric 	register ENVELOPE *e;
42483b7ddc9Seric 	register ADDRESS *q;
42583b7ddc9Seric 	int rcode;
42683b7ddc9Seric {
42783b7ddc9Seric 	if (rcode == EX_OK)
42883b7ddc9Seric 		return;
42983b7ddc9Seric 	else if (rcode != EX_TEMPFAIL)
43083b7ddc9Seric 		q->q_flags |= QBADADDR;
43183b7ddc9Seric 	else if (curtime() > e->e_ctime + TimeOut)
43283b7ddc9Seric 	{
43383b7ddc9Seric 		extern char *pintvl();
434198d9be0Seric 		char buf[MAXLINE];
43583b7ddc9Seric 
43683b7ddc9Seric 		if (!bitset(EF_TIMEOUT, e->e_flags))
437198d9be0Seric 		{
438198d9be0Seric 			(void) sprintf(buf, "Cannot send message for %s",
43983b7ddc9Seric 				pintvl(TimeOut, FALSE));
440198d9be0Seric 			if (e->e_message != NULL)
441198d9be0Seric 				free(e->e_message);
442198d9be0Seric 			e->e_message = newstr(buf);
443198d9be0Seric 			message(Arpa_Info, buf);
444198d9be0Seric 		}
44583b7ddc9Seric 		q->q_flags |= QBADADDR;
44683b7ddc9Seric 		e->e_flags |= EF_TIMEOUT;
44783b7ddc9Seric 	}
44883b7ddc9Seric 	else
44983b7ddc9Seric 		q->q_flags |= QQUEUEUP;
45083b7ddc9Seric }
45183b7ddc9Seric /*
45232d19d43Seric **  DOFORK -- do a fork, retrying a couple of times on failure.
45332d19d43Seric **
45432d19d43Seric **	This MUST be a macro, since after a vfork we are running
45532d19d43Seric **	two processes on the same stack!!!
45632d19d43Seric **
45732d19d43Seric **	Parameters:
45832d19d43Seric **		none.
45932d19d43Seric **
46032d19d43Seric **	Returns:
46132d19d43Seric **		From a macro???  You've got to be kidding!
46232d19d43Seric **
46332d19d43Seric **	Side Effects:
46432d19d43Seric **		Modifies the ==> LOCAL <== variable 'pid', leaving:
46532d19d43Seric **			pid of child in parent, zero in child.
46632d19d43Seric **			-1 on unrecoverable error.
46732d19d43Seric **
46832d19d43Seric **	Notes:
46932d19d43Seric **		I'm awfully sorry this looks so awful.  That's
47032d19d43Seric **		vfork for you.....
47132d19d43Seric */
47232d19d43Seric 
47332d19d43Seric # define NFORKTRIES	5
4744300ddf0Seric # ifdef VMUNIX
47532d19d43Seric # define XFORK	vfork
4764300ddf0Seric # else VMUNIX
47732d19d43Seric # define XFORK	fork
4784300ddf0Seric # endif VMUNIX
47932d19d43Seric 
48032d19d43Seric # define DOFORK(fORKfN) \
48132d19d43Seric {\
48232d19d43Seric 	register int i;\
48332d19d43Seric \
48432d19d43Seric 	for (i = NFORKTRIES; i-- > 0; )\
48532d19d43Seric 	{\
48632d19d43Seric 		pid = fORKfN();\
48732d19d43Seric 		if (pid >= 0)\
48832d19d43Seric 			break;\
4895e663df1Seric 		sleep(NFORKTRIES - i);\
49032d19d43Seric 	}\
49132d19d43Seric }
49232d19d43Seric /*
4932ed72599Seric **  DOFORK -- simple fork interface to DOFORK.
4942ed72599Seric **
4952ed72599Seric **	Parameters:
4962ed72599Seric **		none.
4972ed72599Seric **
4982ed72599Seric **	Returns:
4992ed72599Seric **		pid of child in parent.
5002ed72599Seric **		zero in child.
5012ed72599Seric **		-1 on error.
5022ed72599Seric **
5032ed72599Seric **	Side Effects:
5042ed72599Seric **		returns twice, once in parent and once in child.
5052ed72599Seric */
5062ed72599Seric 
5072ed72599Seric dofork()
5082ed72599Seric {
5092ed72599Seric 	register int pid;
5102ed72599Seric 
5112ed72599Seric 	DOFORK(fork);
5122ed72599Seric 	return (pid);
5132ed72599Seric }
5142ed72599Seric /*
5155dfc646bSeric **  SENDOFF -- send off call to mailer & collect response.
5165dfc646bSeric **
5175dfc646bSeric **	Parameters:
518588cad61Seric **		e -- the envelope to mail.
5195dfc646bSeric **		m -- mailer descriptor.
5205dfc646bSeric **		pvp -- parameter vector to send to it.
5216259796dSeric **		ctladdr -- an address pointer controlling the
5226259796dSeric **			user/groupid etc. of the mailer.
5235dfc646bSeric **
5245dfc646bSeric **	Returns:
5255dfc646bSeric **		exit status of mailer.
5265dfc646bSeric **
5275dfc646bSeric **	Side Effects:
5285dfc646bSeric **		none.
5295dfc646bSeric */
5305dfc646bSeric 
53177b52738Seric sendoff(e, m, pvp, ctladdr)
532588cad61Seric 	register ENVELOPE *e;
533588cad61Seric 	MAILER *m;
5345dfc646bSeric 	char **pvp;
5356259796dSeric 	ADDRESS *ctladdr;
5365dfc646bSeric {
537c579ef51Seric 	auto FILE *mfile;
538c579ef51Seric 	auto FILE *rfile;
5395dfc646bSeric 	register int i;
540c579ef51Seric 	int pid;
541c579ef51Seric 
542c579ef51Seric 	/*
543c579ef51Seric 	**  Create connection to mailer.
544c579ef51Seric 	*/
545c579ef51Seric 
546c579ef51Seric 	pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile);
547c579ef51Seric 	if (pid < 0)
548c579ef51Seric 		return (-1);
549c579ef51Seric 
550c579ef51Seric 	/*
551c579ef51Seric 	**  Format and send message.
552c579ef51Seric 	*/
553c579ef51Seric 
55477b52738Seric 	putfromline(mfile, m);
55577b52738Seric 	(*e->e_puthdr)(mfile, m, e);
55677b52738Seric 	putline("\n", mfile, m);
55777b52738Seric 	(*e->e_putbody)(mfile, m, e);
558c579ef51Seric 	(void) fclose(mfile);
559c579ef51Seric 
560c579ef51Seric 	i = endmailer(pid, pvp[0]);
561bc6e2962Seric 
562bc6e2962Seric 	/* arrange a return receipt if requested */
563*57fc6f17Seric 	if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
564bc6e2962Seric 	{
565588cad61Seric 		e->e_flags |= EF_SENDRECEIPT;
566bc6e2962Seric 		/* do we want to send back more info? */
567bc6e2962Seric 	}
568bc6e2962Seric 
569c579ef51Seric 	return (i);
570c579ef51Seric }
571c579ef51Seric /*
572c579ef51Seric **  ENDMAILER -- Wait for mailer to terminate.
573c579ef51Seric **
574c579ef51Seric **	We should never get fatal errors (e.g., segmentation
575c579ef51Seric **	violation), so we report those specially.  For other
576c579ef51Seric **	errors, we choose a status message (into statmsg),
577c579ef51Seric **	and if it represents an error, we print it.
578c579ef51Seric **
579c579ef51Seric **	Parameters:
580c579ef51Seric **		pid -- pid of mailer.
581c579ef51Seric **		name -- name of mailer (for error messages).
582c579ef51Seric **
583c579ef51Seric **	Returns:
584c579ef51Seric **		exit code of mailer.
585c579ef51Seric **
586c579ef51Seric **	Side Effects:
587c579ef51Seric **		none.
588c579ef51Seric */
589c579ef51Seric 
590c579ef51Seric endmailer(pid, name)
591c579ef51Seric 	int pid;
592c579ef51Seric 	char *name;
593c579ef51Seric {
594588cad61Seric 	int st;
595c579ef51Seric 
59633db8731Seric 	/* in the IPC case there is nothing to wait for */
59733db8731Seric 	if (pid == 0)
59833db8731Seric 		return (EX_OK);
59933db8731Seric 
60033db8731Seric 	/* wait for the mailer process to die and collect status */
601588cad61Seric 	st = waitfor(pid);
602588cad61Seric 	if (st == -1)
60378de67c1Seric 	{
604588cad61Seric 		syserr("endmailer %s: wait", name);
605588cad61Seric 		return (EX_SOFTWARE);
606c579ef51Seric 	}
60733db8731Seric 
60833db8731Seric 	/* see if it died a horrid death */
609c579ef51Seric 	if ((st & 0377) != 0)
610c579ef51Seric 	{
611588cad61Seric 		syserr("endmailer %s: stat %o", name, st);
612c579ef51Seric 		ExitStat = EX_UNAVAILABLE;
613588cad61Seric 		return (EX_UNAVAILABLE);
614c579ef51Seric 	}
61533db8731Seric 
61633db8731Seric 	/* normal death -- return status */
617588cad61Seric 	st = (st >> 8) & 0377;
618588cad61Seric 	return (st);
619c579ef51Seric }
620c579ef51Seric /*
621c579ef51Seric **  OPENMAILER -- open connection to mailer.
622c579ef51Seric **
623c579ef51Seric **	Parameters:
624c579ef51Seric **		m -- mailer descriptor.
625c579ef51Seric **		pvp -- parameter vector to pass to mailer.
626c579ef51Seric **		ctladdr -- controlling address for user.
627c579ef51Seric **		clever -- create a full duplex connection.
628c579ef51Seric **		pmfile -- pointer to mfile (to mailer) connection.
629c579ef51Seric **		prfile -- pointer to rfile (from mailer) connection.
630c579ef51Seric **
631c579ef51Seric **	Returns:
63233db8731Seric **		pid of mailer ( > 0 ).
633c579ef51Seric **		-1 on error.
63433db8731Seric **		zero on an IPC connection.
635c579ef51Seric **
636c579ef51Seric **	Side Effects:
637c579ef51Seric **		creates a mailer in a subprocess.
638c579ef51Seric */
639c579ef51Seric 
640c579ef51Seric openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
641588cad61Seric 	MAILER *m;
642c579ef51Seric 	char **pvp;
643c579ef51Seric 	ADDRESS *ctladdr;
644c579ef51Seric 	bool clever;
645c579ef51Seric 	FILE **pmfile;
646c579ef51Seric 	FILE **prfile;
647c579ef51Seric {
6485dfc646bSeric 	int pid;
649f8952a83Seric 	int mpvect[2];
650c579ef51Seric 	int rpvect[2];
6515dfc646bSeric 	FILE *mfile;
652c579ef51Seric 	FILE *rfile;
6535dfc646bSeric 	extern FILE *fdopen();
6545dfc646bSeric 
6555dfc646bSeric # ifdef DEBUG
6566ef48975Seric 	if (tTd(11, 1))
6575dfc646bSeric 	{
6588c57e552Seric 		printf("openmailer:");
6595dfc646bSeric 		printav(pvp);
6605dfc646bSeric 	}
6615dfc646bSeric # endif DEBUG
66235490626Seric 	errno = 0;
6635dfc646bSeric 
66433db8731Seric 	/*
66533db8731Seric 	**  Deal with the special case of mail handled through an IPC
66633db8731Seric 	**  connection.
66733db8731Seric 	**	In this case we don't actually fork.  We must be
66833db8731Seric 	**	running SMTP for this to work.  We will return a
66933db8731Seric 	**	zero pid to indicate that we are running IPC.
67033db8731Seric 	*/
67133db8731Seric 
67233db8731Seric 	if (strcmp(m->m_mailer, "[IPC]") == 0)
67333db8731Seric 	{
674588cad61Seric #ifdef DAEMON
67533db8731Seric 		register int i;
6761277f9a8Seric 		register u_short port;
67733db8731Seric 
67833db8731Seric 		if (!clever)
67933db8731Seric 			syserr("non-clever IPC");
68093b6e3cfSeric 		if (pvp[2] != NULL)
6811277f9a8Seric 			port = atoi(pvp[2]);
68293b6e3cfSeric 		else
6831277f9a8Seric 			port = 0;
6841277f9a8Seric 		i = makeconnection(pvp[1], port, pmfile, prfile);
68533db8731Seric 		if (i != EX_OK)
686ed854c7bSeric 		{
687ed854c7bSeric 			ExitStat = i;
68833db8731Seric 			return (-1);
689ed854c7bSeric 		}
69033db8731Seric 		else
69133db8731Seric 			return (0);
692588cad61Seric #else DAEMON
693588cad61Seric 		syserr("openmailer: no IPC");
694588cad61Seric 		return (-1);
69533db8731Seric #endif DAEMON
696588cad61Seric 	}
69733db8731Seric 
6986328bdf7Seric 	/* create a pipe to shove the mail through */
699f8952a83Seric 	if (pipe(mpvect) < 0)
70025a99e2eSeric 	{
701588cad61Seric 		syserr("openmailer: pipe (to mailer)");
70225a99e2eSeric 		return (-1);
70325a99e2eSeric 	}
704c579ef51Seric 
7052c7e1b8dSeric #ifdef SMTP
706c579ef51Seric 	/* if this mailer speaks smtp, create a return pipe */
707c579ef51Seric 	if (clever && pipe(rpvect) < 0)
708c579ef51Seric 	{
709588cad61Seric 		syserr("openmailer: pipe (from mailer)");
710c579ef51Seric 		(void) close(mpvect[0]);
711c579ef51Seric 		(void) close(mpvect[1]);
712c579ef51Seric 		return (-1);
713c579ef51Seric 	}
7142c7e1b8dSeric #endif SMTP
715c579ef51Seric 
71633db8731Seric 	/*
71733db8731Seric 	**  Actually fork the mailer process.
71833db8731Seric 	**	DOFORK is clever about retrying.
71933db8731Seric 	*/
72033db8731Seric 
7219a6a5f55Seric 	if (CurEnv->e_xfp != NULL)
7229a6a5f55Seric 		(void) fflush(CurEnv->e_xfp);		/* for debugging */
723588cad61Seric 	(void) fflush(stdout);
72432d19d43Seric 	DOFORK(XFORK);
725f129ec7dSeric 	/* pid is set by DOFORK */
72625a99e2eSeric 	if (pid < 0)
72725a99e2eSeric 	{
72833db8731Seric 		/* failure */
729588cad61Seric 		syserr("openmailer: cannot fork");
730f8952a83Seric 		(void) close(mpvect[0]);
731f8952a83Seric 		(void) close(mpvect[1]);
732588cad61Seric #ifdef SMTP
733c579ef51Seric 		if (clever)
734c579ef51Seric 		{
735c579ef51Seric 			(void) close(rpvect[0]);
736c579ef51Seric 			(void) close(rpvect[1]);
737c579ef51Seric 		}
738588cad61Seric #endif SMTP
73925a99e2eSeric 		return (-1);
74025a99e2eSeric 	}
74125a99e2eSeric 	else if (pid == 0)
74225a99e2eSeric 	{
74325a99e2eSeric 		/* child -- set up input & exec mailer */
74403ab8e55Seric 		/* make diagnostic output be standard output */
7458f0e7860Seric 		(void) signal(SIGINT, SIG_IGN);
7468f0e7860Seric 		(void) signal(SIGHUP, SIG_IGN);
7470984da9fSeric 		(void) signal(SIGTERM, SIG_DFL);
748f8952a83Seric 
749f8952a83Seric 		/* arrange to filter standard & diag output of command */
750c579ef51Seric 		if (clever)
751c579ef51Seric 		{
752c579ef51Seric 			(void) close(rpvect[0]);
753c579ef51Seric 			(void) close(1);
754c579ef51Seric 			(void) dup(rpvect[1]);
755c579ef51Seric 			(void) close(rpvect[1]);
756c579ef51Seric 		}
757276723a8Seric 		else if (OpMode == MD_SMTP || HoldErrs)
758f8952a83Seric 		{
759588cad61Seric 			/* put mailer output in transcript */
760f8952a83Seric 			(void) close(1);
7619a6a5f55Seric 			(void) dup(fileno(CurEnv->e_xfp));
762f8952a83Seric 		}
763db8841e9Seric 		(void) close(2);
764db8841e9Seric 		(void) dup(1);
765f8952a83Seric 
766f8952a83Seric 		/* arrange to get standard input */
767f8952a83Seric 		(void) close(mpvect[1]);
768db8841e9Seric 		(void) close(0);
769f8952a83Seric 		if (dup(mpvect[0]) < 0)
77025a99e2eSeric 		{
77125a99e2eSeric 			syserr("Cannot dup to zero!");
772a590b978Seric 			_exit(EX_OSERR);
77325a99e2eSeric 		}
774f8952a83Seric 		(void) close(mpvect[0]);
775*57fc6f17Seric 		if (!bitnset(M_RESTR, m->m_flags))
7760984da9fSeric 		{
777e36b99e2Seric 			if (ctladdr->q_uid == 0)
778e36b99e2Seric 			{
779e36b99e2Seric 				(void) setgid(DefGid);
780e36b99e2Seric 				(void) setuid(DefUid);
781e36b99e2Seric 			}
782e36b99e2Seric 			else
78369f29479Seric 			{
784e36b99e2Seric 				(void) setgid(ctladdr->q_gid);
785e36b99e2Seric 				(void) setuid(ctladdr->q_uid);
78669f29479Seric 			}
7870984da9fSeric 		}
788588cad61Seric 
789e374fd72Seric 		/*
790e374fd72Seric 		**  We have to be careful with vfork - we can't mung up the
791e374fd72Seric 		**  memory but we don't want the mailer to inherit any extra
792e374fd72Seric 		**  open files.  Chances are the mailer won't
793e374fd72Seric 		**  care about an extra file, but then again you never know.
794e374fd72Seric 		**  Actually, we would like to close(fileno(pwf)), but it's
795e374fd72Seric 		**  declared static so we can't.  But if we fclose(pwf), which
796e374fd72Seric 		**  is what endpwent does, it closes it in the parent too and
797e374fd72Seric 		**  the next getpwnam will be slower.  If you have a weird
798e374fd72Seric 		**  mailer that chokes on the extra file you should do the
7994300ddf0Seric 		**  endpwent().			-MRH
800e374fd72Seric 		**
801e374fd72Seric 		**  Similar comments apply to log.  However, openlog is
802e374fd72Seric 		**  clever enough to set the FIOCLEX mode on the file,
803e374fd72Seric 		**  so it will be closed automatically on the exec.
804e374fd72Seric 		*/
805e374fd72Seric 
806588cad61Seric 		closeall();
80733db8731Seric 
80833db8731Seric 		/* try to execute the mailer */
80925a99e2eSeric 		execv(m->m_mailer, pvp);
81033db8731Seric 
81125a99e2eSeric 		/* syserr fails because log is closed */
81225a99e2eSeric 		/* syserr("Cannot exec %s", m->m_mailer); */
81332d19d43Seric 		printf("Cannot exec '%s' errno=%d\n", m->m_mailer, errno);
814db8841e9Seric 		(void) fflush(stdout);
815a590b978Seric 		_exit(EX_UNAVAILABLE);
81625a99e2eSeric 	}
81725a99e2eSeric 
818f8952a83Seric 	/*
819c579ef51Seric 	**  Set up return value.
820f8952a83Seric 	*/
821f8952a83Seric 
822f8952a83Seric 	(void) close(mpvect[0]);
823f8952a83Seric 	mfile = fdopen(mpvect[1], "w");
824c579ef51Seric 	if (clever)
82525a99e2eSeric 	{
826c579ef51Seric 		(void) close(rpvect[1]);
827c579ef51Seric 		rfile = fdopen(rpvect[0], "r");
82825a99e2eSeric 	}
829c579ef51Seric 
830c579ef51Seric 	*pmfile = mfile;
831c579ef51Seric 	*prfile = rfile;
832c579ef51Seric 
833c579ef51Seric 	return (pid);
83425a99e2eSeric }
83525a99e2eSeric /*
83625a99e2eSeric **  GIVERESPONSE -- Interpret an error response from a mailer
83725a99e2eSeric **
83825a99e2eSeric **	Parameters:
83925a99e2eSeric **		stat -- the status code from the mailer (high byte
84025a99e2eSeric **			only; core dumps must have been taken care of
84125a99e2eSeric **			already).
84225a99e2eSeric **		m -- the mailer descriptor for this mailer.
84325a99e2eSeric **
84425a99e2eSeric **	Returns:
845db8841e9Seric **		none.
84625a99e2eSeric **
84725a99e2eSeric **	Side Effects:
848c1f9df2cSeric **		Errors may be incremented.
84925a99e2eSeric **		ExitStat may be set.
85025a99e2eSeric */
85125a99e2eSeric 
852588cad61Seric /*ARGSUSED*/
853198d9be0Seric giveresponse(stat, m, e)
85425a99e2eSeric 	int stat;
855588cad61Seric 	register MAILER *m;
856198d9be0Seric 	ENVELOPE *e;
85725a99e2eSeric {
85825a99e2eSeric 	register char *statmsg;
85925a99e2eSeric 	extern char *SysExMsg[];
86025a99e2eSeric 	register int i;
86125a99e2eSeric 	extern int N_SysEx;
862198d9be0Seric 	char buf[MAXLINE];
86325a99e2eSeric 
86413bbc08cSeric 	/*
86513bbc08cSeric 	**  Compute status message from code.
86613bbc08cSeric 	*/
86713bbc08cSeric 
86825a99e2eSeric 	i = stat - EX__BASE;
869588cad61Seric 	if (stat == 0)
870588cad61Seric 		statmsg = "250 Sent";
871588cad61Seric 	else if (i < 0 || i > N_SysEx)
872588cad61Seric 	{
873588cad61Seric 		(void) sprintf(buf, "554 unknown mailer error %d", stat);
874588cad61Seric 		stat = EX_UNAVAILABLE;
875588cad61Seric 		statmsg = buf;
876588cad61Seric 	}
877198d9be0Seric 	else if (stat == EX_TEMPFAIL)
878198d9be0Seric 	{
879198d9be0Seric 		extern char *sys_errlist[];
880198d9be0Seric 		extern int sys_nerr;
881198d9be0Seric 
8828557d168Seric 		(void) strcpy(buf, SysExMsg[i]);
8838557d168Seric 		if (errno != 0)
884198d9be0Seric 		{
8858557d168Seric 			(void) strcat(buf, ": ");
8868557d168Seric 			if (errno > 0 && errno < sys_nerr)
887198d9be0Seric 				(void) strcat(buf, sys_errlist[errno]);
888198d9be0Seric 			else
8898557d168Seric 			{
8908557d168Seric 				char xbuf[30];
8918557d168Seric 
8928557d168Seric 				(void) sprintf(xbuf, "Error %d", errno);
8938557d168Seric 				(void) strcat(buf, xbuf);
8948557d168Seric 			}
8958557d168Seric 		}
896198d9be0Seric 		statmsg = buf;
897198d9be0Seric 	}
89825a99e2eSeric 	else
89925a99e2eSeric 		statmsg = SysExMsg[i];
900588cad61Seric 
901588cad61Seric 	/*
902588cad61Seric 	**  Print the message as appropriate
903588cad61Seric 	*/
904588cad61Seric 
905198d9be0Seric 	if (stat == EX_OK || stat == EX_TEMPFAIL)
9065826d9d3Seric 		message(Arpa_Info, &statmsg[4]);
90725a99e2eSeric 	else
90825a99e2eSeric 	{
909c1f9df2cSeric 		Errors++;
9105826d9d3Seric 		usrerr(statmsg);
91125a99e2eSeric 	}
91225a99e2eSeric 
91325a99e2eSeric 	/*
91425a99e2eSeric 	**  Final cleanup.
91525a99e2eSeric 	**	Log a record of the transaction.  Compute the new
91625a99e2eSeric 	**	ExitStat -- if we already had an error, stick with
91725a99e2eSeric 	**	that.
91825a99e2eSeric 	*/
91925a99e2eSeric 
92061f5a1d4Seric 	if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
921eb238f8cSeric 		logdelivery(&statmsg[4]);
922eb238f8cSeric 
923eb238f8cSeric 	if (stat != EX_TEMPFAIL)
924eb238f8cSeric 		setstat(stat);
925198d9be0Seric 	if (stat != EX_OK)
926198d9be0Seric 	{
927198d9be0Seric 		if (e->e_message != NULL)
928198d9be0Seric 			free(e->e_message);
929198d9be0Seric 		e->e_message = newstr(&statmsg[4]);
930198d9be0Seric 	}
9318557d168Seric 	errno = 0;
932eb238f8cSeric }
933eb238f8cSeric /*
934eb238f8cSeric **  LOGDELIVERY -- log the delivery in the system log
935eb238f8cSeric **
936eb238f8cSeric **	Parameters:
937eb238f8cSeric **		stat -- the message to print for the status
938eb238f8cSeric **
939eb238f8cSeric **	Returns:
940eb238f8cSeric **		none
941eb238f8cSeric **
942eb238f8cSeric **	Side Effects:
943eb238f8cSeric **		none
944eb238f8cSeric */
945eb238f8cSeric 
946eb238f8cSeric logdelivery(stat)
947eb238f8cSeric 	char *stat;
9485cf56be3Seric {
9495cf56be3Seric 	extern char *pintvl();
9505cf56be3Seric 
951eb238f8cSeric # ifdef LOG
9525cf56be3Seric 	syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id,
953eb238f8cSeric 	       CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);
95425a99e2eSeric # endif LOG
95525a99e2eSeric }
95625a99e2eSeric /*
95751552439Seric **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
95825a99e2eSeric **
95951552439Seric **	This can be made an arbitrary message separator by changing $l
96051552439Seric **
96151552439Seric **	One of the ugliest hacks seen by human eyes is
96251552439Seric **	contained herein: UUCP wants those stupid
963588cad61Seric **	"emote from <host>" lines.  Why oh why does a
96451552439Seric **	well-meaning programmer such as myself have to
96551552439Seric **	deal with this kind of antique garbage????
96625a99e2eSeric **
96725a99e2eSeric **	Parameters:
96851552439Seric **		fp -- the file to output to.
96951552439Seric **		m -- the mailer describing this entry.
97025a99e2eSeric **
97125a99e2eSeric **	Returns:
97251552439Seric **		none
97325a99e2eSeric **
97425a99e2eSeric **	Side Effects:
97551552439Seric **		outputs some text to fp.
97625a99e2eSeric */
97725a99e2eSeric 
97877b52738Seric putfromline(fp, m)
97951552439Seric 	register FILE *fp;
98051552439Seric 	register MAILER *m;
98125a99e2eSeric {
98251552439Seric 	char buf[MAXLINE];
98325a99e2eSeric 
984*57fc6f17Seric 	if (bitnset(M_NHDR, m->m_flags))
98551552439Seric 		return;
98613bbc08cSeric 
9872c7e1b8dSeric # ifdef UGLYUUCP
988*57fc6f17Seric 	if (bitnset(M_UGLYUUCP, m->m_flags))
98974b6e67bSeric 	{
99074b6e67bSeric 		extern char *macvalue();
9918c57e552Seric 		char *sys = macvalue('g', CurEnv);
99274b6e67bSeric 		char *bang = index(sys, '!');
99374b6e67bSeric 
99474b6e67bSeric 		if (bang == NULL)
99574b6e67bSeric 			syserr("No ! in UUCP! (%s)", sys);
99674b6e67bSeric 		else
997588cad61Seric 		{
99874b6e67bSeric 			*bang = '\0';
999518ad6b6Seric 			expand("From $f  $d remote from $g\n", buf,
100051552439Seric 					&buf[sizeof buf - 1], CurEnv);
100174b6e67bSeric 			*bang = '!';
100274b6e67bSeric 		}
1003588cad61Seric 	}
1004a36e30c9Seric 	else
10052c7e1b8dSeric # endif UGLYUUCP
100651552439Seric 		expand("$l\n", buf, &buf[sizeof buf - 1], CurEnv);
100777b52738Seric 	putline(buf, fp, m);
1008bc6e2962Seric }
1009bc6e2962Seric /*
101051552439Seric **  PUTBODY -- put the body of a message.
101151552439Seric **
101251552439Seric **	Parameters:
101351552439Seric **		fp -- file to output onto.
101477b52738Seric **		m -- a mailer descriptor to control output format.
10159a6a5f55Seric **		e -- the envelope to put out.
101651552439Seric **
101751552439Seric **	Returns:
101851552439Seric **		none.
101951552439Seric **
102051552439Seric **	Side Effects:
102151552439Seric **		The message is written onto fp.
102251552439Seric */
102351552439Seric 
102477b52738Seric putbody(fp, m, e)
102551552439Seric 	FILE *fp;
1026588cad61Seric 	MAILER *m;
10279a6a5f55Seric 	register ENVELOPE *e;
102851552439Seric {
102977b52738Seric 	char buf[MAXLINE];
103051552439Seric 
103151552439Seric 	/*
103251552439Seric 	**  Output the body of the message
103351552439Seric 	*/
103451552439Seric 
10359a6a5f55Seric 	if (e->e_dfp == NULL)
103651552439Seric 	{
10379a6a5f55Seric 		if (e->e_df != NULL)
10389a6a5f55Seric 		{
10399a6a5f55Seric 			e->e_dfp = fopen(e->e_df, "r");
10409a6a5f55Seric 			if (e->e_dfp == NULL)
10419a6a5f55Seric 				syserr("Cannot open %s", e->e_df);
10429a6a5f55Seric 		}
10439a6a5f55Seric 		else
104477b52738Seric 			putline("<<< No Message Collected >>>", fp, m);
10459a6a5f55Seric 	}
10469a6a5f55Seric 	if (e->e_dfp != NULL)
10479a6a5f55Seric 	{
10489a6a5f55Seric 		rewind(e->e_dfp);
104977b52738Seric 		while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
105077b52738Seric 			putline(buf, fp, m);
105151552439Seric 
10529a6a5f55Seric 		if (ferror(e->e_dfp))
105351552439Seric 		{
105451552439Seric 			syserr("putbody: read error");
105551552439Seric 			ExitStat = EX_IOERR;
105651552439Seric 		}
105751552439Seric 	}
105851552439Seric 
105951552439Seric 	(void) fflush(fp);
106051552439Seric 	if (ferror(fp) && errno != EPIPE)
106151552439Seric 	{
106251552439Seric 		syserr("putbody: write error");
106351552439Seric 		ExitStat = EX_IOERR;
106451552439Seric 	}
106551552439Seric 	errno = 0;
106625a99e2eSeric }
106725a99e2eSeric /*
106825a99e2eSeric **  MAILFILE -- Send a message to a file.
106925a99e2eSeric **
1070f129ec7dSeric **	If the file has the setuid/setgid bits set, but NO execute
1071f129ec7dSeric **	bits, sendmail will try to become the owner of that file
1072f129ec7dSeric **	rather than the real user.  Obviously, this only works if
1073f129ec7dSeric **	sendmail runs as root.
1074f129ec7dSeric **
1075588cad61Seric **	This could be done as a subordinate mailer, except that it
1076588cad61Seric **	is used implicitly to save messages in ~/dead.letter.  We
1077588cad61Seric **	view this as being sufficiently important as to include it
1078588cad61Seric **	here.  For example, if the system is dying, we shouldn't have
1079588cad61Seric **	to create another process plus some pipes to save the message.
1080588cad61Seric **
108125a99e2eSeric **	Parameters:
108225a99e2eSeric **		filename -- the name of the file to send to.
10836259796dSeric **		ctladdr -- the controlling address header -- includes
10846259796dSeric **			the userid/groupid to be when sending.
108525a99e2eSeric **
108625a99e2eSeric **	Returns:
108725a99e2eSeric **		The exit code associated with the operation.
108825a99e2eSeric **
108925a99e2eSeric **	Side Effects:
109025a99e2eSeric **		none.
109125a99e2eSeric */
109225a99e2eSeric 
10936259796dSeric mailfile(filename, ctladdr)
109425a99e2eSeric 	char *filename;
10956259796dSeric 	ADDRESS *ctladdr;
109625a99e2eSeric {
109725a99e2eSeric 	register FILE *f;
109832d19d43Seric 	register int pid;
109925a99e2eSeric 
110032d19d43Seric 	/*
110132d19d43Seric 	**  Fork so we can change permissions here.
110232d19d43Seric 	**	Note that we MUST use fork, not vfork, because of
110332d19d43Seric 	**	the complications of calling subroutines, etc.
110432d19d43Seric 	*/
110532d19d43Seric 
110632d19d43Seric 	DOFORK(fork);
110732d19d43Seric 
110832d19d43Seric 	if (pid < 0)
110932d19d43Seric 		return (EX_OSERR);
111032d19d43Seric 	else if (pid == 0)
111132d19d43Seric 	{
111232d19d43Seric 		/* child -- actually write to file */
1113f129ec7dSeric 		struct stat stb;
1114f129ec7dSeric 
11150984da9fSeric 		(void) signal(SIGINT, SIG_DFL);
11160984da9fSeric 		(void) signal(SIGHUP, SIG_DFL);
11170984da9fSeric 		(void) signal(SIGTERM, SIG_DFL);
1118f129ec7dSeric 		umask(OldUmask);
1119f129ec7dSeric 		if (stat(filename, &stb) < 0)
1120e6e1265fSeric 			stb.st_mode = 0666;
1121f129ec7dSeric 		if (bitset(0111, stb.st_mode))
1122f129ec7dSeric 			exit(EX_CANTCREAT);
112303827b5fSeric 		if (ctladdr == NULL)
11247a941e7aSeric 			ctladdr = &CurEnv->e_from;
1125f129ec7dSeric 		if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0)
1126e36b99e2Seric 		{
1127e36b99e2Seric 			if (ctladdr->q_uid == 0)
1128e36b99e2Seric 				(void) setgid(DefGid);
1129e36b99e2Seric 			else
11306259796dSeric 				(void) setgid(ctladdr->q_gid);
1131e36b99e2Seric 		}
1132f129ec7dSeric 		if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0)
1133e36b99e2Seric 		{
1134e36b99e2Seric 			if (ctladdr->q_uid == 0)
1135e36b99e2Seric 				(void) setuid(DefUid);
1136e36b99e2Seric 			else
11376259796dSeric 				(void) setuid(ctladdr->q_uid);
1138e36b99e2Seric 		}
113927628d59Seric 		f = dfopen(filename, "a");
114025a99e2eSeric 		if (f == NULL)
114132d19d43Seric 			exit(EX_CANTCREAT);
114225a99e2eSeric 
114377b52738Seric 		putfromline(f, ProgMailer);
114477b52738Seric 		(*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
114577b52738Seric 		putline("\n", f, ProgMailer);
114677b52738Seric 		(*CurEnv->e_putbody)(f, ProgMailer, CurEnv);
114777b52738Seric 		putline("\n", f, ProgMailer);
1148db8841e9Seric 		(void) fclose(f);
114932d19d43Seric 		(void) fflush(stdout);
1150e36b99e2Seric 
115127628d59Seric 		/* reset ISUID & ISGID bits for paranoid systems */
1152c77d1c25Seric 		(void) chmod(filename, (int) stb.st_mode);
115332d19d43Seric 		exit(EX_OK);
115413bbc08cSeric 		/*NOTREACHED*/
115532d19d43Seric 	}
115632d19d43Seric 	else
115732d19d43Seric 	{
115832d19d43Seric 		/* parent -- wait for exit status */
1159588cad61Seric 		int st;
116032d19d43Seric 
1161588cad61Seric 		st = waitfor(pid);
1162588cad61Seric 		if ((st & 0377) != 0)
1163588cad61Seric 			return (EX_UNAVAILABLE);
1164588cad61Seric 		else
1165588cad61Seric 			return ((st >> 8) & 0377);
116632d19d43Seric 	}
116725a99e2eSeric }
1168ea4dc939Seric /*
1169ea4dc939Seric **  SENDALL -- actually send all the messages.
1170ea4dc939Seric **
1171ea4dc939Seric **	Parameters:
11720c52a0b3Seric **		e -- the envelope to send.
1173276723a8Seric **		mode -- the delivery mode to use.
1174ea4dc939Seric **
1175ea4dc939Seric **	Returns:
1176ea4dc939Seric **		none.
1177ea4dc939Seric **
1178ea4dc939Seric **	Side Effects:
1179ea4dc939Seric **		Scans the send lists and sends everything it finds.
11800c52a0b3Seric **		Delivers any appropriate error messages.
1181276723a8Seric **		If we are running in a non-interactive mode, takes the
1182276723a8Seric **			appropriate action.
1183ea4dc939Seric */
1184ea4dc939Seric 
1185276723a8Seric sendall(e, mode)
11860c52a0b3Seric 	ENVELOPE *e;
1187276723a8Seric 	char mode;
1188ea4dc939Seric {
1189e77e673fSeric 	register ADDRESS *q;
119014a8ed7aSeric 	bool oldverbose;
1191276723a8Seric 	int pid;
1192ea4dc939Seric 
1193772e6e50Seric #ifdef DEBUG
1194df864a8fSeric 	if (tTd(13, 1))
1195772e6e50Seric 	{
1196276723a8Seric 		printf("\nSENDALL: mode %c, sendqueue:\n", mode);
11970c52a0b3Seric 		printaddr(e->e_sendqueue, TRUE);
1198772e6e50Seric 	}
1199772e6e50Seric #endif DEBUG
1200ea4dc939Seric 
12010c52a0b3Seric 	/*
1202276723a8Seric 	**  Do any preprocessing necessary for the mode we are running.
1203588cad61Seric 	**	Check to make sure the hop count is reasonable.
1204588cad61Seric 	**	Delete sends to the sender in mailing lists.
1205276723a8Seric 	*/
1206276723a8Seric 
1207588cad61Seric 	CurEnv = e;
1208276723a8Seric 
1209588cad61Seric 	if (e->e_hopcount > MAXHOP)
1210276723a8Seric 	{
1211588cad61Seric 		syserr("sendall: too many hops (%d max)", MAXHOP);
1212588cad61Seric 		return;
1213588cad61Seric 	}
1214588cad61Seric 
1215588cad61Seric 	if (!MeToo)
1216276723a8Seric 	{
1217588cad61Seric 		e->e_from.q_flags |= QDONTSEND;
1218588cad61Seric 		recipient(&e->e_from, &e->e_sendqueue);
1219276723a8Seric 	}
1220588cad61Seric 
1221588cad61Seric # ifdef QUEUE
1222b254bcb6Seric 	if ((mode == SM_QUEUE || mode == SM_FORK ||
1223b254bcb6Seric 	     (mode != SM_VERIFY && SuperSafe)) &&
1224b254bcb6Seric 	    !bitset(EF_INQUEUE, e->e_flags))
1225588cad61Seric 		queueup(e, TRUE, mode == SM_QUEUE);
1226276723a8Seric #endif QUEUE
1227276723a8Seric 
1228276723a8Seric 	oldverbose = Verbose;
1229276723a8Seric 	switch (mode)
1230276723a8Seric 	{
1231276723a8Seric 	  case SM_VERIFY:
1232276723a8Seric 		Verbose = TRUE;
1233276723a8Seric 		break;
1234276723a8Seric 
1235276723a8Seric 	  case SM_QUEUE:
1236b254bcb6Seric 		e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
1237276723a8Seric 		return;
1238276723a8Seric 
1239276723a8Seric 	  case SM_FORK:
12409a6a5f55Seric 		if (e->e_xfp != NULL)
12419a6a5f55Seric 			(void) fflush(e->e_xfp);
1242276723a8Seric 		pid = fork();
1243276723a8Seric 		if (pid < 0)
1244276723a8Seric 		{
1245276723a8Seric 			mode = SM_DELIVER;
1246276723a8Seric 			break;
1247276723a8Seric 		}
1248276723a8Seric 		else if (pid > 0)
1249a6fce3d8Seric 		{
1250a6fce3d8Seric 			/* be sure we leave the temp files to our child */
1251b254bcb6Seric 			e->e_id = e->e_df = NULL;
1252276723a8Seric 			return;
1253a6fce3d8Seric 		}
1254276723a8Seric 
1255276723a8Seric 		/* double fork to avoid zombies */
1256276723a8Seric 		if (fork() > 0)
1257276723a8Seric 			exit(EX_OK);
1258276723a8Seric 
1259a6fce3d8Seric 		/* be sure we are immune from the terminal */
1260769e215aSeric 		disconnect(FALSE);
1261a6fce3d8Seric 
1262276723a8Seric 		break;
1263276723a8Seric 	}
1264276723a8Seric 
1265276723a8Seric 	/*
12660c52a0b3Seric 	**  Run through the list and send everything.
12670c52a0b3Seric 	*/
12680c52a0b3Seric 
12690c52a0b3Seric 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
1270ea4dc939Seric 	{
1271276723a8Seric 		if (mode == SM_VERIFY)
1272ea4dc939Seric 		{
1273a6fce3d8Seric 			e->e_to = q->q_paddr;
1274e77e673fSeric 			if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
1275ea4dc939Seric 				message(Arpa_Info, "deliverable");
1276ea4dc939Seric 		}
1277ea4dc939Seric 		else
1278588cad61Seric 			(void) deliver(e, q);
1279ea4dc939Seric 	}
128014a8ed7aSeric 	Verbose = oldverbose;
12810c52a0b3Seric 
12820c52a0b3Seric 	/*
12830c52a0b3Seric 	**  Now run through and check for errors.
12840c52a0b3Seric 	*/
12850c52a0b3Seric 
1286276723a8Seric 	if (mode == SM_VERIFY)
12870c52a0b3Seric 		return;
12880c52a0b3Seric 
12890c52a0b3Seric 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
12900c52a0b3Seric 	{
12910c52a0b3Seric 		register ADDRESS *qq;
12920c52a0b3Seric 
1293df864a8fSeric # ifdef DEBUG
1294df864a8fSeric 		if (tTd(13, 3))
1295df864a8fSeric 		{
1296df864a8fSeric 			printf("Checking ");
1297df864a8fSeric 			printaddr(q, FALSE);
1298df864a8fSeric 		}
1299df864a8fSeric # endif DEBUG
1300df864a8fSeric 
1301b254bcb6Seric 		/* only send errors if the message failed */
1302b254bcb6Seric 		if (!bitset(QBADADDR, q->q_flags))
1303b254bcb6Seric 			continue;
13040c52a0b3Seric 
13050c52a0b3Seric 		/* we have an address that failed -- find the parent */
13060c52a0b3Seric 		for (qq = q; qq != NULL; qq = qq->q_alias)
13070c52a0b3Seric 		{
13080c52a0b3Seric 			char obuf[MAXNAME + 6];
13090c52a0b3Seric 			extern char *aliaslookup();
13100c52a0b3Seric 
13110c52a0b3Seric 			/* we can only have owners for local addresses */
1312*57fc6f17Seric 			if (!bitnset(M_LOCAL, qq->q_mailer->m_flags))
13130c52a0b3Seric 				continue;
13140c52a0b3Seric 
13150c52a0b3Seric 			/* see if the owner list exists */
13160c52a0b3Seric 			(void) strcpy(obuf, "owner-");
1317cec031e3Seric 			if (strncmp(qq->q_user, "owner-", 6) == 0)
1318cec031e3Seric 				(void) strcat(obuf, "owner");
1319cec031e3Seric 			else
13200c52a0b3Seric 				(void) strcat(obuf, qq->q_user);
13210c52a0b3Seric 			if (aliaslookup(obuf) == NULL)
13220c52a0b3Seric 				continue;
13230c52a0b3Seric 
1324df864a8fSeric # ifdef DEBUG
1325df864a8fSeric 			if (tTd(13, 4))
1326df864a8fSeric 				printf("Errors to %s\n", obuf);
1327df864a8fSeric # endif DEBUG
1328df864a8fSeric 
13290c52a0b3Seric 			/* owner list exists -- add it to the error queue */
1330e3e4ed86Seric 			sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
1331588cad61Seric 			ErrorMode == EM_MAIL;
13320c52a0b3Seric 			break;
13330c52a0b3Seric 		}
13340c52a0b3Seric 
13350c52a0b3Seric 		/* if we did not find an owner, send to the sender */
13367455aa0bSeric 		if (qq == NULL && bitset(QBADADDR, q->q_flags))
1337e3e4ed86Seric 			sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue);
13380c52a0b3Seric 	}
1339276723a8Seric 
1340276723a8Seric 	if (mode == SM_FORK)
1341276723a8Seric 		finis();
13420c52a0b3Seric }
1343