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*67417Seric static char sccsid[] = "@(#)usersmtp.c	8.19 (Berkeley) 06/17/94 (with SMTP)";
1433731Sbostic #else
15*67417Seric static char sccsid[] = "@(#)usersmtp.c	8.19 (Berkeley) 06/17/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.
16364651Seric 	**
16464651Seric 	**	If this code remains at all, "CheckLoopBack" should be
16564651Seric 	**	a mailer flag.  This is a MAYBENEXTRELEASE feature.
16658957Seric 	*/
16758957Seric 
16859026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16958957Seric 	if (p != NULL)
17061707Seric 		*p = '\0';
17164644Seric 	if (CheckLoopBack && strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
17258957Seric 	{
17358957Seric 		syserr("553 %s config error: mail loops back to myself",
17458957Seric 			MyHostName);
17558957Seric 		mci->mci_exitstat = EX_CONFIG;
17658957Seric 		mci->mci_errno = 0;
17758957Seric 		smtpquit(m, mci, e);
17858957Seric 		return;
17958957Seric 	}
18058957Seric 
18158957Seric 	/*
1829315Seric 	**  If this is expected to be another sendmail, send some internal
1839315Seric 	**  commands.
1849315Seric 	*/
1859315Seric 
18610688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1879315Seric 	{
1889315Seric 		/* tell it to be verbose */
18953751Seric 		smtpmessage("VERB", m, mci);
19059285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1919315Seric 		if (r < 0)
19252104Seric 			goto tempfail2;
1939315Seric 	}
1949315Seric 
19565057Seric 	if (mci->mci_state != MCIS_CLOSED)
19665057Seric 	{
19765057Seric 		mci->mci_state = MCIS_OPEN;
19865057Seric 		return;
19965057Seric 	}
20053751Seric 
20165057Seric 	/* got a 421 error code during startup */
20265057Seric 
20353751Seric   tempfail1:
20453751Seric   tempfail2:
20553751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
20657379Seric 	if (mci->mci_errno == 0)
20757379Seric 		mci->mci_errno = errno;
20857379Seric 	if (mci->mci_state != MCIS_CLOSED)
20957379Seric 		smtpquit(m, mci, e);
21054967Seric 	return;
21153751Seric 
21253751Seric   unavailable:
21353751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
21453751Seric 	mci->mci_errno = errno;
21553751Seric 	smtpquit(m, mci, e);
21654967Seric 	return;
21753751Seric }
21859285Seric /*
21960210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
22060210Seric **
22160210Seric **
22260210Seric **	Parameters:
22360210Seric **		line -- the response line.
22460210Seric **		m -- the mailer.
22560210Seric **		mci -- the mailer connection info.
22660210Seric **		e -- the envelope.
22760210Seric **
22860210Seric **	Returns:
22960210Seric **		none.
23060210Seric */
23160210Seric 
23260210Seric void
23360210Seric esmtp_check(line, m, mci, e)
23460210Seric 	char *line;
23560210Seric 	MAILER *m;
23660210Seric 	register MCI *mci;
23760210Seric 	ENVELOPE *e;
23860210Seric {
23960210Seric 	if (strlen(line) < 5)
24060210Seric 		return;
24160210Seric 	line += 4;
24260210Seric 	if (strncmp(line, "ESMTP ", 6) == 0)
24360210Seric 		mci->mci_flags |= MCIF_ESMTP;
24460210Seric }
24560210Seric /*
24659285Seric **  HELO_OPTIONS -- process the options on a HELO line.
24759285Seric **
24859285Seric **	Parameters:
24959285Seric **		line -- the response line.
25059285Seric **		m -- the mailer.
25159285Seric **		mci -- the mailer connection info.
25259285Seric **		e -- the envelope.
25359285Seric **
25459285Seric **	Returns:
25559285Seric **		none.
25659285Seric */
25753751Seric 
25859285Seric void
25959285Seric helo_options(line, m, mci, e)
26059285Seric 	char *line;
26159285Seric 	MAILER *m;
26259285Seric 	register MCI *mci;
26359285Seric 	ENVELOPE *e;
26459285Seric {
26559285Seric 	register char *p;
26659285Seric 
26759285Seric 	if (strlen(line) < 5)
26859285Seric 		return;
26959285Seric 	line += 4;
27059285Seric 	p = strchr(line, ' ');
27159285Seric 	if (p != NULL)
27259285Seric 		*p++ = '\0';
27359285Seric 	if (strcasecmp(line, "size") == 0)
27459285Seric 	{
27559285Seric 		mci->mci_flags |= MCIF_SIZE;
27659285Seric 		if (p != NULL)
27759285Seric 			mci->mci_maxsize = atol(p);
27859285Seric 	}
27959285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
28065870Seric 	{
28159285Seric 		mci->mci_flags |= MCIF_8BITMIME;
28265870Seric 		mci->mci_flags &= ~MCIF_7BIT;
28365870Seric 	}
28459285Seric 	else if (strcasecmp(line, "expn") == 0)
28559285Seric 		mci->mci_flags |= MCIF_EXPN;
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 
315*67417Seric 	if (e->e_bodytype != NULL)
316*67417Seric 	{
317*67417Seric 		if (bitset(MCIF_8BITMIME, mci->mci_flags))
318*67417Seric 		{
319*67417Seric 			strcat(optbuf, " BODY=");
320*67417Seric 			strcat(optbuf, e->e_bodytype);
321*67417Seric 		}
322*67417Seric 		else if (strcasecmp(e->e_bodytype, "7bit") != 0)
323*67417Seric 		{
324*67417Seric 			/* cannot just send a 7-bit version */
325*67417Seric 			usrerr("%s does not support 8BITMIME", mci->mci_host);
326*67417Seric 			return EX_DATAERR;
327*67417Seric 		}
328*67417Seric 	}
329*67417Seric 
3309315Seric 	/*
3314865Seric 	**  Send the MAIL command.
3324865Seric 	**	Designates the sender.
3334865Seric 	*/
3344796Seric 
33553751Seric 	mci->mci_state = MCIS_ACTIVE;
33653751Seric 
33759540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
33859540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
33958680Seric 		(void) strcpy(buf, "");
34058680Seric 	else
34158680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
34265494Seric 	if (buf[0] == '<')
34365494Seric 	{
34465494Seric 		/* strip off <angle brackets> (put back on below) */
34565494Seric 		bufp = &buf[strlen(buf) - 1];
34665494Seric 		if (*bufp == '>')
34765494Seric 			*bufp = '\0';
34865494Seric 		bufp = &buf[1];
34965494Seric 	}
35065494Seric 	else
35165494Seric 		bufp = buf;
35253751Seric 	if (e->e_from.q_mailer == LocalMailer ||
35310688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
3548436Seric 	{
35565494Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
3568436Seric 	}
3578436Seric 	else
3588436Seric 	{
35959285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
36065494Seric 			*bufp == '@' ? ',' : ':', bufp, optbuf);
3618436Seric 	}
36261093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
36353751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
36459285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
3658005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
36653751Seric 	{
36753751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
36853751Seric 		mci->mci_errno = errno;
36953751Seric 		smtpquit(m, mci, e);
37053751Seric 		return EX_TEMPFAIL;
37153751Seric 	}
3727963Seric 	else if (r == 250)
37353751Seric 	{
37453751Seric 		mci->mci_exitstat = EX_OK;
37553751Seric 		return EX_OK;
37653751Seric 	}
3777963Seric 	else if (r == 552)
37853751Seric 	{
37953751Seric 		/* signal service unavailable */
38053751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
38153751Seric 		smtpquit(m, mci, e);
38253751Seric 		return EX_UNAVAILABLE;
38353751Seric 	}
38414913Seric 
38558008Seric #ifdef LOG
38658020Seric 	if (LogLevel > 1)
38758008Seric 	{
38858008Seric 		syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
38958008Seric 			e->e_id, SmtpReplyBuffer);
39058008Seric 	}
39158008Seric #endif
39258008Seric 
39314913Seric 	/* protocol error -- close up */
39453751Seric 	smtpquit(m, mci, e);
39553751Seric 	mci->mci_exitstat = EX_PROTOCOL;
39653751Seric 	return EX_PROTOCOL;
3974684Seric }
3984684Seric /*
3994976Seric **  SMTPRCPT -- designate recipient.
4004797Seric **
4014797Seric **	Parameters:
4024865Seric **		to -- address of recipient.
40310175Seric **		m -- the mailer we are sending to.
40457379Seric **		mci -- the connection info for this transaction.
40557379Seric **		e -- the envelope for this transaction.
4064797Seric **
4074797Seric **	Returns:
4084865Seric **		exit status corresponding to recipient status.
4094797Seric **
4104797Seric **	Side Effects:
4114865Seric **		Sends the mail via SMTP.
4124797Seric */
4134797Seric 
41453751Seric smtprcpt(to, m, mci, e)
4154865Seric 	ADDRESS *to;
41610175Seric 	register MAILER *m;
41754967Seric 	MCI *mci;
41853751Seric 	ENVELOPE *e;
4194797Seric {
4204797Seric 	register int r;
4214797Seric 
42253751Seric 	smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
4234865Seric 
42461093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
42553751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
42659285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
4278005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
4284865Seric 		return (EX_TEMPFAIL);
4297963Seric 	else if (REPLYTYPE(r) == 2)
4307963Seric 		return (EX_OK);
4317964Seric 	else if (r == 550 || r == 551 || r == 553)
4327964Seric 		return (EX_NOUSER);
4337964Seric 	else if (r == 552 || r == 554)
4347964Seric 		return (EX_UNAVAILABLE);
43558008Seric 
43658008Seric #ifdef LOG
43758020Seric 	if (LogLevel > 1)
43858008Seric 	{
43958008Seric 		syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
44058008Seric 			e->e_id, SmtpReplyBuffer);
44158008Seric 	}
44258008Seric #endif
44358008Seric 
4447964Seric 	return (EX_PROTOCOL);
4454797Seric }
4464797Seric /*
44710175Seric **  SMTPDATA -- send the data and clean up the transaction.
4484684Seric **
4494684Seric **	Parameters:
4504865Seric **		m -- mailer being sent to.
4516980Seric **		e -- the envelope for this message.
4524684Seric **
4534684Seric **	Returns:
4544976Seric **		exit status corresponding to DATA command.
4554684Seric **
4564684Seric **	Side Effects:
4574865Seric **		none.
4584684Seric */
4594684Seric 
46063753Seric static jmp_buf	CtxDataTimeout;
46163937Seric static int	datatimeout();
46263753Seric 
46353740Seric smtpdata(m, mci, e)
4644865Seric 	struct mailer *m;
46554967Seric 	register MCI *mci;
4666980Seric 	register ENVELOPE *e;
4674684Seric {
4684684Seric 	register int r;
46963753Seric 	register EVENT *ev;
47063753Seric 	time_t timeout;
4714684Seric 
4724797Seric 	/*
4734797Seric 	**  Send the data.
47410175Seric 	**	First send the command and check that it is ok.
47510175Seric 	**	Then send the data.
47610175Seric 	**	Follow it up with a dot to terminate.
47710175Seric 	**	Finally get the results of the transaction.
4784797Seric 	*/
4794797Seric 
48010175Seric 	/* send the command and check ok to proceed */
48153751Seric 	smtpmessage("DATA", m, mci);
48261093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
48353751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
48459285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
4858005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
48657990Seric 	{
48757990Seric 		smtpquit(m, mci, e);
4884797Seric 		return (EX_TEMPFAIL);
48957990Seric 	}
4907963Seric 	else if (r == 554)
49157990Seric 	{
49257990Seric 		smtprset(m, mci, e);
4937963Seric 		return (EX_UNAVAILABLE);
49457990Seric 	}
4957963Seric 	else if (r != 354)
49657990Seric 	{
49758008Seric #ifdef LOG
49858020Seric 		if (LogLevel > 1)
49958008Seric 		{
50058008Seric 			syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
50158008Seric 				e->e_id, SmtpReplyBuffer);
50258008Seric 		}
50358008Seric #endif
50457990Seric 		smtprset(m, mci, e);
5057964Seric 		return (EX_PROTOCOL);
50657990Seric 	}
50710175Seric 
50863757Seric 	/*
50963757Seric 	**  Set timeout around data writes.  Make it at least large
51063757Seric 	**  enough for DNS timeouts on all recipients plus some fudge
51163757Seric 	**  factor.  The main thing is that it should not be infinite.
51263757Seric 	*/
51363757Seric 
51463753Seric 	if (setjmp(CtxDataTimeout) != 0)
51563753Seric 	{
51663753Seric 		mci->mci_errno = errno;
51763753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
51863753Seric 		mci->mci_state = MCIS_ERROR;
51963753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
52063753Seric 		smtpquit(m, mci, e);
52163753Seric 		return EX_TEMPFAIL;
52263753Seric 	}
52363753Seric 
52463787Seric 	timeout = e->e_msgsize / 16;
52563787Seric 	if (timeout < (time_t) 60)
52663787Seric 		timeout = (time_t) 60;
52763787Seric 	timeout += e->e_nrcpts * 90;
52863753Seric 	ev = setevent(timeout, datatimeout, 0);
52963753Seric 
53010175Seric 	/* now output the actual message */
53165870Seric 	(*e->e_puthdr)(mci, e);
53265870Seric 	putline("\n", mci);
53365870Seric 	(*e->e_putbody)(mci, e, NULL);
53410175Seric 
53563753Seric 	clrevent(ev);
53663753Seric 
53764718Seric 	if (ferror(mci->mci_out))
53864718Seric 	{
53964718Seric 		/* error during processing -- don't send the dot */
54064718Seric 		mci->mci_errno = EIO;
54164718Seric 		mci->mci_exitstat = EX_IOERR;
54264718Seric 		mci->mci_state = MCIS_ERROR;
54364718Seric 		smtpquit(m, mci, e);
54464718Seric 		return EX_IOERR;
54564718Seric 	}
54664718Seric 
54710175Seric 	/* terminate the message */
54853740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
54963753Seric 	if (TrafficLogFile != NULL)
55063753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
55158120Seric 	if (Verbose)
55258151Seric 		nmessage(">>> .");
55310175Seric 
55410175Seric 	/* check for the results of the transaction */
55561093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
55653751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
55759285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
55853751Seric 	if (r < 0)
55957990Seric 	{
56057990Seric 		smtpquit(m, mci, e);
5614797Seric 		return (EX_TEMPFAIL);
56257990Seric 	}
56353751Seric 	mci->mci_state = MCIS_OPEN;
56458917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
56553751Seric 	if (REPLYTYPE(r) == 4)
56653751Seric 		return (EX_TEMPFAIL);
5677963Seric 	else if (r == 250)
5687963Seric 		return (EX_OK);
5697963Seric 	else if (r == 552 || r == 554)
5707963Seric 		return (EX_UNAVAILABLE);
57158008Seric #ifdef LOG
57258020Seric 	if (LogLevel > 1)
57358008Seric 	{
57458008Seric 		syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
57558008Seric 			e->e_id, SmtpReplyBuffer);
57658008Seric 	}
57758008Seric #endif
5787964Seric 	return (EX_PROTOCOL);
5794684Seric }
58063753Seric 
58163753Seric 
58263753Seric static int
58363753Seric datatimeout()
58463753Seric {
58563753Seric 	longjmp(CtxDataTimeout, 1);
58663753Seric }
5874684Seric /*
5884865Seric **  SMTPQUIT -- close the SMTP connection.
5894865Seric **
5904865Seric **	Parameters:
59115535Seric **		m -- a pointer to the mailer.
5924865Seric **
5934865Seric **	Returns:
5944865Seric **		none.
5954865Seric **
5964865Seric **	Side Effects:
5974865Seric **		sends the final protocol and closes the connection.
5984865Seric */
5994865Seric 
60053751Seric smtpquit(m, mci, e)
60153751Seric 	register MAILER *m;
60254967Seric 	register MCI *mci;
60353751Seric 	ENVELOPE *e;
6044865Seric {
60564822Seric 	bool oldSuprErrs = SuprErrs;
6064865Seric 
60764822Seric 	/*
60864822Seric 	**	Suppress errors here -- we may be processing a different
60964822Seric 	**	job when we do the quit connection, and we don't want the
61064822Seric 	**	new job to be penalized for something that isn't it's
61164822Seric 	**	problem.
61264822Seric 	*/
61364822Seric 
61464822Seric 	SuprErrs = TRUE;
61564822Seric 
61654967Seric 	/* send the quit message if we haven't gotten I/O error */
61753751Seric 	if (mci->mci_state != MCIS_ERROR)
6189391Seric 	{
61961093Seric 		SmtpPhase = "client QUIT";
62053751Seric 		smtpmessage("QUIT", m, mci);
62159285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
62264822Seric 		SuprErrs = oldSuprErrs;
62353740Seric 		if (mci->mci_state == MCIS_CLOSED)
62464822Seric 		{
62564822Seric 			SuprErrs = oldSuprErrs;
62610159Seric 			return;
62764822Seric 		}
6289391Seric 	}
6299391Seric 
63052676Seric 	/* now actually close the connection and pick up the zombie */
63165194Seric 	(void) endmailer(mci, e, NULL);
63264822Seric 
63364822Seric 	SuprErrs = oldSuprErrs;
6344865Seric }
6354865Seric /*
63654967Seric **  SMTPRSET -- send a RSET (reset) command
63754967Seric */
63854967Seric 
63954967Seric smtprset(m, mci, e)
64054967Seric 	register MAILER *m;
64154967Seric 	register MCI *mci;
64254967Seric 	ENVELOPE *e;
64354967Seric {
64454967Seric 	int r;
64554967Seric 
64661093Seric 	SmtpPhase = "client RSET";
64754967Seric 	smtpmessage("RSET", m, mci);
64859285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
64957734Seric 	if (r < 0)
65057734Seric 		mci->mci_state = MCIS_ERROR;
65154967Seric 	else if (REPLYTYPE(r) == 2)
65257734Seric 	{
65357734Seric 		mci->mci_state = MCIS_OPEN;
65457734Seric 		return;
65557734Seric 	}
65657734Seric 	smtpquit(m, mci, e);
65754967Seric }
65854967Seric /*
65958867Seric **  SMTPPROBE -- check the connection state
66054967Seric */
66154967Seric 
66258867Seric smtpprobe(mci)
66354967Seric 	register MCI *mci;
66454967Seric {
66554967Seric 	int r;
66654967Seric 	MAILER *m = mci->mci_mailer;
66754967Seric 	extern ENVELOPE BlankEnvelope;
66854967Seric 	ENVELOPE *e = &BlankEnvelope;
66954967Seric 
67061093Seric 	SmtpPhase = "client probe";
67158867Seric 	smtpmessage("RSET", m, mci);
67259285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
67358061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
67454967Seric 		smtpquit(m, mci, e);
67554967Seric 	return r;
67654967Seric }
67754967Seric /*
6784684Seric **  REPLY -- read arpanet reply
6794684Seric **
6804684Seric **	Parameters:
68110175Seric **		m -- the mailer we are reading the reply from.
68257379Seric **		mci -- the mailer connection info structure.
68357379Seric **		e -- the current envelope.
68457379Seric **		timeout -- the timeout for reads.
68559285Seric **		pfunc -- processing function for second and subsequent
68659285Seric **			lines of response -- if null, no special
68759285Seric **			processing is done.
6884684Seric **
6894684Seric **	Returns:
6904684Seric **		reply code it reads.
6914684Seric **
6924684Seric **	Side Effects:
6934684Seric **		flushes the mail file.
6944684Seric */
6954684Seric 
69659285Seric reply(m, mci, e, timeout, pfunc)
69753751Seric 	MAILER *m;
69854967Seric 	MCI *mci;
69953751Seric 	ENVELOPE *e;
70059285Seric 	time_t timeout;
70159285Seric 	void (*pfunc)();
7024684Seric {
70359014Seric 	register char *bufp;
70459014Seric 	register int r;
70559285Seric 	bool firstline = TRUE;
70658957Seric 	char junkbuf[MAXLINE];
70758957Seric 
70857379Seric 	if (mci->mci_out != NULL)
70957379Seric 		(void) fflush(mci->mci_out);
7104684Seric 
7117677Seric 	if (tTd(18, 1))
7124796Seric 		printf("reply\n");
7134796Seric 
7147356Seric 	/*
7157356Seric 	**  Read the input line, being careful not to hang.
7167356Seric 	*/
7177356Seric 
71859014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
7194684Seric 	{
7207356Seric 		register char *p;
72153751Seric 		extern time_t curtime();
7224684Seric 
7237685Seric 		/* actually do the read */
72453751Seric 		if (e->e_xfp != NULL)
72553751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
7267356Seric 
72710054Seric 		/* if we are in the process of closing just give the code */
72853740Seric 		if (mci->mci_state == MCIS_CLOSED)
72910054Seric 			return (SMTPCLOSING);
73010054Seric 
73158680Seric 		if (mci->mci_out != NULL)
73258680Seric 			fflush(mci->mci_out);
73358680Seric 
73410054Seric 		/* get the line from the other side */
73561093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
73653751Seric 		mci->mci_lastuse = curtime();
73753751Seric 
73810054Seric 		if (p == NULL)
73910131Seric 		{
74063753Seric 			bool oldholderrs;
74110148Seric 			extern char MsgBuf[];		/* err.c */
74210148Seric 
74321065Seric 			/* if the remote end closed early, fake an error */
74421065Seric 			if (errno == 0)
74521065Seric # ifdef ECONNRESET
74621065Seric 				errno = ECONNRESET;
74756795Seric # else /* ECONNRESET */
74821065Seric 				errno = EPIPE;
74956795Seric # endif /* ECONNRESET */
75021065Seric 
75157379Seric 			mci->mci_errno = errno;
75257642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
75363753Seric 			oldholderrs = HoldErrs;
75463753Seric 			HoldErrs = TRUE;
75563753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
75663753Seric 
75710420Seric 			/* if debugging, pause so we can see state */
75810420Seric 			if (tTd(18, 100))
75910420Seric 				pause();
76054967Seric 			mci->mci_state = MCIS_ERROR;
76153751Seric 			smtpquit(m, mci, e);
76263753Seric #ifdef XDEBUG
76363753Seric 			{
76463753Seric 				char wbuf[MAXLINE];
76564082Seric 				char *p = wbuf;
76664082Seric 				if (e->e_to != NULL)
76764082Seric 				{
76864082Seric 					sprintf(p, "%s... ", e->e_to);
76964082Seric 					p += strlen(p);
77064082Seric 				}
77164082Seric 				sprintf(p, "reply(%s) during %s",
77264082Seric 					mci->mci_host, SmtpPhase);
77363753Seric 				checkfd012(wbuf);
77463753Seric 			}
77563753Seric #endif
77663753Seric 			HoldErrs = oldholderrs;
77710054Seric 			return (-1);
77810131Seric 		}
77958957Seric 		fixcrlf(bufp, TRUE);
78010054Seric 
78163753Seric 		/* EHLO failure is not a real error */
78263753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
78363753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
78414900Seric 		{
78514900Seric 			/* serious error -- log the previous command */
78664071Seric 			if (SmtpNeedIntro)
78764071Seric 			{
78864071Seric 				/* inform user who we are chatting with */
78964071Seric 				fprintf(CurEnv->e_xfp,
79064071Seric 					"... while talking to %s:\n",
79164071Seric 					CurHostName);
79264071Seric 				SmtpNeedIntro = FALSE;
79364071Seric 			}
79459014Seric 			if (SmtpMsgBuffer[0] != '\0')
79559014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
79659014Seric 			SmtpMsgBuffer[0] = '\0';
79714900Seric 
79814900Seric 			/* now log the message as from the other side */
79958957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
80014900Seric 		}
80114900Seric 
80214900Seric 		/* display the input for verbose mode */
80358120Seric 		if (Verbose)
80459956Seric 			nmessage("050 %s", bufp);
8057356Seric 
80659285Seric 		/* process the line */
80759285Seric 		if (pfunc != NULL && !firstline)
80859285Seric 			(*pfunc)(bufp, m, mci, e);
80959285Seric 
81059285Seric 		firstline = FALSE;
81159285Seric 
8127356Seric 		/* if continuation is required, we can go on */
81359014Seric 		if (bufp[3] == '-')
8144684Seric 			continue;
8157356Seric 
81659014Seric 		/* ignore improperly formated input */
81759014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
81859014Seric 			continue;
81959014Seric 
8207356Seric 		/* decode the reply code */
82159014Seric 		r = atoi(bufp);
8227356Seric 
8237356Seric 		/* extra semantics: 0xx codes are "informational" */
82459014Seric 		if (r >= 100)
82559014Seric 			break;
82659014Seric 	}
8277356Seric 
82859014Seric 	/*
82959014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
83059014Seric 	**  line of the response from here on out.
83159014Seric 	*/
83258061Seric 
83359014Seric 	/* save temporary failure messages for posterity */
83459014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
83559014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
8369391Seric 
83759014Seric 	/* reply code 421 is "Service Shutting Down" */
83859014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
83959014Seric 	{
84059014Seric 		/* send the quit protocol */
84159014Seric 		mci->mci_state = MCIS_SSD;
84259014Seric 		smtpquit(m, mci, e);
8434684Seric 	}
84459014Seric 
84559014Seric 	return (r);
8464684Seric }
8474796Seric /*
8484865Seric **  SMTPMESSAGE -- send message to server
8494796Seric **
8504796Seric **	Parameters:
8514796Seric **		f -- format
85210175Seric **		m -- the mailer to control formatting.
8534796Seric **		a, b, c -- parameters
8544796Seric **
8554796Seric **	Returns:
8564796Seric **		none.
8574796Seric **
8584796Seric **	Side Effects:
85953740Seric **		writes message to mci->mci_out.
8604796Seric */
8614796Seric 
8624865Seric /*VARARGS1*/
86357642Seric #ifdef __STDC__
86457642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
86557642Seric #else
86657642Seric smtpmessage(f, m, mci, va_alist)
8674796Seric 	char *f;
86810175Seric 	MAILER *m;
86954967Seric 	MCI *mci;
87057642Seric 	va_dcl
87157642Seric #endif
8724796Seric {
87356852Seric 	VA_LOCAL_DECL
87456852Seric 
87557135Seric 	VA_START(mci);
87656852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
87756852Seric 	VA_END;
87858680Seric 
87958120Seric 	if (tTd(18, 1) || Verbose)
88058151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
88163753Seric 	if (TrafficLogFile != NULL)
88263753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
88353740Seric 	if (mci->mci_out != NULL)
88458680Seric 	{
88553740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
88654967Seric 			m == NULL ? "\r\n" : m->m_eol);
88758680Seric 	}
88859149Seric 	else if (tTd(18, 1))
88958725Seric 	{
89059149Seric 		printf("smtpmessage: NULL mci_out\n");
89158725Seric 	}
8924796Seric }
8935182Seric 
89456795Seric # endif /* SMTP */
895