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*63753Seric static char sccsid[] = "@(#)usersmtp.c	8.2 (Berkeley) 07/11/93 (with SMTP)";
1433731Sbostic #else
15*63753Seric static char sccsid[] = "@(#)usersmtp.c	8.2 (Berkeley) 07/11/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 */
3858671Seric 
3958671Seric #ifdef __STDC__
4058671Seric extern	smtpmessage(char *f, MAILER *m, MCI *mci, ...);
4158671Seric #endif
429391Seric /*
434865Seric **  SMTPINIT -- initialize SMTP.
444684Seric **
454865Seric **	Opens the connection and sends the initial protocol.
464684Seric **
474684Seric **	Parameters:
484865Seric **		m -- mailer to create connection to.
494865Seric **		pvp -- pointer to parameter vector to pass to
504865Seric **			the mailer.
514684Seric **
524684Seric **	Returns:
5354967Seric **		none.
544684Seric **
554684Seric **	Side Effects:
564865Seric **		creates connection and sends initial protocol.
574684Seric */
584684Seric 
5954967Seric smtpinit(m, mci, e)
604865Seric 	struct mailer *m;
6154967Seric 	register MCI *mci;
6253751Seric 	ENVELOPE *e;
634684Seric {
644865Seric 	register int r;
6558957Seric 	register char *p;
6660210Seric 	extern void esmtp_check();
6759285Seric 	extern void helo_options();
684684Seric 
69*63753Seric 	if (tTd(18, 1))
7057379Seric 	{
7157379Seric 		printf("smtpinit ");
7257379Seric 		mci_dump(mci);
7357379Seric 	}
7457379Seric 
754865Seric 	/*
764865Seric 	**  Open the connection to the mailer.
774865Seric 	*/
784684Seric 
7921065Seric 	SmtpError[0] = '\0';
8057379Seric 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
8154967Seric 	switch (mci->mci_state)
826051Seric 	{
8354967Seric 	  case MCIS_ACTIVE:
8454967Seric 		/* need to clear old information */
8554967Seric 		smtprset(m, mci, e);
8657734Seric 		/* fall through */
8715139Seric 
8854967Seric 	  case MCIS_OPEN:
8954967Seric 		return;
9054967Seric 
9154967Seric 	  case MCIS_ERROR:
9254967Seric 	  case MCIS_SSD:
9354967Seric 		/* shouldn't happen */
9454967Seric 		smtpquit(m, mci, e);
9557734Seric 		/* fall through */
9654967Seric 
9754967Seric 	  case MCIS_CLOSED:
9858151Seric 		syserr("451 smtpinit: state CLOSED");
9954967Seric 		return;
10054967Seric 
10154967Seric 	  case MCIS_OPENING:
10254967Seric 		break;
1036051Seric 	}
1044796Seric 
10554967Seric 	mci->mci_state = MCIS_OPENING;
10654967Seric 
1074865Seric 	/*
1084865Seric 	**  Get the greeting message.
10914913Seric 	**	This should appear spontaneously.  Give it five minutes to
11014886Seric 	**	happen.
1114865Seric 	*/
1124797Seric 
11361093Seric 	SmtpPhase = mci->mci_phase = "client greeting";
11453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
11560210Seric 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
1168005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
11752104Seric 		goto tempfail1;
1184684Seric 
1194865Seric 	/*
1204976Seric 	**  Send the HELO command.
1217963Seric 	**	My mother taught me to always introduce myself.
1224976Seric 	*/
1234976Seric 
12459285Seric 	if (bitnset(M_ESMTP, m->m_flags))
12559285Seric 		mci->mci_flags |= MCIF_ESMTP;
12659285Seric 
12759285Seric tryhelo:
12859285Seric 	if (bitset(MCIF_ESMTP, mci->mci_flags))
12959285Seric 	{
13059285Seric 		smtpmessage("EHLO %s", m, mci, MyHostName);
13161093Seric 		SmtpPhase = mci->mci_phase = "client EHLO";
13259285Seric 	}
13359285Seric 	else
13459285Seric 	{
13559285Seric 		smtpmessage("HELO %s", m, mci, MyHostName);
13661093Seric 		SmtpPhase = mci->mci_phase = "client HELO";
13759285Seric 	}
13853751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
13959285Seric 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1408005Seric 	if (r < 0)
14152104Seric 		goto tempfail1;
1428005Seric 	else if (REPLYTYPE(r) == 5)
14359285Seric 	{
14459285Seric 		if (bitset(MCIF_ESMTP, mci->mci_flags))
14559285Seric 		{
14659285Seric 			/* try old SMTP instead */
14759285Seric 			mci->mci_flags &= ~MCIF_ESMTP;
14859285Seric 			goto tryhelo;
14959285Seric 		}
15014913Seric 		goto unavailable;
15159285Seric 	}
1527963Seric 	else if (REPLYTYPE(r) != 2)
15352104Seric 		goto tempfail1;
1544976Seric 
1554976Seric 	/*
15658957Seric 	**  Check to see if we actually ended up talking to ourself.
15758957Seric 	**  This means we didn't know about an alias or MX, or we managed
15858957Seric 	**  to connect to an echo server.
15958957Seric 	*/
16058957Seric 
16159026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16258957Seric 	if (p != NULL)
16361707Seric 		*p = '\0';
16459026Seric 	if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
16558957Seric 	{
16658957Seric 		syserr("553 %s config error: mail loops back to myself",
16758957Seric 			MyHostName);
16858957Seric 		mci->mci_exitstat = EX_CONFIG;
16958957Seric 		mci->mci_errno = 0;
17058957Seric 		smtpquit(m, mci, e);
17158957Seric 		return;
17258957Seric 	}
17358957Seric 
17458957Seric 	/*
1759315Seric 	**  If this is expected to be another sendmail, send some internal
1769315Seric 	**  commands.
1779315Seric 	*/
1789315Seric 
17910688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1809315Seric 	{
1819315Seric 		/* tell it to be verbose */
18253751Seric 		smtpmessage("VERB", m, mci);
18359285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1849315Seric 		if (r < 0)
18552104Seric 			goto tempfail2;
1869315Seric 	}
1879315Seric 
18853751Seric 	mci->mci_state = MCIS_OPEN;
18954967Seric 	return;
19053751Seric 
19153751Seric   tempfail1:
19253751Seric   tempfail2:
19353751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
19457379Seric 	if (mci->mci_errno == 0)
19557379Seric 		mci->mci_errno = errno;
19657379Seric 	if (mci->mci_state != MCIS_CLOSED)
19757379Seric 		smtpquit(m, mci, e);
19854967Seric 	return;
19953751Seric 
20053751Seric   unavailable:
20153751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
20253751Seric 	mci->mci_errno = errno;
20353751Seric 	smtpquit(m, mci, e);
20454967Seric 	return;
20553751Seric }
20659285Seric /*
20760210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
20860210Seric **
20960210Seric **
21060210Seric **	Parameters:
21160210Seric **		line -- the response line.
21260210Seric **		m -- the mailer.
21360210Seric **		mci -- the mailer connection info.
21460210Seric **		e -- the envelope.
21560210Seric **
21660210Seric **	Returns:
21760210Seric **		none.
21860210Seric */
21960210Seric 
22060210Seric void
22160210Seric esmtp_check(line, m, mci, e)
22260210Seric 	char *line;
22360210Seric 	MAILER *m;
22460210Seric 	register MCI *mci;
22560210Seric 	ENVELOPE *e;
22660210Seric {
22760210Seric 	if (strlen(line) < 5)
22860210Seric 		return;
22960210Seric 	line += 4;
23060210Seric 	if (strncmp(line, "ESMTP ", 6) == 0)
23160210Seric 		mci->mci_flags |= MCIF_ESMTP;
23260210Seric }
23360210Seric /*
23459285Seric **  HELO_OPTIONS -- process the options on a HELO line.
23559285Seric **
23659285Seric **	Parameters:
23759285Seric **		line -- the response line.
23859285Seric **		m -- the mailer.
23959285Seric **		mci -- the mailer connection info.
24059285Seric **		e -- the envelope.
24159285Seric **
24259285Seric **	Returns:
24359285Seric **		none.
24459285Seric */
24553751Seric 
24659285Seric void
24759285Seric helo_options(line, m, mci, e)
24859285Seric 	char *line;
24959285Seric 	MAILER *m;
25059285Seric 	register MCI *mci;
25159285Seric 	ENVELOPE *e;
25259285Seric {
25359285Seric 	register char *p;
25459285Seric 
25559285Seric 	if (strlen(line) < 5)
25659285Seric 		return;
25759285Seric 	line += 4;
25859285Seric 	p = strchr(line, ' ');
25959285Seric 	if (p != NULL)
26059285Seric 		*p++ = '\0';
26159285Seric 	if (strcasecmp(line, "size") == 0)
26259285Seric 	{
26359285Seric 		mci->mci_flags |= MCIF_SIZE;
26459285Seric 		if (p != NULL)
26559285Seric 			mci->mci_maxsize = atol(p);
26659285Seric 	}
26759285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
26859285Seric 		mci->mci_flags |= MCIF_8BITMIME;
26959285Seric 	else if (strcasecmp(line, "expn") == 0)
27059285Seric 		mci->mci_flags |= MCIF_EXPN;
27159285Seric }
27259285Seric /*
27359285Seric **  SMTPMAILFROM -- send MAIL command
27459285Seric **
27559285Seric **	Parameters:
27659285Seric **		m -- the mailer.
27759285Seric **		mci -- the mailer connection structure.
27859285Seric **		e -- the envelope (including the sender to specify).
27959285Seric */
28059285Seric 
28153751Seric smtpmailfrom(m, mci, e)
28253751Seric 	struct mailer *m;
28354967Seric 	MCI *mci;
28453751Seric 	ENVELOPE *e;
28553751Seric {
28653751Seric 	int r;
28753751Seric 	char buf[MAXNAME];
28859285Seric 	char optbuf[MAXLINE];
28953751Seric 
290*63753Seric 	if (tTd(18, 2))
29157943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
29257943Seric 
29359285Seric 	/* set up appropriate options to include */
29459285Seric 	if (bitset(MCIF_SIZE, mci->mci_flags))
29559285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
29659285Seric 	else
29759285Seric 		strcpy(optbuf, "");
29859285Seric 
2999315Seric 	/*
3004865Seric 	**  Send the MAIL command.
3014865Seric 	**	Designates the sender.
3024865Seric 	*/
3034796Seric 
30453751Seric 	mci->mci_state = MCIS_ACTIVE;
30553751Seric 
30659540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
30759540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
30858680Seric 		(void) strcpy(buf, "");
30958680Seric 	else
31058680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
31153751Seric 	if (e->e_from.q_mailer == LocalMailer ||
31210688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
3138436Seric 	{
31459285Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf);
3158436Seric 	}
3168436Seric 	else
3178436Seric 	{
31859285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
31959285Seric 			buf[0] == '@' ? ',' : ':', buf, optbuf);
3208436Seric 	}
32161093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
32253751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
32359285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
3248005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
32553751Seric 	{
32653751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
32753751Seric 		mci->mci_errno = errno;
32853751Seric 		smtpquit(m, mci, e);
32953751Seric 		return EX_TEMPFAIL;
33053751Seric 	}
3317963Seric 	else if (r == 250)
33253751Seric 	{
33353751Seric 		mci->mci_exitstat = EX_OK;
33453751Seric 		return EX_OK;
33553751Seric 	}
3367963Seric 	else if (r == 552)
33753751Seric 	{
33853751Seric 		/* signal service unavailable */
33953751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
34053751Seric 		smtpquit(m, mci, e);
34153751Seric 		return EX_UNAVAILABLE;
34253751Seric 	}
34314913Seric 
34458008Seric #ifdef LOG
34558020Seric 	if (LogLevel > 1)
34658008Seric 	{
34758008Seric 		syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
34858008Seric 			e->e_id, SmtpReplyBuffer);
34958008Seric 	}
35058008Seric #endif
35158008Seric 
35214913Seric 	/* protocol error -- close up */
35353751Seric 	smtpquit(m, mci, e);
35453751Seric 	mci->mci_exitstat = EX_PROTOCOL;
35553751Seric 	return EX_PROTOCOL;
3564684Seric }
3574684Seric /*
3584976Seric **  SMTPRCPT -- designate recipient.
3594797Seric **
3604797Seric **	Parameters:
3614865Seric **		to -- address of recipient.
36210175Seric **		m -- the mailer we are sending to.
36357379Seric **		mci -- the connection info for this transaction.
36457379Seric **		e -- the envelope for this transaction.
3654797Seric **
3664797Seric **	Returns:
3674865Seric **		exit status corresponding to recipient status.
3684797Seric **
3694797Seric **	Side Effects:
3704865Seric **		Sends the mail via SMTP.
3714797Seric */
3724797Seric 
37353751Seric smtprcpt(to, m, mci, e)
3744865Seric 	ADDRESS *to;
37510175Seric 	register MAILER *m;
37654967Seric 	MCI *mci;
37753751Seric 	ENVELOPE *e;
3784797Seric {
3794797Seric 	register int r;
3804797Seric 
38153751Seric 	smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
3824865Seric 
38361093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
38453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
38559285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
3868005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3874865Seric 		return (EX_TEMPFAIL);
3887963Seric 	else if (REPLYTYPE(r) == 2)
3897963Seric 		return (EX_OK);
3907964Seric 	else if (r == 550 || r == 551 || r == 553)
3917964Seric 		return (EX_NOUSER);
3927964Seric 	else if (r == 552 || r == 554)
3937964Seric 		return (EX_UNAVAILABLE);
39458008Seric 
39558008Seric #ifdef LOG
39658020Seric 	if (LogLevel > 1)
39758008Seric 	{
39858008Seric 		syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
39958008Seric 			e->e_id, SmtpReplyBuffer);
40058008Seric 	}
40158008Seric #endif
40258008Seric 
4037964Seric 	return (EX_PROTOCOL);
4044797Seric }
4054797Seric /*
40610175Seric **  SMTPDATA -- send the data and clean up the transaction.
4074684Seric **
4084684Seric **	Parameters:
4094865Seric **		m -- mailer being sent to.
4106980Seric **		e -- the envelope for this message.
4114684Seric **
4124684Seric **	Returns:
4134976Seric **		exit status corresponding to DATA command.
4144684Seric **
4154684Seric **	Side Effects:
4164865Seric **		none.
4174684Seric */
4184684Seric 
419*63753Seric static jmp_buf	CtxDataTimeout;
420*63753Seric 
42153740Seric smtpdata(m, mci, e)
4224865Seric 	struct mailer *m;
42354967Seric 	register MCI *mci;
4246980Seric 	register ENVELOPE *e;
4254684Seric {
4264684Seric 	register int r;
427*63753Seric 	register EVENT *ev;
428*63753Seric 	time_t timeout;
429*63753Seric 	static int datatimeout();
4304684Seric 
4314797Seric 	/*
4324797Seric 	**  Send the data.
43310175Seric 	**	First send the command and check that it is ok.
43410175Seric 	**	Then send the data.
43510175Seric 	**	Follow it up with a dot to terminate.
43610175Seric 	**	Finally get the results of the transaction.
4374797Seric 	*/
4384797Seric 
43910175Seric 	/* send the command and check ok to proceed */
44053751Seric 	smtpmessage("DATA", m, mci);
44161093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
44253751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
44359285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
4448005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
44557990Seric 	{
44657990Seric 		smtpquit(m, mci, e);
4474797Seric 		return (EX_TEMPFAIL);
44857990Seric 	}
4497963Seric 	else if (r == 554)
45057990Seric 	{
45157990Seric 		smtprset(m, mci, e);
4527963Seric 		return (EX_UNAVAILABLE);
45357990Seric 	}
4547963Seric 	else if (r != 354)
45557990Seric 	{
45658008Seric #ifdef LOG
45758020Seric 		if (LogLevel > 1)
45858008Seric 		{
45958008Seric 			syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
46058008Seric 				e->e_id, SmtpReplyBuffer);
46158008Seric 		}
46258008Seric #endif
46357990Seric 		smtprset(m, mci, e);
4647964Seric 		return (EX_PROTOCOL);
46557990Seric 	}
46610175Seric 
467*63753Seric 	if (setjmp(CtxDataTimeout) != 0)
468*63753Seric 	{
469*63753Seric 		mci->mci_errno = errno;
470*63753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
471*63753Seric 		mci->mci_state = MCIS_ERROR;
472*63753Seric #ifdef LOG
473*63753Seric 		syslog(LOG_NOTICE, "%s: timeout writing message to %s",
474*63753Seric 			e->e_id, mci->mci_host);
475*63753Seric #endif
476*63753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
477*63753Seric 		smtpquit(m, mci, e);
478*63753Seric 		return EX_TEMPFAIL;
479*63753Seric 	}
480*63753Seric 
481*63753Seric 	timeout = e->e_msgsize / 64;
482*63753Seric 	if (timeout < (time_t) 60)
483*63753Seric 		timeout = 60;
484*63753Seric 	ev = setevent(timeout, datatimeout, 0);
485*63753Seric 
48610175Seric 	/* now output the actual message */
48753751Seric 	(*e->e_puthdr)(mci->mci_out, m, e);
48853740Seric 	putline("\n", mci->mci_out, m);
48959730Seric 	(*e->e_putbody)(mci->mci_out, m, e, NULL);
49010175Seric 
491*63753Seric 	clrevent(ev);
492*63753Seric 
49310175Seric 	/* terminate the message */
49453740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
495*63753Seric 	if (TrafficLogFile != NULL)
496*63753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
49758120Seric 	if (Verbose)
49858151Seric 		nmessage(">>> .");
49910175Seric 
50010175Seric 	/* check for the results of the transaction */
50161093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
50253751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
50359285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
50453751Seric 	if (r < 0)
50557990Seric 	{
50657990Seric 		smtpquit(m, mci, e);
5074797Seric 		return (EX_TEMPFAIL);
50857990Seric 	}
50953751Seric 	mci->mci_state = MCIS_OPEN;
51058917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
51153751Seric 	if (REPLYTYPE(r) == 4)
51253751Seric 		return (EX_TEMPFAIL);
5137963Seric 	else if (r == 250)
5147963Seric 		return (EX_OK);
5157963Seric 	else if (r == 552 || r == 554)
5167963Seric 		return (EX_UNAVAILABLE);
51758008Seric #ifdef LOG
51858020Seric 	if (LogLevel > 1)
51958008Seric 	{
52058008Seric 		syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
52158008Seric 			e->e_id, SmtpReplyBuffer);
52258008Seric 	}
52358008Seric #endif
5247964Seric 	return (EX_PROTOCOL);
5254684Seric }
526*63753Seric 
527*63753Seric 
528*63753Seric static int
529*63753Seric datatimeout()
530*63753Seric {
531*63753Seric 	longjmp(CtxDataTimeout, 1);
532*63753Seric }
5334684Seric /*
5344865Seric **  SMTPQUIT -- close the SMTP connection.
5354865Seric **
5364865Seric **	Parameters:
53715535Seric **		m -- a pointer to the mailer.
5384865Seric **
5394865Seric **	Returns:
5404865Seric **		none.
5414865Seric **
5424865Seric **	Side Effects:
5434865Seric **		sends the final protocol and closes the connection.
5444865Seric */
5454865Seric 
54653751Seric smtpquit(m, mci, e)
54753751Seric 	register MAILER *m;
54854967Seric 	register MCI *mci;
54953751Seric 	ENVELOPE *e;
5504865Seric {
5519391Seric 	int i;
5524865Seric 
55354967Seric 	/* send the quit message if we haven't gotten I/O error */
55453751Seric 	if (mci->mci_state != MCIS_ERROR)
5559391Seric 	{
55661093Seric 		SmtpPhase = "client QUIT";
55753751Seric 		smtpmessage("QUIT", m, mci);
55859285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
55953740Seric 		if (mci->mci_state == MCIS_CLOSED)
56010159Seric 			return;
5619391Seric 	}
5629391Seric 
56352676Seric 	/* now actually close the connection and pick up the zombie */
56458846Seric 	i = endmailer(mci, e, m->m_argv);
5659391Seric 	if (i != EX_OK)
56658151Seric 		syserr("451 smtpquit %s: stat %d", m->m_argv[0], i);
5674865Seric }
5684865Seric /*
56954967Seric **  SMTPRSET -- send a RSET (reset) command
57054967Seric */
57154967Seric 
57254967Seric smtprset(m, mci, e)
57354967Seric 	register MAILER *m;
57454967Seric 	register MCI *mci;
57554967Seric 	ENVELOPE *e;
57654967Seric {
57754967Seric 	int r;
57854967Seric 
57961093Seric 	SmtpPhase = "client RSET";
58054967Seric 	smtpmessage("RSET", m, mci);
58159285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
58257734Seric 	if (r < 0)
58357734Seric 		mci->mci_state = MCIS_ERROR;
58454967Seric 	else if (REPLYTYPE(r) == 2)
58557734Seric 	{
58657734Seric 		mci->mci_state = MCIS_OPEN;
58757734Seric 		return;
58857734Seric 	}
58957734Seric 	smtpquit(m, mci, e);
59054967Seric }
59154967Seric /*
59258867Seric **  SMTPPROBE -- check the connection state
59354967Seric */
59454967Seric 
59558867Seric smtpprobe(mci)
59654967Seric 	register MCI *mci;
59754967Seric {
59854967Seric 	int r;
59954967Seric 	MAILER *m = mci->mci_mailer;
60054967Seric 	extern ENVELOPE BlankEnvelope;
60154967Seric 	ENVELOPE *e = &BlankEnvelope;
60254967Seric 
60361093Seric 	SmtpPhase = "client probe";
60458867Seric 	smtpmessage("RSET", m, mci);
60559285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
60658061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
60754967Seric 		smtpquit(m, mci, e);
60854967Seric 	return r;
60954967Seric }
61054967Seric /*
6114684Seric **  REPLY -- read arpanet reply
6124684Seric **
6134684Seric **	Parameters:
61410175Seric **		m -- the mailer we are reading the reply from.
61557379Seric **		mci -- the mailer connection info structure.
61657379Seric **		e -- the current envelope.
61757379Seric **		timeout -- the timeout for reads.
61859285Seric **		pfunc -- processing function for second and subsequent
61959285Seric **			lines of response -- if null, no special
62059285Seric **			processing is done.
6214684Seric **
6224684Seric **	Returns:
6234684Seric **		reply code it reads.
6244684Seric **
6254684Seric **	Side Effects:
6264684Seric **		flushes the mail file.
6274684Seric */
6284684Seric 
62959285Seric reply(m, mci, e, timeout, pfunc)
63053751Seric 	MAILER *m;
63154967Seric 	MCI *mci;
63253751Seric 	ENVELOPE *e;
63359285Seric 	time_t timeout;
63459285Seric 	void (*pfunc)();
6354684Seric {
63659014Seric 	register char *bufp;
63759014Seric 	register int r;
63859285Seric 	bool firstline = TRUE;
63958957Seric 	char junkbuf[MAXLINE];
64058957Seric 
64157379Seric 	if (mci->mci_out != NULL)
64257379Seric 		(void) fflush(mci->mci_out);
6434684Seric 
6447677Seric 	if (tTd(18, 1))
6454796Seric 		printf("reply\n");
6464796Seric 
6477356Seric 	/*
6487356Seric 	**  Read the input line, being careful not to hang.
6497356Seric 	*/
6507356Seric 
65159014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
6524684Seric 	{
6537356Seric 		register char *p;
65453751Seric 		extern time_t curtime();
6554684Seric 
6567685Seric 		/* actually do the read */
65753751Seric 		if (e->e_xfp != NULL)
65853751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
6597356Seric 
66010054Seric 		/* if we are in the process of closing just give the code */
66153740Seric 		if (mci->mci_state == MCIS_CLOSED)
66210054Seric 			return (SMTPCLOSING);
66310054Seric 
66458680Seric 		if (mci->mci_out != NULL)
66558680Seric 			fflush(mci->mci_out);
66658680Seric 
66710054Seric 		/* get the line from the other side */
66861093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
66953751Seric 		mci->mci_lastuse = curtime();
67053751Seric 
67110054Seric 		if (p == NULL)
67210131Seric 		{
673*63753Seric 			bool oldholderrs;
67410148Seric 			extern char MsgBuf[];		/* err.c */
67510148Seric 
67621065Seric 			/* if the remote end closed early, fake an error */
67721065Seric 			if (errno == 0)
67821065Seric # ifdef ECONNRESET
67921065Seric 				errno = ECONNRESET;
68056795Seric # else /* ECONNRESET */
68121065Seric 				errno = EPIPE;
68256795Seric # endif /* ECONNRESET */
68321065Seric 
68457379Seric 			mci->mci_errno = errno;
68557642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
686*63753Seric 			oldholderrs = HoldErrs;
687*63753Seric 			HoldErrs = TRUE;
688*63753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
689*63753Seric 
69010420Seric 			/* if debugging, pause so we can see state */
69110420Seric 			if (tTd(18, 100))
69210420Seric 				pause();
69354967Seric 			mci->mci_state = MCIS_ERROR;
69453751Seric 			smtpquit(m, mci, e);
695*63753Seric #ifdef XDEBUG
696*63753Seric 			{
697*63753Seric 				char wbuf[MAXLINE];
698*63753Seric 				sprintf(wbuf, "%s... reply(%s) during %s",
699*63753Seric 					e->e_to, mci->mci_host, SmtpPhase);
700*63753Seric 				checkfd012(wbuf);
701*63753Seric 			}
702*63753Seric #endif
703*63753Seric 			HoldErrs = oldholderrs;
70410054Seric 			return (-1);
70510131Seric 		}
70658957Seric 		fixcrlf(bufp, TRUE);
70710054Seric 
708*63753Seric 		/* EHLO failure is not a real error */
709*63753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
710*63753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
71114900Seric 		{
71214900Seric 			/* serious error -- log the previous command */
71359014Seric 			if (SmtpMsgBuffer[0] != '\0')
71459014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
71559014Seric 			SmtpMsgBuffer[0] = '\0';
71614900Seric 
71714900Seric 			/* now log the message as from the other side */
71858957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
71914900Seric 		}
72014900Seric 
72114900Seric 		/* display the input for verbose mode */
72258120Seric 		if (Verbose)
72359956Seric 			nmessage("050 %s", bufp);
7247356Seric 
72559285Seric 		/* process the line */
72659285Seric 		if (pfunc != NULL && !firstline)
72759285Seric 			(*pfunc)(bufp, m, mci, e);
72859285Seric 
72959285Seric 		firstline = FALSE;
73059285Seric 
7317356Seric 		/* if continuation is required, we can go on */
73259014Seric 		if (bufp[3] == '-')
7334684Seric 			continue;
7347356Seric 
73559014Seric 		/* ignore improperly formated input */
73659014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
73759014Seric 			continue;
73859014Seric 
7397356Seric 		/* decode the reply code */
74059014Seric 		r = atoi(bufp);
7417356Seric 
7427356Seric 		/* extra semantics: 0xx codes are "informational" */
74359014Seric 		if (r >= 100)
74459014Seric 			break;
74559014Seric 	}
7467356Seric 
74759014Seric 	/*
74859014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
74959014Seric 	**  line of the response from here on out.
75059014Seric 	*/
75158061Seric 
75259014Seric 	/* save temporary failure messages for posterity */
75359014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
75459014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
7559391Seric 
75659014Seric 	/* reply code 421 is "Service Shutting Down" */
75759014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
75859014Seric 	{
75959014Seric 		/* send the quit protocol */
76059014Seric 		mci->mci_state = MCIS_SSD;
76159014Seric 		smtpquit(m, mci, e);
7624684Seric 	}
76359014Seric 
76459014Seric 	return (r);
7654684Seric }
7664796Seric /*
7674865Seric **  SMTPMESSAGE -- send message to server
7684796Seric **
7694796Seric **	Parameters:
7704796Seric **		f -- format
77110175Seric **		m -- the mailer to control formatting.
7724796Seric **		a, b, c -- parameters
7734796Seric **
7744796Seric **	Returns:
7754796Seric **		none.
7764796Seric **
7774796Seric **	Side Effects:
77853740Seric **		writes message to mci->mci_out.
7794796Seric */
7804796Seric 
7814865Seric /*VARARGS1*/
78257642Seric #ifdef __STDC__
78357642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
78457642Seric #else
78557642Seric smtpmessage(f, m, mci, va_alist)
7864796Seric 	char *f;
78710175Seric 	MAILER *m;
78854967Seric 	MCI *mci;
78957642Seric 	va_dcl
79057642Seric #endif
7914796Seric {
79256852Seric 	VA_LOCAL_DECL
79356852Seric 
79457135Seric 	VA_START(mci);
79556852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
79656852Seric 	VA_END;
79758680Seric 
79858120Seric 	if (tTd(18, 1) || Verbose)
79958151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
800*63753Seric 	if (TrafficLogFile != NULL)
801*63753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
80253740Seric 	if (mci->mci_out != NULL)
80358680Seric 	{
80453740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
80554967Seric 			m == NULL ? "\r\n" : m->m_eol);
80658680Seric 	}
80759149Seric 	else if (tTd(18, 1))
80858725Seric 	{
80959149Seric 		printf("smtpmessage: NULL mci_out\n");
81058725Seric 	}
8114796Seric }
8125182Seric 
81356795Seric # endif /* SMTP */
814