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*62667331Seric static char SccsId[] = "@(#)deliver.c	3.58	11/27/81";
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 
7025a99e2eSeric 	/*
715dfc646bSeric 	**  Do initial argv setup.
725dfc646bSeric 	**	Insert the mailer name.  Notice that $x expansion is
735dfc646bSeric 	**	NOT done on the mailer name.  Then, if the mailer has
745dfc646bSeric 	**	a picky -f flag, we insert it as appropriate.  This
755dfc646bSeric 	**	code does not check for 'pv' overflow; this places a
765dfc646bSeric 	**	manifest lower limit of 4 for MAXPV.
775dfc646bSeric 	*/
785dfc646bSeric 
797da1035fSeric 	m = to->q_mailer;
805dfc646bSeric 	host = to->q_host;
8178442df3Seric 
8278442df3Seric 	/* rewrite from address, using rewriting rules */
8378442df3Seric 	(void) expand(m->m_from, buf, &buf[sizeof buf - 1]);
8478442df3Seric 	mvp = prescan(buf, '\0');
8578442df3Seric 	if (mvp == NULL)
8678442df3Seric 	{
8778442df3Seric 		syserr("bad mailer from translate \"%s\"", buf);
8878442df3Seric 		return (EX_SOFTWARE);
8978442df3Seric 	}
9078442df3Seric 	rewrite(mvp, 2);
9178442df3Seric 	cataddr(mvp, tfrombuf, sizeof tfrombuf);
9278442df3Seric 
9378442df3Seric 	define('g', tfrombuf);		/* translated sender address */
945dfc646bSeric 	define('h', host);		/* to host */
955dfc646bSeric 	Errors = 0;
965dfc646bSeric 	pvp = pv;
975dfc646bSeric 	*pvp++ = m->m_argv[0];
985dfc646bSeric 
995dfc646bSeric 	/* insert -f or -r flag as appropriate */
1005dfc646bSeric 	if (bitset(M_FOPT|M_ROPT, m->m_flags) && FromFlag)
1015dfc646bSeric 	{
1025dfc646bSeric 		if (bitset(M_FOPT, m->m_flags))
1035dfc646bSeric 			*pvp++ = "-f";
1045dfc646bSeric 		else
1055dfc646bSeric 			*pvp++ = "-r";
106db8841e9Seric 		(void) expand("$g", buf, &buf[sizeof buf - 1]);
1075dfc646bSeric 		*pvp++ = newstr(buf);
1085dfc646bSeric 	}
1095dfc646bSeric 
1105dfc646bSeric 	/*
1115dfc646bSeric 	**  Append the other fixed parts of the argv.  These run
1125dfc646bSeric 	**  up to the first entry containing "$u".  There can only
1135dfc646bSeric 	**  be one of these, and there are only a few more slots
1145dfc646bSeric 	**  in the pv after it.
1155dfc646bSeric 	*/
1165dfc646bSeric 
1175dfc646bSeric 	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
1185dfc646bSeric 	{
1195dfc646bSeric 		while ((p = index(p, '$')) != NULL)
1205dfc646bSeric 			if (*++p == 'u')
1215dfc646bSeric 				break;
1225dfc646bSeric 		if (p != NULL)
1235dfc646bSeric 			break;
1245dfc646bSeric 
1255dfc646bSeric 		/* this entry is safe -- go ahead and process it */
126db8841e9Seric 		(void) expand(*mvp, buf, &buf[sizeof buf - 1]);
1275dfc646bSeric 		*pvp++ = newstr(buf);
1285dfc646bSeric 		if (pvp >= &pv[MAXPV - 3])
1295dfc646bSeric 		{
1305dfc646bSeric 			syserr("Too many parameters to %s before $u", pv[0]);
1315dfc646bSeric 			return (-1);
1325dfc646bSeric 		}
1335dfc646bSeric 	}
134c579ef51Seric 
1355dfc646bSeric 	if (*mvp == NULL)
136c579ef51Seric 	{
137c579ef51Seric 		/* running SMTP */
138c579ef51Seric 		clever = TRUE;
139c579ef51Seric 		*pvp = NULL;
140c579ef51Seric 		i = smtpinit(m, pv, (ADDRESS *) NULL);
141c579ef51Seric 		giveresponse(i, TRUE, m);
142c579ef51Seric 		if (i == EX_TEMPFAIL)
143c579ef51Seric 		{
144c579ef51Seric 			QueueUp = TRUE;
145c579ef51Seric 			tempfail = TRUE;
146c579ef51Seric 		}
147c579ef51Seric 	}
1485dfc646bSeric 
1495dfc646bSeric 	/*
1505dfc646bSeric 	**  At this point *mvp points to the argument with $u.  We
1515dfc646bSeric 	**  run through our address list and append all the addresses
1525dfc646bSeric 	**  we can.  If we run out of space, do not fret!  We can
1535dfc646bSeric 	**  always send another copy later.
1545dfc646bSeric 	*/
1555dfc646bSeric 
1565dfc646bSeric 	tobuf[0] = '\0';
1575dfc646bSeric 	To = tobuf;
1586259796dSeric 	ctladdr = NULL;
1595dfc646bSeric 	for (; to != NULL; to = to->q_next)
1605dfc646bSeric 	{
1615dfc646bSeric 		/* avoid sending multiple recipients to dumb mailers */
162bea22b26Seric 		if (tobuf[0] != '\0' && !bitset(M_MUSER, m->m_flags))
1635dfc646bSeric 			break;
1645dfc646bSeric 
1655dfc646bSeric 		/* if already sent or not for this host, don't send */
166e77e673fSeric 		if ((!ForceMail && bitset(QDONTSEND, to->q_flags)) ||
167e77e673fSeric 		    strcmp(to->q_host, host) != 0 || to->q_mailer != firstto->q_mailer)
1685dfc646bSeric 			continue;
1696259796dSeric 
170772e6e50Seric # ifdef DEBUG
171772e6e50Seric 		if (Debug)
172772e6e50Seric 		{
173772e6e50Seric 			printf("\nsend to ");
174772e6e50Seric 			printaddr(to, FALSE);
175772e6e50Seric 		}
176772e6e50Seric # endif DEBUG
177772e6e50Seric 
178772e6e50Seric 		/* link together the chain of recipients */
179772e6e50Seric 		if (!bitset(QDONTSEND, to->q_flags))
180772e6e50Seric 		{
181772e6e50Seric 			to->q_tchain = tochain;
182772e6e50Seric 			tochain = to;
183772e6e50Seric 		}
184772e6e50Seric 
1856259796dSeric 		/* compute effective uid/gid when sending */
1867da1035fSeric 		if (to->q_mailer == ProgMailer)
1876259796dSeric 			ctladdr = getctladdr(to);
1886259796dSeric 
1895dfc646bSeric 		user = to->q_user;
1905dfc646bSeric 		To = to->q_paddr;
1915dfc646bSeric 		to->q_flags |= QDONTSEND;
192c579ef51Seric 		if (tempfail)
193772e6e50Seric 		{
194c579ef51Seric 			to->q_flags |= QQUEUEUP;
195772e6e50Seric 			continue;
196772e6e50Seric 		}
1975dfc646bSeric 
1985dfc646bSeric 		/*
1995dfc646bSeric 		**  Check to see that these people are allowed to
2005dfc646bSeric 		**  talk to each other.
2012a6e0786Seric 		*/
2022a6e0786Seric 
2032a6e0786Seric 		if (!checkcompat(to))
2045dfc646bSeric 		{
2055dfc646bSeric 			giveresponse(EX_UNAVAILABLE, TRUE, m);
2065dfc646bSeric 			continue;
2075dfc646bSeric 		}
2082a6e0786Seric 
2092a6e0786Seric 		/*
2109ec9501bSeric 		**  Strip quote bits from names if the mailer is dumb
2119ec9501bSeric 		**	about them.
21225a99e2eSeric 		*/
21325a99e2eSeric 
2142a6e0786Seric 		if (bitset(M_STRIPQ, m->m_flags))
21525a99e2eSeric 		{
2169ec9501bSeric 			stripquotes(user, TRUE);
2179ec9501bSeric 			stripquotes(host, TRUE);
2189ec9501bSeric 		}
2199ec9501bSeric 		else
2209ec9501bSeric 		{
2219ec9501bSeric 			stripquotes(user, FALSE);
2229ec9501bSeric 			stripquotes(host, FALSE);
22325a99e2eSeric 		}
22425a99e2eSeric 
22525a99e2eSeric 		/*
2263efaed6eSeric 		**  If an error message has already been given, don't
2273efaed6eSeric 		**	bother to send to this address.
2283efaed6eSeric 		**
2293efaed6eSeric 		**	>>>>>>>>>> This clause assumes that the local mailer
2303efaed6eSeric 		**	>> NOTE >> cannot do any further aliasing; that
2313efaed6eSeric 		**	>>>>>>>>>> function is subsumed by sendmail.
2323efaed6eSeric 		*/
2333efaed6eSeric 
2343efaed6eSeric 		if (bitset(QBADADDR, to->q_flags))
2353efaed6eSeric 			continue;
2363efaed6eSeric 
237f2fec898Seric 		/* save statistics.... */
2387da1035fSeric 		Stat.stat_nt[to->q_mailer->m_mno]++;
2397da1035fSeric 		Stat.stat_bt[to->q_mailer->m_mno] += kbytes(MsgSize);
240f2fec898Seric 
2413efaed6eSeric 		/*
24225a99e2eSeric 		**  See if this user name is "special".
24325a99e2eSeric 		**	If the user name has a slash in it, assume that this
24425a99e2eSeric 		**	is a file -- send it off without further ado.
24525a99e2eSeric 		**	Note that this means that editfcn's will not
2465dfc646bSeric 		**	be applied to the message.  Also note that
2475dfc646bSeric 		**	this type of addresses is not processed along
2485dfc646bSeric 		**	with the others, so we fudge on the To person.
24925a99e2eSeric 		*/
25025a99e2eSeric 
2517da1035fSeric 		if (m == LocalMailer)
25225a99e2eSeric 		{
25325a99e2eSeric 			if (index(user, '/') != NULL)
25425a99e2eSeric 			{
2556259796dSeric 				i = mailfile(user, getctladdr(to));
25625a99e2eSeric 				giveresponse(i, TRUE, m);
2575dfc646bSeric 				continue;
25825a99e2eSeric 			}
25925a99e2eSeric 		}
26025a99e2eSeric 
26113bbc08cSeric 		/*
26213bbc08cSeric 		**  Address is verified -- add this user to mailer
26313bbc08cSeric 		**  argv, and add it to the print list of recipients.
26413bbc08cSeric 		*/
26513bbc08cSeric 
2665dfc646bSeric 		/* create list of users for error messages */
2675dfc646bSeric 		if (tobuf[0] != '\0')
268db8841e9Seric 			(void) strcat(tobuf, ",");
269db8841e9Seric 		(void) strcat(tobuf, to->q_paddr);
2705dfc646bSeric 		define('u', user);		/* to user */
271c2567733Seric 		define('z', to->q_home);	/* user's home */
2725dfc646bSeric 
273c579ef51Seric 		/*
274c579ef51Seric 		**  Expand out this user into argument list or
275c579ef51Seric 		**  send it to our SMTP server.
276c579ef51Seric 		*/
277c579ef51Seric 
278c579ef51Seric 		if (clever)
279c579ef51Seric 		{
280d2b7d202Seric 			i = smtprcpt(to);
281c579ef51Seric 			if (i == EX_TEMPFAIL)
282c579ef51Seric 			{
283c579ef51Seric 				QueueUp = TRUE;
284c579ef51Seric 				to->q_flags |= QQUEUEUP;
285c579ef51Seric 			}
286c579ef51Seric 			else if (i != EX_OK)
287c579ef51Seric 			{
288c579ef51Seric 				to->q_flags |= QBADADDR;
289c579ef51Seric 				giveresponse(i, TRUE, m);
290c579ef51Seric 			}
291c579ef51Seric 		}
292c579ef51Seric 		else
293c579ef51Seric 		{
294d4ccc802Seric 			(void) expand(*mvp, buf, &buf[sizeof buf - 1]);
2955dfc646bSeric 			*pvp++ = newstr(buf);
2965dfc646bSeric 			if (pvp >= &pv[MAXPV - 2])
2975dfc646bSeric 			{
2985dfc646bSeric 				/* allow some space for trailing parms */
2995dfc646bSeric 				break;
3005dfc646bSeric 			}
3015dfc646bSeric 		}
302c579ef51Seric 	}
3035dfc646bSeric 
304145b49b1Seric 	/* see if any addresses still exist */
305145b49b1Seric 	if (tobuf[0] == '\0')
306c579ef51Seric 	{
307c579ef51Seric 		if (clever)
308c579ef51Seric 			smtpquit(pv[0]);
309145b49b1Seric 		return (0);
310c579ef51Seric 	}
311145b49b1Seric 
3125dfc646bSeric 	/* print out messages as full list */
3135dfc646bSeric 	To = tobuf;
3145dfc646bSeric 
3155dfc646bSeric 	/*
3165dfc646bSeric 	**  Fill out any parameters after the $u parameter.
3175dfc646bSeric 	*/
3185dfc646bSeric 
319c579ef51Seric 	while (!clever && *++mvp != NULL)
3205dfc646bSeric 	{
321db8841e9Seric 		(void) expand(*mvp, buf, &buf[sizeof buf - 1]);
3225dfc646bSeric 		*pvp++ = newstr(buf);
3235dfc646bSeric 		if (pvp >= &pv[MAXPV])
3245dfc646bSeric 			syserr("deliver: pv overflow after $u for %s", pv[0]);
3255dfc646bSeric 	}
3265dfc646bSeric 	*pvp++ = NULL;
3275dfc646bSeric 
32825a99e2eSeric 	/*
32925a99e2eSeric 	**  Call the mailer.
3306328bdf7Seric 	**	The argument vector gets built, pipes
33125a99e2eSeric 	**	are created as necessary, and we fork & exec as
3326328bdf7Seric 	**	appropriate.
333c579ef51Seric 	**	If we are running SMTP, we just need to clean up.
33425a99e2eSeric 	*/
33525a99e2eSeric 
3365dfc646bSeric 	if (editfcn == NULL)
3375dfc646bSeric 		editfcn = putmessage;
3386259796dSeric 	if (ctladdr == NULL)
3396259796dSeric 		ctladdr = &From;
340c579ef51Seric 	if (clever)
341c579ef51Seric 	{
342c579ef51Seric 		i = smtpfinish(m, editfcn);
343c579ef51Seric 		smtpquit(pv[0]);
344c579ef51Seric 	}
345c579ef51Seric 	else
3466259796dSeric 		i = sendoff(m, pv, editfcn, ctladdr);
3475dfc646bSeric 
348c77d1c25Seric 	/*
349c77d1c25Seric 	**  If we got a temporary failure, arrange to queue the
350c77d1c25Seric 	**  addressees.
351c77d1c25Seric 	*/
352c77d1c25Seric 
353c77d1c25Seric 	if (i == EX_TEMPFAIL)
354c77d1c25Seric 	{
355c77d1c25Seric 		QueueUp = TRUE;
356772e6e50Seric 		for (to = tochain; to != NULL; to = to->q_tchain)
357c77d1c25Seric 			to->q_flags |= QQUEUEUP;
358c77d1c25Seric 	}
359c77d1c25Seric 
36035490626Seric 	errno = 0;
3615dfc646bSeric 	return (i);
36225a99e2eSeric }
3635dfc646bSeric /*
36432d19d43Seric **  DOFORK -- do a fork, retrying a couple of times on failure.
36532d19d43Seric **
36632d19d43Seric **	This MUST be a macro, since after a vfork we are running
36732d19d43Seric **	two processes on the same stack!!!
36832d19d43Seric **
36932d19d43Seric **	Parameters:
37032d19d43Seric **		none.
37132d19d43Seric **
37232d19d43Seric **	Returns:
37332d19d43Seric **		From a macro???  You've got to be kidding!
37432d19d43Seric **
37532d19d43Seric **	Side Effects:
37632d19d43Seric **		Modifies the ==> LOCAL <== variable 'pid', leaving:
37732d19d43Seric **			pid of child in parent, zero in child.
37832d19d43Seric **			-1 on unrecoverable error.
37932d19d43Seric **
38032d19d43Seric **	Notes:
38132d19d43Seric **		I'm awfully sorry this looks so awful.  That's
38232d19d43Seric **		vfork for you.....
38332d19d43Seric */
38432d19d43Seric 
38532d19d43Seric # define NFORKTRIES	5
38632d19d43Seric # ifdef VFORK
38732d19d43Seric # define XFORK	vfork
38832d19d43Seric # else VFORK
38932d19d43Seric # define XFORK	fork
39032d19d43Seric # endif VFORK
39132d19d43Seric 
39232d19d43Seric # define DOFORK(fORKfN) \
39332d19d43Seric {\
39432d19d43Seric 	register int i;\
39532d19d43Seric \
39632d19d43Seric 	for (i = NFORKTRIES; i-- > 0; )\
39732d19d43Seric 	{\
39832d19d43Seric 		pid = fORKfN();\
39932d19d43Seric 		if (pid >= 0)\
40032d19d43Seric 			break;\
40132d19d43Seric 		sleep((unsigned) NFORKTRIES - i);\
40232d19d43Seric 	}\
40332d19d43Seric }
40432d19d43Seric /*
4052ed72599Seric **  DOFORK -- simple fork interface to DOFORK.
4062ed72599Seric **
4072ed72599Seric **	Parameters:
4082ed72599Seric **		none.
4092ed72599Seric **
4102ed72599Seric **	Returns:
4112ed72599Seric **		pid of child in parent.
4122ed72599Seric **		zero in child.
4132ed72599Seric **		-1 on error.
4142ed72599Seric **
4152ed72599Seric **	Side Effects:
4162ed72599Seric **		returns twice, once in parent and once in child.
4172ed72599Seric */
4182ed72599Seric 
4192ed72599Seric dofork()
4202ed72599Seric {
4212ed72599Seric 	register int pid;
4222ed72599Seric 
4232ed72599Seric 	DOFORK(fork);
4242ed72599Seric 	return (pid);
4252ed72599Seric }
4262ed72599Seric /*
4275dfc646bSeric **  SENDOFF -- send off call to mailer & collect response.
4285dfc646bSeric **
4295dfc646bSeric **	Parameters:
4305dfc646bSeric **		m -- mailer descriptor.
4315dfc646bSeric **		pvp -- parameter vector to send to it.
4325dfc646bSeric **		editfcn -- function to pipe it through.
4336259796dSeric **		ctladdr -- an address pointer controlling the
4346259796dSeric **			user/groupid etc. of the mailer.
4355dfc646bSeric **
4365dfc646bSeric **	Returns:
4375dfc646bSeric **		exit status of mailer.
4385dfc646bSeric **
4395dfc646bSeric **	Side Effects:
4405dfc646bSeric **		none.
4415dfc646bSeric */
4425dfc646bSeric 
4436259796dSeric sendoff(m, pvp, editfcn, ctladdr)
4445dfc646bSeric 	struct mailer *m;
4455dfc646bSeric 	char **pvp;
4465dfc646bSeric 	int (*editfcn)();
4476259796dSeric 	ADDRESS *ctladdr;
4485dfc646bSeric {
449c579ef51Seric 	auto FILE *mfile;
450c579ef51Seric 	auto FILE *rfile;
4515dfc646bSeric 	register int i;
452c579ef51Seric 	extern putmessage();
453c579ef51Seric 	int pid;
454c579ef51Seric 
455c579ef51Seric 	/*
456c579ef51Seric 	**  Create connection to mailer.
457c579ef51Seric 	*/
458c579ef51Seric 
459c579ef51Seric 	pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile);
460c579ef51Seric 	if (pid < 0)
461c579ef51Seric 		return (-1);
462c579ef51Seric 
463c579ef51Seric 	/*
464c579ef51Seric 	**  Format and send message.
465c579ef51Seric 	*/
466c579ef51Seric 
467c579ef51Seric 	(void) signal(SIGPIPE, SIG_IGN);
468c579ef51Seric 	if (editfcn == NULL)
469c579ef51Seric 		editfcn = putmessage;
470c579ef51Seric 
471c579ef51Seric 	(*editfcn)(mfile, m, FALSE);
472c579ef51Seric 	(void) fclose(mfile);
473c579ef51Seric 
474c579ef51Seric 	i = endmailer(pid, pvp[0]);
475c579ef51Seric 	giveresponse(i, TRUE, m);
476c579ef51Seric 	return (i);
477c579ef51Seric }
478c579ef51Seric /*
479c579ef51Seric **  ENDMAILER -- Wait for mailer to terminate.
480c579ef51Seric **
481c579ef51Seric **	We should never get fatal errors (e.g., segmentation
482c579ef51Seric **	violation), so we report those specially.  For other
483c579ef51Seric **	errors, we choose a status message (into statmsg),
484c579ef51Seric **	and if it represents an error, we print it.
485c579ef51Seric **
486c579ef51Seric **	Parameters:
487c579ef51Seric **		pid -- pid of mailer.
488c579ef51Seric **		name -- name of mailer (for error messages).
489c579ef51Seric **
490c579ef51Seric **	Returns:
491c579ef51Seric **		exit code of mailer.
492c579ef51Seric **
493c579ef51Seric **	Side Effects:
494c579ef51Seric **		none.
495c579ef51Seric */
496c579ef51Seric 
497c579ef51Seric endmailer(pid, name)
498c579ef51Seric 	int pid;
499c579ef51Seric 	char *name;
500c579ef51Seric {
501c579ef51Seric 	register int i;
502c579ef51Seric 	auto int st;
503c579ef51Seric 
504c579ef51Seric 	while ((i = wait(&st)) > 0 && i != pid)
505c579ef51Seric 		continue;
506c579ef51Seric 	if (i < 0)
507c579ef51Seric 	{
508c579ef51Seric 		syserr("wait");
509c579ef51Seric 		return (-1);
510c579ef51Seric 	}
511c579ef51Seric 	if ((st & 0377) != 0)
512c579ef51Seric 	{
513c579ef51Seric 		syserr("%s: stat %o", name, st);
514c579ef51Seric 		ExitStat = EX_UNAVAILABLE;
515c579ef51Seric 		return (-1);
516c579ef51Seric 	}
517c579ef51Seric 	i = (st >> 8) & 0377;
518c579ef51Seric 	return (i);
519c579ef51Seric }
520c579ef51Seric /*
521c579ef51Seric **  OPENMAILER -- open connection to mailer.
522c579ef51Seric **
523c579ef51Seric **	Parameters:
524c579ef51Seric **		m -- mailer descriptor.
525c579ef51Seric **		pvp -- parameter vector to pass to mailer.
526c579ef51Seric **		ctladdr -- controlling address for user.
527c579ef51Seric **		clever -- create a full duplex connection.
528c579ef51Seric **		pmfile -- pointer to mfile (to mailer) connection.
529c579ef51Seric **		prfile -- pointer to rfile (from mailer) connection.
530c579ef51Seric **
531c579ef51Seric **	Returns:
532c579ef51Seric **		pid of mailer.
533c579ef51Seric **		-1 on error.
534c579ef51Seric **
535c579ef51Seric **	Side Effects:
536c579ef51Seric **		creates a mailer in a subprocess.
537c579ef51Seric */
538c579ef51Seric 
539c579ef51Seric openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
540c579ef51Seric 	struct mailer *m;
541c579ef51Seric 	char **pvp;
542c579ef51Seric 	ADDRESS *ctladdr;
543c579ef51Seric 	bool clever;
544c579ef51Seric 	FILE **pmfile;
545c579ef51Seric 	FILE **prfile;
546c579ef51Seric {
5475dfc646bSeric 	int pid;
548f8952a83Seric 	int mpvect[2];
549c579ef51Seric 	int rpvect[2];
5505dfc646bSeric 	FILE *mfile;
551c579ef51Seric 	FILE *rfile;
5525dfc646bSeric 	extern FILE *fdopen();
5535dfc646bSeric 
5545dfc646bSeric # ifdef DEBUG
5555dfc646bSeric 	if (Debug)
5565dfc646bSeric 	{
557c579ef51Seric 		printf("openmailer:\n");
5585dfc646bSeric 		printav(pvp);
5595dfc646bSeric 	}
5605dfc646bSeric # endif DEBUG
56135490626Seric 	errno = 0;
5625dfc646bSeric 
5636328bdf7Seric 	/* create a pipe to shove the mail through */
564f8952a83Seric 	if (pipe(mpvect) < 0)
56525a99e2eSeric 	{
566c579ef51Seric 		syserr("pipe (to mailer)");
56725a99e2eSeric 		return (-1);
56825a99e2eSeric 	}
569c579ef51Seric 
570c579ef51Seric 	/* if this mailer speaks smtp, create a return pipe */
571c579ef51Seric 	if (clever && pipe(rpvect) < 0)
572c579ef51Seric 	{
573c579ef51Seric 		syserr("pipe (from mailer)");
574c579ef51Seric 		(void) close(mpvect[0]);
575c579ef51Seric 		(void) close(mpvect[1]);
576c579ef51Seric 		return (-1);
577c579ef51Seric 	}
578c579ef51Seric 
57932d19d43Seric 	DOFORK(XFORK);
580f129ec7dSeric 	/* pid is set by DOFORK */
58125a99e2eSeric 	if (pid < 0)
58225a99e2eSeric 	{
58325a99e2eSeric 		syserr("Cannot fork");
584f8952a83Seric 		(void) close(mpvect[0]);
585f8952a83Seric 		(void) close(mpvect[1]);
586c579ef51Seric 		if (clever)
587c579ef51Seric 		{
588c579ef51Seric 			(void) close(rpvect[0]);
589c579ef51Seric 			(void) close(rpvect[1]);
590c579ef51Seric 		}
59125a99e2eSeric 		return (-1);
59225a99e2eSeric 	}
59325a99e2eSeric 	else if (pid == 0)
59425a99e2eSeric 	{
59525a99e2eSeric 		/* child -- set up input & exec mailer */
59603ab8e55Seric 		/* make diagnostic output be standard output */
5978f0e7860Seric 		(void) signal(SIGINT, SIG_IGN);
5988f0e7860Seric 		(void) signal(SIGHUP, SIG_IGN);
5990984da9fSeric 		(void) signal(SIGTERM, SIG_DFL);
600f8952a83Seric 
601f8952a83Seric 		/* arrange to filter standard & diag output of command */
602c579ef51Seric 		if (clever)
603c579ef51Seric 		{
604c579ef51Seric 			(void) close(rpvect[0]);
605c579ef51Seric 			(void) close(1);
606c579ef51Seric 			(void) dup(rpvect[1]);
607c579ef51Seric 			(void) close(rpvect[1]);
608c579ef51Seric 		}
609c579ef51Seric 		else if (OutChannel != stdout)
610f8952a83Seric 		{
611f8952a83Seric 			(void) close(1);
612f8952a83Seric 			(void) dup(fileno(OutChannel));
613f8952a83Seric 		}
614db8841e9Seric 		(void) close(2);
615db8841e9Seric 		(void) dup(1);
616f8952a83Seric 
617f8952a83Seric 		/* arrange to get standard input */
618f8952a83Seric 		(void) close(mpvect[1]);
619db8841e9Seric 		(void) close(0);
620f8952a83Seric 		if (dup(mpvect[0]) < 0)
62125a99e2eSeric 		{
62225a99e2eSeric 			syserr("Cannot dup to zero!");
623a590b978Seric 			_exit(EX_OSERR);
62425a99e2eSeric 		}
625f8952a83Seric 		(void) close(mpvect[0]);
6262a6e0786Seric 		if (!bitset(M_RESTR, m->m_flags))
6270984da9fSeric 		{
628e36b99e2Seric 			if (ctladdr->q_uid == 0)
629e36b99e2Seric 			{
63069f29479Seric 				extern int DefUid, DefGid;
63169f29479Seric 
632e36b99e2Seric 				(void) setgid(DefGid);
633e36b99e2Seric 				(void) setuid(DefUid);
634e36b99e2Seric 			}
635e36b99e2Seric 			else
63669f29479Seric 			{
637e36b99e2Seric 				(void) setgid(ctladdr->q_gid);
638e36b99e2Seric 				(void) setuid(ctladdr->q_uid);
63969f29479Seric 			}
6400984da9fSeric 		}
641e374fd72Seric # ifndef VFORK
642e374fd72Seric 		/*
643e374fd72Seric 		**  We have to be careful with vfork - we can't mung up the
644e374fd72Seric 		**  memory but we don't want the mailer to inherit any extra
645e374fd72Seric 		**  open files.  Chances are the mailer won't
646e374fd72Seric 		**  care about an extra file, but then again you never know.
647e374fd72Seric 		**  Actually, we would like to close(fileno(pwf)), but it's
648e374fd72Seric 		**  declared static so we can't.  But if we fclose(pwf), which
649e374fd72Seric 		**  is what endpwent does, it closes it in the parent too and
650e374fd72Seric 		**  the next getpwnam will be slower.  If you have a weird
651e374fd72Seric 		**  mailer that chokes on the extra file you should do the
652e374fd72Seric 		**  endpwent().
653e374fd72Seric 		**
654e374fd72Seric 		**  Similar comments apply to log.  However, openlog is
655e374fd72Seric 		**  clever enough to set the FIOCLEX mode on the file,
656e374fd72Seric 		**  so it will be closed automatically on the exec.
657e374fd72Seric 		*/
658e374fd72Seric 
659e374fd72Seric 		endpwent();
66025a99e2eSeric # ifdef LOG
661f9fe028fSeric 		closelog();
66225a99e2eSeric # endif LOG
663e374fd72Seric # endif VFORK
66425a99e2eSeric 		execv(m->m_mailer, pvp);
66525a99e2eSeric 		/* syserr fails because log is closed */
66625a99e2eSeric 		/* syserr("Cannot exec %s", m->m_mailer); */
66732d19d43Seric 		printf("Cannot exec '%s' errno=%d\n", m->m_mailer, errno);
668db8841e9Seric 		(void) fflush(stdout);
669a590b978Seric 		_exit(EX_UNAVAILABLE);
67025a99e2eSeric 	}
67125a99e2eSeric 
672f8952a83Seric 	/*
673c579ef51Seric 	**  Set up return value.
674f8952a83Seric 	*/
675f8952a83Seric 
676f8952a83Seric 	(void) close(mpvect[0]);
677f8952a83Seric 	mfile = fdopen(mpvect[1], "w");
678c579ef51Seric 	if (clever)
67925a99e2eSeric 	{
680c579ef51Seric 		(void) close(rpvect[1]);
681c579ef51Seric 		rfile = fdopen(rpvect[0], "r");
68225a99e2eSeric 	}
683c579ef51Seric 
684c579ef51Seric 	*pmfile = mfile;
685c579ef51Seric 	*prfile = rfile;
686c579ef51Seric 
687c579ef51Seric 	return (pid);
68825a99e2eSeric }
68925a99e2eSeric /*
69025a99e2eSeric **  GIVERESPONSE -- Interpret an error response from a mailer
69125a99e2eSeric **
69225a99e2eSeric **	Parameters:
69325a99e2eSeric **		stat -- the status code from the mailer (high byte
69425a99e2eSeric **			only; core dumps must have been taken care of
69525a99e2eSeric **			already).
69625a99e2eSeric **		force -- if set, force an error message output, even
69725a99e2eSeric **			if the mailer seems to like to print its own
69825a99e2eSeric **			messages.
69925a99e2eSeric **		m -- the mailer descriptor for this mailer.
70025a99e2eSeric **
70125a99e2eSeric **	Returns:
702db8841e9Seric **		none.
70325a99e2eSeric **
70425a99e2eSeric **	Side Effects:
705c1f9df2cSeric **		Errors may be incremented.
70625a99e2eSeric **		ExitStat may be set.
70725a99e2eSeric */
70825a99e2eSeric 
70925a99e2eSeric giveresponse(stat, force, m)
71025a99e2eSeric 	int stat;
71125a99e2eSeric 	int force;
71225a99e2eSeric 	register struct mailer *m;
71325a99e2eSeric {
71425a99e2eSeric 	register char *statmsg;
71525a99e2eSeric 	extern char *SysExMsg[];
71625a99e2eSeric 	register int i;
71725a99e2eSeric 	extern int N_SysEx;
71829dd97a3Seric 	char buf[30];
71925a99e2eSeric 
72013bbc08cSeric 	/*
72113bbc08cSeric 	**  Compute status message from code.
72213bbc08cSeric 	*/
72313bbc08cSeric 
72425a99e2eSeric 	i = stat - EX__BASE;
72525a99e2eSeric 	if (i < 0 || i > N_SysEx)
72625a99e2eSeric 		statmsg = NULL;
72725a99e2eSeric 	else
72825a99e2eSeric 		statmsg = SysExMsg[i];
72925a99e2eSeric 	if (stat == 0)
730c38ba59cSeric 	{
7316cbfa739Seric 		if (bitset(M_LOCAL, m->m_flags))
7323efaed6eSeric 			statmsg = "delivered";
7333efaed6eSeric 		else
7343efaed6eSeric 			statmsg = "queued";
735c38ba59cSeric 		if (Verbose)
736544026c5Seric 			message(Arpa_Info, statmsg);
737c38ba59cSeric 	}
738c77d1c25Seric 	else if (stat == EX_TEMPFAIL)
739c77d1c25Seric 	{
740c77d1c25Seric 		if (Verbose)
741c77d1c25Seric 			message(Arpa_Info, "transmission deferred");
742c77d1c25Seric 	}
74325a99e2eSeric 	else
74425a99e2eSeric 	{
745c1f9df2cSeric 		Errors++;
74625a99e2eSeric 		if (statmsg == NULL && m->m_badstat != 0)
74725a99e2eSeric 		{
74825a99e2eSeric 			stat = m->m_badstat;
74925a99e2eSeric 			i = stat - EX__BASE;
75025a99e2eSeric # ifdef DEBUG
75125a99e2eSeric 			if (i < 0 || i >= N_SysEx)
75225a99e2eSeric 				syserr("Bad m_badstat %d", stat);
75325a99e2eSeric 			else
75425a99e2eSeric # endif DEBUG
75525a99e2eSeric 			statmsg = SysExMsg[i];
75625a99e2eSeric 		}
75725a99e2eSeric 		if (statmsg == NULL)
75825a99e2eSeric 			usrerr("unknown mailer response %d", stat);
759c38ba59cSeric 		else if (force || !bitset(M_QUIET, m->m_flags) || Verbose)
76025a99e2eSeric 			usrerr("%s", statmsg);
76125a99e2eSeric 	}
76225a99e2eSeric 
76325a99e2eSeric 	/*
76425a99e2eSeric 	**  Final cleanup.
76525a99e2eSeric 	**	Log a record of the transaction.  Compute the new
76625a99e2eSeric 	**	ExitStat -- if we already had an error, stick with
76725a99e2eSeric 	**	that.
76825a99e2eSeric 	*/
76925a99e2eSeric 
77025a99e2eSeric 	if (statmsg == NULL)
77129dd97a3Seric 	{
772db8841e9Seric 		(void) sprintf(buf, "error %d", stat);
77329dd97a3Seric 		statmsg = buf;
77429dd97a3Seric 	}
77529dd97a3Seric 
77629dd97a3Seric # ifdef LOG
777e374fd72Seric 	syslog(LOG_INFO, "%s->%s: %ld: %s", From.q_paddr, To, MsgSize, statmsg);
77825a99e2eSeric # endif LOG
779c77d1c25Seric 	if (stat != EX_TEMPFAIL)
780243921eeSeric 		setstat(stat);
78125a99e2eSeric }
78225a99e2eSeric /*
7836328bdf7Seric **  PUTMESSAGE -- output a message to the final mailer.
78425a99e2eSeric **
7856328bdf7Seric **	This routine takes care of recreating the header from the
7866328bdf7Seric **	in-core copy, etc.
78725a99e2eSeric **
78825a99e2eSeric **	Parameters:
7896328bdf7Seric **		fp -- file to output onto.
7906328bdf7Seric **		m -- a mailer descriptor.
791c579ef51Seric **		xdot -- if set, hide lines beginning with dot.
79225a99e2eSeric **
79325a99e2eSeric **	Returns:
7946328bdf7Seric **		none.
79525a99e2eSeric **
79625a99e2eSeric **	Side Effects:
7976328bdf7Seric **		The message is written onto fp.
79825a99e2eSeric */
79925a99e2eSeric 
800c579ef51Seric putmessage(fp, m, xdot)
8016328bdf7Seric 	FILE *fp;
8026328bdf7Seric 	struct mailer *m;
803c579ef51Seric 	bool xdot;
80425a99e2eSeric {
8056328bdf7Seric 	char buf[BUFSIZ];
80613bbc08cSeric 	register HDR *h;
8076328bdf7Seric 	extern char *arpadate();
8086328bdf7Seric 	bool anyheader = FALSE;
809e9ff65b0Seric 	extern char *capitalize();
81087bd9a02Seric 	extern char *hvalue();
81187bd9a02Seric 	extern bool samefrom();
812894de7daSeric 	char *of_line;
81325a99e2eSeric 
81413bbc08cSeric 	/*
81513bbc08cSeric 	**  Output "From" line unless supressed
816a36e30c9Seric 	**
817a36e30c9Seric 	**  >>>>>>>>>>	One of the ugliest hacks seen by human eyes is
818a36e30c9Seric 	**  >>>>>>>>>>	contained herein: UUCP wants those stupid
819a36e30c9Seric 	**  >> NOTE >>	"remote from <host>" lines.  Why oh why does a
820a36e30c9Seric 	**  >>>>>>>>>>	well-meaning programmer such as myself have to
821a36e30c9Seric 	**  >>>>>>>>>>	deal with this kind of antique garbage????
82213bbc08cSeric 	*/
82313bbc08cSeric 
82440e4ab56Seric 	if (!bitset(M_NHDR, m->m_flags))
8251412cc7cSeric 	{
826*62667331Seric 		register char *p = rindex(m->m_mailer, '/');
827*62667331Seric 
828*62667331Seric 		if (p != NULL && strcmp(p, "/uux") == 0 &&
829*62667331Seric 		    strcmp(m->m_name, "uucp") == 0)
830a36e30c9Seric 			(void) expand("From $f  $d remote from $h", buf,
831a36e30c9Seric 					&buf[sizeof buf - 1]);
832a36e30c9Seric 		else
8331412cc7cSeric 			(void) expand("$l", buf, &buf[sizeof buf - 1]);
8341412cc7cSeric 		fprintf(fp, "%s\n", buf);
8351412cc7cSeric 	}
83640e4ab56Seric 
83713bbc08cSeric 	/*
83813bbc08cSeric 	**  Output all header lines
83913bbc08cSeric 	*/
84013bbc08cSeric 
841894de7daSeric 	of_line = hvalue("original-from");
8426328bdf7Seric 	for (h = Header; h != NULL; h = h->h_link)
8436328bdf7Seric 	{
84413bbc08cSeric 		register char *p;
84587bd9a02Seric 		char *origfrom = OrigFrom;
846894de7daSeric 		bool nooutput;
84713bbc08cSeric 
848894de7daSeric 		nooutput = FALSE;
8499e9163a0Seric 		if (bitset(H_CHECK|H_ACHECK, h->h_flags) && !bitset(h->h_mflags, m->m_flags))
8505a0dcb5fSeric 		{
8515a0dcb5fSeric 			p = ")><(";		/* can't happen (I hope) */
852894de7daSeric 			nooutput = TRUE;
8535a0dcb5fSeric 		}
854894de7daSeric 
855894de7daSeric 		/* use From: line from message if generated is the same */
85687bd9a02Seric 		if (strcmp(h->h_field, "from") == 0 && origfrom != NULL &&
857894de7daSeric 		    strcmp(m->m_from, "$f") == 0 && of_line == NULL)
85887bd9a02Seric 		{
85987bd9a02Seric 			p = origfrom;
86087bd9a02Seric 			origfrom = NULL;
86187bd9a02Seric 		}
86287bd9a02Seric 		else if (bitset(H_DEFAULT, h->h_flags))
8634ae18d0eSeric 		{
864db8841e9Seric 			(void) expand(h->h_value, buf, &buf[sizeof buf]);
8654ae18d0eSeric 			p = buf;
8664ae18d0eSeric 		}
8674ae18d0eSeric 		else
8684ae18d0eSeric 			p = h->h_value;
869894de7daSeric 		if (p == NULL || *p == '\0')
8709e9163a0Seric 			continue;
8715a0dcb5fSeric 
8725a0dcb5fSeric 		/* hack, hack -- output Original-From field if different */
873894de7daSeric 		if (strcmp(h->h_field, "from") == 0 && origfrom != NULL)
874894de7daSeric 		{
875894de7daSeric 			/* output new Original-From line if needed */
876894de7daSeric 			if (of_line == NULL && !samefrom(p, origfrom))
8775a0dcb5fSeric 			{
87887bd9a02Seric 				fprintf(fp, "Original-From: %s\n", origfrom);
87987bd9a02Seric 				anyheader = TRUE;
8805a0dcb5fSeric 			}
881894de7daSeric 			if (of_line != NULL && !nooutput && samefrom(p, of_line))
882894de7daSeric 			{
883894de7daSeric 				/* delete Original-From: line if redundant */
884894de7daSeric 				p = of_line;
885894de7daSeric 				of_line = NULL;
886894de7daSeric 			}
887894de7daSeric 		}
888894de7daSeric 		else if (strcmp(h->h_field, "original-from") == 0 && of_line == NULL)
889894de7daSeric 			nooutput = TRUE;
890894de7daSeric 
891894de7daSeric 		/* finally, output the header line */
892894de7daSeric 		if (!nooutput)
893894de7daSeric 		{
894894de7daSeric 			fprintf(fp, "%s: %s\n", capitalize(h->h_field), p);
895894de7daSeric 			h->h_flags |= H_USED;
896894de7daSeric 			anyheader = TRUE;
897894de7daSeric 		}
8986328bdf7Seric 	}
8996328bdf7Seric 	if (anyheader)
9006328bdf7Seric 		fprintf(fp, "\n");
9016328bdf7Seric 
90213bbc08cSeric 	/*
90313bbc08cSeric 	**  Output the body of the message
90413bbc08cSeric 	*/
90513bbc08cSeric 
90678442df3Seric 	if (TempFile != NULL)
90778442df3Seric 	{
908b7902a1dSeric 		rewind(TempFile);
909c579ef51Seric 		while (!ferror(fp) && fgets(buf, sizeof buf, TempFile) != NULL)
910c579ef51Seric 			fprintf(fp, "%s%s", xdot && buf[0] == '.' ? "." : "", buf);
9116328bdf7Seric 
91278442df3Seric 		if (ferror(TempFile))
91378442df3Seric 		{
91478442df3Seric 			syserr("putmessage: read error");
91578442df3Seric 			setstat(EX_IOERR);
91678442df3Seric 		}
91778442df3Seric 	}
91878442df3Seric 
919c77d1c25Seric 	(void) fflush(fp);
92054aa2b0fSeric 	if (ferror(fp) && errno != EPIPE)
92125a99e2eSeric 	{
9226328bdf7Seric 		syserr("putmessage: write error");
92325a99e2eSeric 		setstat(EX_IOERR);
92425a99e2eSeric 	}
92554aa2b0fSeric 	errno = 0;
92625a99e2eSeric }
92725a99e2eSeric /*
92887bd9a02Seric **  SAMEFROM -- tell if two text addresses represent the same from address.
92987bd9a02Seric **
93087bd9a02Seric **	Parameters:
93187bd9a02Seric **		ifrom -- internally generated form of from address.
93287bd9a02Seric **		efrom -- external form of from address.
93387bd9a02Seric **
93487bd9a02Seric **	Returns:
93587bd9a02Seric **		TRUE -- if they convey the same info.
93687bd9a02Seric **		FALSE -- if any information has been lost.
93787bd9a02Seric **
93887bd9a02Seric **	Side Effects:
93987bd9a02Seric **		none.
94087bd9a02Seric */
94187bd9a02Seric 
94287bd9a02Seric bool
94387bd9a02Seric samefrom(ifrom, efrom)
94487bd9a02Seric 	char *ifrom;
94587bd9a02Seric 	char *efrom;
94687bd9a02Seric {
947894de7daSeric 	register char *p;
948894de7daSeric 	char buf[MAXNAME + 4];
949894de7daSeric 
950894de7daSeric # ifdef DEBUG
951894de7daSeric 	if (Debug > 7)
952894de7daSeric 		printf("samefrom(%s,%s)-->", ifrom, efrom);
953894de7daSeric # endif DEBUG
954894de7daSeric 	if (strcmp(ifrom, efrom) == 0)
955894de7daSeric 		goto success;
956894de7daSeric 	p = index(ifrom, '@');
957894de7daSeric 	if (p == NULL)
958894de7daSeric 		goto failure;
959894de7daSeric 	*p = '\0';
960894de7daSeric 	strcpy(buf, ifrom);
961894de7daSeric 	strcat(buf, " at ");
962894de7daSeric 	*p++ = '@';
963894de7daSeric 	strcat(buf, p);
964894de7daSeric 	if (strcmp(buf, efrom) == 0)
965894de7daSeric 		goto success;
966894de7daSeric 
967894de7daSeric   failure:
968894de7daSeric # ifdef DEBUG
969894de7daSeric 	if (Debug > 7)
970894de7daSeric 		printf("FALSE\n");
971894de7daSeric # endif DEBUG
972894de7daSeric 	return (FALSE);
973894de7daSeric 
974894de7daSeric   success:
975894de7daSeric # ifdef DEBUG
976894de7daSeric 	if (Debug > 7)
977894de7daSeric 		printf("TRUE\n");
978894de7daSeric # endif DEBUG
979894de7daSeric 	return (TRUE);
98087bd9a02Seric }
98187bd9a02Seric /*
98225a99e2eSeric **  MAILFILE -- Send a message to a file.
98325a99e2eSeric **
984f129ec7dSeric **	If the file has the setuid/setgid bits set, but NO execute
985f129ec7dSeric **	bits, sendmail will try to become the owner of that file
986f129ec7dSeric **	rather than the real user.  Obviously, this only works if
987f129ec7dSeric **	sendmail runs as root.
988f129ec7dSeric **
98925a99e2eSeric **	Parameters:
99025a99e2eSeric **		filename -- the name of the file to send to.
9916259796dSeric **		ctladdr -- the controlling address header -- includes
9926259796dSeric **			the userid/groupid to be when sending.
99325a99e2eSeric **
99425a99e2eSeric **	Returns:
99525a99e2eSeric **		The exit code associated with the operation.
99625a99e2eSeric **
99725a99e2eSeric **	Side Effects:
99825a99e2eSeric **		none.
99925a99e2eSeric */
100025a99e2eSeric 
10016259796dSeric mailfile(filename, ctladdr)
100225a99e2eSeric 	char *filename;
10036259796dSeric 	ADDRESS *ctladdr;
100425a99e2eSeric {
100525a99e2eSeric 	register FILE *f;
100632d19d43Seric 	register int pid;
100725a99e2eSeric 
100832d19d43Seric 	/*
100932d19d43Seric 	**  Fork so we can change permissions here.
101032d19d43Seric 	**	Note that we MUST use fork, not vfork, because of
101132d19d43Seric 	**	the complications of calling subroutines, etc.
101232d19d43Seric 	*/
101332d19d43Seric 
101432d19d43Seric 	DOFORK(fork);
101532d19d43Seric 
101632d19d43Seric 	if (pid < 0)
101732d19d43Seric 		return (EX_OSERR);
101832d19d43Seric 	else if (pid == 0)
101932d19d43Seric 	{
102032d19d43Seric 		/* child -- actually write to file */
1021f129ec7dSeric 		struct stat stb;
1022e36b99e2Seric 		extern int DefUid, DefGid;
1023f129ec7dSeric 
10240984da9fSeric 		(void) signal(SIGINT, SIG_DFL);
10250984da9fSeric 		(void) signal(SIGHUP, SIG_DFL);
10260984da9fSeric 		(void) signal(SIGTERM, SIG_DFL);
1027f129ec7dSeric 		umask(OldUmask);
1028f129ec7dSeric 		if (stat(filename, &stb) < 0)
1029e6e1265fSeric 			stb.st_mode = 0666;
1030f129ec7dSeric 		if (bitset(0111, stb.st_mode))
1031f129ec7dSeric 			exit(EX_CANTCREAT);
103203827b5fSeric 		if (ctladdr == NULL)
103303827b5fSeric 			ctladdr = &From;
1034f129ec7dSeric 		if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0)
1035e36b99e2Seric 		{
1036e36b99e2Seric 			if (ctladdr->q_uid == 0)
1037e36b99e2Seric 				(void) setgid(DefGid);
1038e36b99e2Seric 			else
10396259796dSeric 				(void) setgid(ctladdr->q_gid);
1040e36b99e2Seric 		}
1041f129ec7dSeric 		if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0)
1042e36b99e2Seric 		{
1043e36b99e2Seric 			if (ctladdr->q_uid == 0)
1044e36b99e2Seric 				(void) setuid(DefUid);
1045e36b99e2Seric 			else
10466259796dSeric 				(void) setuid(ctladdr->q_uid);
1047e36b99e2Seric 		}
104825a99e2eSeric 		f = fopen(filename, "a");
104925a99e2eSeric 		if (f == NULL)
105032d19d43Seric 			exit(EX_CANTCREAT);
105125a99e2eSeric 
1052c579ef51Seric 		putmessage(f, Mailer[1], FALSE);
105325a99e2eSeric 		fputs("\n", f);
1054db8841e9Seric 		(void) fclose(f);
105532d19d43Seric 		(void) fflush(stdout);
1056e36b99e2Seric 
1057e36b99e2Seric 		/* reset ISUID & ISGID bits */
1058c77d1c25Seric 		(void) chmod(filename, (int) stb.st_mode);
105932d19d43Seric 		exit(EX_OK);
106013bbc08cSeric 		/*NOTREACHED*/
106132d19d43Seric 	}
106232d19d43Seric 	else
106332d19d43Seric 	{
106432d19d43Seric 		/* parent -- wait for exit status */
106532d19d43Seric 		register int i;
106632d19d43Seric 		auto int stat;
106732d19d43Seric 
106832d19d43Seric 		while ((i = wait(&stat)) != pid)
106932d19d43Seric 		{
107032d19d43Seric 			if (i < 0)
107132d19d43Seric 			{
107232d19d43Seric 				stat = EX_OSERR << 8;
107332d19d43Seric 				break;
107432d19d43Seric 			}
107532d19d43Seric 		}
10760984da9fSeric 		if ((stat & 0377) != 0)
10770984da9fSeric 			stat = EX_UNAVAILABLE << 8;
107832d19d43Seric 		return ((stat >> 8) & 0377);
107932d19d43Seric 	}
108025a99e2eSeric }
1081ea4dc939Seric /*
1082ea4dc939Seric **  SENDALL -- actually send all the messages.
1083ea4dc939Seric **
1084ea4dc939Seric **	Parameters:
1085ea4dc939Seric **		verifyonly -- if set, only give verification messages.
1086ea4dc939Seric **
1087ea4dc939Seric **	Returns:
1088ea4dc939Seric **		none.
1089ea4dc939Seric **
1090ea4dc939Seric **	Side Effects:
1091ea4dc939Seric **		Scans the send lists and sends everything it finds.
1092ea4dc939Seric */
1093ea4dc939Seric 
1094ea4dc939Seric sendall(verifyonly)
1095ea4dc939Seric 	bool verifyonly;
1096ea4dc939Seric {
1097e77e673fSeric 	register ADDRESS *q;
1098ea4dc939Seric 	typedef int (*fnptr)();
1099ea4dc939Seric 
1100772e6e50Seric # ifdef DEBUG
1101772e6e50Seric 	if (Debug > 1)
1102772e6e50Seric 	{
1103772e6e50Seric 		printf("\nSendQueue:\n");
1104772e6e50Seric 		printaddr(SendQueue, TRUE);
1105772e6e50Seric 	}
1106772e6e50Seric # endif DEBUG
1107ea4dc939Seric 
1108e77e673fSeric 	for (q = SendQueue; q != NULL; q = q->q_next)
1109ea4dc939Seric 	{
1110ea4dc939Seric 		if (verifyonly)
1111ea4dc939Seric 		{
1112ea4dc939Seric 			To = q->q_paddr;
1113e77e673fSeric 			if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
1114ea4dc939Seric 			{
11157da1035fSeric 				if (bitset(M_LOCAL, q->q_mailer->m_flags))
1116ea4dc939Seric 					message(Arpa_Info, "deliverable");
1117ea4dc939Seric 				else
1118ea4dc939Seric 					message(Arpa_Info, "queueable");
1119ea4dc939Seric 			}
1120ea4dc939Seric 		}
1121ea4dc939Seric 		else
1122ea4dc939Seric 			(void) deliver(q, (fnptr) NULL);
1123ea4dc939Seric 	}
1124ea4dc939Seric }
1125