125a99e2eSeric # include <signal.h>
254aa2b0fSeric # include <errno.h>
3b20b3270Seric # include "sendmail.h"
4c77d1c25Seric # include <sys/stat.h>
525a99e2eSeric # ifdef LOG
6e374fd72Seric # include <syslog.h>
725a99e2eSeric # endif LOG
825a99e2eSeric 
9*f3dbc832Seric SCCSID(@(#)deliver.c	3.65		02/20/82);
10259cace7Seric 
1125a99e2eSeric /*
1213bbc08cSeric **  DELIVER -- Deliver a message to a list of addresses.
1313bbc08cSeric **
1413bbc08cSeric **	This routine delivers to everyone on the same host as the
1513bbc08cSeric **	user on the head of the list.  It is clever about mailers
1613bbc08cSeric **	that don't handle multiple users.  It is NOT guaranteed
1713bbc08cSeric **	that it will deliver to all these addresses however -- so
1813bbc08cSeric **	deliver should be called once for each address on the
1913bbc08cSeric **	list.
2025a99e2eSeric **
2125a99e2eSeric **	Parameters:
22c77d1c25Seric **		firstto -- head of the address list to deliver to.
2325a99e2eSeric **		editfcn -- if non-NULL, we want to call this function
2425a99e2eSeric **			to output the letter (instead of just out-
2525a99e2eSeric **			putting it raw).
2625a99e2eSeric **
2725a99e2eSeric **	Returns:
2825a99e2eSeric **		zero -- successfully delivered.
2925a99e2eSeric **		else -- some failure, see ExitStat for more info.
3025a99e2eSeric **
3125a99e2eSeric **	Side Effects:
3225a99e2eSeric **		The standard input is passed off to someone.
3325a99e2eSeric */
3425a99e2eSeric 
35c77d1c25Seric deliver(firstto, editfcn)
36c77d1c25Seric 	ADDRESS *firstto;
3725a99e2eSeric 	int (*editfcn)();
3825a99e2eSeric {
3978442df3Seric 	char *host;			/* host being sent to */
4078442df3Seric 	char *user;			/* user being sent to */
4125a99e2eSeric 	char **pvp;
425dfc646bSeric 	register char **mvp;
4325a99e2eSeric 	register char *p;
4478442df3Seric 	register struct mailer *m;	/* mailer for this recipient */
455dfc646bSeric 	register int i;
466328bdf7Seric 	extern putmessage();
472a6e0786Seric 	extern bool checkcompat();
485dfc646bSeric 	char *pv[MAXPV+1];
4978442df3Seric 	char tobuf[MAXLINE];		/* text line of to people */
505dfc646bSeric 	char buf[MAXNAME];
516259796dSeric 	ADDRESS *ctladdr;
526259796dSeric 	extern ADDRESS *getctladdr();
5378442df3Seric 	char tfrombuf[MAXNAME];		/* translated from person */
5478442df3Seric 	extern char **prescan();
55c77d1c25Seric 	register ADDRESS *to = firstto;
56c579ef51Seric 	bool clever = FALSE;		/* running user smtp to this mailer */
57c579ef51Seric 	bool tempfail = FALSE;
58772e6e50Seric 	ADDRESS *tochain = NULL;	/* chain of users in this mailer call */
5925a99e2eSeric 
6035490626Seric 	errno = 0;
61e77e673fSeric 	if (!ForceMail && bitset(QDONTSEND, to->q_flags))
625dfc646bSeric 		return (0);
6325a99e2eSeric 
6425a99e2eSeric # ifdef DEBUG
6525a99e2eSeric 	if (Debug)
665dfc646bSeric 		printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
677da1035fSeric 			to->q_mailer->m_mno, to->q_host, to->q_user);
6825a99e2eSeric # endif DEBUG
6925a99e2eSeric 
70*f3dbc832Seric 	m = to->q_mailer;
71*f3dbc832Seric 	host = to->q_host;
72*f3dbc832Seric 
73*f3dbc832Seric 	/*
74*f3dbc832Seric 	**  If this mailer is expensive, and if we don't want to make
75*f3dbc832Seric 	**  connections now, just mark these addresses and return.
76*f3dbc832Seric 	**	This is useful if we want to batch connections to
77*f3dbc832Seric 	**	reduce load.  This will cause the messages to be
78*f3dbc832Seric 	**	queued up, and a daemon will come along to send the
79*f3dbc832Seric 	**	messages later.
80*f3dbc832Seric 	**		This should be on a per-mailer basis.
81*f3dbc832Seric 	*/
82*f3dbc832Seric 
83*f3dbc832Seric 	if (NoConnect && !QueueRun && bitset(M_EXPENSIVE, m->m_flags))
84*f3dbc832Seric 	{
85*f3dbc832Seric 		QueueUp = TRUE;
86*f3dbc832Seric 		for (; to != NULL; to = to->q_next)
87*f3dbc832Seric 			if (!bitset(QDONTSEND, to->q_flags))
88*f3dbc832Seric 				to->q_flags |= QQUEUEUP|QDONTSEND;
89*f3dbc832Seric 		return (0);
90*f3dbc832Seric 	}
91*f3dbc832Seric 
9225a99e2eSeric 	/*
935dfc646bSeric 	**  Do initial argv setup.
945dfc646bSeric 	**	Insert the mailer name.  Notice that $x expansion is
955dfc646bSeric 	**	NOT done on the mailer name.  Then, if the mailer has
965dfc646bSeric 	**	a picky -f flag, we insert it as appropriate.  This
975dfc646bSeric 	**	code does not check for 'pv' overflow; this places a
985dfc646bSeric 	**	manifest lower limit of 4 for MAXPV.
99*f3dbc832Seric 	**		We rewrite the from address here, being careful
100*f3dbc832Seric 	**		to also rewrite it again using ruleset 2 to
101*f3dbc832Seric 	**		eliminate redundancies.
1025dfc646bSeric 	*/
1035dfc646bSeric 
10478442df3Seric 	/* rewrite from address, using rewriting rules */
10578442df3Seric 	(void) expand(m->m_from, buf, &buf[sizeof buf - 1]);
10678442df3Seric 	mvp = prescan(buf, '\0');
10778442df3Seric 	if (mvp == NULL)
10878442df3Seric 	{
10978442df3Seric 		syserr("bad mailer from translate \"%s\"", buf);
11078442df3Seric 		return (EX_SOFTWARE);
11178442df3Seric 	}
11278442df3Seric 	rewrite(mvp, 2);
11378442df3Seric 	cataddr(mvp, tfrombuf, sizeof tfrombuf);
11478442df3Seric 
11578442df3Seric 	define('g', tfrombuf);		/* translated sender address */
1165dfc646bSeric 	define('h', host);		/* to host */
1175dfc646bSeric 	Errors = 0;
1185dfc646bSeric 	pvp = pv;
1195dfc646bSeric 	*pvp++ = m->m_argv[0];
1205dfc646bSeric 
1215dfc646bSeric 	/* insert -f or -r flag as appropriate */
1225dfc646bSeric 	if (bitset(M_FOPT|M_ROPT, m->m_flags) && FromFlag)
1235dfc646bSeric 	{
1245dfc646bSeric 		if (bitset(M_FOPT, m->m_flags))
1255dfc646bSeric 			*pvp++ = "-f";
1265dfc646bSeric 		else
1275dfc646bSeric 			*pvp++ = "-r";
128db8841e9Seric 		(void) expand("$g", buf, &buf[sizeof buf - 1]);
1295dfc646bSeric 		*pvp++ = newstr(buf);
1305dfc646bSeric 	}
1315dfc646bSeric 
1325dfc646bSeric 	/*
1335dfc646bSeric 	**  Append the other fixed parts of the argv.  These run
1345dfc646bSeric 	**  up to the first entry containing "$u".  There can only
1355dfc646bSeric 	**  be one of these, and there are only a few more slots
1365dfc646bSeric 	**  in the pv after it.
1375dfc646bSeric 	*/
1385dfc646bSeric 
1395dfc646bSeric 	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
1405dfc646bSeric 	{
1415dfc646bSeric 		while ((p = index(p, '$')) != NULL)
1425dfc646bSeric 			if (*++p == 'u')
1435dfc646bSeric 				break;
1445dfc646bSeric 		if (p != NULL)
1455dfc646bSeric 			break;
1465dfc646bSeric 
1475dfc646bSeric 		/* this entry is safe -- go ahead and process it */
148db8841e9Seric 		(void) expand(*mvp, buf, &buf[sizeof buf - 1]);
1495dfc646bSeric 		*pvp++ = newstr(buf);
1505dfc646bSeric 		if (pvp >= &pv[MAXPV - 3])
1515dfc646bSeric 		{
1525dfc646bSeric 			syserr("Too many parameters to %s before $u", pv[0]);
1535dfc646bSeric 			return (-1);
1545dfc646bSeric 		}
1555dfc646bSeric 	}
156c579ef51Seric 
1575dfc646bSeric 	if (*mvp == NULL)
158c579ef51Seric 	{
159c579ef51Seric 		/* running SMTP */
1602c7e1b8dSeric # ifdef SMTP
161c579ef51Seric 		clever = TRUE;
162c579ef51Seric 		*pvp = NULL;
163c579ef51Seric 		i = smtpinit(m, pv, (ADDRESS *) NULL);
164c579ef51Seric 		giveresponse(i, TRUE, m);
1652c7e1b8dSeric # ifdef QUEUE
166c579ef51Seric 		if (i == EX_TEMPFAIL)
167c579ef51Seric 		{
168c579ef51Seric 			QueueUp = TRUE;
169c579ef51Seric 			tempfail = TRUE;
170c579ef51Seric 		}
1712c7e1b8dSeric # endif QUEUE
1722c7e1b8dSeric # else SMTP
1732c7e1b8dSeric 		syserr("SMTP style mailer");
1742c7e1b8dSeric 		return (EX_SOFTWARE);
1752c7e1b8dSeric # endif SMTP
176c579ef51Seric 	}
1775dfc646bSeric 
1785dfc646bSeric 	/*
1795dfc646bSeric 	**  At this point *mvp points to the argument with $u.  We
1805dfc646bSeric 	**  run through our address list and append all the addresses
1815dfc646bSeric 	**  we can.  If we run out of space, do not fret!  We can
1825dfc646bSeric 	**  always send another copy later.
1835dfc646bSeric 	*/
1845dfc646bSeric 
1855dfc646bSeric 	tobuf[0] = '\0';
1865dfc646bSeric 	To = tobuf;
1876259796dSeric 	ctladdr = NULL;
1885dfc646bSeric 	for (; to != NULL; to = to->q_next)
1895dfc646bSeric 	{
1905dfc646bSeric 		/* avoid sending multiple recipients to dumb mailers */
191bea22b26Seric 		if (tobuf[0] != '\0' && !bitset(M_MUSER, m->m_flags))
1925dfc646bSeric 			break;
1935dfc646bSeric 
1945dfc646bSeric 		/* if already sent or not for this host, don't send */
195e77e673fSeric 		if ((!ForceMail && bitset(QDONTSEND, to->q_flags)) ||
196e77e673fSeric 		    strcmp(to->q_host, host) != 0 || to->q_mailer != firstto->q_mailer)
1975dfc646bSeric 			continue;
1986259796dSeric 
199772e6e50Seric # ifdef DEBUG
200772e6e50Seric 		if (Debug)
201772e6e50Seric 		{
202772e6e50Seric 			printf("\nsend to ");
203772e6e50Seric 			printaddr(to, FALSE);
204772e6e50Seric 		}
205772e6e50Seric # endif DEBUG
206772e6e50Seric 
207772e6e50Seric 		/* link together the chain of recipients */
208772e6e50Seric 		if (!bitset(QDONTSEND, to->q_flags))
209772e6e50Seric 		{
210772e6e50Seric 			to->q_tchain = tochain;
211772e6e50Seric 			tochain = to;
212772e6e50Seric 		}
213772e6e50Seric 
2146259796dSeric 		/* compute effective uid/gid when sending */
2157da1035fSeric 		if (to->q_mailer == ProgMailer)
2166259796dSeric 			ctladdr = getctladdr(to);
2176259796dSeric 
2185dfc646bSeric 		user = to->q_user;
2195dfc646bSeric 		To = to->q_paddr;
2205dfc646bSeric 		to->q_flags |= QDONTSEND;
221c579ef51Seric 		if (tempfail)
222772e6e50Seric 		{
223c579ef51Seric 			to->q_flags |= QQUEUEUP;
224772e6e50Seric 			continue;
225772e6e50Seric 		}
2265dfc646bSeric 
2275dfc646bSeric 		/*
2285dfc646bSeric 		**  Check to see that these people are allowed to
2295dfc646bSeric 		**  talk to each other.
2302a6e0786Seric 		*/
2312a6e0786Seric 
2322a6e0786Seric 		if (!checkcompat(to))
2335dfc646bSeric 		{
2345dfc646bSeric 			giveresponse(EX_UNAVAILABLE, TRUE, m);
2355dfc646bSeric 			continue;
2365dfc646bSeric 		}
2372a6e0786Seric 
2382a6e0786Seric 		/*
2399ec9501bSeric 		**  Strip quote bits from names if the mailer is dumb
2409ec9501bSeric 		**	about them.
24125a99e2eSeric 		*/
24225a99e2eSeric 
2432a6e0786Seric 		if (bitset(M_STRIPQ, m->m_flags))
24425a99e2eSeric 		{
2459ec9501bSeric 			stripquotes(user, TRUE);
2469ec9501bSeric 			stripquotes(host, TRUE);
2479ec9501bSeric 		}
2489ec9501bSeric 		else
2499ec9501bSeric 		{
2509ec9501bSeric 			stripquotes(user, FALSE);
2519ec9501bSeric 			stripquotes(host, FALSE);
25225a99e2eSeric 		}
25325a99e2eSeric 
25425a99e2eSeric 		/*
2553efaed6eSeric 		**  If an error message has already been given, don't
2563efaed6eSeric 		**	bother to send to this address.
2573efaed6eSeric 		**
2583efaed6eSeric 		**	>>>>>>>>>> This clause assumes that the local mailer
2593efaed6eSeric 		**	>> NOTE >> cannot do any further aliasing; that
2603efaed6eSeric 		**	>>>>>>>>>> function is subsumed by sendmail.
2613efaed6eSeric 		*/
2623efaed6eSeric 
2633efaed6eSeric 		if (bitset(QBADADDR, to->q_flags))
2643efaed6eSeric 			continue;
2653efaed6eSeric 
266f2fec898Seric 		/* save statistics.... */
2677da1035fSeric 		Stat.stat_nt[to->q_mailer->m_mno]++;
2687da1035fSeric 		Stat.stat_bt[to->q_mailer->m_mno] += kbytes(MsgSize);
269f2fec898Seric 
2703efaed6eSeric 		/*
27125a99e2eSeric 		**  See if this user name is "special".
27225a99e2eSeric 		**	If the user name has a slash in it, assume that this
27325a99e2eSeric 		**	is a file -- send it off without further ado.
27425a99e2eSeric 		**	Note that this means that editfcn's will not
2755dfc646bSeric 		**	be applied to the message.  Also note that
2765dfc646bSeric 		**	this type of addresses is not processed along
2775dfc646bSeric 		**	with the others, so we fudge on the To person.
27825a99e2eSeric 		*/
27925a99e2eSeric 
2807da1035fSeric 		if (m == LocalMailer)
28125a99e2eSeric 		{
282a49f24c0Seric 			if (user[0] == '/')
28325a99e2eSeric 			{
2846259796dSeric 				i = mailfile(user, getctladdr(to));
28525a99e2eSeric 				giveresponse(i, TRUE, m);
2865dfc646bSeric 				continue;
28725a99e2eSeric 			}
28825a99e2eSeric 		}
28925a99e2eSeric 
29013bbc08cSeric 		/*
29113bbc08cSeric 		**  Address is verified -- add this user to mailer
29213bbc08cSeric 		**  argv, and add it to the print list of recipients.
29313bbc08cSeric 		*/
29413bbc08cSeric 
2955dfc646bSeric 		/* create list of users for error messages */
2965dfc646bSeric 		if (tobuf[0] != '\0')
297db8841e9Seric 			(void) strcat(tobuf, ",");
298db8841e9Seric 		(void) strcat(tobuf, to->q_paddr);
2995dfc646bSeric 		define('u', user);		/* to user */
300c2567733Seric 		define('z', to->q_home);	/* user's home */
3015dfc646bSeric 
302c579ef51Seric 		/*
303c579ef51Seric 		**  Expand out this user into argument list or
304c579ef51Seric 		**  send it to our SMTP server.
305c579ef51Seric 		*/
306c579ef51Seric 
307c579ef51Seric 		if (clever)
308c579ef51Seric 		{
3092c7e1b8dSeric # ifdef SMTP
310d2b7d202Seric 			i = smtprcpt(to);
3112c7e1b8dSeric 			if (i != EX_OK)
3122c7e1b8dSeric 			{
3132c7e1b8dSeric # ifdef QUEUE
314c579ef51Seric 				if (i == EX_TEMPFAIL)
315c579ef51Seric 				{
316c579ef51Seric 					QueueUp = TRUE;
317c579ef51Seric 					to->q_flags |= QQUEUEUP;
318c579ef51Seric 				}
3192c7e1b8dSeric 				else
3202c7e1b8dSeric # endif QUEUE
321c579ef51Seric 				{
322c579ef51Seric 					to->q_flags |= QBADADDR;
323c579ef51Seric 					giveresponse(i, TRUE, m);
324c579ef51Seric 				}
325c579ef51Seric 			}
3262c7e1b8dSeric # else SMTP
3272c7e1b8dSeric 			syserr("trying to be clever");
3282c7e1b8dSeric # endif SMTP
3292c7e1b8dSeric 		}
330c579ef51Seric 		else
331c579ef51Seric 		{
332d4ccc802Seric 			(void) expand(*mvp, buf, &buf[sizeof buf - 1]);
3335dfc646bSeric 			*pvp++ = newstr(buf);
3345dfc646bSeric 			if (pvp >= &pv[MAXPV - 2])
3355dfc646bSeric 			{
3365dfc646bSeric 				/* allow some space for trailing parms */
3375dfc646bSeric 				break;
3385dfc646bSeric 			}
3395dfc646bSeric 		}
340c579ef51Seric 	}
3415dfc646bSeric 
342145b49b1Seric 	/* see if any addresses still exist */
343145b49b1Seric 	if (tobuf[0] == '\0')
344c579ef51Seric 	{
3452c7e1b8dSeric # ifdef SMTP
346c579ef51Seric 		if (clever)
347c579ef51Seric 			smtpquit(pv[0]);
3482c7e1b8dSeric # endif SMTP
349145b49b1Seric 		return (0);
350c579ef51Seric 	}
351145b49b1Seric 
3525dfc646bSeric 	/* print out messages as full list */
3535dfc646bSeric 	To = tobuf;
3545dfc646bSeric 
3555dfc646bSeric 	/*
3565dfc646bSeric 	**  Fill out any parameters after the $u parameter.
3575dfc646bSeric 	*/
3585dfc646bSeric 
359c579ef51Seric 	while (!clever && *++mvp != NULL)
3605dfc646bSeric 	{
361db8841e9Seric 		(void) expand(*mvp, buf, &buf[sizeof buf - 1]);
3625dfc646bSeric 		*pvp++ = newstr(buf);
3635dfc646bSeric 		if (pvp >= &pv[MAXPV])
3645dfc646bSeric 			syserr("deliver: pv overflow after $u for %s", pv[0]);
3655dfc646bSeric 	}
3665dfc646bSeric 	*pvp++ = NULL;
3675dfc646bSeric 
36825a99e2eSeric 	/*
36925a99e2eSeric 	**  Call the mailer.
3706328bdf7Seric 	**	The argument vector gets built, pipes
37125a99e2eSeric 	**	are created as necessary, and we fork & exec as
3726328bdf7Seric 	**	appropriate.
373c579ef51Seric 	**	If we are running SMTP, we just need to clean up.
37425a99e2eSeric 	*/
37525a99e2eSeric 
3765dfc646bSeric 	if (editfcn == NULL)
3775dfc646bSeric 		editfcn = putmessage;
3786259796dSeric 	if (ctladdr == NULL)
3796259796dSeric 		ctladdr = &From;
3802c7e1b8dSeric # ifdef SMTP
381c579ef51Seric 	if (clever)
382c579ef51Seric 	{
383c579ef51Seric 		i = smtpfinish(m, editfcn);
384c579ef51Seric 		smtpquit(pv[0]);
385c579ef51Seric 	}
386c579ef51Seric 	else
3872c7e1b8dSeric # endif SMTP
3886259796dSeric 		i = sendoff(m, pv, editfcn, ctladdr);
3895dfc646bSeric 
390c77d1c25Seric 	/*
391c77d1c25Seric 	**  If we got a temporary failure, arrange to queue the
392c77d1c25Seric 	**  addressees.
393c77d1c25Seric 	*/
394c77d1c25Seric 
3952c7e1b8dSeric # ifdef QUEUE
396c77d1c25Seric 	if (i == EX_TEMPFAIL)
397c77d1c25Seric 	{
398c77d1c25Seric 		QueueUp = TRUE;
399772e6e50Seric 		for (to = tochain; to != NULL; to = to->q_tchain)
400c77d1c25Seric 			to->q_flags |= QQUEUEUP;
401c77d1c25Seric 	}
4022c7e1b8dSeric # endif QUEUE
403c77d1c25Seric 
40435490626Seric 	errno = 0;
4055dfc646bSeric 	return (i);
40625a99e2eSeric }
4075dfc646bSeric /*
40832d19d43Seric **  DOFORK -- do a fork, retrying a couple of times on failure.
40932d19d43Seric **
41032d19d43Seric **	This MUST be a macro, since after a vfork we are running
41132d19d43Seric **	two processes on the same stack!!!
41232d19d43Seric **
41332d19d43Seric **	Parameters:
41432d19d43Seric **		none.
41532d19d43Seric **
41632d19d43Seric **	Returns:
41732d19d43Seric **		From a macro???  You've got to be kidding!
41832d19d43Seric **
41932d19d43Seric **	Side Effects:
42032d19d43Seric **		Modifies the ==> LOCAL <== variable 'pid', leaving:
42132d19d43Seric **			pid of child in parent, zero in child.
42232d19d43Seric **			-1 on unrecoverable error.
42332d19d43Seric **
42432d19d43Seric **	Notes:
42532d19d43Seric **		I'm awfully sorry this looks so awful.  That's
42632d19d43Seric **		vfork for you.....
42732d19d43Seric */
42832d19d43Seric 
42932d19d43Seric # define NFORKTRIES	5
43032d19d43Seric # ifdef VFORK
43132d19d43Seric # define XFORK	vfork
43232d19d43Seric # else VFORK
43332d19d43Seric # define XFORK	fork
43432d19d43Seric # endif VFORK
43532d19d43Seric 
43632d19d43Seric # define DOFORK(fORKfN) \
43732d19d43Seric {\
43832d19d43Seric 	register int i;\
43932d19d43Seric \
44032d19d43Seric 	for (i = NFORKTRIES; i-- > 0; )\
44132d19d43Seric 	{\
44232d19d43Seric 		pid = fORKfN();\
44332d19d43Seric 		if (pid >= 0)\
44432d19d43Seric 			break;\
44532d19d43Seric 		sleep((unsigned) NFORKTRIES - i);\
44632d19d43Seric 	}\
44732d19d43Seric }
44832d19d43Seric /*
4492ed72599Seric **  DOFORK -- simple fork interface to DOFORK.
4502ed72599Seric **
4512ed72599Seric **	Parameters:
4522ed72599Seric **		none.
4532ed72599Seric **
4542ed72599Seric **	Returns:
4552ed72599Seric **		pid of child in parent.
4562ed72599Seric **		zero in child.
4572ed72599Seric **		-1 on error.
4582ed72599Seric **
4592ed72599Seric **	Side Effects:
4602ed72599Seric **		returns twice, once in parent and once in child.
4612ed72599Seric */
4622ed72599Seric 
4632ed72599Seric dofork()
4642ed72599Seric {
4652ed72599Seric 	register int pid;
4662ed72599Seric 
4672ed72599Seric 	DOFORK(fork);
4682ed72599Seric 	return (pid);
4692ed72599Seric }
4702ed72599Seric /*
4715dfc646bSeric **  SENDOFF -- send off call to mailer & collect response.
4725dfc646bSeric **
4735dfc646bSeric **	Parameters:
4745dfc646bSeric **		m -- mailer descriptor.
4755dfc646bSeric **		pvp -- parameter vector to send to it.
4765dfc646bSeric **		editfcn -- function to pipe it through.
4776259796dSeric **		ctladdr -- an address pointer controlling the
4786259796dSeric **			user/groupid etc. of the mailer.
4795dfc646bSeric **
4805dfc646bSeric **	Returns:
4815dfc646bSeric **		exit status of mailer.
4825dfc646bSeric **
4835dfc646bSeric **	Side Effects:
4845dfc646bSeric **		none.
4855dfc646bSeric */
4865dfc646bSeric 
4876259796dSeric sendoff(m, pvp, editfcn, ctladdr)
4885dfc646bSeric 	struct mailer *m;
4895dfc646bSeric 	char **pvp;
4905dfc646bSeric 	int (*editfcn)();
4916259796dSeric 	ADDRESS *ctladdr;
4925dfc646bSeric {
493c579ef51Seric 	auto FILE *mfile;
494c579ef51Seric 	auto FILE *rfile;
4955dfc646bSeric 	register int i;
496c579ef51Seric 	extern putmessage();
497c579ef51Seric 	int pid;
498c579ef51Seric 
499c579ef51Seric 	/*
500c579ef51Seric 	**  Create connection to mailer.
501c579ef51Seric 	*/
502c579ef51Seric 
503c579ef51Seric 	pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile);
504c579ef51Seric 	if (pid < 0)
505c579ef51Seric 		return (-1);
506c579ef51Seric 
507c579ef51Seric 	/*
508c579ef51Seric 	**  Format and send message.
509c579ef51Seric 	*/
510c579ef51Seric 
511c579ef51Seric 	(void) signal(SIGPIPE, SIG_IGN);
512c579ef51Seric 	if (editfcn == NULL)
513c579ef51Seric 		editfcn = putmessage;
514c579ef51Seric 
515c579ef51Seric 	(*editfcn)(mfile, m, FALSE);
516c579ef51Seric 	(void) fclose(mfile);
517c579ef51Seric 
518c579ef51Seric 	i = endmailer(pid, pvp[0]);
519c579ef51Seric 	giveresponse(i, TRUE, m);
520c579ef51Seric 	return (i);
521c579ef51Seric }
522c579ef51Seric /*
523c579ef51Seric **  ENDMAILER -- Wait for mailer to terminate.
524c579ef51Seric **
525c579ef51Seric **	We should never get fatal errors (e.g., segmentation
526c579ef51Seric **	violation), so we report those specially.  For other
527c579ef51Seric **	errors, we choose a status message (into statmsg),
528c579ef51Seric **	and if it represents an error, we print it.
529c579ef51Seric **
530c579ef51Seric **	Parameters:
531c579ef51Seric **		pid -- pid of mailer.
532c579ef51Seric **		name -- name of mailer (for error messages).
533c579ef51Seric **
534c579ef51Seric **	Returns:
535c579ef51Seric **		exit code of mailer.
536c579ef51Seric **
537c579ef51Seric **	Side Effects:
538c579ef51Seric **		none.
539c579ef51Seric */
540c579ef51Seric 
541c579ef51Seric endmailer(pid, name)
542c579ef51Seric 	int pid;
543c579ef51Seric 	char *name;
544c579ef51Seric {
545c579ef51Seric 	register int i;
546c579ef51Seric 	auto int st;
547c579ef51Seric 
548c579ef51Seric 	while ((i = wait(&st)) > 0 && i != pid)
549c579ef51Seric 		continue;
550c579ef51Seric 	if (i < 0)
551c579ef51Seric 	{
552c579ef51Seric 		syserr("wait");
553c579ef51Seric 		return (-1);
554c579ef51Seric 	}
555c579ef51Seric 	if ((st & 0377) != 0)
556c579ef51Seric 	{
557c579ef51Seric 		syserr("%s: stat %o", name, st);
558c579ef51Seric 		ExitStat = EX_UNAVAILABLE;
559c579ef51Seric 		return (-1);
560c579ef51Seric 	}
561c579ef51Seric 	i = (st >> 8) & 0377;
562c579ef51Seric 	return (i);
563c579ef51Seric }
564c579ef51Seric /*
565c579ef51Seric **  OPENMAILER -- open connection to mailer.
566c579ef51Seric **
567c579ef51Seric **	Parameters:
568c579ef51Seric **		m -- mailer descriptor.
569c579ef51Seric **		pvp -- parameter vector to pass to mailer.
570c579ef51Seric **		ctladdr -- controlling address for user.
571c579ef51Seric **		clever -- create a full duplex connection.
572c579ef51Seric **		pmfile -- pointer to mfile (to mailer) connection.
573c579ef51Seric **		prfile -- pointer to rfile (from mailer) connection.
574c579ef51Seric **
575c579ef51Seric **	Returns:
576c579ef51Seric **		pid of mailer.
577c579ef51Seric **		-1 on error.
578c579ef51Seric **
579c579ef51Seric **	Side Effects:
580c579ef51Seric **		creates a mailer in a subprocess.
581c579ef51Seric */
582c579ef51Seric 
583c579ef51Seric openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
584c579ef51Seric 	struct mailer *m;
585c579ef51Seric 	char **pvp;
586c579ef51Seric 	ADDRESS *ctladdr;
587c579ef51Seric 	bool clever;
588c579ef51Seric 	FILE **pmfile;
589c579ef51Seric 	FILE **prfile;
590c579ef51Seric {
5915dfc646bSeric 	int pid;
592f8952a83Seric 	int mpvect[2];
593c579ef51Seric 	int rpvect[2];
5945dfc646bSeric 	FILE *mfile;
595c579ef51Seric 	FILE *rfile;
5965dfc646bSeric 	extern FILE *fdopen();
5975dfc646bSeric 
5985dfc646bSeric # ifdef DEBUG
5995dfc646bSeric 	if (Debug)
6005dfc646bSeric 	{
601c579ef51Seric 		printf("openmailer:\n");
6025dfc646bSeric 		printav(pvp);
6035dfc646bSeric 	}
6045dfc646bSeric # endif DEBUG
60535490626Seric 	errno = 0;
6065dfc646bSeric 
6076328bdf7Seric 	/* create a pipe to shove the mail through */
608f8952a83Seric 	if (pipe(mpvect) < 0)
60925a99e2eSeric 	{
610c579ef51Seric 		syserr("pipe (to mailer)");
61125a99e2eSeric 		return (-1);
61225a99e2eSeric 	}
613c579ef51Seric 
6142c7e1b8dSeric # ifdef SMTP
615c579ef51Seric 	/* if this mailer speaks smtp, create a return pipe */
616c579ef51Seric 	if (clever && pipe(rpvect) < 0)
617c579ef51Seric 	{
618c579ef51Seric 		syserr("pipe (from mailer)");
619c579ef51Seric 		(void) close(mpvect[0]);
620c579ef51Seric 		(void) close(mpvect[1]);
621c579ef51Seric 		return (-1);
622c579ef51Seric 	}
6232c7e1b8dSeric # endif SMTP
624c579ef51Seric 
62532d19d43Seric 	DOFORK(XFORK);
626f129ec7dSeric 	/* pid is set by DOFORK */
62725a99e2eSeric 	if (pid < 0)
62825a99e2eSeric 	{
62925a99e2eSeric 		syserr("Cannot fork");
630f8952a83Seric 		(void) close(mpvect[0]);
631f8952a83Seric 		(void) close(mpvect[1]);
632c579ef51Seric 		if (clever)
633c579ef51Seric 		{
634c579ef51Seric 			(void) close(rpvect[0]);
635c579ef51Seric 			(void) close(rpvect[1]);
636c579ef51Seric 		}
63725a99e2eSeric 		return (-1);
63825a99e2eSeric 	}
63925a99e2eSeric 	else if (pid == 0)
64025a99e2eSeric 	{
64125a99e2eSeric 		/* child -- set up input & exec mailer */
64203ab8e55Seric 		/* make diagnostic output be standard output */
6438f0e7860Seric 		(void) signal(SIGINT, SIG_IGN);
6448f0e7860Seric 		(void) signal(SIGHUP, SIG_IGN);
6450984da9fSeric 		(void) signal(SIGTERM, SIG_DFL);
646f8952a83Seric 
647f8952a83Seric 		/* arrange to filter standard & diag output of command */
648c579ef51Seric 		if (clever)
649c579ef51Seric 		{
650c579ef51Seric 			(void) close(rpvect[0]);
651c579ef51Seric 			(void) close(1);
652c579ef51Seric 			(void) dup(rpvect[1]);
653c579ef51Seric 			(void) close(rpvect[1]);
654c579ef51Seric 		}
655c579ef51Seric 		else if (OutChannel != stdout)
656f8952a83Seric 		{
657f8952a83Seric 			(void) close(1);
658f8952a83Seric 			(void) dup(fileno(OutChannel));
659f8952a83Seric 		}
660db8841e9Seric 		(void) close(2);
661db8841e9Seric 		(void) dup(1);
662f8952a83Seric 
663f8952a83Seric 		/* arrange to get standard input */
664f8952a83Seric 		(void) close(mpvect[1]);
665db8841e9Seric 		(void) close(0);
666f8952a83Seric 		if (dup(mpvect[0]) < 0)
66725a99e2eSeric 		{
66825a99e2eSeric 			syserr("Cannot dup to zero!");
669a590b978Seric 			_exit(EX_OSERR);
67025a99e2eSeric 		}
671f8952a83Seric 		(void) close(mpvect[0]);
6722a6e0786Seric 		if (!bitset(M_RESTR, m->m_flags))
6730984da9fSeric 		{
674e36b99e2Seric 			if (ctladdr->q_uid == 0)
675e36b99e2Seric 			{
676e36b99e2Seric 				(void) setgid(DefGid);
677e36b99e2Seric 				(void) setuid(DefUid);
678e36b99e2Seric 			}
679e36b99e2Seric 			else
68069f29479Seric 			{
681e36b99e2Seric 				(void) setgid(ctladdr->q_gid);
682e36b99e2Seric 				(void) setuid(ctladdr->q_uid);
68369f29479Seric 			}
6840984da9fSeric 		}
685e374fd72Seric # ifndef VFORK
686e374fd72Seric 		/*
687e374fd72Seric 		**  We have to be careful with vfork - we can't mung up the
688e374fd72Seric 		**  memory but we don't want the mailer to inherit any extra
689e374fd72Seric 		**  open files.  Chances are the mailer won't
690e374fd72Seric 		**  care about an extra file, but then again you never know.
691e374fd72Seric 		**  Actually, we would like to close(fileno(pwf)), but it's
692e374fd72Seric 		**  declared static so we can't.  But if we fclose(pwf), which
693e374fd72Seric 		**  is what endpwent does, it closes it in the parent too and
694e374fd72Seric 		**  the next getpwnam will be slower.  If you have a weird
695e374fd72Seric 		**  mailer that chokes on the extra file you should do the
696e374fd72Seric 		**  endpwent().
697e374fd72Seric 		**
698e374fd72Seric 		**  Similar comments apply to log.  However, openlog is
699e374fd72Seric 		**  clever enough to set the FIOCLEX mode on the file,
700e374fd72Seric 		**  so it will be closed automatically on the exec.
701e374fd72Seric 		*/
702e374fd72Seric 
703e374fd72Seric 		endpwent();
70425a99e2eSeric # ifdef LOG
705f9fe028fSeric 		closelog();
70625a99e2eSeric # endif LOG
707e374fd72Seric # endif VFORK
70825a99e2eSeric 		execv(m->m_mailer, pvp);
70925a99e2eSeric 		/* syserr fails because log is closed */
71025a99e2eSeric 		/* syserr("Cannot exec %s", m->m_mailer); */
71132d19d43Seric 		printf("Cannot exec '%s' errno=%d\n", m->m_mailer, errno);
712db8841e9Seric 		(void) fflush(stdout);
713a590b978Seric 		_exit(EX_UNAVAILABLE);
71425a99e2eSeric 	}
71525a99e2eSeric 
716f8952a83Seric 	/*
717c579ef51Seric 	**  Set up return value.
718f8952a83Seric 	*/
719f8952a83Seric 
720f8952a83Seric 	(void) close(mpvect[0]);
721f8952a83Seric 	mfile = fdopen(mpvect[1], "w");
722c579ef51Seric 	if (clever)
72325a99e2eSeric 	{
724c579ef51Seric 		(void) close(rpvect[1]);
725c579ef51Seric 		rfile = fdopen(rpvect[0], "r");
72625a99e2eSeric 	}
727c579ef51Seric 
728c579ef51Seric 	*pmfile = mfile;
729c579ef51Seric 	*prfile = rfile;
730c579ef51Seric 
731c579ef51Seric 	return (pid);
73225a99e2eSeric }
73325a99e2eSeric /*
73425a99e2eSeric **  GIVERESPONSE -- Interpret an error response from a mailer
73525a99e2eSeric **
73625a99e2eSeric **	Parameters:
73725a99e2eSeric **		stat -- the status code from the mailer (high byte
73825a99e2eSeric **			only; core dumps must have been taken care of
73925a99e2eSeric **			already).
74025a99e2eSeric **		force -- if set, force an error message output, even
74125a99e2eSeric **			if the mailer seems to like to print its own
74225a99e2eSeric **			messages.
74325a99e2eSeric **		m -- the mailer descriptor for this mailer.
74425a99e2eSeric **
74525a99e2eSeric **	Returns:
746db8841e9Seric **		none.
74725a99e2eSeric **
74825a99e2eSeric **	Side Effects:
749c1f9df2cSeric **		Errors may be incremented.
75025a99e2eSeric **		ExitStat may be set.
75125a99e2eSeric */
75225a99e2eSeric 
75325a99e2eSeric giveresponse(stat, force, m)
75425a99e2eSeric 	int stat;
75525a99e2eSeric 	int force;
75625a99e2eSeric 	register struct mailer *m;
75725a99e2eSeric {
75825a99e2eSeric 	register char *statmsg;
75925a99e2eSeric 	extern char *SysExMsg[];
76025a99e2eSeric 	register int i;
76125a99e2eSeric 	extern int N_SysEx;
76229dd97a3Seric 	char buf[30];
76325a99e2eSeric 
76413bbc08cSeric 	/*
76513bbc08cSeric 	**  Compute status message from code.
76613bbc08cSeric 	*/
76713bbc08cSeric 
76825a99e2eSeric 	i = stat - EX__BASE;
76925a99e2eSeric 	if (i < 0 || i > N_SysEx)
77025a99e2eSeric 		statmsg = NULL;
77125a99e2eSeric 	else
77225a99e2eSeric 		statmsg = SysExMsg[i];
77325a99e2eSeric 	if (stat == 0)
774c38ba59cSeric 	{
7756cbfa739Seric 		if (bitset(M_LOCAL, m->m_flags))
7763efaed6eSeric 			statmsg = "delivered";
7773efaed6eSeric 		else
7783efaed6eSeric 			statmsg = "queued";
779c38ba59cSeric 		if (Verbose)
780544026c5Seric 			message(Arpa_Info, statmsg);
781c38ba59cSeric 	}
7822c7e1b8dSeric # ifdef QUEUE
783c77d1c25Seric 	else if (stat == EX_TEMPFAIL)
784c77d1c25Seric 	{
785c77d1c25Seric 		if (Verbose)
786c77d1c25Seric 			message(Arpa_Info, "transmission deferred");
787c77d1c25Seric 	}
7882c7e1b8dSeric # endif QUEUE
78925a99e2eSeric 	else
79025a99e2eSeric 	{
791c1f9df2cSeric 		Errors++;
79225a99e2eSeric 		if (statmsg == NULL && m->m_badstat != 0)
79325a99e2eSeric 		{
79425a99e2eSeric 			stat = m->m_badstat;
79525a99e2eSeric 			i = stat - EX__BASE;
79625a99e2eSeric # ifdef DEBUG
79725a99e2eSeric 			if (i < 0 || i >= N_SysEx)
79825a99e2eSeric 				syserr("Bad m_badstat %d", stat);
79925a99e2eSeric 			else
80025a99e2eSeric # endif DEBUG
80125a99e2eSeric 			statmsg = SysExMsg[i];
80225a99e2eSeric 		}
80325a99e2eSeric 		if (statmsg == NULL)
80425a99e2eSeric 			usrerr("unknown mailer response %d", stat);
805c38ba59cSeric 		else if (force || !bitset(M_QUIET, m->m_flags) || Verbose)
80625a99e2eSeric 			usrerr("%s", statmsg);
80725a99e2eSeric 	}
80825a99e2eSeric 
80925a99e2eSeric 	/*
81025a99e2eSeric 	**  Final cleanup.
81125a99e2eSeric 	**	Log a record of the transaction.  Compute the new
81225a99e2eSeric 	**	ExitStat -- if we already had an error, stick with
81325a99e2eSeric 	**	that.
81425a99e2eSeric 	*/
81525a99e2eSeric 
81625a99e2eSeric 	if (statmsg == NULL)
81729dd97a3Seric 	{
818db8841e9Seric 		(void) sprintf(buf, "error %d", stat);
81929dd97a3Seric 		statmsg = buf;
82029dd97a3Seric 	}
82129dd97a3Seric 
82229dd97a3Seric # ifdef LOG
823e374fd72Seric 	syslog(LOG_INFO, "%s->%s: %ld: %s", From.q_paddr, To, MsgSize, statmsg);
82425a99e2eSeric # endif LOG
8252c7e1b8dSeric # ifdef QUEUE
826c77d1c25Seric 	if (stat != EX_TEMPFAIL)
8272c7e1b8dSeric # endif QUEUE
828243921eeSeric 		setstat(stat);
82925a99e2eSeric }
83025a99e2eSeric /*
8316328bdf7Seric **  PUTMESSAGE -- output a message to the final mailer.
83225a99e2eSeric **
8336328bdf7Seric **	This routine takes care of recreating the header from the
8346328bdf7Seric **	in-core copy, etc.
83525a99e2eSeric **
83625a99e2eSeric **	Parameters:
8376328bdf7Seric **		fp -- file to output onto.
8386328bdf7Seric **		m -- a mailer descriptor.
839c579ef51Seric **		xdot -- if set, hide lines beginning with dot.
84025a99e2eSeric **
84125a99e2eSeric **	Returns:
8426328bdf7Seric **		none.
84325a99e2eSeric **
84425a99e2eSeric **	Side Effects:
8456328bdf7Seric **		The message is written onto fp.
84625a99e2eSeric */
84725a99e2eSeric 
848c579ef51Seric putmessage(fp, m, xdot)
8496328bdf7Seric 	FILE *fp;
8506328bdf7Seric 	struct mailer *m;
851c579ef51Seric 	bool xdot;
85225a99e2eSeric {
8536328bdf7Seric 	char buf[BUFSIZ];
85413bbc08cSeric 	register HDR *h;
8556328bdf7Seric 	extern char *arpadate();
8566328bdf7Seric 	bool anyheader = FALSE;
857e9ff65b0Seric 	extern char *capitalize();
85887bd9a02Seric 	extern char *hvalue();
85987bd9a02Seric 	extern bool samefrom();
860894de7daSeric 	char *of_line;
86125a99e2eSeric 
86213bbc08cSeric 	/*
86313bbc08cSeric 	**  Output "From" line unless supressed
864a36e30c9Seric 	**
865a36e30c9Seric 	**  >>>>>>>>>>	One of the ugliest hacks seen by human eyes is
866a36e30c9Seric 	**  >>>>>>>>>>	contained herein: UUCP wants those stupid
867bba2edb3Seric 	**  >>>>>>>>>>	"remote from <host>" lines.  Why oh why does a
868bba2edb3Seric 	**  >> NOTE >>	well-meaning programmer such as myself have to
869a36e30c9Seric 	**  >>>>>>>>>>	deal with this kind of antique garbage????
870bba2edb3Seric 	**  >>>>>>>>>>  This even depends on the local UUCP host name
871bba2edb3Seric 	**  >>>>>>>>>>  being in the $U macro!!!!
87213bbc08cSeric 	*/
87313bbc08cSeric 
87440e4ab56Seric 	if (!bitset(M_NHDR, m->m_flags))
8751412cc7cSeric 	{
8762c7e1b8dSeric # ifdef UGLYUUCP
877a49f24c0Seric 		if (bitset(M_UGLYUUCP, m->m_flags))
878bba2edb3Seric 			(void) expand("From $f  $d remote from $U", buf,
879a36e30c9Seric 					&buf[sizeof buf - 1]);
880a36e30c9Seric 		else
8812c7e1b8dSeric # endif UGLYUUCP
8821412cc7cSeric 			(void) expand("$l", buf, &buf[sizeof buf - 1]);
8831412cc7cSeric 		fprintf(fp, "%s\n", buf);
8841412cc7cSeric 	}
88540e4ab56Seric 
88613bbc08cSeric 	/*
88713bbc08cSeric 	**  Output all header lines
88813bbc08cSeric 	*/
88913bbc08cSeric 
890894de7daSeric 	of_line = hvalue("original-from");
8916328bdf7Seric 	for (h = Header; h != NULL; h = h->h_link)
8926328bdf7Seric 	{
89313bbc08cSeric 		register char *p;
89487bd9a02Seric 		char *origfrom = OrigFrom;
895894de7daSeric 		bool nooutput;
89613bbc08cSeric 
897894de7daSeric 		nooutput = FALSE;
8989e9163a0Seric 		if (bitset(H_CHECK|H_ACHECK, h->h_flags) && !bitset(h->h_mflags, m->m_flags))
8995a0dcb5fSeric 		{
9005a0dcb5fSeric 			p = ")><(";		/* can't happen (I hope) */
901894de7daSeric 			nooutput = TRUE;
9025a0dcb5fSeric 		}
903894de7daSeric 
904894de7daSeric 		/* use From: line from message if generated is the same */
90587bd9a02Seric 		if (strcmp(h->h_field, "from") == 0 && origfrom != NULL &&
906894de7daSeric 		    strcmp(m->m_from, "$f") == 0 && of_line == NULL)
90787bd9a02Seric 		{
90887bd9a02Seric 			p = origfrom;
90987bd9a02Seric 			origfrom = NULL;
91087bd9a02Seric 		}
91187bd9a02Seric 		else if (bitset(H_DEFAULT, h->h_flags))
9124ae18d0eSeric 		{
913db8841e9Seric 			(void) expand(h->h_value, buf, &buf[sizeof buf]);
9144ae18d0eSeric 			p = buf;
9154ae18d0eSeric 		}
9164ae18d0eSeric 		else
9174ae18d0eSeric 			p = h->h_value;
918894de7daSeric 		if (p == NULL || *p == '\0')
9199e9163a0Seric 			continue;
9205a0dcb5fSeric 
9215a0dcb5fSeric 		/* hack, hack -- output Original-From field if different */
922894de7daSeric 		if (strcmp(h->h_field, "from") == 0 && origfrom != NULL)
923894de7daSeric 		{
924894de7daSeric 			/* output new Original-From line if needed */
925894de7daSeric 			if (of_line == NULL && !samefrom(p, origfrom))
9265a0dcb5fSeric 			{
92787bd9a02Seric 				fprintf(fp, "Original-From: %s\n", origfrom);
92887bd9a02Seric 				anyheader = TRUE;
9295a0dcb5fSeric 			}
930894de7daSeric 			if (of_line != NULL && !nooutput && samefrom(p, of_line))
931894de7daSeric 			{
932894de7daSeric 				/* delete Original-From: line if redundant */
933894de7daSeric 				p = of_line;
934894de7daSeric 				of_line = NULL;
935894de7daSeric 			}
936894de7daSeric 		}
937894de7daSeric 		else if (strcmp(h->h_field, "original-from") == 0 && of_line == NULL)
938894de7daSeric 			nooutput = TRUE;
939894de7daSeric 
940894de7daSeric 		/* finally, output the header line */
941894de7daSeric 		if (!nooutput)
942894de7daSeric 		{
943894de7daSeric 			fprintf(fp, "%s: %s\n", capitalize(h->h_field), p);
944894de7daSeric 			h->h_flags |= H_USED;
945894de7daSeric 			anyheader = TRUE;
946894de7daSeric 		}
9476328bdf7Seric 	}
9486328bdf7Seric 	if (anyheader)
9496328bdf7Seric 		fprintf(fp, "\n");
9506328bdf7Seric 
95113bbc08cSeric 	/*
95213bbc08cSeric 	**  Output the body of the message
95313bbc08cSeric 	*/
95413bbc08cSeric 
95578442df3Seric 	if (TempFile != NULL)
95678442df3Seric 	{
957b7902a1dSeric 		rewind(TempFile);
958c579ef51Seric 		while (!ferror(fp) && fgets(buf, sizeof buf, TempFile) != NULL)
959c579ef51Seric 			fprintf(fp, "%s%s", xdot && buf[0] == '.' ? "." : "", buf);
9606328bdf7Seric 
96178442df3Seric 		if (ferror(TempFile))
96278442df3Seric 		{
96378442df3Seric 			syserr("putmessage: read error");
96478442df3Seric 			setstat(EX_IOERR);
96578442df3Seric 		}
96678442df3Seric 	}
96778442df3Seric 
968c77d1c25Seric 	(void) fflush(fp);
96954aa2b0fSeric 	if (ferror(fp) && errno != EPIPE)
97025a99e2eSeric 	{
9716328bdf7Seric 		syserr("putmessage: write error");
97225a99e2eSeric 		setstat(EX_IOERR);
97325a99e2eSeric 	}
97454aa2b0fSeric 	errno = 0;
97525a99e2eSeric }
97625a99e2eSeric /*
97787bd9a02Seric **  SAMEFROM -- tell if two text addresses represent the same from address.
97887bd9a02Seric **
97987bd9a02Seric **	Parameters:
98087bd9a02Seric **		ifrom -- internally generated form of from address.
98187bd9a02Seric **		efrom -- external form of from address.
98287bd9a02Seric **
98387bd9a02Seric **	Returns:
98487bd9a02Seric **		TRUE -- if they convey the same info.
98587bd9a02Seric **		FALSE -- if any information has been lost.
98687bd9a02Seric **
98787bd9a02Seric **	Side Effects:
98887bd9a02Seric **		none.
98987bd9a02Seric */
99087bd9a02Seric 
99187bd9a02Seric bool
99287bd9a02Seric samefrom(ifrom, efrom)
99387bd9a02Seric 	char *ifrom;
99487bd9a02Seric 	char *efrom;
99587bd9a02Seric {
996894de7daSeric 	register char *p;
997894de7daSeric 	char buf[MAXNAME + 4];
998894de7daSeric 
999894de7daSeric # ifdef DEBUG
1000894de7daSeric 	if (Debug > 7)
1001894de7daSeric 		printf("samefrom(%s,%s)-->", ifrom, efrom);
1002894de7daSeric # endif DEBUG
1003894de7daSeric 	if (strcmp(ifrom, efrom) == 0)
1004894de7daSeric 		goto success;
1005894de7daSeric 	p = index(ifrom, '@');
1006894de7daSeric 	if (p == NULL)
1007894de7daSeric 		goto failure;
1008894de7daSeric 	*p = '\0';
1009894de7daSeric 	strcpy(buf, ifrom);
1010894de7daSeric 	strcat(buf, " at ");
1011894de7daSeric 	*p++ = '@';
1012894de7daSeric 	strcat(buf, p);
1013894de7daSeric 	if (strcmp(buf, efrom) == 0)
1014894de7daSeric 		goto success;
1015894de7daSeric 
1016894de7daSeric   failure:
1017894de7daSeric # ifdef DEBUG
1018894de7daSeric 	if (Debug > 7)
1019894de7daSeric 		printf("FALSE\n");
1020894de7daSeric # endif DEBUG
1021894de7daSeric 	return (FALSE);
1022894de7daSeric 
1023894de7daSeric   success:
1024894de7daSeric # ifdef DEBUG
1025894de7daSeric 	if (Debug > 7)
1026894de7daSeric 		printf("TRUE\n");
1027894de7daSeric # endif DEBUG
1028894de7daSeric 	return (TRUE);
102987bd9a02Seric }
103087bd9a02Seric /*
103125a99e2eSeric **  MAILFILE -- Send a message to a file.
103225a99e2eSeric **
1033f129ec7dSeric **	If the file has the setuid/setgid bits set, but NO execute
1034f129ec7dSeric **	bits, sendmail will try to become the owner of that file
1035f129ec7dSeric **	rather than the real user.  Obviously, this only works if
1036f129ec7dSeric **	sendmail runs as root.
1037f129ec7dSeric **
103825a99e2eSeric **	Parameters:
103925a99e2eSeric **		filename -- the name of the file to send to.
10406259796dSeric **		ctladdr -- the controlling address header -- includes
10416259796dSeric **			the userid/groupid to be when sending.
104225a99e2eSeric **
104325a99e2eSeric **	Returns:
104425a99e2eSeric **		The exit code associated with the operation.
104525a99e2eSeric **
104625a99e2eSeric **	Side Effects:
104725a99e2eSeric **		none.
104825a99e2eSeric */
104925a99e2eSeric 
10506259796dSeric mailfile(filename, ctladdr)
105125a99e2eSeric 	char *filename;
10526259796dSeric 	ADDRESS *ctladdr;
105325a99e2eSeric {
105425a99e2eSeric 	register FILE *f;
105532d19d43Seric 	register int pid;
105625a99e2eSeric 
105732d19d43Seric 	/*
105832d19d43Seric 	**  Fork so we can change permissions here.
105932d19d43Seric 	**	Note that we MUST use fork, not vfork, because of
106032d19d43Seric 	**	the complications of calling subroutines, etc.
106132d19d43Seric 	*/
106232d19d43Seric 
106332d19d43Seric 	DOFORK(fork);
106432d19d43Seric 
106532d19d43Seric 	if (pid < 0)
106632d19d43Seric 		return (EX_OSERR);
106732d19d43Seric 	else if (pid == 0)
106832d19d43Seric 	{
106932d19d43Seric 		/* child -- actually write to file */
1070f129ec7dSeric 		struct stat stb;
1071f129ec7dSeric 
10720984da9fSeric 		(void) signal(SIGINT, SIG_DFL);
10730984da9fSeric 		(void) signal(SIGHUP, SIG_DFL);
10740984da9fSeric 		(void) signal(SIGTERM, SIG_DFL);
1075f129ec7dSeric 		umask(OldUmask);
1076f129ec7dSeric 		if (stat(filename, &stb) < 0)
1077e6e1265fSeric 			stb.st_mode = 0666;
1078f129ec7dSeric 		if (bitset(0111, stb.st_mode))
1079f129ec7dSeric 			exit(EX_CANTCREAT);
108003827b5fSeric 		if (ctladdr == NULL)
108103827b5fSeric 			ctladdr = &From;
1082f129ec7dSeric 		if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0)
1083e36b99e2Seric 		{
1084e36b99e2Seric 			if (ctladdr->q_uid == 0)
1085e36b99e2Seric 				(void) setgid(DefGid);
1086e36b99e2Seric 			else
10876259796dSeric 				(void) setgid(ctladdr->q_gid);
1088e36b99e2Seric 		}
1089f129ec7dSeric 		if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0)
1090e36b99e2Seric 		{
1091e36b99e2Seric 			if (ctladdr->q_uid == 0)
1092e36b99e2Seric 				(void) setuid(DefUid);
1093e36b99e2Seric 			else
10946259796dSeric 				(void) setuid(ctladdr->q_uid);
1095e36b99e2Seric 		}
109625a99e2eSeric 		f = fopen(filename, "a");
109725a99e2eSeric 		if (f == NULL)
109832d19d43Seric 			exit(EX_CANTCREAT);
109925a99e2eSeric 
1100c579ef51Seric 		putmessage(f, Mailer[1], FALSE);
110125a99e2eSeric 		fputs("\n", f);
1102db8841e9Seric 		(void) fclose(f);
110332d19d43Seric 		(void) fflush(stdout);
1104e36b99e2Seric 
1105e36b99e2Seric 		/* reset ISUID & ISGID bits */
1106c77d1c25Seric 		(void) chmod(filename, (int) stb.st_mode);
110732d19d43Seric 		exit(EX_OK);
110813bbc08cSeric 		/*NOTREACHED*/
110932d19d43Seric 	}
111032d19d43Seric 	else
111132d19d43Seric 	{
111232d19d43Seric 		/* parent -- wait for exit status */
111332d19d43Seric 		register int i;
111432d19d43Seric 		auto int stat;
111532d19d43Seric 
111632d19d43Seric 		while ((i = wait(&stat)) != pid)
111732d19d43Seric 		{
111832d19d43Seric 			if (i < 0)
111932d19d43Seric 			{
112032d19d43Seric 				stat = EX_OSERR << 8;
112132d19d43Seric 				break;
112232d19d43Seric 			}
112332d19d43Seric 		}
11240984da9fSeric 		if ((stat & 0377) != 0)
11250984da9fSeric 			stat = EX_UNAVAILABLE << 8;
112632d19d43Seric 		return ((stat >> 8) & 0377);
112732d19d43Seric 	}
112825a99e2eSeric }
1129ea4dc939Seric /*
1130ea4dc939Seric **  SENDALL -- actually send all the messages.
1131ea4dc939Seric **
1132ea4dc939Seric **	Parameters:
1133ea4dc939Seric **		verifyonly -- if set, only give verification messages.
1134ea4dc939Seric **
1135ea4dc939Seric **	Returns:
1136ea4dc939Seric **		none.
1137ea4dc939Seric **
1138ea4dc939Seric **	Side Effects:
1139ea4dc939Seric **		Scans the send lists and sends everything it finds.
1140ea4dc939Seric */
1141ea4dc939Seric 
1142ea4dc939Seric sendall(verifyonly)
1143ea4dc939Seric 	bool verifyonly;
1144ea4dc939Seric {
1145e77e673fSeric 	register ADDRESS *q;
1146ea4dc939Seric 	typedef int (*fnptr)();
1147ea4dc939Seric 
1148772e6e50Seric # ifdef DEBUG
1149772e6e50Seric 	if (Debug > 1)
1150772e6e50Seric 	{
1151772e6e50Seric 		printf("\nSendQueue:\n");
1152772e6e50Seric 		printaddr(SendQueue, TRUE);
1153772e6e50Seric 	}
1154772e6e50Seric # endif DEBUG
1155ea4dc939Seric 
1156e77e673fSeric 	for (q = SendQueue; q != NULL; q = q->q_next)
1157ea4dc939Seric 	{
1158ea4dc939Seric 		if (verifyonly)
1159ea4dc939Seric 		{
1160ea4dc939Seric 			To = q->q_paddr;
1161e77e673fSeric 			if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
1162ea4dc939Seric 			{
11637da1035fSeric 				if (bitset(M_LOCAL, q->q_mailer->m_flags))
1164ea4dc939Seric 					message(Arpa_Info, "deliverable");
1165ea4dc939Seric 				else
1166ea4dc939Seric 					message(Arpa_Info, "queueable");
1167ea4dc939Seric 			}
1168ea4dc939Seric 		}
1169ea4dc939Seric 		else
1170ea4dc939Seric 			(void) deliver(q, (fnptr) NULL);
1171ea4dc939Seric 	}
1172ea4dc939Seric }
1173