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*64071Seric static char sccsid[] = "@(#)usersmtp.c	8.6 (Berkeley) 07/27/93 (with SMTP)";
1433731Sbostic #else
15*64071Seric static char sccsid[] = "@(#)usersmtp.c	8.6 (Berkeley) 07/27/93 (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 */
38*64071Seric 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 ");
7357379Seric 		mci_dump(mci);
7457379Seric 	}
7557379Seric 
764865Seric 	/*
774865Seric 	**  Open the connection to the mailer.
784865Seric 	*/
794684Seric 
8021065Seric 	SmtpError[0] = '\0';
8157379Seric 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
82*64071Seric 	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);
1188005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
11952104Seric 		goto tempfail1;
1204684Seric 
1214865Seric 	/*
1224976Seric 	**  Send the HELO command.
1237963Seric 	**	My mother taught me to always introduce myself.
1244976Seric 	*/
1254976Seric 
12659285Seric 	if (bitnset(M_ESMTP, m->m_flags))
12759285Seric 		mci->mci_flags |= MCIF_ESMTP;
12859285Seric 
12959285Seric tryhelo:
13059285Seric 	if (bitset(MCIF_ESMTP, mci->mci_flags))
13159285Seric 	{
13259285Seric 		smtpmessage("EHLO %s", m, mci, MyHostName);
13361093Seric 		SmtpPhase = mci->mci_phase = "client EHLO";
13459285Seric 	}
13559285Seric 	else
13659285Seric 	{
13759285Seric 		smtpmessage("HELO %s", m, mci, MyHostName);
13861093Seric 		SmtpPhase = mci->mci_phase = "client HELO";
13959285Seric 	}
14053751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
14159285Seric 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1428005Seric 	if (r < 0)
14352104Seric 		goto tempfail1;
1448005Seric 	else if (REPLYTYPE(r) == 5)
14559285Seric 	{
14659285Seric 		if (bitset(MCIF_ESMTP, mci->mci_flags))
14759285Seric 		{
14859285Seric 			/* try old SMTP instead */
14959285Seric 			mci->mci_flags &= ~MCIF_ESMTP;
15059285Seric 			goto tryhelo;
15159285Seric 		}
15214913Seric 		goto unavailable;
15359285Seric 	}
1547963Seric 	else if (REPLYTYPE(r) != 2)
15552104Seric 		goto tempfail1;
1564976Seric 
1574976Seric 	/*
15858957Seric 	**  Check to see if we actually ended up talking to ourself.
15958957Seric 	**  This means we didn't know about an alias or MX, or we managed
16058957Seric 	**  to connect to an echo server.
16158957Seric 	*/
16258957Seric 
16359026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16458957Seric 	if (p != NULL)
16561707Seric 		*p = '\0';
16659026Seric 	if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
16758957Seric 	{
16858957Seric 		syserr("553 %s config error: mail loops back to myself",
16958957Seric 			MyHostName);
17058957Seric 		mci->mci_exitstat = EX_CONFIG;
17158957Seric 		mci->mci_errno = 0;
17258957Seric 		smtpquit(m, mci, e);
17358957Seric 		return;
17458957Seric 	}
17558957Seric 
17658957Seric 	/*
1779315Seric 	**  If this is expected to be another sendmail, send some internal
1789315Seric 	**  commands.
1799315Seric 	*/
1809315Seric 
18110688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1829315Seric 	{
1839315Seric 		/* tell it to be verbose */
18453751Seric 		smtpmessage("VERB", m, mci);
18559285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1869315Seric 		if (r < 0)
18752104Seric 			goto tempfail2;
1889315Seric 	}
1899315Seric 
19053751Seric 	mci->mci_state = MCIS_OPEN;
19154967Seric 	return;
19253751Seric 
19353751Seric   tempfail1:
19453751Seric   tempfail2:
19553751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
19657379Seric 	if (mci->mci_errno == 0)
19757379Seric 		mci->mci_errno = errno;
19857379Seric 	if (mci->mci_state != MCIS_CLOSED)
19957379Seric 		smtpquit(m, mci, e);
20054967Seric 	return;
20153751Seric 
20253751Seric   unavailable:
20353751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
20453751Seric 	mci->mci_errno = errno;
20553751Seric 	smtpquit(m, mci, e);
20654967Seric 	return;
20753751Seric }
20859285Seric /*
20960210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
21060210Seric **
21160210Seric **
21260210Seric **	Parameters:
21360210Seric **		line -- the response line.
21460210Seric **		m -- the mailer.
21560210Seric **		mci -- the mailer connection info.
21660210Seric **		e -- the envelope.
21760210Seric **
21860210Seric **	Returns:
21960210Seric **		none.
22060210Seric */
22160210Seric 
22260210Seric void
22360210Seric esmtp_check(line, m, mci, e)
22460210Seric 	char *line;
22560210Seric 	MAILER *m;
22660210Seric 	register MCI *mci;
22760210Seric 	ENVELOPE *e;
22860210Seric {
22960210Seric 	if (strlen(line) < 5)
23060210Seric 		return;
23160210Seric 	line += 4;
23260210Seric 	if (strncmp(line, "ESMTP ", 6) == 0)
23360210Seric 		mci->mci_flags |= MCIF_ESMTP;
23460210Seric }
23560210Seric /*
23659285Seric **  HELO_OPTIONS -- process the options on a HELO line.
23759285Seric **
23859285Seric **	Parameters:
23959285Seric **		line -- the response line.
24059285Seric **		m -- the mailer.
24159285Seric **		mci -- the mailer connection info.
24259285Seric **		e -- the envelope.
24359285Seric **
24459285Seric **	Returns:
24559285Seric **		none.
24659285Seric */
24753751Seric 
24859285Seric void
24959285Seric helo_options(line, m, mci, e)
25059285Seric 	char *line;
25159285Seric 	MAILER *m;
25259285Seric 	register MCI *mci;
25359285Seric 	ENVELOPE *e;
25459285Seric {
25559285Seric 	register char *p;
25659285Seric 
25759285Seric 	if (strlen(line) < 5)
25859285Seric 		return;
25959285Seric 	line += 4;
26059285Seric 	p = strchr(line, ' ');
26159285Seric 	if (p != NULL)
26259285Seric 		*p++ = '\0';
26359285Seric 	if (strcasecmp(line, "size") == 0)
26459285Seric 	{
26559285Seric 		mci->mci_flags |= MCIF_SIZE;
26659285Seric 		if (p != NULL)
26759285Seric 			mci->mci_maxsize = atol(p);
26859285Seric 	}
26959285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
27059285Seric 		mci->mci_flags |= MCIF_8BITMIME;
27159285Seric 	else if (strcasecmp(line, "expn") == 0)
27259285Seric 		mci->mci_flags |= MCIF_EXPN;
27359285Seric }
27459285Seric /*
27559285Seric **  SMTPMAILFROM -- send MAIL command
27659285Seric **
27759285Seric **	Parameters:
27859285Seric **		m -- the mailer.
27959285Seric **		mci -- the mailer connection structure.
28059285Seric **		e -- the envelope (including the sender to specify).
28159285Seric */
28259285Seric 
28353751Seric smtpmailfrom(m, mci, e)
28453751Seric 	struct mailer *m;
28554967Seric 	MCI *mci;
28653751Seric 	ENVELOPE *e;
28753751Seric {
28853751Seric 	int r;
28953751Seric 	char buf[MAXNAME];
29059285Seric 	char optbuf[MAXLINE];
29153751Seric 
29263753Seric 	if (tTd(18, 2))
29357943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
29457943Seric 
29559285Seric 	/* set up appropriate options to include */
29659285Seric 	if (bitset(MCIF_SIZE, mci->mci_flags))
29759285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
29859285Seric 	else
29959285Seric 		strcpy(optbuf, "");
30059285Seric 
3019315Seric 	/*
3024865Seric 	**  Send the MAIL command.
3034865Seric 	**	Designates the sender.
3044865Seric 	*/
3054796Seric 
30653751Seric 	mci->mci_state = MCIS_ACTIVE;
30753751Seric 
30859540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
30959540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
31058680Seric 		(void) strcpy(buf, "");
31158680Seric 	else
31258680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
31353751Seric 	if (e->e_from.q_mailer == LocalMailer ||
31410688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
3158436Seric 	{
31659285Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf);
3178436Seric 	}
3188436Seric 	else
3198436Seric 	{
32059285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
32159285Seric 			buf[0] == '@' ? ',' : ':', buf, optbuf);
3228436Seric 	}
32361093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
32453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
32559285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
3268005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
32753751Seric 	{
32853751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
32953751Seric 		mci->mci_errno = errno;
33053751Seric 		smtpquit(m, mci, e);
33153751Seric 		return EX_TEMPFAIL;
33253751Seric 	}
3337963Seric 	else if (r == 250)
33453751Seric 	{
33553751Seric 		mci->mci_exitstat = EX_OK;
33653751Seric 		return EX_OK;
33753751Seric 	}
3387963Seric 	else if (r == 552)
33953751Seric 	{
34053751Seric 		/* signal service unavailable */
34153751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
34253751Seric 		smtpquit(m, mci, e);
34353751Seric 		return EX_UNAVAILABLE;
34453751Seric 	}
34514913Seric 
34658008Seric #ifdef LOG
34758020Seric 	if (LogLevel > 1)
34858008Seric 	{
34958008Seric 		syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
35058008Seric 			e->e_id, SmtpReplyBuffer);
35158008Seric 	}
35258008Seric #endif
35358008Seric 
35414913Seric 	/* protocol error -- close up */
35553751Seric 	smtpquit(m, mci, e);
35653751Seric 	mci->mci_exitstat = EX_PROTOCOL;
35753751Seric 	return EX_PROTOCOL;
3584684Seric }
3594684Seric /*
3604976Seric **  SMTPRCPT -- designate recipient.
3614797Seric **
3624797Seric **	Parameters:
3634865Seric **		to -- address of recipient.
36410175Seric **		m -- the mailer we are sending to.
36557379Seric **		mci -- the connection info for this transaction.
36657379Seric **		e -- the envelope for this transaction.
3674797Seric **
3684797Seric **	Returns:
3694865Seric **		exit status corresponding to recipient status.
3704797Seric **
3714797Seric **	Side Effects:
3724865Seric **		Sends the mail via SMTP.
3734797Seric */
3744797Seric 
37553751Seric smtprcpt(to, m, mci, e)
3764865Seric 	ADDRESS *to;
37710175Seric 	register MAILER *m;
37854967Seric 	MCI *mci;
37953751Seric 	ENVELOPE *e;
3804797Seric {
3814797Seric 	register int r;
3824797Seric 
38353751Seric 	smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
3844865Seric 
38561093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
38653751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
38759285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
3888005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3894865Seric 		return (EX_TEMPFAIL);
3907963Seric 	else if (REPLYTYPE(r) == 2)
3917963Seric 		return (EX_OK);
3927964Seric 	else if (r == 550 || r == 551 || r == 553)
3937964Seric 		return (EX_NOUSER);
3947964Seric 	else if (r == 552 || r == 554)
3957964Seric 		return (EX_UNAVAILABLE);
39658008Seric 
39758008Seric #ifdef LOG
39858020Seric 	if (LogLevel > 1)
39958008Seric 	{
40058008Seric 		syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
40158008Seric 			e->e_id, SmtpReplyBuffer);
40258008Seric 	}
40358008Seric #endif
40458008Seric 
4057964Seric 	return (EX_PROTOCOL);
4064797Seric }
4074797Seric /*
40810175Seric **  SMTPDATA -- send the data and clean up the transaction.
4094684Seric **
4104684Seric **	Parameters:
4114865Seric **		m -- mailer being sent to.
4126980Seric **		e -- the envelope for this message.
4134684Seric **
4144684Seric **	Returns:
4154976Seric **		exit status corresponding to DATA command.
4164684Seric **
4174684Seric **	Side Effects:
4184865Seric **		none.
4194684Seric */
4204684Seric 
42163753Seric static jmp_buf	CtxDataTimeout;
42263937Seric static int	datatimeout();
42363753Seric 
42453740Seric smtpdata(m, mci, e)
4254865Seric 	struct mailer *m;
42654967Seric 	register MCI *mci;
4276980Seric 	register ENVELOPE *e;
4284684Seric {
4294684Seric 	register int r;
43063753Seric 	register EVENT *ev;
43163753Seric 	time_t timeout;
4324684Seric 
4334797Seric 	/*
4344797Seric 	**  Send the data.
43510175Seric 	**	First send the command and check that it is ok.
43610175Seric 	**	Then send the data.
43710175Seric 	**	Follow it up with a dot to terminate.
43810175Seric 	**	Finally get the results of the transaction.
4394797Seric 	*/
4404797Seric 
44110175Seric 	/* send the command and check ok to proceed */
44253751Seric 	smtpmessage("DATA", m, mci);
44361093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
44453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
44559285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
4468005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
44757990Seric 	{
44857990Seric 		smtpquit(m, mci, e);
4494797Seric 		return (EX_TEMPFAIL);
45057990Seric 	}
4517963Seric 	else if (r == 554)
45257990Seric 	{
45357990Seric 		smtprset(m, mci, e);
4547963Seric 		return (EX_UNAVAILABLE);
45557990Seric 	}
4567963Seric 	else if (r != 354)
45757990Seric 	{
45858008Seric #ifdef LOG
45958020Seric 		if (LogLevel > 1)
46058008Seric 		{
46158008Seric 			syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
46258008Seric 				e->e_id, SmtpReplyBuffer);
46358008Seric 		}
46458008Seric #endif
46557990Seric 		smtprset(m, mci, e);
4667964Seric 		return (EX_PROTOCOL);
46757990Seric 	}
46810175Seric 
46963757Seric 	/*
47063757Seric 	**  Set timeout around data writes.  Make it at least large
47163757Seric 	**  enough for DNS timeouts on all recipients plus some fudge
47263757Seric 	**  factor.  The main thing is that it should not be infinite.
47363757Seric 	*/
47463757Seric 
47563753Seric 	if (setjmp(CtxDataTimeout) != 0)
47663753Seric 	{
47763753Seric 		mci->mci_errno = errno;
47863753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
47963753Seric 		mci->mci_state = MCIS_ERROR;
48063753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
48163753Seric 		smtpquit(m, mci, e);
48263753Seric 		return EX_TEMPFAIL;
48363753Seric 	}
48463753Seric 
48563787Seric 	timeout = e->e_msgsize / 16;
48663787Seric 	if (timeout < (time_t) 60)
48763787Seric 		timeout = (time_t) 60;
48863787Seric 	timeout += e->e_nrcpts * 90;
48963753Seric 	ev = setevent(timeout, datatimeout, 0);
49063753Seric 
49110175Seric 	/* now output the actual message */
49253751Seric 	(*e->e_puthdr)(mci->mci_out, m, e);
49353740Seric 	putline("\n", mci->mci_out, m);
49459730Seric 	(*e->e_putbody)(mci->mci_out, m, e, NULL);
49510175Seric 
49663753Seric 	clrevent(ev);
49763753Seric 
49810175Seric 	/* terminate the message */
49953740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
50063753Seric 	if (TrafficLogFile != NULL)
50163753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
50258120Seric 	if (Verbose)
50358151Seric 		nmessage(">>> .");
50410175Seric 
50510175Seric 	/* check for the results of the transaction */
50661093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
50753751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
50859285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
50953751Seric 	if (r < 0)
51057990Seric 	{
51157990Seric 		smtpquit(m, mci, e);
5124797Seric 		return (EX_TEMPFAIL);
51357990Seric 	}
51453751Seric 	mci->mci_state = MCIS_OPEN;
51558917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
51653751Seric 	if (REPLYTYPE(r) == 4)
51753751Seric 		return (EX_TEMPFAIL);
5187963Seric 	else if (r == 250)
5197963Seric 		return (EX_OK);
5207963Seric 	else if (r == 552 || r == 554)
5217963Seric 		return (EX_UNAVAILABLE);
52258008Seric #ifdef LOG
52358020Seric 	if (LogLevel > 1)
52458008Seric 	{
52558008Seric 		syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
52658008Seric 			e->e_id, SmtpReplyBuffer);
52758008Seric 	}
52858008Seric #endif
5297964Seric 	return (EX_PROTOCOL);
5304684Seric }
53163753Seric 
53263753Seric 
53363753Seric static int
53463753Seric datatimeout()
53563753Seric {
53663753Seric 	longjmp(CtxDataTimeout, 1);
53763753Seric }
5384684Seric /*
5394865Seric **  SMTPQUIT -- close the SMTP connection.
5404865Seric **
5414865Seric **	Parameters:
54215535Seric **		m -- a pointer to the mailer.
5434865Seric **
5444865Seric **	Returns:
5454865Seric **		none.
5464865Seric **
5474865Seric **	Side Effects:
5484865Seric **		sends the final protocol and closes the connection.
5494865Seric */
5504865Seric 
55153751Seric smtpquit(m, mci, e)
55253751Seric 	register MAILER *m;
55354967Seric 	register MCI *mci;
55453751Seric 	ENVELOPE *e;
5554865Seric {
5569391Seric 	int i;
5574865Seric 
55854967Seric 	/* send the quit message if we haven't gotten I/O error */
55953751Seric 	if (mci->mci_state != MCIS_ERROR)
5609391Seric 	{
56161093Seric 		SmtpPhase = "client QUIT";
56253751Seric 		smtpmessage("QUIT", m, mci);
56359285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
56453740Seric 		if (mci->mci_state == MCIS_CLOSED)
56510159Seric 			return;
5669391Seric 	}
5679391Seric 
56852676Seric 	/* now actually close the connection and pick up the zombie */
56958846Seric 	i = endmailer(mci, e, m->m_argv);
5709391Seric 	if (i != EX_OK)
57158151Seric 		syserr("451 smtpquit %s: stat %d", m->m_argv[0], i);
5724865Seric }
5734865Seric /*
57454967Seric **  SMTPRSET -- send a RSET (reset) command
57554967Seric */
57654967Seric 
57754967Seric smtprset(m, mci, e)
57854967Seric 	register MAILER *m;
57954967Seric 	register MCI *mci;
58054967Seric 	ENVELOPE *e;
58154967Seric {
58254967Seric 	int r;
58354967Seric 
58461093Seric 	SmtpPhase = "client RSET";
58554967Seric 	smtpmessage("RSET", m, mci);
58659285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
58757734Seric 	if (r < 0)
58857734Seric 		mci->mci_state = MCIS_ERROR;
58954967Seric 	else if (REPLYTYPE(r) == 2)
59057734Seric 	{
59157734Seric 		mci->mci_state = MCIS_OPEN;
59257734Seric 		return;
59357734Seric 	}
59457734Seric 	smtpquit(m, mci, e);
59554967Seric }
59654967Seric /*
59758867Seric **  SMTPPROBE -- check the connection state
59854967Seric */
59954967Seric 
60058867Seric smtpprobe(mci)
60154967Seric 	register MCI *mci;
60254967Seric {
60354967Seric 	int r;
60454967Seric 	MAILER *m = mci->mci_mailer;
60554967Seric 	extern ENVELOPE BlankEnvelope;
60654967Seric 	ENVELOPE *e = &BlankEnvelope;
60754967Seric 
60861093Seric 	SmtpPhase = "client probe";
60958867Seric 	smtpmessage("RSET", m, mci);
61059285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
61158061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
61254967Seric 		smtpquit(m, mci, e);
61354967Seric 	return r;
61454967Seric }
61554967Seric /*
6164684Seric **  REPLY -- read arpanet reply
6174684Seric **
6184684Seric **	Parameters:
61910175Seric **		m -- the mailer we are reading the reply from.
62057379Seric **		mci -- the mailer connection info structure.
62157379Seric **		e -- the current envelope.
62257379Seric **		timeout -- the timeout for reads.
62359285Seric **		pfunc -- processing function for second and subsequent
62459285Seric **			lines of response -- if null, no special
62559285Seric **			processing is done.
6264684Seric **
6274684Seric **	Returns:
6284684Seric **		reply code it reads.
6294684Seric **
6304684Seric **	Side Effects:
6314684Seric **		flushes the mail file.
6324684Seric */
6334684Seric 
63459285Seric reply(m, mci, e, timeout, pfunc)
63553751Seric 	MAILER *m;
63654967Seric 	MCI *mci;
63753751Seric 	ENVELOPE *e;
63859285Seric 	time_t timeout;
63959285Seric 	void (*pfunc)();
6404684Seric {
64159014Seric 	register char *bufp;
64259014Seric 	register int r;
64359285Seric 	bool firstline = TRUE;
64458957Seric 	char junkbuf[MAXLINE];
64558957Seric 
64657379Seric 	if (mci->mci_out != NULL)
64757379Seric 		(void) fflush(mci->mci_out);
6484684Seric 
6497677Seric 	if (tTd(18, 1))
6504796Seric 		printf("reply\n");
6514796Seric 
6527356Seric 	/*
6537356Seric 	**  Read the input line, being careful not to hang.
6547356Seric 	*/
6557356Seric 
65659014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
6574684Seric 	{
6587356Seric 		register char *p;
65953751Seric 		extern time_t curtime();
6604684Seric 
6617685Seric 		/* actually do the read */
66253751Seric 		if (e->e_xfp != NULL)
66353751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
6647356Seric 
66510054Seric 		/* if we are in the process of closing just give the code */
66653740Seric 		if (mci->mci_state == MCIS_CLOSED)
66710054Seric 			return (SMTPCLOSING);
66810054Seric 
66958680Seric 		if (mci->mci_out != NULL)
67058680Seric 			fflush(mci->mci_out);
67158680Seric 
67210054Seric 		/* get the line from the other side */
67361093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
67453751Seric 		mci->mci_lastuse = curtime();
67553751Seric 
67610054Seric 		if (p == NULL)
67710131Seric 		{
67863753Seric 			bool oldholderrs;
67910148Seric 			extern char MsgBuf[];		/* err.c */
68010148Seric 
68121065Seric 			/* if the remote end closed early, fake an error */
68221065Seric 			if (errno == 0)
68321065Seric # ifdef ECONNRESET
68421065Seric 				errno = ECONNRESET;
68556795Seric # else /* ECONNRESET */
68621065Seric 				errno = EPIPE;
68756795Seric # endif /* ECONNRESET */
68821065Seric 
68957379Seric 			mci->mci_errno = errno;
69057642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
69163753Seric 			oldholderrs = HoldErrs;
69263753Seric 			HoldErrs = TRUE;
69363753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
69463753Seric 
69510420Seric 			/* if debugging, pause so we can see state */
69610420Seric 			if (tTd(18, 100))
69710420Seric 				pause();
69854967Seric 			mci->mci_state = MCIS_ERROR;
69953751Seric 			smtpquit(m, mci, e);
70063753Seric #ifdef XDEBUG
70163753Seric 			{
70263753Seric 				char wbuf[MAXLINE];
70363753Seric 				sprintf(wbuf, "%s... reply(%s) during %s",
70463753Seric 					e->e_to, mci->mci_host, SmtpPhase);
70563753Seric 				checkfd012(wbuf);
70663753Seric 			}
70763753Seric #endif
70863753Seric 			HoldErrs = oldholderrs;
70910054Seric 			return (-1);
71010131Seric 		}
71158957Seric 		fixcrlf(bufp, TRUE);
71210054Seric 
71363753Seric 		/* EHLO failure is not a real error */
71463753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
71563753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
71614900Seric 		{
71714900Seric 			/* serious error -- log the previous command */
718*64071Seric 			if (SmtpNeedIntro)
719*64071Seric 			{
720*64071Seric 				/* inform user who we are chatting with */
721*64071Seric 				fprintf(CurEnv->e_xfp,
722*64071Seric 					"... while talking to %s:\n",
723*64071Seric 					CurHostName);
724*64071Seric 				SmtpNeedIntro = FALSE;
725*64071Seric 			}
72659014Seric 			if (SmtpMsgBuffer[0] != '\0')
72759014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
72859014Seric 			SmtpMsgBuffer[0] = '\0';
72914900Seric 
73014900Seric 			/* now log the message as from the other side */
73158957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
73214900Seric 		}
73314900Seric 
73414900Seric 		/* display the input for verbose mode */
73558120Seric 		if (Verbose)
73659956Seric 			nmessage("050 %s", bufp);
7377356Seric 
73859285Seric 		/* process the line */
73959285Seric 		if (pfunc != NULL && !firstline)
74059285Seric 			(*pfunc)(bufp, m, mci, e);
74159285Seric 
74259285Seric 		firstline = FALSE;
74359285Seric 
7447356Seric 		/* if continuation is required, we can go on */
74559014Seric 		if (bufp[3] == '-')
7464684Seric 			continue;
7477356Seric 
74859014Seric 		/* ignore improperly formated input */
74959014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
75059014Seric 			continue;
75159014Seric 
7527356Seric 		/* decode the reply code */
75359014Seric 		r = atoi(bufp);
7547356Seric 
7557356Seric 		/* extra semantics: 0xx codes are "informational" */
75659014Seric 		if (r >= 100)
75759014Seric 			break;
75859014Seric 	}
7597356Seric 
76059014Seric 	/*
76159014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
76259014Seric 	**  line of the response from here on out.
76359014Seric 	*/
76458061Seric 
76559014Seric 	/* save temporary failure messages for posterity */
76659014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
76759014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
7689391Seric 
76959014Seric 	/* reply code 421 is "Service Shutting Down" */
77059014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
77159014Seric 	{
77259014Seric 		/* send the quit protocol */
77359014Seric 		mci->mci_state = MCIS_SSD;
77459014Seric 		smtpquit(m, mci, e);
7754684Seric 	}
77659014Seric 
77759014Seric 	return (r);
7784684Seric }
7794796Seric /*
7804865Seric **  SMTPMESSAGE -- send message to server
7814796Seric **
7824796Seric **	Parameters:
7834796Seric **		f -- format
78410175Seric **		m -- the mailer to control formatting.
7854796Seric **		a, b, c -- parameters
7864796Seric **
7874796Seric **	Returns:
7884796Seric **		none.
7894796Seric **
7904796Seric **	Side Effects:
79153740Seric **		writes message to mci->mci_out.
7924796Seric */
7934796Seric 
7944865Seric /*VARARGS1*/
79557642Seric #ifdef __STDC__
79657642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
79757642Seric #else
79857642Seric smtpmessage(f, m, mci, va_alist)
7994796Seric 	char *f;
80010175Seric 	MAILER *m;
80154967Seric 	MCI *mci;
80257642Seric 	va_dcl
80357642Seric #endif
8044796Seric {
80556852Seric 	VA_LOCAL_DECL
80656852Seric 
80757135Seric 	VA_START(mci);
80856852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
80956852Seric 	VA_END;
81058680Seric 
81158120Seric 	if (tTd(18, 1) || Verbose)
81258151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
81363753Seric 	if (TrafficLogFile != NULL)
81463753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
81553740Seric 	if (mci->mci_out != NULL)
81658680Seric 	{
81753740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
81854967Seric 			m == NULL ? "\r\n" : m->m_eol);
81958680Seric 	}
82059149Seric 	else if (tTd(18, 1))
82158725Seric 	{
82259149Seric 		printf("smtpmessage: NULL mci_out\n");
82358725Seric 	}
8244796Seric }
8255182Seric 
82656795Seric # endif /* SMTP */
827