122716Sdist /*
268839Seric  * Copyright (c) 1983, 1995 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*69928Seric static char sccsid[] = "@(#)usersmtp.c	8.57 (Berkeley) 06/19/95 (with SMTP)";
1433731Sbostic #else
15*69928Seric static char sccsid[] = "@(#)usersmtp.c	8.57 (Berkeley) 06/19/95 (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 
4069748Seric extern void	smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
419391Seric /*
424865Seric **  SMTPINIT -- initialize SMTP.
434684Seric **
444865Seric **	Opens the connection and sends the initial protocol.
454684Seric **
464684Seric **	Parameters:
474865Seric **		m -- mailer to create connection to.
484865Seric **		pvp -- pointer to parameter vector to pass to
494865Seric **			the mailer.
504684Seric **
514684Seric **	Returns:
5254967Seric **		none.
534684Seric **
544684Seric **	Side Effects:
554865Seric **		creates connection and sends initial protocol.
564684Seric */
574684Seric 
5869748Seric void
smtpinit(m,mci,e)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 
6963753Seric 	if (tTd(18, 1))
7057379Seric 	{
7157379Seric 		printf("smtpinit ");
7264731Seric 		mci_dump(mci, FALSE);
7357379Seric 	}
7457379Seric 
754865Seric 	/*
764865Seric 	**  Open the connection to the mailer.
774865Seric 	*/
784684Seric 
7921065Seric 	SmtpError[0] = '\0';
8057379Seric 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
8168100Seric 	if (CurHostName == NULL)
8268100Seric 		CurHostName = MyHostName;
8364071Seric 	SmtpNeedIntro = TRUE;
8454967Seric 	switch (mci->mci_state)
856051Seric 	{
8654967Seric 	  case MCIS_ACTIVE:
8754967Seric 		/* need to clear old information */
8854967Seric 		smtprset(m, mci, e);
8957734Seric 		/* fall through */
9015139Seric 
9154967Seric 	  case MCIS_OPEN:
9254967Seric 		return;
9354967Seric 
9454967Seric 	  case MCIS_ERROR:
9554967Seric 	  case MCIS_SSD:
9654967Seric 		/* shouldn't happen */
9754967Seric 		smtpquit(m, mci, e);
9857734Seric 		/* fall through */
9954967Seric 
10054967Seric 	  case MCIS_CLOSED:
10158151Seric 		syserr("451 smtpinit: state CLOSED");
10254967Seric 		return;
10354967Seric 
10454967Seric 	  case MCIS_OPENING:
10554967Seric 		break;
1066051Seric 	}
1074796Seric 
10854967Seric 	mci->mci_state = MCIS_OPENING;
10954967Seric 
1104865Seric 	/*
1114865Seric 	**  Get the greeting message.
11214913Seric 	**	This should appear spontaneously.  Give it five minutes to
11314886Seric 	**	happen.
1144865Seric 	*/
1154797Seric 
11661093Seric 	SmtpPhase = mci->mci_phase = "client greeting";
11753751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
11860210Seric 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
11964750Seric 	if (r < 0 || REPLYTYPE(r) == 4)
12052104Seric 		goto tempfail1;
12164750Seric 	if (REPLYTYPE(r) != 2)
12264750Seric 		goto unavailable;
1234684Seric 
1244865Seric 	/*
1254976Seric 	**  Send the HELO command.
1267963Seric 	**	My mother taught me to always introduce myself.
1274976Seric 	*/
1284976Seric 
12959285Seric 	if (bitnset(M_ESMTP, m->m_flags))
13059285Seric 		mci->mci_flags |= MCIF_ESMTP;
13159285Seric 
13259285Seric tryhelo:
13359285Seric 	if (bitset(MCIF_ESMTP, mci->mci_flags))
13459285Seric 	{
13559285Seric 		smtpmessage("EHLO %s", m, mci, MyHostName);
13661093Seric 		SmtpPhase = mci->mci_phase = "client EHLO";
13759285Seric 	}
13859285Seric 	else
13959285Seric 	{
14059285Seric 		smtpmessage("HELO %s", m, mci, MyHostName);
14161093Seric 		SmtpPhase = mci->mci_phase = "client HELO";
14259285Seric 	}
14353751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
14459285Seric 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1458005Seric 	if (r < 0)
14652104Seric 		goto tempfail1;
1478005Seric 	else if (REPLYTYPE(r) == 5)
14859285Seric 	{
14959285Seric 		if (bitset(MCIF_ESMTP, mci->mci_flags))
15059285Seric 		{
15159285Seric 			/* try old SMTP instead */
15259285Seric 			mci->mci_flags &= ~MCIF_ESMTP;
15359285Seric 			goto tryhelo;
15459285Seric 		}
15514913Seric 		goto unavailable;
15659285Seric 	}
1577963Seric 	else if (REPLYTYPE(r) != 2)
15852104Seric 		goto tempfail1;
1594976Seric 
1604976Seric 	/*
16158957Seric 	**  Check to see if we actually ended up talking to ourself.
16258957Seric 	**  This means we didn't know about an alias or MX, or we managed
16358957Seric 	**  to connect to an echo server.
16458957Seric 	*/
16558957Seric 
16659026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16758957Seric 	if (p != NULL)
16861707Seric 		*p = '\0';
16967472Seric 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
17067472Seric 	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
17158957Seric 	{
17258957Seric 		syserr("553 %s config error: mail loops back to myself",
17358957Seric 			MyHostName);
17458957Seric 		mci->mci_exitstat = EX_CONFIG;
17558957Seric 		mci->mci_errno = 0;
17658957Seric 		smtpquit(m, mci, e);
17758957Seric 		return;
17858957Seric 	}
17958957Seric 
18058957Seric 	/*
1819315Seric 	**  If this is expected to be another sendmail, send some internal
1829315Seric 	**  commands.
1839315Seric 	*/
1849315Seric 
18510688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1869315Seric 	{
1879315Seric 		/* tell it to be verbose */
18853751Seric 		smtpmessage("VERB", m, mci);
18959285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1909315Seric 		if (r < 0)
19152104Seric 			goto tempfail2;
1929315Seric 	}
1939315Seric 
19465057Seric 	if (mci->mci_state != MCIS_CLOSED)
19565057Seric 	{
19665057Seric 		mci->mci_state = MCIS_OPEN;
19765057Seric 		return;
19865057Seric 	}
19953751Seric 
20065057Seric 	/* got a 421 error code during startup */
20165057Seric 
20253751Seric   tempfail1:
20353751Seric   tempfail2:
20453751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
20557379Seric 	if (mci->mci_errno == 0)
20657379Seric 		mci->mci_errno = errno;
20757379Seric 	if (mci->mci_state != MCIS_CLOSED)
20857379Seric 		smtpquit(m, mci, e);
20954967Seric 	return;
21053751Seric 
21153751Seric   unavailable:
21253751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
21353751Seric 	mci->mci_errno = errno;
21453751Seric 	smtpquit(m, mci, e);
21554967Seric 	return;
21653751Seric }
21759285Seric /*
21860210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
21960210Seric **
22060210Seric **	Parameters:
22160210Seric **		line -- the response line.
22267893Seric **		firstline -- set if this is the first line of the reply.
22360210Seric **		m -- the mailer.
22460210Seric **		mci -- the mailer connection info.
22560210Seric **		e -- the envelope.
22660210Seric **
22760210Seric **	Returns:
22860210Seric **		none.
22960210Seric */
23060210Seric 
23160210Seric void
esmtp_check(line,firstline,m,mci,e)23267893Seric esmtp_check(line, firstline, m, mci, e)
23360210Seric 	char *line;
23467893Seric 	bool firstline;
23560210Seric 	MAILER *m;
23660210Seric 	register MCI *mci;
23760210Seric 	ENVELOPE *e;
23860210Seric {
23969650Seric 	if (strstr(line, "ESMTP ") != NULL)
24069650Seric 		mci->mci_flags |= MCIF_ESMTP;
241*69928Seric 	if (strstr(line, "8BIT-OK") != NULL)
24269650Seric 		mci->mci_flags |= MCIF_8BITOK;
24360210Seric }
24460210Seric /*
24559285Seric **  HELO_OPTIONS -- process the options on a HELO line.
24659285Seric **
24759285Seric **	Parameters:
24859285Seric **		line -- the response line.
24967893Seric **		firstline -- set if this is the first line of the reply.
25059285Seric **		m -- the mailer.
25159285Seric **		mci -- the mailer connection info.
25259285Seric **		e -- the envelope.
25359285Seric **
25459285Seric **	Returns:
25559285Seric **		none.
25659285Seric */
25753751Seric 
25859285Seric void
helo_options(line,firstline,m,mci,e)25967893Seric helo_options(line, firstline, m, mci, e)
26059285Seric 	char *line;
26167893Seric 	bool firstline;
26259285Seric 	MAILER *m;
26359285Seric 	register MCI *mci;
26459285Seric 	ENVELOPE *e;
26559285Seric {
26659285Seric 	register char *p;
26759285Seric 
26867971Seric 	if (firstline)
26967893Seric 		return;
27067893Seric 
27168706Seric 	if (strlen(line) < (SIZE_T) 5)
27259285Seric 		return;
27359285Seric 	line += 4;
27459285Seric 	p = strchr(line, ' ');
27559285Seric 	if (p != NULL)
27659285Seric 		*p++ = '\0';
27759285Seric 	if (strcasecmp(line, "size") == 0)
27859285Seric 	{
27959285Seric 		mci->mci_flags |= MCIF_SIZE;
28059285Seric 		if (p != NULL)
28159285Seric 			mci->mci_maxsize = atol(p);
28259285Seric 	}
28359285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
28465870Seric 	{
28559285Seric 		mci->mci_flags |= MCIF_8BITMIME;
28665870Seric 		mci->mci_flags &= ~MCIF_7BIT;
28765870Seric 	}
28859285Seric 	else if (strcasecmp(line, "expn") == 0)
28959285Seric 		mci->mci_flags |= MCIF_EXPN;
29068606Seric 	else if (strcasecmp(line, "x-dsn-03") == 0)
29167880Seric 		mci->mci_flags |= MCIF_DSN;
29259285Seric }
29359285Seric /*
29459285Seric **  SMTPMAILFROM -- send MAIL command
29559285Seric **
29659285Seric **	Parameters:
29759285Seric **		m -- the mailer.
29859285Seric **		mci -- the mailer connection structure.
29959285Seric **		e -- the envelope (including the sender to specify).
30059285Seric */
30159285Seric 
30269748Seric int
smtpmailfrom(m,mci,e)30353751Seric smtpmailfrom(m, mci, e)
30453751Seric 	struct mailer *m;
30554967Seric 	MCI *mci;
30653751Seric 	ENVELOPE *e;
30753751Seric {
30853751Seric 	int r;
30965494Seric 	char *bufp;
31067887Seric 	char *bodytype;
31168528Seric 	char buf[MAXNAME + 1];
31259285Seric 	char optbuf[MAXLINE];
31353751Seric 
31463753Seric 	if (tTd(18, 2))
31557943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
31657943Seric 
31759285Seric 	/* set up appropriate options to include */
31864254Seric 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
31959285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
32059285Seric 	else
32159285Seric 		strcpy(optbuf, "");
32259285Seric 
32367887Seric 	bodytype = e->e_bodytype;
32467887Seric 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
32567417Seric 	{
32667887Seric 		if (bodytype == NULL &&
32767887Seric 		    bitset(MM_MIME8BIT, MimeMode) &&
32867887Seric 		    bitset(EF_HAS8BIT, e->e_flags) &&
32969922Seric 		    !bitset(EF_DONT_MIME, e->e_flags) &&
33067887Seric 		    !bitnset(M_8BITS, m->m_flags))
33167887Seric 			bodytype = "8BITMIME";
33267887Seric 		if (bodytype != NULL)
33367417Seric 		{
33467417Seric 			strcat(optbuf, " BODY=");
33567887Seric 			strcat(optbuf, bodytype);
33667417Seric 		}
33767417Seric 	}
33867995Seric 	else if (bitnset(M_8BITS, m->m_flags) ||
339*69928Seric 		 !bitset(EF_HAS8BIT, e->e_flags) ||
340*69928Seric 		 bitset(MCIF_8BITOK, mci->mci_flags))
34167887Seric 	{
34267887Seric 		/* just pass it through */
34367887Seric 	}
34469480Seric #if MIME8TO7
34567887Seric 	else if (bitset(MM_CVTMIME, MimeMode) &&
34669922Seric 		 !bitset(EF_DONT_MIME, e->e_flags) &&
34768884Seric 		 (!bitset(MM_PASS8BIT, MimeMode) ||
34868884Seric 		  bitset(EF_IS_MIME, e->e_flags)))
34967887Seric 	{
35067887Seric 		/* must convert from 8bit MIME format to 7bit encoded */
35167887Seric 		mci->mci_flags |= MCIF_CVT8TO7;
35267887Seric 	}
35369480Seric #endif
35467887Seric 	else if (!bitset(MM_PASS8BIT, MimeMode))
35567887Seric 	{
35667887Seric 		/* cannot just send a 8-bit version */
35767887Seric 		usrerr("%s does not support 8BITMIME", mci->mci_host);
35868857Seric 		mci->mci_status = "5.6.3";
35967887Seric 		return EX_DATAERR;
36067887Seric 	}
36167417Seric 
36267963Seric 	if (bitset(MCIF_DSN, mci->mci_flags))
36367880Seric 	{
36467963Seric 		if (e->e_envid != NULL)
36567963Seric 		{
36667963Seric 			strcat(optbuf, " ENVID=");
36767963Seric 			strcat(optbuf, e->e_envid);
36867963Seric 		}
36968559Seric 
37068559Seric 		/* RET= parameter */
37168559Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
37268559Seric 		{
37368559Seric 			strcat(optbuf, " RET=");
37468559Seric 			if (bitset(EF_NO_BODY_RETN, e->e_flags))
37568559Seric 				strcat(optbuf, "HDRS");
37668559Seric 			else
37768559Seric 				strcat(optbuf, "FULL");
37868559Seric 		}
37967880Seric 	}
38067880Seric 
3819315Seric 	/*
3824865Seric 	**  Send the MAIL command.
3834865Seric 	**	Designates the sender.
3844865Seric 	*/
3854796Seric 
38653751Seric 	mci->mci_state = MCIS_ACTIVE;
38753751Seric 
38859540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
38959540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
39058680Seric 		(void) strcpy(buf, "");
39158680Seric 	else
39268529Seric 		expand("\201g", buf, sizeof buf, e);
39365494Seric 	if (buf[0] == '<')
39465494Seric 	{
39565494Seric 		/* strip off <angle brackets> (put back on below) */
39665494Seric 		bufp = &buf[strlen(buf) - 1];
39765494Seric 		if (*bufp == '>')
39865494Seric 			*bufp = '\0';
39965494Seric 		bufp = &buf[1];
40065494Seric 	}
40165494Seric 	else
40265494Seric 		bufp = buf;
40367472Seric 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
40410688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
4058436Seric 	{
40665494Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
4078436Seric 	}
4088436Seric 	else
4098436Seric 	{
41059285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
41165494Seric 			*bufp == '@' ? ',' : ':', bufp, optbuf);
4128436Seric 	}
41361093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
41453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
41559285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
41668811Seric 	if (r < 0 || r == 421)
41753751Seric 	{
41868811Seric 		/* communications failure/service shutting down */
41953751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
42053751Seric 		mci->mci_errno = errno;
42153751Seric 		smtpquit(m, mci, e);
42253751Seric 		return EX_TEMPFAIL;
42353751Seric 	}
42468811Seric 	else if (REPLYTYPE(r) == 4)
42568811Seric 	{
42668811Seric 		return EX_TEMPFAIL;
42768811Seric 	}
4287963Seric 	else if (r == 250)
42953751Seric 	{
43053751Seric 		return EX_OK;
43153751Seric 	}
43268857Seric 	else if (r == 501)
43368075Seric 	{
43468857Seric 		/* syntax error in arguments */
43568857Seric 		mci->mci_status = "5.5.2";
43668075Seric 		return EX_DATAERR;
43768075Seric 	}
43868857Seric 	else if (r == 553)
43968857Seric 	{
44068857Seric 		/* mailbox name not allowed */
44168857Seric 		mci->mci_status = "5.1.3";
44268857Seric 		return EX_DATAERR;
44368857Seric 	}
4447963Seric 	else if (r == 552)
44553751Seric 	{
44668811Seric 		/* exceeded storage allocation */
44768857Seric 		mci->mci_status = "5.2.2";
44853751Seric 		return EX_UNAVAILABLE;
44953751Seric 	}
45014913Seric 
45158008Seric #ifdef LOG
45258020Seric 	if (LogLevel > 1)
45358008Seric 	{
45467860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s",
45567860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
45658008Seric 	}
45758008Seric #endif
45858008Seric 
45914913Seric 	/* protocol error -- close up */
46053751Seric 	smtpquit(m, mci, e);
46153751Seric 	return EX_PROTOCOL;
4624684Seric }
4634684Seric /*
4644976Seric **  SMTPRCPT -- designate recipient.
4654797Seric **
4664797Seric **	Parameters:
4674865Seric **		to -- address of recipient.
46810175Seric **		m -- the mailer we are sending to.
46957379Seric **		mci -- the connection info for this transaction.
47057379Seric **		e -- the envelope for this transaction.
4714797Seric **
4724797Seric **	Returns:
4734865Seric **		exit status corresponding to recipient status.
4744797Seric **
4754797Seric **	Side Effects:
4764865Seric **		Sends the mail via SMTP.
4774797Seric */
4784797Seric 
47969748Seric int
smtprcpt(to,m,mci,e)48053751Seric smtprcpt(to, m, mci, e)
4814865Seric 	ADDRESS *to;
48210175Seric 	register MAILER *m;
48354967Seric 	MCI *mci;
48453751Seric 	ENVELOPE *e;
4854797Seric {
4864797Seric 	register int r;
48767880Seric 	char optbuf[MAXLINE];
48868857Seric 	extern char *smtptodsn();
4894797Seric 
49067880Seric 	strcpy(optbuf, "");
49167880Seric 	if (bitset(MCIF_DSN, mci->mci_flags))
49267880Seric 	{
49367987Seric 		/* NOTIFY= parameter */
49468603Seric 		if (bitset(QHASNOTIFY, to->q_flags) &&
49568603Seric 		    bitset(QPRIMARY, to->q_flags))
49667880Seric 		{
49768595Seric 			bool firstone = TRUE;
49868595Seric 
49968595Seric 			strcat(optbuf, " NOTIFY=");
50068595Seric 			if (bitset(QPINGONSUCCESS, to->q_flags))
50168595Seric 			{
50268595Seric 				strcat(optbuf, "SUCCESS");
50368595Seric 				firstone = FALSE;
50468595Seric 			}
50568595Seric 			if (bitset(QPINGONFAILURE, to->q_flags))
50668595Seric 			{
50768595Seric 				if (!firstone)
50868595Seric 					strcat(optbuf, ",");
50968595Seric 				strcat(optbuf, "FAILURE");
51068595Seric 				firstone = FALSE;
51168595Seric 			}
51268595Seric 			if (bitset(QPINGONDELAY, to->q_flags))
51368595Seric 			{
51468595Seric 				if (!firstone)
51568595Seric 					strcat(optbuf, ",");
51668595Seric 				strcat(optbuf, "DELAY");
51768595Seric 				firstone = FALSE;
51868595Seric 			}
51968595Seric 			if (firstone)
52068595Seric 				strcat(optbuf, "NEVER");
52167880Seric 		}
52267963Seric 
52367987Seric 		/* ORCPT= parameter */
52467987Seric 		if (to->q_orcpt != NULL)
52567987Seric 		{
52667987Seric 			strcat(optbuf, " ORCPT=");
52767987Seric 			strcat(optbuf, to->q_orcpt);
52867987Seric 		}
52967880Seric 	}
5304865Seric 
53167880Seric 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
53267880Seric 
53361093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
53453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
53559285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
53668867Seric 	to->q_rstatus = newstr(SmtpReplyBuffer);
53768857Seric 	to->q_status = smtptodsn(r);
5388005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
53968857Seric 		return EX_TEMPFAIL;
5407963Seric 	else if (REPLYTYPE(r) == 2)
54168857Seric 		return EX_OK;
5427964Seric 	else if (r == 550 || r == 551 || r == 553)
54368857Seric 		return EX_NOUSER;
5447964Seric 	else if (r == 552 || r == 554)
54568857Seric 		return EX_UNAVAILABLE;
54658008Seric 
54758008Seric #ifdef LOG
54858020Seric 	if (LogLevel > 1)
54958008Seric 	{
55067860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s",
55167860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
55258008Seric 	}
55358008Seric #endif
55458008Seric 
5557964Seric 	return (EX_PROTOCOL);
5564797Seric }
5574797Seric /*
55810175Seric **  SMTPDATA -- send the data and clean up the transaction.
5594684Seric **
5604684Seric **	Parameters:
5614865Seric **		m -- mailer being sent to.
5626980Seric **		e -- the envelope for this message.
5634684Seric **
5644684Seric **	Returns:
5654976Seric **		exit status corresponding to DATA command.
5664684Seric **
5674684Seric **	Side Effects:
5684865Seric **		none.
5694684Seric */
5704684Seric 
57163753Seric static jmp_buf	CtxDataTimeout;
57268433Seric static void	datatimeout();
57363753Seric 
57469748Seric int
smtpdata(m,mci,e)57553740Seric smtpdata(m, mci, e)
5764865Seric 	struct mailer *m;
57754967Seric 	register MCI *mci;
5786980Seric 	register ENVELOPE *e;
5794684Seric {
5804684Seric 	register int r;
58163753Seric 	register EVENT *ev;
58263753Seric 	time_t timeout;
5834684Seric 
5844797Seric 	/*
5854797Seric 	**  Send the data.
58610175Seric 	**	First send the command and check that it is ok.
58710175Seric 	**	Then send the data.
58810175Seric 	**	Follow it up with a dot to terminate.
58910175Seric 	**	Finally get the results of the transaction.
5904797Seric 	*/
5914797Seric 
59210175Seric 	/* send the command and check ok to proceed */
59353751Seric 	smtpmessage("DATA", m, mci);
59461093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
59553751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
59659285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
5978005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
59857990Seric 	{
59957990Seric 		smtpquit(m, mci, e);
6004797Seric 		return (EX_TEMPFAIL);
60157990Seric 	}
6027963Seric 	else if (r == 554)
60357990Seric 	{
60457990Seric 		smtprset(m, mci, e);
6057963Seric 		return (EX_UNAVAILABLE);
60657990Seric 	}
6077963Seric 	else if (r != 354)
60857990Seric 	{
60958008Seric #ifdef LOG
61058020Seric 		if (LogLevel > 1)
61158008Seric 		{
61267860Seric 			syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s",
61367860Seric 				e->e_id, mci->mci_host, SmtpReplyBuffer);
61458008Seric 		}
61558008Seric #endif
61657990Seric 		smtprset(m, mci, e);
6177964Seric 		return (EX_PROTOCOL);
61857990Seric 	}
61910175Seric 
62063757Seric 	/*
62163757Seric 	**  Set timeout around data writes.  Make it at least large
62263757Seric 	**  enough for DNS timeouts on all recipients plus some fudge
62363757Seric 	**  factor.  The main thing is that it should not be infinite.
62463757Seric 	*/
62563757Seric 
62663753Seric 	if (setjmp(CtxDataTimeout) != 0)
62763753Seric 	{
62863753Seric 		mci->mci_errno = errno;
62963753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
63063753Seric 		mci->mci_state = MCIS_ERROR;
63163753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
63263753Seric 		smtpquit(m, mci, e);
63363753Seric 		return EX_TEMPFAIL;
63463753Seric 	}
63563753Seric 
63663787Seric 	timeout = e->e_msgsize / 16;
63768740Seric 	if (timeout < (time_t) 600)
63868740Seric 		timeout = (time_t) 600;
63968740Seric 	timeout += e->e_nrcpts * 300;
64063753Seric 	ev = setevent(timeout, datatimeout, 0);
64163753Seric 
64267546Seric 	/*
64367546Seric 	**  Output the actual message.
64467546Seric 	*/
64567546Seric 
64668228Seric 	(*e->e_puthdr)(mci, e->e_header, e);
64768228Seric 	(*e->e_putbody)(mci, e, NULL);
64810175Seric 
64967546Seric 	/*
65067546Seric 	**  Cleanup after sending message.
65167546Seric 	*/
65267546Seric 
65363753Seric 	clrevent(ev);
65463753Seric 
65564718Seric 	if (ferror(mci->mci_out))
65664718Seric 	{
65764718Seric 		/* error during processing -- don't send the dot */
65864718Seric 		mci->mci_errno = EIO;
65964718Seric 		mci->mci_exitstat = EX_IOERR;
66064718Seric 		mci->mci_state = MCIS_ERROR;
66164718Seric 		smtpquit(m, mci, e);
66264718Seric 		return EX_IOERR;
66364718Seric 	}
66464718Seric 
66510175Seric 	/* terminate the message */
66653740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
66763753Seric 	if (TrafficLogFile != NULL)
66863753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
66958120Seric 	if (Verbose)
67058151Seric 		nmessage(">>> .");
67110175Seric 
67210175Seric 	/* check for the results of the transaction */
67361093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
67453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
67559285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
67653751Seric 	if (r < 0)
67757990Seric 	{
67857990Seric 		smtpquit(m, mci, e);
6794797Seric 		return (EX_TEMPFAIL);
68057990Seric 	}
68153751Seric 	mci->mci_state = MCIS_OPEN;
68258917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
68353751Seric 	if (REPLYTYPE(r) == 4)
68453751Seric 		return (EX_TEMPFAIL);
6857963Seric 	else if (r == 250)
6867963Seric 		return (EX_OK);
6877963Seric 	else if (r == 552 || r == 554)
6887963Seric 		return (EX_UNAVAILABLE);
68958008Seric #ifdef LOG
69058020Seric 	if (LogLevel > 1)
69158008Seric 	{
69267860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s",
69367860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
69458008Seric 	}
69558008Seric #endif
6967964Seric 	return (EX_PROTOCOL);
6974684Seric }
69863753Seric 
69963753Seric 
70068433Seric static void
datatimeout()70163753Seric datatimeout()
70263753Seric {
70363753Seric 	longjmp(CtxDataTimeout, 1);
70463753Seric }
7054684Seric /*
7064865Seric **  SMTPQUIT -- close the SMTP connection.
7074865Seric **
7084865Seric **	Parameters:
70915535Seric **		m -- a pointer to the mailer.
7104865Seric **
7114865Seric **	Returns:
7124865Seric **		none.
7134865Seric **
7144865Seric **	Side Effects:
7154865Seric **		sends the final protocol and closes the connection.
7164865Seric */
7174865Seric 
71869748Seric void
smtpquit(m,mci,e)71953751Seric smtpquit(m, mci, e)
72053751Seric 	register MAILER *m;
72154967Seric 	register MCI *mci;
72253751Seric 	ENVELOPE *e;
7234865Seric {
72464822Seric 	bool oldSuprErrs = SuprErrs;
7254865Seric 
72664822Seric 	/*
72764822Seric 	**	Suppress errors here -- we may be processing a different
72864822Seric 	**	job when we do the quit connection, and we don't want the
72964822Seric 	**	new job to be penalized for something that isn't it's
73064822Seric 	**	problem.
73164822Seric 	*/
73264822Seric 
73364822Seric 	SuprErrs = TRUE;
73464822Seric 
73554967Seric 	/* send the quit message if we haven't gotten I/O error */
73653751Seric 	if (mci->mci_state != MCIS_ERROR)
7379391Seric 	{
73861093Seric 		SmtpPhase = "client QUIT";
73953751Seric 		smtpmessage("QUIT", m, mci);
74059285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
74164822Seric 		SuprErrs = oldSuprErrs;
74253740Seric 		if (mci->mci_state == MCIS_CLOSED)
74364822Seric 		{
74464822Seric 			SuprErrs = oldSuprErrs;
74510159Seric 			return;
74664822Seric 		}
7479391Seric 	}
7489391Seric 
74952676Seric 	/* now actually close the connection and pick up the zombie */
75065194Seric 	(void) endmailer(mci, e, NULL);
75164822Seric 
75264822Seric 	SuprErrs = oldSuprErrs;
7534865Seric }
7544865Seric /*
75554967Seric **  SMTPRSET -- send a RSET (reset) command
75654967Seric */
75754967Seric 
75869748Seric void
smtprset(m,mci,e)75954967Seric smtprset(m, mci, e)
76054967Seric 	register MAILER *m;
76154967Seric 	register MCI *mci;
76254967Seric 	ENVELOPE *e;
76354967Seric {
76454967Seric 	int r;
76554967Seric 
76661093Seric 	SmtpPhase = "client RSET";
76754967Seric 	smtpmessage("RSET", m, mci);
76859285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
76957734Seric 	if (r < 0)
77057734Seric 		mci->mci_state = MCIS_ERROR;
77154967Seric 	else if (REPLYTYPE(r) == 2)
77257734Seric 	{
77357734Seric 		mci->mci_state = MCIS_OPEN;
77457734Seric 		return;
77557734Seric 	}
77657734Seric 	smtpquit(m, mci, e);
77754967Seric }
77854967Seric /*
77958867Seric **  SMTPPROBE -- check the connection state
78054967Seric */
78154967Seric 
78269748Seric int
smtpprobe(mci)78358867Seric smtpprobe(mci)
78454967Seric 	register MCI *mci;
78554967Seric {
78654967Seric 	int r;
78754967Seric 	MAILER *m = mci->mci_mailer;
78854967Seric 	extern ENVELOPE BlankEnvelope;
78954967Seric 	ENVELOPE *e = &BlankEnvelope;
79054967Seric 
79161093Seric 	SmtpPhase = "client probe";
79258867Seric 	smtpmessage("RSET", m, mci);
79359285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
79458061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
79554967Seric 		smtpquit(m, mci, e);
79654967Seric 	return r;
79754967Seric }
79854967Seric /*
7994684Seric **  REPLY -- read arpanet reply
8004684Seric **
8014684Seric **	Parameters:
80210175Seric **		m -- the mailer we are reading the reply from.
80357379Seric **		mci -- the mailer connection info structure.
80457379Seric **		e -- the current envelope.
80557379Seric **		timeout -- the timeout for reads.
80669107Seric **		pfunc -- processing function called on each line of response.
80769107Seric **			If null, no special processing is done.
8084684Seric **
8094684Seric **	Returns:
8104684Seric **		reply code it reads.
8114684Seric **
8124684Seric **	Side Effects:
8134684Seric **		flushes the mail file.
8144684Seric */
8154684Seric 
81669748Seric int
reply(m,mci,e,timeout,pfunc)81759285Seric reply(m, mci, e, timeout, pfunc)
81853751Seric 	MAILER *m;
81954967Seric 	MCI *mci;
82053751Seric 	ENVELOPE *e;
82159285Seric 	time_t timeout;
82259285Seric 	void (*pfunc)();
8234684Seric {
82459014Seric 	register char *bufp;
82559014Seric 	register int r;
82659285Seric 	bool firstline = TRUE;
82758957Seric 	char junkbuf[MAXLINE];
82858957Seric 
82957379Seric 	if (mci->mci_out != NULL)
83057379Seric 		(void) fflush(mci->mci_out);
8314684Seric 
8327677Seric 	if (tTd(18, 1))
8334796Seric 		printf("reply\n");
8344796Seric 
8357356Seric 	/*
8367356Seric 	**  Read the input line, being careful not to hang.
8377356Seric 	*/
8387356Seric 
83959014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
8404684Seric 	{
8417356Seric 		register char *p;
84253751Seric 		extern time_t curtime();
8434684Seric 
8447685Seric 		/* actually do the read */
84553751Seric 		if (e->e_xfp != NULL)
84653751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
8477356Seric 
84810054Seric 		/* if we are in the process of closing just give the code */
84953740Seric 		if (mci->mci_state == MCIS_CLOSED)
85010054Seric 			return (SMTPCLOSING);
85110054Seric 
85258680Seric 		if (mci->mci_out != NULL)
85358680Seric 			fflush(mci->mci_out);
85458680Seric 
85510054Seric 		/* get the line from the other side */
85661093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
85753751Seric 		mci->mci_lastuse = curtime();
85853751Seric 
85910054Seric 		if (p == NULL)
86010131Seric 		{
86163753Seric 			bool oldholderrs;
86210148Seric 
86321065Seric 			/* if the remote end closed early, fake an error */
86421065Seric 			if (errno == 0)
86521065Seric # ifdef ECONNRESET
86621065Seric 				errno = ECONNRESET;
86756795Seric # else /* ECONNRESET */
86821065Seric 				errno = EPIPE;
86956795Seric # endif /* ECONNRESET */
87021065Seric 
87157379Seric 			mci->mci_errno = errno;
87257642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
87363753Seric 			oldholderrs = HoldErrs;
87463753Seric 			HoldErrs = TRUE;
87563753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
87663753Seric 
87710420Seric 			/* if debugging, pause so we can see state */
87810420Seric 			if (tTd(18, 100))
87910420Seric 				pause();
88054967Seric 			mci->mci_state = MCIS_ERROR;
88153751Seric 			smtpquit(m, mci, e);
88269881Seric #if XDEBUG
88363753Seric 			{
88463753Seric 				char wbuf[MAXLINE];
88564082Seric 				char *p = wbuf;
88664082Seric 				if (e->e_to != NULL)
88764082Seric 				{
88864082Seric 					sprintf(p, "%s... ", e->e_to);
88964082Seric 					p += strlen(p);
89064082Seric 				}
89164082Seric 				sprintf(p, "reply(%s) during %s",
89264082Seric 					mci->mci_host, SmtpPhase);
89363753Seric 				checkfd012(wbuf);
89463753Seric 			}
89563753Seric #endif
89663753Seric 			HoldErrs = oldholderrs;
89710054Seric 			return (-1);
89810131Seric 		}
89958957Seric 		fixcrlf(bufp, TRUE);
90010054Seric 
90163753Seric 		/* EHLO failure is not a real error */
90263753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
90363753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
90414900Seric 		{
90514900Seric 			/* serious error -- log the previous command */
90664071Seric 			if (SmtpNeedIntro)
90764071Seric 			{
90864071Seric 				/* inform user who we are chatting with */
90964071Seric 				fprintf(CurEnv->e_xfp,
91064071Seric 					"... while talking to %s:\n",
91164071Seric 					CurHostName);
91264071Seric 				SmtpNeedIntro = FALSE;
91364071Seric 			}
91459014Seric 			if (SmtpMsgBuffer[0] != '\0')
91559014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
91659014Seric 			SmtpMsgBuffer[0] = '\0';
91714900Seric 
91814900Seric 			/* now log the message as from the other side */
91958957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
92014900Seric 		}
92114900Seric 
92214900Seric 		/* display the input for verbose mode */
92358120Seric 		if (Verbose)
92459956Seric 			nmessage("050 %s", bufp);
9257356Seric 
92659285Seric 		/* process the line */
92767893Seric 		if (pfunc != NULL)
92867893Seric 			(*pfunc)(bufp, firstline, m, mci, e);
92959285Seric 
93059285Seric 		firstline = FALSE;
93159285Seric 
9327356Seric 		/* if continuation is required, we can go on */
93359014Seric 		if (bufp[3] == '-')
9344684Seric 			continue;
9357356Seric 
93659014Seric 		/* ignore improperly formated input */
93759014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
93859014Seric 			continue;
93959014Seric 
9407356Seric 		/* decode the reply code */
94159014Seric 		r = atoi(bufp);
9427356Seric 
9437356Seric 		/* extra semantics: 0xx codes are "informational" */
94459014Seric 		if (r >= 100)
94559014Seric 			break;
94659014Seric 	}
9477356Seric 
94859014Seric 	/*
94959014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
95059014Seric 	**  line of the response from here on out.
95159014Seric 	*/
95258061Seric 
95359014Seric 	/* save temporary failure messages for posterity */
95459014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
95559014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
9569391Seric 
95759014Seric 	/* reply code 421 is "Service Shutting Down" */
95859014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
95959014Seric 	{
96059014Seric 		/* send the quit protocol */
96159014Seric 		mci->mci_state = MCIS_SSD;
96259014Seric 		smtpquit(m, mci, e);
9634684Seric 	}
96459014Seric 
96559014Seric 	return (r);
9664684Seric }
9674796Seric /*
9684865Seric **  SMTPMESSAGE -- send message to server
9694796Seric **
9704796Seric **	Parameters:
9714796Seric **		f -- format
97210175Seric **		m -- the mailer to control formatting.
9734796Seric **		a, b, c -- parameters
9744796Seric **
9754796Seric **	Returns:
9764796Seric **		none.
9774796Seric **
9784796Seric **	Side Effects:
97953740Seric **		writes message to mci->mci_out.
9804796Seric */
9814796Seric 
9824865Seric /*VARARGS1*/
98369748Seric void
98457642Seric #ifdef __STDC__
smtpmessage(char * f,MAILER * m,MCI * mci,...)98557642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
98657642Seric #else
98757642Seric smtpmessage(f, m, mci, va_alist)
9884796Seric 	char *f;
98910175Seric 	MAILER *m;
99054967Seric 	MCI *mci;
99157642Seric 	va_dcl
99257642Seric #endif
9934796Seric {
99456852Seric 	VA_LOCAL_DECL
99556852Seric 
99657135Seric 	VA_START(mci);
99756852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
99856852Seric 	VA_END;
99958680Seric 
100058120Seric 	if (tTd(18, 1) || Verbose)
100158151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
100263753Seric 	if (TrafficLogFile != NULL)
100363753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
100453740Seric 	if (mci->mci_out != NULL)
100558680Seric 	{
100653740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
100754967Seric 			m == NULL ? "\r\n" : m->m_eol);
100858680Seric 	}
100959149Seric 	else if (tTd(18, 1))
101058725Seric 	{
101159149Seric 		printf("smtpmessage: NULL mci_out\n");
101258725Seric 	}
10134796Seric }
10145182Seric 
101556795Seric # endif /* SMTP */
1016