122716Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
362532Sbostic  * Copyright (c) 1988, 1993
462532Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642831Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822716Sdist 
933731Sbostic # include "sendmail.h"
1022716Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*67880Seric static char sccsid[] = "@(#)usersmtp.c	8.23 (Berkeley) 11/04/94 (with SMTP)";
1433731Sbostic #else
15*67880Seric static char sccsid[] = "@(#)usersmtp.c	8.23 (Berkeley) 11/04/94 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
194684Seric # include <sysexits.h>
2021065Seric # include <errno.h>
214684Seric 
2233731Sbostic # ifdef SMTP
234684Seric 
244684Seric /*
259391Seric **  USERSMTP -- run SMTP protocol from the user end.
269391Seric **
279391Seric **	This protocol is described in RFC821.
289391Seric */
299391Seric 
309391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
319391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
329391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
339391Seric 
3414900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
3510054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
3621065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
3710054Seric int	SmtpPid;			/* pid of mailer */
3864071Seric bool	SmtpNeedIntro;			/* need "while talking" in transcript */
3958671Seric 
4058671Seric #ifdef __STDC__
4158671Seric extern	smtpmessage(char *f, MAILER *m, MCI *mci, ...);
4258671Seric #endif
439391Seric /*
444865Seric **  SMTPINIT -- initialize SMTP.
454684Seric **
464865Seric **	Opens the connection and sends the initial protocol.
474684Seric **
484684Seric **	Parameters:
494865Seric **		m -- mailer to create connection to.
504865Seric **		pvp -- pointer to parameter vector to pass to
514865Seric **			the mailer.
524684Seric **
534684Seric **	Returns:
5454967Seric **		none.
554684Seric **
564684Seric **	Side Effects:
574865Seric **		creates connection and sends initial protocol.
584684Seric */
594684Seric 
6054967Seric smtpinit(m, mci, e)
614865Seric 	struct mailer *m;
6254967Seric 	register MCI *mci;
6353751Seric 	ENVELOPE *e;
644684Seric {
654865Seric 	register int r;
6658957Seric 	register char *p;
6760210Seric 	extern void esmtp_check();
6859285Seric 	extern void helo_options();
694684Seric 
7063753Seric 	if (tTd(18, 1))
7157379Seric 	{
7257379Seric 		printf("smtpinit ");
7364731Seric 		mci_dump(mci, FALSE);
7457379Seric 	}
7557379Seric 
764865Seric 	/*
774865Seric 	**  Open the connection to the mailer.
784865Seric 	*/
794684Seric 
8021065Seric 	SmtpError[0] = '\0';
8157379Seric 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
8264071Seric 	SmtpNeedIntro = TRUE;
8354967Seric 	switch (mci->mci_state)
846051Seric 	{
8554967Seric 	  case MCIS_ACTIVE:
8654967Seric 		/* need to clear old information */
8754967Seric 		smtprset(m, mci, e);
8857734Seric 		/* fall through */
8915139Seric 
9054967Seric 	  case MCIS_OPEN:
9154967Seric 		return;
9254967Seric 
9354967Seric 	  case MCIS_ERROR:
9454967Seric 	  case MCIS_SSD:
9554967Seric 		/* shouldn't happen */
9654967Seric 		smtpquit(m, mci, e);
9757734Seric 		/* fall through */
9854967Seric 
9954967Seric 	  case MCIS_CLOSED:
10058151Seric 		syserr("451 smtpinit: state CLOSED");
10154967Seric 		return;
10254967Seric 
10354967Seric 	  case MCIS_OPENING:
10454967Seric 		break;
1056051Seric 	}
1064796Seric 
10754967Seric 	mci->mci_state = MCIS_OPENING;
10854967Seric 
1094865Seric 	/*
1104865Seric 	**  Get the greeting message.
11114913Seric 	**	This should appear spontaneously.  Give it five minutes to
11214886Seric 	**	happen.
1134865Seric 	*/
1144797Seric 
11561093Seric 	SmtpPhase = mci->mci_phase = "client greeting";
11653751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
11760210Seric 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
11864750Seric 	if (r < 0 || REPLYTYPE(r) == 4)
11952104Seric 		goto tempfail1;
12064750Seric 	if (REPLYTYPE(r) != 2)
12164750Seric 		goto unavailable;
1224684Seric 
1234865Seric 	/*
1244976Seric 	**  Send the HELO command.
1257963Seric 	**	My mother taught me to always introduce myself.
1264976Seric 	*/
1274976Seric 
12859285Seric 	if (bitnset(M_ESMTP, m->m_flags))
12959285Seric 		mci->mci_flags |= MCIF_ESMTP;
13059285Seric 
13159285Seric tryhelo:
13259285Seric 	if (bitset(MCIF_ESMTP, mci->mci_flags))
13359285Seric 	{
13459285Seric 		smtpmessage("EHLO %s", m, mci, MyHostName);
13561093Seric 		SmtpPhase = mci->mci_phase = "client EHLO";
13659285Seric 	}
13759285Seric 	else
13859285Seric 	{
13959285Seric 		smtpmessage("HELO %s", m, mci, MyHostName);
14061093Seric 		SmtpPhase = mci->mci_phase = "client HELO";
14159285Seric 	}
14253751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
14359285Seric 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1448005Seric 	if (r < 0)
14552104Seric 		goto tempfail1;
1468005Seric 	else if (REPLYTYPE(r) == 5)
14759285Seric 	{
14859285Seric 		if (bitset(MCIF_ESMTP, mci->mci_flags))
14959285Seric 		{
15059285Seric 			/* try old SMTP instead */
15159285Seric 			mci->mci_flags &= ~MCIF_ESMTP;
15259285Seric 			goto tryhelo;
15359285Seric 		}
15414913Seric 		goto unavailable;
15559285Seric 	}
1567963Seric 	else if (REPLYTYPE(r) != 2)
15752104Seric 		goto tempfail1;
1584976Seric 
1594976Seric 	/*
16058957Seric 	**  Check to see if we actually ended up talking to ourself.
16158957Seric 	**  This means we didn't know about an alias or MX, or we managed
16258957Seric 	**  to connect to an echo server.
16358957Seric 	*/
16458957Seric 
16559026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16658957Seric 	if (p != NULL)
16761707Seric 		*p = '\0';
16867472Seric 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
16967472Seric 	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
17058957Seric 	{
17158957Seric 		syserr("553 %s config error: mail loops back to myself",
17258957Seric 			MyHostName);
17358957Seric 		mci->mci_exitstat = EX_CONFIG;
17458957Seric 		mci->mci_errno = 0;
17558957Seric 		smtpquit(m, mci, e);
17658957Seric 		return;
17758957Seric 	}
17858957Seric 
17958957Seric 	/*
1809315Seric 	**  If this is expected to be another sendmail, send some internal
1819315Seric 	**  commands.
1829315Seric 	*/
1839315Seric 
18410688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1859315Seric 	{
1869315Seric 		/* tell it to be verbose */
18753751Seric 		smtpmessage("VERB", m, mci);
18859285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1899315Seric 		if (r < 0)
19052104Seric 			goto tempfail2;
1919315Seric 	}
1929315Seric 
19365057Seric 	if (mci->mci_state != MCIS_CLOSED)
19465057Seric 	{
19565057Seric 		mci->mci_state = MCIS_OPEN;
19665057Seric 		return;
19765057Seric 	}
19853751Seric 
19965057Seric 	/* got a 421 error code during startup */
20065057Seric 
20153751Seric   tempfail1:
20253751Seric   tempfail2:
20353751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
20457379Seric 	if (mci->mci_errno == 0)
20557379Seric 		mci->mci_errno = errno;
20657379Seric 	if (mci->mci_state != MCIS_CLOSED)
20757379Seric 		smtpquit(m, mci, e);
20854967Seric 	return;
20953751Seric 
21053751Seric   unavailable:
21153751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
21253751Seric 	mci->mci_errno = errno;
21353751Seric 	smtpquit(m, mci, e);
21454967Seric 	return;
21553751Seric }
21659285Seric /*
21760210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
21860210Seric **
21960210Seric **
22060210Seric **	Parameters:
22160210Seric **		line -- the response line.
22260210Seric **		m -- the mailer.
22360210Seric **		mci -- the mailer connection info.
22460210Seric **		e -- the envelope.
22560210Seric **
22660210Seric **	Returns:
22760210Seric **		none.
22860210Seric */
22960210Seric 
23060210Seric void
23160210Seric esmtp_check(line, m, mci, e)
23260210Seric 	char *line;
23360210Seric 	MAILER *m;
23460210Seric 	register MCI *mci;
23560210Seric 	ENVELOPE *e;
23660210Seric {
23760210Seric 	if (strlen(line) < 5)
23860210Seric 		return;
23960210Seric 	line += 4;
24060210Seric 	if (strncmp(line, "ESMTP ", 6) == 0)
24160210Seric 		mci->mci_flags |= MCIF_ESMTP;
24260210Seric }
24360210Seric /*
24459285Seric **  HELO_OPTIONS -- process the options on a HELO line.
24559285Seric **
24659285Seric **	Parameters:
24759285Seric **		line -- the response line.
24859285Seric **		m -- the mailer.
24959285Seric **		mci -- the mailer connection info.
25059285Seric **		e -- the envelope.
25159285Seric **
25259285Seric **	Returns:
25359285Seric **		none.
25459285Seric */
25553751Seric 
25659285Seric void
25759285Seric helo_options(line, m, mci, e)
25859285Seric 	char *line;
25959285Seric 	MAILER *m;
26059285Seric 	register MCI *mci;
26159285Seric 	ENVELOPE *e;
26259285Seric {
26359285Seric 	register char *p;
26459285Seric 
26559285Seric 	if (strlen(line) < 5)
26659285Seric 		return;
26759285Seric 	line += 4;
26859285Seric 	p = strchr(line, ' ');
26959285Seric 	if (p != NULL)
27059285Seric 		*p++ = '\0';
27159285Seric 	if (strcasecmp(line, "size") == 0)
27259285Seric 	{
27359285Seric 		mci->mci_flags |= MCIF_SIZE;
27459285Seric 		if (p != NULL)
27559285Seric 			mci->mci_maxsize = atol(p);
27659285Seric 	}
27759285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
27865870Seric 	{
27959285Seric 		mci->mci_flags |= MCIF_8BITMIME;
28065870Seric 		mci->mci_flags &= ~MCIF_7BIT;
28165870Seric 	}
28259285Seric 	else if (strcasecmp(line, "expn") == 0)
28359285Seric 		mci->mci_flags |= MCIF_EXPN;
284*67880Seric 	else if (strcasecmp(line, "dsn") == 0)
285*67880Seric 		mci->mci_flags |= MCIF_DSN;
28659285Seric }
28759285Seric /*
28859285Seric **  SMTPMAILFROM -- send MAIL command
28959285Seric **
29059285Seric **	Parameters:
29159285Seric **		m -- the mailer.
29259285Seric **		mci -- the mailer connection structure.
29359285Seric **		e -- the envelope (including the sender to specify).
29459285Seric */
29559285Seric 
29653751Seric smtpmailfrom(m, mci, e)
29753751Seric 	struct mailer *m;
29854967Seric 	MCI *mci;
29953751Seric 	ENVELOPE *e;
30053751Seric {
30153751Seric 	int r;
30265494Seric 	char *bufp;
30353751Seric 	char buf[MAXNAME];
30459285Seric 	char optbuf[MAXLINE];
30553751Seric 
30663753Seric 	if (tTd(18, 2))
30757943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
30857943Seric 
30959285Seric 	/* set up appropriate options to include */
31064254Seric 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
31159285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
31259285Seric 	else
31359285Seric 		strcpy(optbuf, "");
31459285Seric 
31567417Seric 	if (e->e_bodytype != NULL)
31667417Seric 	{
31767417Seric 		if (bitset(MCIF_8BITMIME, mci->mci_flags))
31867417Seric 		{
31967417Seric 			strcat(optbuf, " BODY=");
32067417Seric 			strcat(optbuf, e->e_bodytype);
32167417Seric 		}
32267546Seric 		else if (!bitset(MM_CVTMIME, MimeMode) &&
32367546Seric 			 strcasecmp(e->e_bodytype, "7bit") != 0)
32467417Seric 		{
32567417Seric 			/* cannot just send a 7-bit version */
32667417Seric 			usrerr("%s does not support 8BITMIME", mci->mci_host);
32767417Seric 			return EX_DATAERR;
32867417Seric 		}
32967417Seric 	}
33067417Seric 
331*67880Seric 	if (e->e_envid != NULL && bitset(MCIF_DSN, mci->mci_flags))
332*67880Seric 	{
333*67880Seric 		strcat(optbuf, " ENVID=");
334*67880Seric 		strcat(optbuf, e->e_envid);
335*67880Seric 	}
336*67880Seric 
3379315Seric 	/*
3384865Seric 	**  Send the MAIL command.
3394865Seric 	**	Designates the sender.
3404865Seric 	*/
3414796Seric 
34253751Seric 	mci->mci_state = MCIS_ACTIVE;
34353751Seric 
34459540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
34559540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
34658680Seric 		(void) strcpy(buf, "");
34758680Seric 	else
34858680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
34965494Seric 	if (buf[0] == '<')
35065494Seric 	{
35165494Seric 		/* strip off <angle brackets> (put back on below) */
35265494Seric 		bufp = &buf[strlen(buf) - 1];
35365494Seric 		if (*bufp == '>')
35465494Seric 			*bufp = '\0';
35565494Seric 		bufp = &buf[1];
35665494Seric 	}
35765494Seric 	else
35865494Seric 		bufp = buf;
35967472Seric 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
36010688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
3618436Seric 	{
36265494Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
3638436Seric 	}
3648436Seric 	else
3658436Seric 	{
36659285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
36765494Seric 			*bufp == '@' ? ',' : ':', bufp, optbuf);
3688436Seric 	}
36961093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
37053751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
37159285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
3728005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
37353751Seric 	{
37453751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
37553751Seric 		mci->mci_errno = errno;
37653751Seric 		smtpquit(m, mci, e);
37753751Seric 		return EX_TEMPFAIL;
37853751Seric 	}
3797963Seric 	else if (r == 250)
38053751Seric 	{
38153751Seric 		mci->mci_exitstat = EX_OK;
38253751Seric 		return EX_OK;
38353751Seric 	}
3847963Seric 	else if (r == 552)
38553751Seric 	{
38653751Seric 		/* signal service unavailable */
38753751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
38853751Seric 		smtpquit(m, mci, e);
38953751Seric 		return EX_UNAVAILABLE;
39053751Seric 	}
39114913Seric 
39258008Seric #ifdef LOG
39358020Seric 	if (LogLevel > 1)
39458008Seric 	{
39567860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s",
39667860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
39758008Seric 	}
39858008Seric #endif
39958008Seric 
40014913Seric 	/* protocol error -- close up */
40153751Seric 	smtpquit(m, mci, e);
40253751Seric 	mci->mci_exitstat = EX_PROTOCOL;
40353751Seric 	return EX_PROTOCOL;
4044684Seric }
4054684Seric /*
4064976Seric **  SMTPRCPT -- designate recipient.
4074797Seric **
4084797Seric **	Parameters:
4094865Seric **		to -- address of recipient.
41010175Seric **		m -- the mailer we are sending to.
41157379Seric **		mci -- the connection info for this transaction.
41257379Seric **		e -- the envelope for this transaction.
4134797Seric **
4144797Seric **	Returns:
4154865Seric **		exit status corresponding to recipient status.
4164797Seric **
4174797Seric **	Side Effects:
4184865Seric **		Sends the mail via SMTP.
4194797Seric */
4204797Seric 
42153751Seric smtprcpt(to, m, mci, e)
4224865Seric 	ADDRESS *to;
42310175Seric 	register MAILER *m;
42454967Seric 	MCI *mci;
42553751Seric 	ENVELOPE *e;
4264797Seric {
4274797Seric 	register int r;
428*67880Seric 	char optbuf[MAXLINE];
4294797Seric 
430*67880Seric 	strcpy(optbuf, "");
431*67880Seric 	if (bitset(MCIF_DSN, mci->mci_flags))
432*67880Seric 	{
433*67880Seric 		strcat(optbuf, " NOTIFY=");
434*67880Seric 		if (bitset(QPINGONFAILURE, to->q_flags))
435*67880Seric 		{
436*67880Seric 			if (bitset(QPINGONSUCCESS, to->q_flags))
437*67880Seric 				strcat(optbuf, "ALWAYS");
438*67880Seric 			else
439*67880Seric 				strcat(optbuf, "FAILURE");
440*67880Seric 		}
441*67880Seric 		else
442*67880Seric 		{
443*67880Seric 			if (bitset(QPINGONSUCCESS, to->q_flags))
444*67880Seric 				strcat(optbuf, "SUCCESS");
445*67880Seric 			else
446*67880Seric 				strcat(optbuf, "NEVER");
447*67880Seric 		}
448*67880Seric 		if (bitset(QHASRETPARAM, to->q_flags))
449*67880Seric 		{
450*67880Seric 			strcat(optbuf, " RET=");
451*67880Seric 			if (bitset(QNOBODYRETURN, to->q_flags))
452*67880Seric 				strcat(optbuf, "NO");
453*67880Seric 			else
454*67880Seric 				strcat(optbuf, "YES");
455*67880Seric 		}
456*67880Seric 	}
457*67880Seric 	else if (bitset(QPINGONSUCCESS, to->q_flags))
458*67880Seric 	{
459*67880Seric 		to->q_flags |= QRELAYED;
460*67880Seric 		fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n",
461*67880Seric 			to->q_paddr);
462*67880Seric 	}
4634865Seric 
464*67880Seric 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
465*67880Seric 
46661093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
46753751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
46859285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
469*67880Seric 	setstatus(to, SmtpReplyBuffer);
4708005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
4714865Seric 		return (EX_TEMPFAIL);
4727963Seric 	else if (REPLYTYPE(r) == 2)
4737963Seric 		return (EX_OK);
4747964Seric 	else if (r == 550 || r == 551 || r == 553)
4757964Seric 		return (EX_NOUSER);
4767964Seric 	else if (r == 552 || r == 554)
4777964Seric 		return (EX_UNAVAILABLE);
47858008Seric 
47958008Seric #ifdef LOG
48058020Seric 	if (LogLevel > 1)
48158008Seric 	{
48267860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s",
48367860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
48458008Seric 	}
48558008Seric #endif
48658008Seric 
4877964Seric 	return (EX_PROTOCOL);
4884797Seric }
4894797Seric /*
49010175Seric **  SMTPDATA -- send the data and clean up the transaction.
4914684Seric **
4924684Seric **	Parameters:
4934865Seric **		m -- mailer being sent to.
4946980Seric **		e -- the envelope for this message.
4954684Seric **
4964684Seric **	Returns:
4974976Seric **		exit status corresponding to DATA command.
4984684Seric **
4994684Seric **	Side Effects:
5004865Seric **		none.
5014684Seric */
5024684Seric 
50363753Seric static jmp_buf	CtxDataTimeout;
50463937Seric static int	datatimeout();
50563753Seric 
50653740Seric smtpdata(m, mci, e)
5074865Seric 	struct mailer *m;
50854967Seric 	register MCI *mci;
5096980Seric 	register ENVELOPE *e;
5104684Seric {
5114684Seric 	register int r;
51263753Seric 	register EVENT *ev;
51363753Seric 	time_t timeout;
5144684Seric 
5154797Seric 	/*
5164797Seric 	**  Send the data.
51710175Seric 	**	First send the command and check that it is ok.
51810175Seric 	**	Then send the data.
51910175Seric 	**	Follow it up with a dot to terminate.
52010175Seric 	**	Finally get the results of the transaction.
5214797Seric 	*/
5224797Seric 
52310175Seric 	/* send the command and check ok to proceed */
52453751Seric 	smtpmessage("DATA", m, mci);
52561093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
52653751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
52759285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
5288005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
52957990Seric 	{
53057990Seric 		smtpquit(m, mci, e);
5314797Seric 		return (EX_TEMPFAIL);
53257990Seric 	}
5337963Seric 	else if (r == 554)
53457990Seric 	{
53557990Seric 		smtprset(m, mci, e);
5367963Seric 		return (EX_UNAVAILABLE);
53757990Seric 	}
5387963Seric 	else if (r != 354)
53957990Seric 	{
54058008Seric #ifdef LOG
54158020Seric 		if (LogLevel > 1)
54258008Seric 		{
54367860Seric 			syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s",
54467860Seric 				e->e_id, mci->mci_host, SmtpReplyBuffer);
54558008Seric 		}
54658008Seric #endif
54757990Seric 		smtprset(m, mci, e);
5487964Seric 		return (EX_PROTOCOL);
54957990Seric 	}
55010175Seric 
55163757Seric 	/*
55263757Seric 	**  Set timeout around data writes.  Make it at least large
55363757Seric 	**  enough for DNS timeouts on all recipients plus some fudge
55463757Seric 	**  factor.  The main thing is that it should not be infinite.
55563757Seric 	*/
55663757Seric 
55763753Seric 	if (setjmp(CtxDataTimeout) != 0)
55863753Seric 	{
55963753Seric 		mci->mci_errno = errno;
56063753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
56163753Seric 		mci->mci_state = MCIS_ERROR;
56263753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
56363753Seric 		smtpquit(m, mci, e);
56463753Seric 		return EX_TEMPFAIL;
56563753Seric 	}
56663753Seric 
56763787Seric 	timeout = e->e_msgsize / 16;
56863787Seric 	if (timeout < (time_t) 60)
56963787Seric 		timeout = (time_t) 60;
57063787Seric 	timeout += e->e_nrcpts * 90;
57163753Seric 	ev = setevent(timeout, datatimeout, 0);
57263753Seric 
57367546Seric 	/*
57467546Seric 	**  Output the actual message.
57567546Seric 	*/
57667546Seric 
57767546Seric 	if (!bitset(MCIF_8BITMIME, mci->mci_flags) &&
57867546Seric 	    e->e_bodytype != NULL &&
57967546Seric 	    strcasecmp(e->e_bodytype, "7bit") != 0)
58067546Seric 	{
58167546Seric 		/* must convert from 8bit MIME format to 7bit encoded */
58267546Seric 		mci->mci_flags |= MCIF_CVT8TO7;
58367546Seric 	}
58467546Seric 
58567546Seric 	(*e->e_puthdr)(mci, e->e_header, e);
58665870Seric 	(*e->e_putbody)(mci, e, NULL);
58710175Seric 
58867546Seric 	/*
58967546Seric 	**  Cleanup after sending message.
59067546Seric 	*/
59167546Seric 
59263753Seric 	clrevent(ev);
59363753Seric 
59464718Seric 	if (ferror(mci->mci_out))
59564718Seric 	{
59664718Seric 		/* error during processing -- don't send the dot */
59764718Seric 		mci->mci_errno = EIO;
59864718Seric 		mci->mci_exitstat = EX_IOERR;
59964718Seric 		mci->mci_state = MCIS_ERROR;
60064718Seric 		smtpquit(m, mci, e);
60164718Seric 		return EX_IOERR;
60264718Seric 	}
60364718Seric 
60410175Seric 	/* terminate the message */
60553740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
60663753Seric 	if (TrafficLogFile != NULL)
60763753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
60858120Seric 	if (Verbose)
60958151Seric 		nmessage(">>> .");
61010175Seric 
61110175Seric 	/* check for the results of the transaction */
61261093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
61353751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
61459285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
61553751Seric 	if (r < 0)
61657990Seric 	{
61757990Seric 		smtpquit(m, mci, e);
6184797Seric 		return (EX_TEMPFAIL);
61957990Seric 	}
62053751Seric 	mci->mci_state = MCIS_OPEN;
62158917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
62253751Seric 	if (REPLYTYPE(r) == 4)
62353751Seric 		return (EX_TEMPFAIL);
6247963Seric 	else if (r == 250)
6257963Seric 		return (EX_OK);
6267963Seric 	else if (r == 552 || r == 554)
6277963Seric 		return (EX_UNAVAILABLE);
62858008Seric #ifdef LOG
62958020Seric 	if (LogLevel > 1)
63058008Seric 	{
63167860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s",
63267860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
63358008Seric 	}
63458008Seric #endif
6357964Seric 	return (EX_PROTOCOL);
6364684Seric }
63763753Seric 
63863753Seric 
63963753Seric static int
64063753Seric datatimeout()
64163753Seric {
64263753Seric 	longjmp(CtxDataTimeout, 1);
64363753Seric }
6444684Seric /*
6454865Seric **  SMTPQUIT -- close the SMTP connection.
6464865Seric **
6474865Seric **	Parameters:
64815535Seric **		m -- a pointer to the mailer.
6494865Seric **
6504865Seric **	Returns:
6514865Seric **		none.
6524865Seric **
6534865Seric **	Side Effects:
6544865Seric **		sends the final protocol and closes the connection.
6554865Seric */
6564865Seric 
65753751Seric smtpquit(m, mci, e)
65853751Seric 	register MAILER *m;
65954967Seric 	register MCI *mci;
66053751Seric 	ENVELOPE *e;
6614865Seric {
66264822Seric 	bool oldSuprErrs = SuprErrs;
6634865Seric 
66464822Seric 	/*
66564822Seric 	**	Suppress errors here -- we may be processing a different
66664822Seric 	**	job when we do the quit connection, and we don't want the
66764822Seric 	**	new job to be penalized for something that isn't it's
66864822Seric 	**	problem.
66964822Seric 	*/
67064822Seric 
67164822Seric 	SuprErrs = TRUE;
67264822Seric 
67354967Seric 	/* send the quit message if we haven't gotten I/O error */
67453751Seric 	if (mci->mci_state != MCIS_ERROR)
6759391Seric 	{
67661093Seric 		SmtpPhase = "client QUIT";
67753751Seric 		smtpmessage("QUIT", m, mci);
67859285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
67964822Seric 		SuprErrs = oldSuprErrs;
68053740Seric 		if (mci->mci_state == MCIS_CLOSED)
68164822Seric 		{
68264822Seric 			SuprErrs = oldSuprErrs;
68310159Seric 			return;
68464822Seric 		}
6859391Seric 	}
6869391Seric 
68752676Seric 	/* now actually close the connection and pick up the zombie */
68865194Seric 	(void) endmailer(mci, e, NULL);
68964822Seric 
69064822Seric 	SuprErrs = oldSuprErrs;
6914865Seric }
6924865Seric /*
69354967Seric **  SMTPRSET -- send a RSET (reset) command
69454967Seric */
69554967Seric 
69654967Seric smtprset(m, mci, e)
69754967Seric 	register MAILER *m;
69854967Seric 	register MCI *mci;
69954967Seric 	ENVELOPE *e;
70054967Seric {
70154967Seric 	int r;
70254967Seric 
70361093Seric 	SmtpPhase = "client RSET";
70454967Seric 	smtpmessage("RSET", m, mci);
70559285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
70657734Seric 	if (r < 0)
70757734Seric 		mci->mci_state = MCIS_ERROR;
70854967Seric 	else if (REPLYTYPE(r) == 2)
70957734Seric 	{
71057734Seric 		mci->mci_state = MCIS_OPEN;
71157734Seric 		return;
71257734Seric 	}
71357734Seric 	smtpquit(m, mci, e);
71454967Seric }
71554967Seric /*
71658867Seric **  SMTPPROBE -- check the connection state
71754967Seric */
71854967Seric 
71958867Seric smtpprobe(mci)
72054967Seric 	register MCI *mci;
72154967Seric {
72254967Seric 	int r;
72354967Seric 	MAILER *m = mci->mci_mailer;
72454967Seric 	extern ENVELOPE BlankEnvelope;
72554967Seric 	ENVELOPE *e = &BlankEnvelope;
72654967Seric 
72761093Seric 	SmtpPhase = "client probe";
72858867Seric 	smtpmessage("RSET", m, mci);
72959285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
73058061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
73154967Seric 		smtpquit(m, mci, e);
73254967Seric 	return r;
73354967Seric }
73454967Seric /*
7354684Seric **  REPLY -- read arpanet reply
7364684Seric **
7374684Seric **	Parameters:
73810175Seric **		m -- the mailer we are reading the reply from.
73957379Seric **		mci -- the mailer connection info structure.
74057379Seric **		e -- the current envelope.
74157379Seric **		timeout -- the timeout for reads.
74259285Seric **		pfunc -- processing function for second and subsequent
74359285Seric **			lines of response -- if null, no special
74459285Seric **			processing is done.
7454684Seric **
7464684Seric **	Returns:
7474684Seric **		reply code it reads.
7484684Seric **
7494684Seric **	Side Effects:
7504684Seric **		flushes the mail file.
7514684Seric */
7524684Seric 
75359285Seric reply(m, mci, e, timeout, pfunc)
75453751Seric 	MAILER *m;
75554967Seric 	MCI *mci;
75653751Seric 	ENVELOPE *e;
75759285Seric 	time_t timeout;
75859285Seric 	void (*pfunc)();
7594684Seric {
76059014Seric 	register char *bufp;
76159014Seric 	register int r;
76259285Seric 	bool firstline = TRUE;
76358957Seric 	char junkbuf[MAXLINE];
76458957Seric 
76557379Seric 	if (mci->mci_out != NULL)
76657379Seric 		(void) fflush(mci->mci_out);
7674684Seric 
7687677Seric 	if (tTd(18, 1))
7694796Seric 		printf("reply\n");
7704796Seric 
7717356Seric 	/*
7727356Seric 	**  Read the input line, being careful not to hang.
7737356Seric 	*/
7747356Seric 
77559014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
7764684Seric 	{
7777356Seric 		register char *p;
77853751Seric 		extern time_t curtime();
7794684Seric 
7807685Seric 		/* actually do the read */
78153751Seric 		if (e->e_xfp != NULL)
78253751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
7837356Seric 
78410054Seric 		/* if we are in the process of closing just give the code */
78553740Seric 		if (mci->mci_state == MCIS_CLOSED)
78610054Seric 			return (SMTPCLOSING);
78710054Seric 
78858680Seric 		if (mci->mci_out != NULL)
78958680Seric 			fflush(mci->mci_out);
79058680Seric 
79110054Seric 		/* get the line from the other side */
79261093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
79353751Seric 		mci->mci_lastuse = curtime();
79453751Seric 
79510054Seric 		if (p == NULL)
79610131Seric 		{
79763753Seric 			bool oldholderrs;
79810148Seric 			extern char MsgBuf[];		/* err.c */
79910148Seric 
80021065Seric 			/* if the remote end closed early, fake an error */
80121065Seric 			if (errno == 0)
80221065Seric # ifdef ECONNRESET
80321065Seric 				errno = ECONNRESET;
80456795Seric # else /* ECONNRESET */
80521065Seric 				errno = EPIPE;
80656795Seric # endif /* ECONNRESET */
80721065Seric 
80857379Seric 			mci->mci_errno = errno;
80957642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
81063753Seric 			oldholderrs = HoldErrs;
81163753Seric 			HoldErrs = TRUE;
81263753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
81363753Seric 
81410420Seric 			/* if debugging, pause so we can see state */
81510420Seric 			if (tTd(18, 100))
81610420Seric 				pause();
81754967Seric 			mci->mci_state = MCIS_ERROR;
81853751Seric 			smtpquit(m, mci, e);
81963753Seric #ifdef XDEBUG
82063753Seric 			{
82163753Seric 				char wbuf[MAXLINE];
82264082Seric 				char *p = wbuf;
82364082Seric 				if (e->e_to != NULL)
82464082Seric 				{
82564082Seric 					sprintf(p, "%s... ", e->e_to);
82664082Seric 					p += strlen(p);
82764082Seric 				}
82864082Seric 				sprintf(p, "reply(%s) during %s",
82964082Seric 					mci->mci_host, SmtpPhase);
83063753Seric 				checkfd012(wbuf);
83163753Seric 			}
83263753Seric #endif
83363753Seric 			HoldErrs = oldholderrs;
83410054Seric 			return (-1);
83510131Seric 		}
83658957Seric 		fixcrlf(bufp, TRUE);
83710054Seric 
83863753Seric 		/* EHLO failure is not a real error */
83963753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
84063753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
84114900Seric 		{
84214900Seric 			/* serious error -- log the previous command */
84364071Seric 			if (SmtpNeedIntro)
84464071Seric 			{
84564071Seric 				/* inform user who we are chatting with */
84664071Seric 				fprintf(CurEnv->e_xfp,
84764071Seric 					"... while talking to %s:\n",
84864071Seric 					CurHostName);
84964071Seric 				SmtpNeedIntro = FALSE;
85064071Seric 			}
85159014Seric 			if (SmtpMsgBuffer[0] != '\0')
85259014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
85359014Seric 			SmtpMsgBuffer[0] = '\0';
85414900Seric 
85514900Seric 			/* now log the message as from the other side */
85658957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
85714900Seric 		}
85814900Seric 
85914900Seric 		/* display the input for verbose mode */
86058120Seric 		if (Verbose)
86159956Seric 			nmessage("050 %s", bufp);
8627356Seric 
86359285Seric 		/* process the line */
86459285Seric 		if (pfunc != NULL && !firstline)
86559285Seric 			(*pfunc)(bufp, m, mci, e);
86659285Seric 
86759285Seric 		firstline = FALSE;
86859285Seric 
8697356Seric 		/* if continuation is required, we can go on */
87059014Seric 		if (bufp[3] == '-')
8714684Seric 			continue;
8727356Seric 
87359014Seric 		/* ignore improperly formated input */
87459014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
87559014Seric 			continue;
87659014Seric 
8777356Seric 		/* decode the reply code */
87859014Seric 		r = atoi(bufp);
8797356Seric 
8807356Seric 		/* extra semantics: 0xx codes are "informational" */
88159014Seric 		if (r >= 100)
88259014Seric 			break;
88359014Seric 	}
8847356Seric 
88559014Seric 	/*
88659014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
88759014Seric 	**  line of the response from here on out.
88859014Seric 	*/
88958061Seric 
89059014Seric 	/* save temporary failure messages for posterity */
89159014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
89259014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
8939391Seric 
89459014Seric 	/* reply code 421 is "Service Shutting Down" */
89559014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
89659014Seric 	{
89759014Seric 		/* send the quit protocol */
89859014Seric 		mci->mci_state = MCIS_SSD;
89959014Seric 		smtpquit(m, mci, e);
9004684Seric 	}
90159014Seric 
90259014Seric 	return (r);
9034684Seric }
9044796Seric /*
9054865Seric **  SMTPMESSAGE -- send message to server
9064796Seric **
9074796Seric **	Parameters:
9084796Seric **		f -- format
90910175Seric **		m -- the mailer to control formatting.
9104796Seric **		a, b, c -- parameters
9114796Seric **
9124796Seric **	Returns:
9134796Seric **		none.
9144796Seric **
9154796Seric **	Side Effects:
91653740Seric **		writes message to mci->mci_out.
9174796Seric */
9184796Seric 
9194865Seric /*VARARGS1*/
92057642Seric #ifdef __STDC__
92157642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
92257642Seric #else
92357642Seric smtpmessage(f, m, mci, va_alist)
9244796Seric 	char *f;
92510175Seric 	MAILER *m;
92654967Seric 	MCI *mci;
92757642Seric 	va_dcl
92857642Seric #endif
9294796Seric {
93056852Seric 	VA_LOCAL_DECL
93156852Seric 
93257135Seric 	VA_START(mci);
93356852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
93456852Seric 	VA_END;
93558680Seric 
93658120Seric 	if (tTd(18, 1) || Verbose)
93758151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
93863753Seric 	if (TrafficLogFile != NULL)
93963753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
94053740Seric 	if (mci->mci_out != NULL)
94158680Seric 	{
94253740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
94354967Seric 			m == NULL ? "\r\n" : m->m_eol);
94458680Seric 	}
94559149Seric 	else if (tTd(18, 1))
94658725Seric 	{
94759149Seric 		printf("smtpmessage: NULL mci_out\n");
94858725Seric 	}
9494796Seric }
9505182Seric 
95156795Seric # endif /* SMTP */
952