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*64750Seric static char sccsid[] = "@(#)usersmtp.c	8.13 (Berkeley) 10/24/93 (with SMTP)";
1433731Sbostic #else
15*64750Seric static char sccsid[] = "@(#)usersmtp.c	8.13 (Berkeley) 10/24/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 */
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);
118*64750Seric 	if (r < 0 || REPLYTYPE(r) == 4)
11952104Seric 		goto tempfail1;
120*64750Seric 	if (REPLYTYPE(r) != 2)
121*64750Seric 		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 
19553751Seric 	mci->mci_state = MCIS_OPEN;
19654967Seric 	return;
19753751Seric 
19853751Seric   tempfail1:
19953751Seric   tempfail2:
20053751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
20157379Seric 	if (mci->mci_errno == 0)
20257379Seric 		mci->mci_errno = errno;
20357379Seric 	if (mci->mci_state != MCIS_CLOSED)
20457379Seric 		smtpquit(m, mci, e);
20554967Seric 	return;
20653751Seric 
20753751Seric   unavailable:
20853751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
20953751Seric 	mci->mci_errno = errno;
21053751Seric 	smtpquit(m, mci, e);
21154967Seric 	return;
21253751Seric }
21359285Seric /*
21460210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
21560210Seric **
21660210Seric **
21760210Seric **	Parameters:
21860210Seric **		line -- the response line.
21960210Seric **		m -- the mailer.
22060210Seric **		mci -- the mailer connection info.
22160210Seric **		e -- the envelope.
22260210Seric **
22360210Seric **	Returns:
22460210Seric **		none.
22560210Seric */
22660210Seric 
22760210Seric void
22860210Seric esmtp_check(line, m, mci, e)
22960210Seric 	char *line;
23060210Seric 	MAILER *m;
23160210Seric 	register MCI *mci;
23260210Seric 	ENVELOPE *e;
23360210Seric {
23460210Seric 	if (strlen(line) < 5)
23560210Seric 		return;
23660210Seric 	line += 4;
23760210Seric 	if (strncmp(line, "ESMTP ", 6) == 0)
23860210Seric 		mci->mci_flags |= MCIF_ESMTP;
23960210Seric }
24060210Seric /*
24159285Seric **  HELO_OPTIONS -- process the options on a HELO line.
24259285Seric **
24359285Seric **	Parameters:
24459285Seric **		line -- the response line.
24559285Seric **		m -- the mailer.
24659285Seric **		mci -- the mailer connection info.
24759285Seric **		e -- the envelope.
24859285Seric **
24959285Seric **	Returns:
25059285Seric **		none.
25159285Seric */
25253751Seric 
25359285Seric void
25459285Seric helo_options(line, m, mci, e)
25559285Seric 	char *line;
25659285Seric 	MAILER *m;
25759285Seric 	register MCI *mci;
25859285Seric 	ENVELOPE *e;
25959285Seric {
26059285Seric 	register char *p;
26159285Seric 
26259285Seric 	if (strlen(line) < 5)
26359285Seric 		return;
26459285Seric 	line += 4;
26559285Seric 	p = strchr(line, ' ');
26659285Seric 	if (p != NULL)
26759285Seric 		*p++ = '\0';
26859285Seric 	if (strcasecmp(line, "size") == 0)
26959285Seric 	{
27059285Seric 		mci->mci_flags |= MCIF_SIZE;
27159285Seric 		if (p != NULL)
27259285Seric 			mci->mci_maxsize = atol(p);
27359285Seric 	}
27459285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
27559285Seric 		mci->mci_flags |= MCIF_8BITMIME;
27659285Seric 	else if (strcasecmp(line, "expn") == 0)
27759285Seric 		mci->mci_flags |= MCIF_EXPN;
27859285Seric }
27959285Seric /*
28059285Seric **  SMTPMAILFROM -- send MAIL command
28159285Seric **
28259285Seric **	Parameters:
28359285Seric **		m -- the mailer.
28459285Seric **		mci -- the mailer connection structure.
28559285Seric **		e -- the envelope (including the sender to specify).
28659285Seric */
28759285Seric 
28853751Seric smtpmailfrom(m, mci, e)
28953751Seric 	struct mailer *m;
29054967Seric 	MCI *mci;
29153751Seric 	ENVELOPE *e;
29253751Seric {
29353751Seric 	int r;
29453751Seric 	char buf[MAXNAME];
29559285Seric 	char optbuf[MAXLINE];
29653751Seric 
29763753Seric 	if (tTd(18, 2))
29857943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
29957943Seric 
30059285Seric 	/* set up appropriate options to include */
30164254Seric 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
30259285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
30359285Seric 	else
30459285Seric 		strcpy(optbuf, "");
30559285Seric 
3069315Seric 	/*
3074865Seric 	**  Send the MAIL command.
3084865Seric 	**	Designates the sender.
3094865Seric 	*/
3104796Seric 
31153751Seric 	mci->mci_state = MCIS_ACTIVE;
31253751Seric 
31359540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
31459540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
31558680Seric 		(void) strcpy(buf, "");
31658680Seric 	else
31758680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
31853751Seric 	if (e->e_from.q_mailer == LocalMailer ||
31910688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
3208436Seric 	{
32159285Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf);
3228436Seric 	}
3238436Seric 	else
3248436Seric 	{
32559285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
32659285Seric 			buf[0] == '@' ? ',' : ':', buf, optbuf);
3278436Seric 	}
32861093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
32953751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
33059285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
3318005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
33253751Seric 	{
33353751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
33453751Seric 		mci->mci_errno = errno;
33553751Seric 		smtpquit(m, mci, e);
33653751Seric 		return EX_TEMPFAIL;
33753751Seric 	}
3387963Seric 	else if (r == 250)
33953751Seric 	{
34053751Seric 		mci->mci_exitstat = EX_OK;
34153751Seric 		return EX_OK;
34253751Seric 	}
3437963Seric 	else if (r == 552)
34453751Seric 	{
34553751Seric 		/* signal service unavailable */
34653751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
34753751Seric 		smtpquit(m, mci, e);
34853751Seric 		return EX_UNAVAILABLE;
34953751Seric 	}
35014913Seric 
35158008Seric #ifdef LOG
35258020Seric 	if (LogLevel > 1)
35358008Seric 	{
35458008Seric 		syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
35558008Seric 			e->e_id, SmtpReplyBuffer);
35658008Seric 	}
35758008Seric #endif
35858008Seric 
35914913Seric 	/* protocol error -- close up */
36053751Seric 	smtpquit(m, mci, e);
36153751Seric 	mci->mci_exitstat = EX_PROTOCOL;
36253751Seric 	return EX_PROTOCOL;
3634684Seric }
3644684Seric /*
3654976Seric **  SMTPRCPT -- designate recipient.
3664797Seric **
3674797Seric **	Parameters:
3684865Seric **		to -- address of recipient.
36910175Seric **		m -- the mailer we are sending to.
37057379Seric **		mci -- the connection info for this transaction.
37157379Seric **		e -- the envelope for this transaction.
3724797Seric **
3734797Seric **	Returns:
3744865Seric **		exit status corresponding to recipient status.
3754797Seric **
3764797Seric **	Side Effects:
3774865Seric **		Sends the mail via SMTP.
3784797Seric */
3794797Seric 
38053751Seric smtprcpt(to, m, mci, e)
3814865Seric 	ADDRESS *to;
38210175Seric 	register MAILER *m;
38354967Seric 	MCI *mci;
38453751Seric 	ENVELOPE *e;
3854797Seric {
3864797Seric 	register int r;
3874797Seric 
38853751Seric 	smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
3894865Seric 
39061093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
39153751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
39259285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
3938005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3944865Seric 		return (EX_TEMPFAIL);
3957963Seric 	else if (REPLYTYPE(r) == 2)
3967963Seric 		return (EX_OK);
3977964Seric 	else if (r == 550 || r == 551 || r == 553)
3987964Seric 		return (EX_NOUSER);
3997964Seric 	else if (r == 552 || r == 554)
4007964Seric 		return (EX_UNAVAILABLE);
40158008Seric 
40258008Seric #ifdef LOG
40358020Seric 	if (LogLevel > 1)
40458008Seric 	{
40558008Seric 		syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
40658008Seric 			e->e_id, SmtpReplyBuffer);
40758008Seric 	}
40858008Seric #endif
40958008Seric 
4107964Seric 	return (EX_PROTOCOL);
4114797Seric }
4124797Seric /*
41310175Seric **  SMTPDATA -- send the data and clean up the transaction.
4144684Seric **
4154684Seric **	Parameters:
4164865Seric **		m -- mailer being sent to.
4176980Seric **		e -- the envelope for this message.
4184684Seric **
4194684Seric **	Returns:
4204976Seric **		exit status corresponding to DATA command.
4214684Seric **
4224684Seric **	Side Effects:
4234865Seric **		none.
4244684Seric */
4254684Seric 
42663753Seric static jmp_buf	CtxDataTimeout;
42763937Seric static int	datatimeout();
42863753Seric 
42953740Seric smtpdata(m, mci, e)
4304865Seric 	struct mailer *m;
43154967Seric 	register MCI *mci;
4326980Seric 	register ENVELOPE *e;
4334684Seric {
4344684Seric 	register int r;
43563753Seric 	register EVENT *ev;
43663753Seric 	time_t timeout;
4374684Seric 
4384797Seric 	/*
4394797Seric 	**  Send the data.
44010175Seric 	**	First send the command and check that it is ok.
44110175Seric 	**	Then send the data.
44210175Seric 	**	Follow it up with a dot to terminate.
44310175Seric 	**	Finally get the results of the transaction.
4444797Seric 	*/
4454797Seric 
44610175Seric 	/* send the command and check ok to proceed */
44753751Seric 	smtpmessage("DATA", m, mci);
44861093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
44953751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
45059285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
4518005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
45257990Seric 	{
45357990Seric 		smtpquit(m, mci, e);
4544797Seric 		return (EX_TEMPFAIL);
45557990Seric 	}
4567963Seric 	else if (r == 554)
45757990Seric 	{
45857990Seric 		smtprset(m, mci, e);
4597963Seric 		return (EX_UNAVAILABLE);
46057990Seric 	}
4617963Seric 	else if (r != 354)
46257990Seric 	{
46358008Seric #ifdef LOG
46458020Seric 		if (LogLevel > 1)
46558008Seric 		{
46658008Seric 			syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
46758008Seric 				e->e_id, SmtpReplyBuffer);
46858008Seric 		}
46958008Seric #endif
47057990Seric 		smtprset(m, mci, e);
4717964Seric 		return (EX_PROTOCOL);
47257990Seric 	}
47310175Seric 
47463757Seric 	/*
47563757Seric 	**  Set timeout around data writes.  Make it at least large
47663757Seric 	**  enough for DNS timeouts on all recipients plus some fudge
47763757Seric 	**  factor.  The main thing is that it should not be infinite.
47863757Seric 	*/
47963757Seric 
48063753Seric 	if (setjmp(CtxDataTimeout) != 0)
48163753Seric 	{
48263753Seric 		mci->mci_errno = errno;
48363753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
48463753Seric 		mci->mci_state = MCIS_ERROR;
48563753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
48663753Seric 		smtpquit(m, mci, e);
48763753Seric 		return EX_TEMPFAIL;
48863753Seric 	}
48963753Seric 
49063787Seric 	timeout = e->e_msgsize / 16;
49163787Seric 	if (timeout < (time_t) 60)
49263787Seric 		timeout = (time_t) 60;
49363787Seric 	timeout += e->e_nrcpts * 90;
49463753Seric 	ev = setevent(timeout, datatimeout, 0);
49563753Seric 
49610175Seric 	/* now output the actual message */
49753751Seric 	(*e->e_puthdr)(mci->mci_out, m, e);
49853740Seric 	putline("\n", mci->mci_out, m);
49959730Seric 	(*e->e_putbody)(mci->mci_out, m, e, NULL);
50010175Seric 
50163753Seric 	clrevent(ev);
50263753Seric 
50364718Seric 	if (ferror(mci->mci_out))
50464718Seric 	{
50564718Seric 		/* error during processing -- don't send the dot */
50664718Seric 		mci->mci_errno = EIO;
50764718Seric 		mci->mci_exitstat = EX_IOERR;
50864718Seric 		mci->mci_state = MCIS_ERROR;
50964718Seric 		smtpquit(m, mci, e);
51064718Seric 		return EX_IOERR;
51164718Seric 	}
51264718Seric 
51310175Seric 	/* terminate the message */
51453740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
51563753Seric 	if (TrafficLogFile != NULL)
51663753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
51758120Seric 	if (Verbose)
51858151Seric 		nmessage(">>> .");
51910175Seric 
52010175Seric 	/* check for the results of the transaction */
52161093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
52253751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
52359285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
52453751Seric 	if (r < 0)
52557990Seric 	{
52657990Seric 		smtpquit(m, mci, e);
5274797Seric 		return (EX_TEMPFAIL);
52857990Seric 	}
52953751Seric 	mci->mci_state = MCIS_OPEN;
53058917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
53153751Seric 	if (REPLYTYPE(r) == 4)
53253751Seric 		return (EX_TEMPFAIL);
5337963Seric 	else if (r == 250)
5347963Seric 		return (EX_OK);
5357963Seric 	else if (r == 552 || r == 554)
5367963Seric 		return (EX_UNAVAILABLE);
53758008Seric #ifdef LOG
53858020Seric 	if (LogLevel > 1)
53958008Seric 	{
54058008Seric 		syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
54158008Seric 			e->e_id, SmtpReplyBuffer);
54258008Seric 	}
54358008Seric #endif
5447964Seric 	return (EX_PROTOCOL);
5454684Seric }
54663753Seric 
54763753Seric 
54863753Seric static int
54963753Seric datatimeout()
55063753Seric {
55163753Seric 	longjmp(CtxDataTimeout, 1);
55263753Seric }
5534684Seric /*
5544865Seric **  SMTPQUIT -- close the SMTP connection.
5554865Seric **
5564865Seric **	Parameters:
55715535Seric **		m -- a pointer to the mailer.
5584865Seric **
5594865Seric **	Returns:
5604865Seric **		none.
5614865Seric **
5624865Seric **	Side Effects:
5634865Seric **		sends the final protocol and closes the connection.
5644865Seric */
5654865Seric 
56653751Seric smtpquit(m, mci, e)
56753751Seric 	register MAILER *m;
56854967Seric 	register MCI *mci;
56953751Seric 	ENVELOPE *e;
5704865Seric {
5719391Seric 	int i;
5724865Seric 
57354967Seric 	/* send the quit message if we haven't gotten I/O error */
57453751Seric 	if (mci->mci_state != MCIS_ERROR)
5759391Seric 	{
57661093Seric 		SmtpPhase = "client QUIT";
57753751Seric 		smtpmessage("QUIT", m, mci);
57859285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
57953740Seric 		if (mci->mci_state == MCIS_CLOSED)
58010159Seric 			return;
5819391Seric 	}
5829391Seric 
58352676Seric 	/* now actually close the connection and pick up the zombie */
58458846Seric 	i = endmailer(mci, e, m->m_argv);
5859391Seric 	if (i != EX_OK)
58658151Seric 		syserr("451 smtpquit %s: stat %d", m->m_argv[0], i);
5874865Seric }
5884865Seric /*
58954967Seric **  SMTPRSET -- send a RSET (reset) command
59054967Seric */
59154967Seric 
59254967Seric smtprset(m, mci, e)
59354967Seric 	register MAILER *m;
59454967Seric 	register MCI *mci;
59554967Seric 	ENVELOPE *e;
59654967Seric {
59754967Seric 	int r;
59854967Seric 
59961093Seric 	SmtpPhase = "client RSET";
60054967Seric 	smtpmessage("RSET", m, mci);
60159285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
60257734Seric 	if (r < 0)
60357734Seric 		mci->mci_state = MCIS_ERROR;
60454967Seric 	else if (REPLYTYPE(r) == 2)
60557734Seric 	{
60657734Seric 		mci->mci_state = MCIS_OPEN;
60757734Seric 		return;
60857734Seric 	}
60957734Seric 	smtpquit(m, mci, e);
61054967Seric }
61154967Seric /*
61258867Seric **  SMTPPROBE -- check the connection state
61354967Seric */
61454967Seric 
61558867Seric smtpprobe(mci)
61654967Seric 	register MCI *mci;
61754967Seric {
61854967Seric 	int r;
61954967Seric 	MAILER *m = mci->mci_mailer;
62054967Seric 	extern ENVELOPE BlankEnvelope;
62154967Seric 	ENVELOPE *e = &BlankEnvelope;
62254967Seric 
62361093Seric 	SmtpPhase = "client probe";
62458867Seric 	smtpmessage("RSET", m, mci);
62559285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
62658061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
62754967Seric 		smtpquit(m, mci, e);
62854967Seric 	return r;
62954967Seric }
63054967Seric /*
6314684Seric **  REPLY -- read arpanet reply
6324684Seric **
6334684Seric **	Parameters:
63410175Seric **		m -- the mailer we are reading the reply from.
63557379Seric **		mci -- the mailer connection info structure.
63657379Seric **		e -- the current envelope.
63757379Seric **		timeout -- the timeout for reads.
63859285Seric **		pfunc -- processing function for second and subsequent
63959285Seric **			lines of response -- if null, no special
64059285Seric **			processing is done.
6414684Seric **
6424684Seric **	Returns:
6434684Seric **		reply code it reads.
6444684Seric **
6454684Seric **	Side Effects:
6464684Seric **		flushes the mail file.
6474684Seric */
6484684Seric 
64959285Seric reply(m, mci, e, timeout, pfunc)
65053751Seric 	MAILER *m;
65154967Seric 	MCI *mci;
65253751Seric 	ENVELOPE *e;
65359285Seric 	time_t timeout;
65459285Seric 	void (*pfunc)();
6554684Seric {
65659014Seric 	register char *bufp;
65759014Seric 	register int r;
65859285Seric 	bool firstline = TRUE;
65958957Seric 	char junkbuf[MAXLINE];
66058957Seric 
66157379Seric 	if (mci->mci_out != NULL)
66257379Seric 		(void) fflush(mci->mci_out);
6634684Seric 
6647677Seric 	if (tTd(18, 1))
6654796Seric 		printf("reply\n");
6664796Seric 
6677356Seric 	/*
6687356Seric 	**  Read the input line, being careful not to hang.
6697356Seric 	*/
6707356Seric 
67159014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
6724684Seric 	{
6737356Seric 		register char *p;
67453751Seric 		extern time_t curtime();
6754684Seric 
6767685Seric 		/* actually do the read */
67753751Seric 		if (e->e_xfp != NULL)
67853751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
6797356Seric 
68010054Seric 		/* if we are in the process of closing just give the code */
68153740Seric 		if (mci->mci_state == MCIS_CLOSED)
68210054Seric 			return (SMTPCLOSING);
68310054Seric 
68458680Seric 		if (mci->mci_out != NULL)
68558680Seric 			fflush(mci->mci_out);
68658680Seric 
68710054Seric 		/* get the line from the other side */
68861093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
68953751Seric 		mci->mci_lastuse = curtime();
69053751Seric 
69110054Seric 		if (p == NULL)
69210131Seric 		{
69363753Seric 			bool oldholderrs;
69410148Seric 			extern char MsgBuf[];		/* err.c */
69510148Seric 
69621065Seric 			/* if the remote end closed early, fake an error */
69721065Seric 			if (errno == 0)
69821065Seric # ifdef ECONNRESET
69921065Seric 				errno = ECONNRESET;
70056795Seric # else /* ECONNRESET */
70121065Seric 				errno = EPIPE;
70256795Seric # endif /* ECONNRESET */
70321065Seric 
70457379Seric 			mci->mci_errno = errno;
70557642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
70663753Seric 			oldholderrs = HoldErrs;
70763753Seric 			HoldErrs = TRUE;
70863753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
70963753Seric 
71010420Seric 			/* if debugging, pause so we can see state */
71110420Seric 			if (tTd(18, 100))
71210420Seric 				pause();
71354967Seric 			mci->mci_state = MCIS_ERROR;
71453751Seric 			smtpquit(m, mci, e);
71563753Seric #ifdef XDEBUG
71663753Seric 			{
71763753Seric 				char wbuf[MAXLINE];
71864082Seric 				char *p = wbuf;
71964082Seric 				if (e->e_to != NULL)
72064082Seric 				{
72164082Seric 					sprintf(p, "%s... ", e->e_to);
72264082Seric 					p += strlen(p);
72364082Seric 				}
72464082Seric 				sprintf(p, "reply(%s) during %s",
72564082Seric 					mci->mci_host, SmtpPhase);
72663753Seric 				checkfd012(wbuf);
72763753Seric 			}
72863753Seric #endif
72963753Seric 			HoldErrs = oldholderrs;
73010054Seric 			return (-1);
73110131Seric 		}
73258957Seric 		fixcrlf(bufp, TRUE);
73310054Seric 
73463753Seric 		/* EHLO failure is not a real error */
73563753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
73663753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
73714900Seric 		{
73814900Seric 			/* serious error -- log the previous command */
73964071Seric 			if (SmtpNeedIntro)
74064071Seric 			{
74164071Seric 				/* inform user who we are chatting with */
74264071Seric 				fprintf(CurEnv->e_xfp,
74364071Seric 					"... while talking to %s:\n",
74464071Seric 					CurHostName);
74564071Seric 				SmtpNeedIntro = FALSE;
74664071Seric 			}
74759014Seric 			if (SmtpMsgBuffer[0] != '\0')
74859014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
74959014Seric 			SmtpMsgBuffer[0] = '\0';
75014900Seric 
75114900Seric 			/* now log the message as from the other side */
75258957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
75314900Seric 		}
75414900Seric 
75514900Seric 		/* display the input for verbose mode */
75658120Seric 		if (Verbose)
75759956Seric 			nmessage("050 %s", bufp);
7587356Seric 
75959285Seric 		/* process the line */
76059285Seric 		if (pfunc != NULL && !firstline)
76159285Seric 			(*pfunc)(bufp, m, mci, e);
76259285Seric 
76359285Seric 		firstline = FALSE;
76459285Seric 
7657356Seric 		/* if continuation is required, we can go on */
76659014Seric 		if (bufp[3] == '-')
7674684Seric 			continue;
7687356Seric 
76959014Seric 		/* ignore improperly formated input */
77059014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
77159014Seric 			continue;
77259014Seric 
7737356Seric 		/* decode the reply code */
77459014Seric 		r = atoi(bufp);
7757356Seric 
7767356Seric 		/* extra semantics: 0xx codes are "informational" */
77759014Seric 		if (r >= 100)
77859014Seric 			break;
77959014Seric 	}
7807356Seric 
78159014Seric 	/*
78259014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
78359014Seric 	**  line of the response from here on out.
78459014Seric 	*/
78558061Seric 
78659014Seric 	/* save temporary failure messages for posterity */
78759014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
78859014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
7899391Seric 
79059014Seric 	/* reply code 421 is "Service Shutting Down" */
79159014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
79259014Seric 	{
79359014Seric 		/* send the quit protocol */
79459014Seric 		mci->mci_state = MCIS_SSD;
79559014Seric 		smtpquit(m, mci, e);
7964684Seric 	}
79759014Seric 
79859014Seric 	return (r);
7994684Seric }
8004796Seric /*
8014865Seric **  SMTPMESSAGE -- send message to server
8024796Seric **
8034796Seric **	Parameters:
8044796Seric **		f -- format
80510175Seric **		m -- the mailer to control formatting.
8064796Seric **		a, b, c -- parameters
8074796Seric **
8084796Seric **	Returns:
8094796Seric **		none.
8104796Seric **
8114796Seric **	Side Effects:
81253740Seric **		writes message to mci->mci_out.
8134796Seric */
8144796Seric 
8154865Seric /*VARARGS1*/
81657642Seric #ifdef __STDC__
81757642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
81857642Seric #else
81957642Seric smtpmessage(f, m, mci, va_alist)
8204796Seric 	char *f;
82110175Seric 	MAILER *m;
82254967Seric 	MCI *mci;
82357642Seric 	va_dcl
82457642Seric #endif
8254796Seric {
82656852Seric 	VA_LOCAL_DECL
82756852Seric 
82857135Seric 	VA_START(mci);
82956852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
83056852Seric 	VA_END;
83158680Seric 
83258120Seric 	if (tTd(18, 1) || Verbose)
83358151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
83463753Seric 	if (TrafficLogFile != NULL)
83563753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
83653740Seric 	if (mci->mci_out != NULL)
83758680Seric 	{
83853740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
83954967Seric 			m == NULL ? "\r\n" : m->m_eol);
84058680Seric 	}
84159149Seric 	else if (tTd(18, 1))
84258725Seric 	{
84359149Seric 		printf("smtpmessage: NULL mci_out\n");
84458725Seric 	}
8454796Seric }
8465182Seric 
84756795Seric # endif /* SMTP */
848