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*68100Seric static char sccsid[] = "@(#)usersmtp.c	8.33 (Berkeley) 12/28/94 (with SMTP)";
1433731Sbostic #else
15*68100Seric static char sccsid[] = "@(#)usersmtp.c	8.33 (Berkeley) 12/28/94 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
194684Seric # include <sysexits.h>
2021065Seric # include <errno.h>
214684Seric 
2233731Sbostic # ifdef SMTP
234684Seric 
244684Seric /*
259391Seric **  USERSMTP -- run SMTP protocol from the user end.
269391Seric **
279391Seric **	This protocol is described in RFC821.
289391Seric */
299391Seric 
309391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
319391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
329391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
339391Seric 
3414900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
3510054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
3621065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
3710054Seric int	SmtpPid;			/* pid of mailer */
3864071Seric bool	SmtpNeedIntro;			/* need "while talking" in transcript */
3958671Seric 
4058671Seric #ifdef __STDC__
4158671Seric extern	smtpmessage(char *f, MAILER *m, MCI *mci, ...);
4258671Seric #endif
439391Seric /*
444865Seric **  SMTPINIT -- initialize SMTP.
454684Seric **
464865Seric **	Opens the connection and sends the initial protocol.
474684Seric **
484684Seric **	Parameters:
494865Seric **		m -- mailer to create connection to.
504865Seric **		pvp -- pointer to parameter vector to pass to
514865Seric **			the mailer.
524684Seric **
534684Seric **	Returns:
5454967Seric **		none.
554684Seric **
564684Seric **	Side Effects:
574865Seric **		creates connection and sends initial protocol.
584684Seric */
594684Seric 
6054967Seric smtpinit(m, mci, e)
614865Seric 	struct mailer *m;
6254967Seric 	register MCI *mci;
6353751Seric 	ENVELOPE *e;
644684Seric {
654865Seric 	register int r;
6658957Seric 	register char *p;
6760210Seric 	extern void esmtp_check();
6859285Seric 	extern void helo_options();
694684Seric 
7063753Seric 	if (tTd(18, 1))
7157379Seric 	{
7257379Seric 		printf("smtpinit ");
7364731Seric 		mci_dump(mci, FALSE);
7457379Seric 	}
7557379Seric 
764865Seric 	/*
774865Seric 	**  Open the connection to the mailer.
784865Seric 	*/
794684Seric 
8021065Seric 	SmtpError[0] = '\0';
8157379Seric 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
82*68100Seric 	if (CurHostName == NULL)
83*68100Seric 		CurHostName = MyHostName;
8464071Seric 	SmtpNeedIntro = TRUE;
8554967Seric 	switch (mci->mci_state)
866051Seric 	{
8754967Seric 	  case MCIS_ACTIVE:
8854967Seric 		/* need to clear old information */
8954967Seric 		smtprset(m, mci, e);
9057734Seric 		/* fall through */
9115139Seric 
9254967Seric 	  case MCIS_OPEN:
9354967Seric 		return;
9454967Seric 
9554967Seric 	  case MCIS_ERROR:
9654967Seric 	  case MCIS_SSD:
9754967Seric 		/* shouldn't happen */
9854967Seric 		smtpquit(m, mci, e);
9957734Seric 		/* fall through */
10054967Seric 
10154967Seric 	  case MCIS_CLOSED:
10258151Seric 		syserr("451 smtpinit: state CLOSED");
10354967Seric 		return;
10454967Seric 
10554967Seric 	  case MCIS_OPENING:
10654967Seric 		break;
1076051Seric 	}
1084796Seric 
10954967Seric 	mci->mci_state = MCIS_OPENING;
11054967Seric 
1114865Seric 	/*
1124865Seric 	**  Get the greeting message.
11314913Seric 	**	This should appear spontaneously.  Give it five minutes to
11414886Seric 	**	happen.
1154865Seric 	*/
1164797Seric 
11761093Seric 	SmtpPhase = mci->mci_phase = "client greeting";
11853751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
11960210Seric 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
12064750Seric 	if (r < 0 || REPLYTYPE(r) == 4)
12152104Seric 		goto tempfail1;
12264750Seric 	if (REPLYTYPE(r) != 2)
12364750Seric 		goto unavailable;
1244684Seric 
1254865Seric 	/*
1264976Seric 	**  Send the HELO command.
1277963Seric 	**	My mother taught me to always introduce myself.
1284976Seric 	*/
1294976Seric 
13059285Seric 	if (bitnset(M_ESMTP, m->m_flags))
13159285Seric 		mci->mci_flags |= MCIF_ESMTP;
13259285Seric 
13359285Seric tryhelo:
13459285Seric 	if (bitset(MCIF_ESMTP, mci->mci_flags))
13559285Seric 	{
13659285Seric 		smtpmessage("EHLO %s", m, mci, MyHostName);
13761093Seric 		SmtpPhase = mci->mci_phase = "client EHLO";
13859285Seric 	}
13959285Seric 	else
14059285Seric 	{
14159285Seric 		smtpmessage("HELO %s", m, mci, MyHostName);
14261093Seric 		SmtpPhase = mci->mci_phase = "client HELO";
14359285Seric 	}
14453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
14559285Seric 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1468005Seric 	if (r < 0)
14752104Seric 		goto tempfail1;
1488005Seric 	else if (REPLYTYPE(r) == 5)
14959285Seric 	{
15059285Seric 		if (bitset(MCIF_ESMTP, mci->mci_flags))
15159285Seric 		{
15259285Seric 			/* try old SMTP instead */
15359285Seric 			mci->mci_flags &= ~MCIF_ESMTP;
15459285Seric 			goto tryhelo;
15559285Seric 		}
15614913Seric 		goto unavailable;
15759285Seric 	}
1587963Seric 	else if (REPLYTYPE(r) != 2)
15952104Seric 		goto tempfail1;
1604976Seric 
1614976Seric 	/*
16258957Seric 	**  Check to see if we actually ended up talking to ourself.
16358957Seric 	**  This means we didn't know about an alias or MX, or we managed
16458957Seric 	**  to connect to an echo server.
16558957Seric 	*/
16658957Seric 
16759026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16858957Seric 	if (p != NULL)
16961707Seric 		*p = '\0';
17067472Seric 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
17167472Seric 	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
17258957Seric 	{
17358957Seric 		syserr("553 %s config error: mail loops back to myself",
17458957Seric 			MyHostName);
17558957Seric 		mci->mci_exitstat = EX_CONFIG;
17658957Seric 		mci->mci_errno = 0;
17758957Seric 		smtpquit(m, mci, e);
17858957Seric 		return;
17958957Seric 	}
18058957Seric 
18158957Seric 	/*
1829315Seric 	**  If this is expected to be another sendmail, send some internal
1839315Seric 	**  commands.
1849315Seric 	*/
1859315Seric 
18610688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1879315Seric 	{
1889315Seric 		/* tell it to be verbose */
18953751Seric 		smtpmessage("VERB", m, mci);
19059285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1919315Seric 		if (r < 0)
19252104Seric 			goto tempfail2;
1939315Seric 	}
1949315Seric 
19565057Seric 	if (mci->mci_state != MCIS_CLOSED)
19665057Seric 	{
19765057Seric 		mci->mci_state = MCIS_OPEN;
19865057Seric 		return;
19965057Seric 	}
20053751Seric 
20165057Seric 	/* got a 421 error code during startup */
20265057Seric 
20353751Seric   tempfail1:
20453751Seric   tempfail2:
20553751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
20657379Seric 	if (mci->mci_errno == 0)
20757379Seric 		mci->mci_errno = errno;
20857379Seric 	if (mci->mci_state != MCIS_CLOSED)
20957379Seric 		smtpquit(m, mci, e);
21054967Seric 	return;
21153751Seric 
21253751Seric   unavailable:
21353751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
21453751Seric 	mci->mci_errno = errno;
21553751Seric 	smtpquit(m, mci, e);
21654967Seric 	return;
21753751Seric }
21859285Seric /*
21960210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
22060210Seric **
22160210Seric **
22260210Seric **	Parameters:
22360210Seric **		line -- the response line.
22467893Seric **		firstline -- set if this is the first line of the reply.
22560210Seric **		m -- the mailer.
22660210Seric **		mci -- the mailer connection info.
22760210Seric **		e -- the envelope.
22860210Seric **
22960210Seric **	Returns:
23060210Seric **		none.
23160210Seric */
23260210Seric 
23360210Seric void
23467893Seric esmtp_check(line, firstline, m, mci, e)
23560210Seric 	char *line;
23667893Seric 	bool firstline;
23760210Seric 	MAILER *m;
23860210Seric 	register MCI *mci;
23960210Seric 	ENVELOPE *e;
24060210Seric {
24167893Seric 	while ((line = strchr(++line, 'E')) != NULL)
24267893Seric 	{
24367893Seric 		if (strncmp(line, "ESMTP ", 6) == 0)
24467893Seric 		{
24567893Seric 			mci->mci_flags |= MCIF_ESMTP;
24667893Seric 			break;
24767893Seric 		}
24867893Seric 	}
24960210Seric }
25060210Seric /*
25159285Seric **  HELO_OPTIONS -- process the options on a HELO line.
25259285Seric **
25359285Seric **	Parameters:
25459285Seric **		line -- the response line.
25567893Seric **		firstline -- set if this is the first line of the reply.
25659285Seric **		m -- the mailer.
25759285Seric **		mci -- the mailer connection info.
25859285Seric **		e -- the envelope.
25959285Seric **
26059285Seric **	Returns:
26159285Seric **		none.
26259285Seric */
26353751Seric 
26459285Seric void
26567893Seric helo_options(line, firstline, m, mci, e)
26659285Seric 	char *line;
26767893Seric 	bool firstline;
26859285Seric 	MAILER *m;
26959285Seric 	register MCI *mci;
27059285Seric 	ENVELOPE *e;
27159285Seric {
27259285Seric 	register char *p;
27359285Seric 
27467971Seric 	if (firstline)
27567893Seric 		return;
27667893Seric 
27759285Seric 	if (strlen(line) < 5)
27859285Seric 		return;
27959285Seric 	line += 4;
28059285Seric 	p = strchr(line, ' ');
28159285Seric 	if (p != NULL)
28259285Seric 		*p++ = '\0';
28359285Seric 	if (strcasecmp(line, "size") == 0)
28459285Seric 	{
28559285Seric 		mci->mci_flags |= MCIF_SIZE;
28659285Seric 		if (p != NULL)
28759285Seric 			mci->mci_maxsize = atol(p);
28859285Seric 	}
28959285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
29065870Seric 	{
29159285Seric 		mci->mci_flags |= MCIF_8BITMIME;
29265870Seric 		mci->mci_flags &= ~MCIF_7BIT;
29365870Seric 	}
29459285Seric 	else if (strcasecmp(line, "expn") == 0)
29559285Seric 		mci->mci_flags |= MCIF_EXPN;
29667963Seric 	else if (strcasecmp(line, "x-dsn-1") == 0)
29767880Seric 		mci->mci_flags |= MCIF_DSN;
29859285Seric }
29959285Seric /*
30059285Seric **  SMTPMAILFROM -- send MAIL command
30159285Seric **
30259285Seric **	Parameters:
30359285Seric **		m -- the mailer.
30459285Seric **		mci -- the mailer connection structure.
30559285Seric **		e -- the envelope (including the sender to specify).
30659285Seric */
30759285Seric 
30853751Seric smtpmailfrom(m, mci, e)
30953751Seric 	struct mailer *m;
31054967Seric 	MCI *mci;
31153751Seric 	ENVELOPE *e;
31253751Seric {
31353751Seric 	int r;
31465494Seric 	char *bufp;
31567887Seric 	char *bodytype;
31653751Seric 	char buf[MAXNAME];
31759285Seric 	char optbuf[MAXLINE];
31853751Seric 
31963753Seric 	if (tTd(18, 2))
32057943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
32157943Seric 
32259285Seric 	/* set up appropriate options to include */
32364254Seric 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
32459285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
32559285Seric 	else
32659285Seric 		strcpy(optbuf, "");
32759285Seric 
32867887Seric 	bodytype = e->e_bodytype;
32967887Seric 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
33067417Seric 	{
33167887Seric 		if (bodytype == NULL &&
33267887Seric 		    bitset(MM_MIME8BIT, MimeMode) &&
33367887Seric 		    bitset(EF_HAS8BIT, e->e_flags) &&
33467887Seric 		    !bitnset(M_8BITS, m->m_flags))
33567887Seric 			bodytype = "8BITMIME";
33667887Seric 		if (bodytype != NULL)
33767417Seric 		{
33867417Seric 			strcat(optbuf, " BODY=");
33967887Seric 			strcat(optbuf, bodytype);
34067417Seric 		}
34167417Seric 	}
34267995Seric 	else if (bitnset(M_8BITS, m->m_flags) ||
34367995Seric 		 !bitset(EF_HAS8BIT, e->e_flags) ||
34467995Seric 		 (e->e_bodytype != NULL &&
34567995Seric 		  strcasecmp(e->e_bodytype, "7bit") == 0))
34667887Seric 	{
34767887Seric 		/* just pass it through */
34867887Seric 	}
34967887Seric 	else if (bitset(MM_CVTMIME, MimeMode) &&
35067995Seric 		 (e->e_bodytype != NULL || !bitset(MM_PASS8BIT, MimeMode)))
35167887Seric 	{
35267887Seric 		/* must convert from 8bit MIME format to 7bit encoded */
35367887Seric 		mci->mci_flags |= MCIF_CVT8TO7;
35467887Seric 	}
35567887Seric 	else if (!bitset(MM_PASS8BIT, MimeMode))
35667887Seric 	{
35767887Seric 		/* cannot just send a 8-bit version */
35867887Seric 		usrerr("%s does not support 8BITMIME", mci->mci_host);
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 		}
36967963Seric 		if (e->e_omts != NULL)
37067963Seric 		{
37167963Seric 			strcat(optbuf, " OMTS=");
37267963Seric 			strcat(optbuf, e->e_omts);
37367963Seric 		}
37467880Seric 	}
37567880Seric 
3769315Seric 	/*
3774865Seric 	**  Send the MAIL command.
3784865Seric 	**	Designates the sender.
3794865Seric 	*/
3804796Seric 
38153751Seric 	mci->mci_state = MCIS_ACTIVE;
38253751Seric 
38359540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
38459540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
38558680Seric 		(void) strcpy(buf, "");
38658680Seric 	else
38758680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
38865494Seric 	if (buf[0] == '<')
38965494Seric 	{
39065494Seric 		/* strip off <angle brackets> (put back on below) */
39165494Seric 		bufp = &buf[strlen(buf) - 1];
39265494Seric 		if (*bufp == '>')
39365494Seric 			*bufp = '\0';
39465494Seric 		bufp = &buf[1];
39565494Seric 	}
39665494Seric 	else
39765494Seric 		bufp = buf;
39867472Seric 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
39910688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
4008436Seric 	{
40165494Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
4028436Seric 	}
4038436Seric 	else
4048436Seric 	{
40559285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
40665494Seric 			*bufp == '@' ? ',' : ':', bufp, optbuf);
4078436Seric 	}
40861093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
40953751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
41059285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
4118005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
41253751Seric 	{
41353751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
41453751Seric 		mci->mci_errno = errno;
41553751Seric 		smtpquit(m, mci, e);
41653751Seric 		return EX_TEMPFAIL;
41753751Seric 	}
4187963Seric 	else if (r == 250)
41953751Seric 	{
42053751Seric 		return EX_OK;
42153751Seric 	}
42268075Seric 	else if (r == 501 || r == 553)
42368075Seric 	{
42468075Seric 		/* syntax error in arguments */
42568075Seric 		smtpquit(m, mci, e);
42668075Seric 		return EX_DATAERR;
42768075Seric 	}
4287963Seric 	else if (r == 552)
42953751Seric 	{
43053751Seric 		/* signal service unavailable */
43153751Seric 		smtpquit(m, mci, e);
43253751Seric 		return EX_UNAVAILABLE;
43353751Seric 	}
43414913Seric 
43558008Seric #ifdef LOG
43658020Seric 	if (LogLevel > 1)
43758008Seric 	{
43867860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s",
43967860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
44058008Seric 	}
44158008Seric #endif
44258008Seric 
44314913Seric 	/* protocol error -- close up */
44453751Seric 	smtpquit(m, mci, e);
44553751Seric 	return EX_PROTOCOL;
4464684Seric }
4474684Seric /*
4484976Seric **  SMTPRCPT -- designate recipient.
4494797Seric **
4504797Seric **	Parameters:
4514865Seric **		to -- address of recipient.
45210175Seric **		m -- the mailer we are sending to.
45357379Seric **		mci -- the connection info for this transaction.
45457379Seric **		e -- the envelope for this transaction.
4554797Seric **
4564797Seric **	Returns:
4574865Seric **		exit status corresponding to recipient status.
4584797Seric **
4594797Seric **	Side Effects:
4604865Seric **		Sends the mail via SMTP.
4614797Seric */
4624797Seric 
46353751Seric smtprcpt(to, m, mci, e)
4644865Seric 	ADDRESS *to;
46510175Seric 	register MAILER *m;
46654967Seric 	MCI *mci;
46753751Seric 	ENVELOPE *e;
4684797Seric {
4694797Seric 	register int r;
47067880Seric 	char optbuf[MAXLINE];
4714797Seric 
47267880Seric 	strcpy(optbuf, "");
47367880Seric 	if (bitset(MCIF_DSN, mci->mci_flags))
47467880Seric 	{
47567963Seric 		bool firstone = TRUE;
47667963Seric 
47767987Seric 		/* NOTIFY= parameter */
47867880Seric 		strcat(optbuf, " NOTIFY=");
47967987Seric 		if (bitset(QPINGONSUCCESS, to->q_flags))
48067880Seric 		{
48167987Seric 			strcat(optbuf, "SUCCESS");
48267963Seric 			firstone = FALSE;
48367880Seric 		}
48467987Seric 		if (bitset(QPINGONFAILURE, to->q_flags))
48567880Seric 		{
48667971Seric 			if (!firstone)
48767963Seric 				strcat(optbuf, ",");
48867987Seric 			strcat(optbuf, "FAILURE");
48967963Seric 			firstone = FALSE;
49067880Seric 		}
49167963Seric 		if (bitset(QPINGONDELAY, to->q_flags))
49267880Seric 		{
49367971Seric 			if (!firstone)
49467963Seric 				strcat(optbuf, ",");
49567963Seric 			strcat(optbuf, "DELAY");
49667963Seric 			firstone = FALSE;
49767963Seric 		}
49867963Seric 		if (firstone)
49967963Seric 			strcat(optbuf, "NEVER");
50067963Seric 
50167987Seric 		/* RET= parameter */
50267963Seric 		if (bitset(QHAS_RET_PARAM, to->q_flags))
50367963Seric 		{
50467880Seric 			strcat(optbuf, " RET=");
50567963Seric 			if (bitset(QRET_HDRS, to->q_flags))
50667963Seric 				strcat(optbuf, "HDRS");
50767880Seric 			else
50867963Seric 				strcat(optbuf, "FULL");
50967880Seric 		}
51067987Seric 
51167987Seric 		/* ORCPT= parameter */
51267987Seric 		if (to->q_orcpt != NULL)
51367987Seric 		{
51467987Seric 			strcat(optbuf, " ORCPT=");
51567987Seric 			strcat(optbuf, to->q_orcpt);
51667987Seric 		}
51767880Seric 	}
51867880Seric 	else if (bitset(QPINGONSUCCESS, to->q_flags))
51967880Seric 	{
52067880Seric 		to->q_flags |= QRELAYED;
52167880Seric 		fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n",
52267880Seric 			to->q_paddr);
52367880Seric 	}
5244865Seric 
52567880Seric 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
52667880Seric 
52761093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
52853751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
52959285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
53067880Seric 	setstatus(to, SmtpReplyBuffer);
5318005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
5324865Seric 		return (EX_TEMPFAIL);
5337963Seric 	else if (REPLYTYPE(r) == 2)
5347963Seric 		return (EX_OK);
5357964Seric 	else if (r == 550 || r == 551 || r == 553)
5367964Seric 		return (EX_NOUSER);
5377964Seric 	else if (r == 552 || r == 554)
5387964Seric 		return (EX_UNAVAILABLE);
53958008Seric 
54058008Seric #ifdef LOG
54158020Seric 	if (LogLevel > 1)
54258008Seric 	{
54367860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s",
54467860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
54558008Seric 	}
54658008Seric #endif
54758008Seric 
5487964Seric 	return (EX_PROTOCOL);
5494797Seric }
5504797Seric /*
55110175Seric **  SMTPDATA -- send the data and clean up the transaction.
5524684Seric **
5534684Seric **	Parameters:
5544865Seric **		m -- mailer being sent to.
5556980Seric **		e -- the envelope for this message.
5564684Seric **
5574684Seric **	Returns:
5584976Seric **		exit status corresponding to DATA command.
5594684Seric **
5604684Seric **	Side Effects:
5614865Seric **		none.
5624684Seric */
5634684Seric 
56463753Seric static jmp_buf	CtxDataTimeout;
56563937Seric static int	datatimeout();
56663753Seric 
56753740Seric smtpdata(m, mci, e)
5684865Seric 	struct mailer *m;
56954967Seric 	register MCI *mci;
5706980Seric 	register ENVELOPE *e;
5714684Seric {
5724684Seric 	register int r;
57363753Seric 	register EVENT *ev;
57463753Seric 	time_t timeout;
5754684Seric 
5764797Seric 	/*
5774797Seric 	**  Send the data.
57810175Seric 	**	First send the command and check that it is ok.
57910175Seric 	**	Then send the data.
58010175Seric 	**	Follow it up with a dot to terminate.
58110175Seric 	**	Finally get the results of the transaction.
5824797Seric 	*/
5834797Seric 
58410175Seric 	/* send the command and check ok to proceed */
58553751Seric 	smtpmessage("DATA", m, mci);
58661093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
58753751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
58859285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
5898005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
59057990Seric 	{
59157990Seric 		smtpquit(m, mci, e);
5924797Seric 		return (EX_TEMPFAIL);
59357990Seric 	}
5947963Seric 	else if (r == 554)
59557990Seric 	{
59657990Seric 		smtprset(m, mci, e);
5977963Seric 		return (EX_UNAVAILABLE);
59857990Seric 	}
5997963Seric 	else if (r != 354)
60057990Seric 	{
60158008Seric #ifdef LOG
60258020Seric 		if (LogLevel > 1)
60358008Seric 		{
60467860Seric 			syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s",
60567860Seric 				e->e_id, mci->mci_host, SmtpReplyBuffer);
60658008Seric 		}
60758008Seric #endif
60857990Seric 		smtprset(m, mci, e);
6097964Seric 		return (EX_PROTOCOL);
61057990Seric 	}
61110175Seric 
61263757Seric 	/*
61363757Seric 	**  Set timeout around data writes.  Make it at least large
61463757Seric 	**  enough for DNS timeouts on all recipients plus some fudge
61563757Seric 	**  factor.  The main thing is that it should not be infinite.
61663757Seric 	*/
61763757Seric 
61863753Seric 	if (setjmp(CtxDataTimeout) != 0)
61963753Seric 	{
62063753Seric 		mci->mci_errno = errno;
62163753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
62263753Seric 		mci->mci_state = MCIS_ERROR;
62363753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
62463753Seric 		smtpquit(m, mci, e);
62563753Seric 		return EX_TEMPFAIL;
62663753Seric 	}
62763753Seric 
62863787Seric 	timeout = e->e_msgsize / 16;
62963787Seric 	if (timeout < (time_t) 60)
63063787Seric 		timeout = (time_t) 60;
63163787Seric 	timeout += e->e_nrcpts * 90;
63263753Seric 	ev = setevent(timeout, datatimeout, 0);
63363753Seric 
63467546Seric 	/*
63567546Seric 	**  Output the actual message.
63667546Seric 	*/
63767546Seric 
63867936Seric 	(*e->e_puthdr)(mci, e->e_header, e, 0);
63967936Seric 	(*e->e_putbody)(mci, e, NULL, 0);
64010175Seric 
64167546Seric 	/*
64267546Seric 	**  Cleanup after sending message.
64367546Seric 	*/
64467546Seric 
64563753Seric 	clrevent(ev);
64663753Seric 
64764718Seric 	if (ferror(mci->mci_out))
64864718Seric 	{
64964718Seric 		/* error during processing -- don't send the dot */
65064718Seric 		mci->mci_errno = EIO;
65164718Seric 		mci->mci_exitstat = EX_IOERR;
65264718Seric 		mci->mci_state = MCIS_ERROR;
65364718Seric 		smtpquit(m, mci, e);
65464718Seric 		return EX_IOERR;
65564718Seric 	}
65664718Seric 
65710175Seric 	/* terminate the message */
65853740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
65963753Seric 	if (TrafficLogFile != NULL)
66063753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
66158120Seric 	if (Verbose)
66258151Seric 		nmessage(">>> .");
66310175Seric 
66410175Seric 	/* check for the results of the transaction */
66561093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
66653751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
66759285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
66853751Seric 	if (r < 0)
66957990Seric 	{
67057990Seric 		smtpquit(m, mci, e);
6714797Seric 		return (EX_TEMPFAIL);
67257990Seric 	}
67353751Seric 	mci->mci_state = MCIS_OPEN;
67458917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
67553751Seric 	if (REPLYTYPE(r) == 4)
67653751Seric 		return (EX_TEMPFAIL);
6777963Seric 	else if (r == 250)
6787963Seric 		return (EX_OK);
6797963Seric 	else if (r == 552 || r == 554)
6807963Seric 		return (EX_UNAVAILABLE);
68158008Seric #ifdef LOG
68258020Seric 	if (LogLevel > 1)
68358008Seric 	{
68467860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s",
68567860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
68658008Seric 	}
68758008Seric #endif
6887964Seric 	return (EX_PROTOCOL);
6894684Seric }
69063753Seric 
69163753Seric 
69263753Seric static int
69363753Seric datatimeout()
69463753Seric {
69563753Seric 	longjmp(CtxDataTimeout, 1);
69663753Seric }
6974684Seric /*
6984865Seric **  SMTPQUIT -- close the SMTP connection.
6994865Seric **
7004865Seric **	Parameters:
70115535Seric **		m -- a pointer to the mailer.
7024865Seric **
7034865Seric **	Returns:
7044865Seric **		none.
7054865Seric **
7064865Seric **	Side Effects:
7074865Seric **		sends the final protocol and closes the connection.
7084865Seric */
7094865Seric 
71053751Seric smtpquit(m, mci, e)
71153751Seric 	register MAILER *m;
71254967Seric 	register MCI *mci;
71353751Seric 	ENVELOPE *e;
7144865Seric {
71564822Seric 	bool oldSuprErrs = SuprErrs;
7164865Seric 
71764822Seric 	/*
71864822Seric 	**	Suppress errors here -- we may be processing a different
71964822Seric 	**	job when we do the quit connection, and we don't want the
72064822Seric 	**	new job to be penalized for something that isn't it's
72164822Seric 	**	problem.
72264822Seric 	*/
72364822Seric 
72464822Seric 	SuprErrs = TRUE;
72564822Seric 
72654967Seric 	/* send the quit message if we haven't gotten I/O error */
72753751Seric 	if (mci->mci_state != MCIS_ERROR)
7289391Seric 	{
72961093Seric 		SmtpPhase = "client QUIT";
73053751Seric 		smtpmessage("QUIT", m, mci);
73159285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
73264822Seric 		SuprErrs = oldSuprErrs;
73353740Seric 		if (mci->mci_state == MCIS_CLOSED)
73464822Seric 		{
73564822Seric 			SuprErrs = oldSuprErrs;
73610159Seric 			return;
73764822Seric 		}
7389391Seric 	}
7399391Seric 
74052676Seric 	/* now actually close the connection and pick up the zombie */
74165194Seric 	(void) endmailer(mci, e, NULL);
74264822Seric 
74364822Seric 	SuprErrs = oldSuprErrs;
7444865Seric }
7454865Seric /*
74654967Seric **  SMTPRSET -- send a RSET (reset) command
74754967Seric */
74854967Seric 
74954967Seric smtprset(m, mci, e)
75054967Seric 	register MAILER *m;
75154967Seric 	register MCI *mci;
75254967Seric 	ENVELOPE *e;
75354967Seric {
75454967Seric 	int r;
75554967Seric 
75661093Seric 	SmtpPhase = "client RSET";
75754967Seric 	smtpmessage("RSET", m, mci);
75859285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
75957734Seric 	if (r < 0)
76057734Seric 		mci->mci_state = MCIS_ERROR;
76154967Seric 	else if (REPLYTYPE(r) == 2)
76257734Seric 	{
76357734Seric 		mci->mci_state = MCIS_OPEN;
76457734Seric 		return;
76557734Seric 	}
76657734Seric 	smtpquit(m, mci, e);
76754967Seric }
76854967Seric /*
76958867Seric **  SMTPPROBE -- check the connection state
77054967Seric */
77154967Seric 
77258867Seric smtpprobe(mci)
77354967Seric 	register MCI *mci;
77454967Seric {
77554967Seric 	int r;
77654967Seric 	MAILER *m = mci->mci_mailer;
77754967Seric 	extern ENVELOPE BlankEnvelope;
77854967Seric 	ENVELOPE *e = &BlankEnvelope;
77954967Seric 
78061093Seric 	SmtpPhase = "client probe";
78158867Seric 	smtpmessage("RSET", m, mci);
78259285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
78358061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
78454967Seric 		smtpquit(m, mci, e);
78554967Seric 	return r;
78654967Seric }
78754967Seric /*
7884684Seric **  REPLY -- read arpanet reply
7894684Seric **
7904684Seric **	Parameters:
79110175Seric **		m -- the mailer we are reading the reply from.
79257379Seric **		mci -- the mailer connection info structure.
79357379Seric **		e -- the current envelope.
79457379Seric **		timeout -- the timeout for reads.
79559285Seric **		pfunc -- processing function for second and subsequent
79659285Seric **			lines of response -- if null, no special
79759285Seric **			processing is done.
7984684Seric **
7994684Seric **	Returns:
8004684Seric **		reply code it reads.
8014684Seric **
8024684Seric **	Side Effects:
8034684Seric **		flushes the mail file.
8044684Seric */
8054684Seric 
80659285Seric reply(m, mci, e, timeout, pfunc)
80753751Seric 	MAILER *m;
80854967Seric 	MCI *mci;
80953751Seric 	ENVELOPE *e;
81059285Seric 	time_t timeout;
81159285Seric 	void (*pfunc)();
8124684Seric {
81359014Seric 	register char *bufp;
81459014Seric 	register int r;
81559285Seric 	bool firstline = TRUE;
81658957Seric 	char junkbuf[MAXLINE];
81758957Seric 
81857379Seric 	if (mci->mci_out != NULL)
81957379Seric 		(void) fflush(mci->mci_out);
8204684Seric 
8217677Seric 	if (tTd(18, 1))
8224796Seric 		printf("reply\n");
8234796Seric 
8247356Seric 	/*
8257356Seric 	**  Read the input line, being careful not to hang.
8267356Seric 	*/
8277356Seric 
82859014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
8294684Seric 	{
8307356Seric 		register char *p;
83153751Seric 		extern time_t curtime();
8324684Seric 
8337685Seric 		/* actually do the read */
83453751Seric 		if (e->e_xfp != NULL)
83553751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
8367356Seric 
83710054Seric 		/* if we are in the process of closing just give the code */
83853740Seric 		if (mci->mci_state == MCIS_CLOSED)
83910054Seric 			return (SMTPCLOSING);
84010054Seric 
84158680Seric 		if (mci->mci_out != NULL)
84258680Seric 			fflush(mci->mci_out);
84358680Seric 
84410054Seric 		/* get the line from the other side */
84561093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
84653751Seric 		mci->mci_lastuse = curtime();
84753751Seric 
84810054Seric 		if (p == NULL)
84910131Seric 		{
85063753Seric 			bool oldholderrs;
85110148Seric 			extern char MsgBuf[];		/* err.c */
85210148Seric 
85321065Seric 			/* if the remote end closed early, fake an error */
85421065Seric 			if (errno == 0)
85521065Seric # ifdef ECONNRESET
85621065Seric 				errno = ECONNRESET;
85756795Seric # else /* ECONNRESET */
85821065Seric 				errno = EPIPE;
85956795Seric # endif /* ECONNRESET */
86021065Seric 
86157379Seric 			mci->mci_errno = errno;
86257642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
86363753Seric 			oldholderrs = HoldErrs;
86463753Seric 			HoldErrs = TRUE;
86563753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
86663753Seric 
86710420Seric 			/* if debugging, pause so we can see state */
86810420Seric 			if (tTd(18, 100))
86910420Seric 				pause();
87054967Seric 			mci->mci_state = MCIS_ERROR;
87153751Seric 			smtpquit(m, mci, e);
87263753Seric #ifdef XDEBUG
87363753Seric 			{
87463753Seric 				char wbuf[MAXLINE];
87564082Seric 				char *p = wbuf;
87664082Seric 				if (e->e_to != NULL)
87764082Seric 				{
87864082Seric 					sprintf(p, "%s... ", e->e_to);
87964082Seric 					p += strlen(p);
88064082Seric 				}
88164082Seric 				sprintf(p, "reply(%s) during %s",
88264082Seric 					mci->mci_host, SmtpPhase);
88363753Seric 				checkfd012(wbuf);
88463753Seric 			}
88563753Seric #endif
88663753Seric 			HoldErrs = oldholderrs;
88710054Seric 			return (-1);
88810131Seric 		}
88958957Seric 		fixcrlf(bufp, TRUE);
89010054Seric 
89163753Seric 		/* EHLO failure is not a real error */
89263753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
89363753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
89414900Seric 		{
89514900Seric 			/* serious error -- log the previous command */
89664071Seric 			if (SmtpNeedIntro)
89764071Seric 			{
89864071Seric 				/* inform user who we are chatting with */
89964071Seric 				fprintf(CurEnv->e_xfp,
90064071Seric 					"... while talking to %s:\n",
90164071Seric 					CurHostName);
90264071Seric 				SmtpNeedIntro = FALSE;
90364071Seric 			}
90459014Seric 			if (SmtpMsgBuffer[0] != '\0')
90559014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
90659014Seric 			SmtpMsgBuffer[0] = '\0';
90714900Seric 
90814900Seric 			/* now log the message as from the other side */
90958957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
91014900Seric 		}
91114900Seric 
91214900Seric 		/* display the input for verbose mode */
91358120Seric 		if (Verbose)
91459956Seric 			nmessage("050 %s", bufp);
9157356Seric 
91659285Seric 		/* process the line */
91767893Seric 		if (pfunc != NULL)
91867893Seric 			(*pfunc)(bufp, firstline, m, mci, e);
91959285Seric 
92059285Seric 		firstline = FALSE;
92159285Seric 
9227356Seric 		/* if continuation is required, we can go on */
92359014Seric 		if (bufp[3] == '-')
9244684Seric 			continue;
9257356Seric 
92659014Seric 		/* ignore improperly formated input */
92759014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
92859014Seric 			continue;
92959014Seric 
9307356Seric 		/* decode the reply code */
93159014Seric 		r = atoi(bufp);
9327356Seric 
9337356Seric 		/* extra semantics: 0xx codes are "informational" */
93459014Seric 		if (r >= 100)
93559014Seric 			break;
93659014Seric 	}
9377356Seric 
93859014Seric 	/*
93959014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
94059014Seric 	**  line of the response from here on out.
94159014Seric 	*/
94258061Seric 
94359014Seric 	/* save temporary failure messages for posterity */
94459014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
94559014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
9469391Seric 
94759014Seric 	/* reply code 421 is "Service Shutting Down" */
94859014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
94959014Seric 	{
95059014Seric 		/* send the quit protocol */
95159014Seric 		mci->mci_state = MCIS_SSD;
95259014Seric 		smtpquit(m, mci, e);
9534684Seric 	}
95459014Seric 
95559014Seric 	return (r);
9564684Seric }
9574796Seric /*
9584865Seric **  SMTPMESSAGE -- send message to server
9594796Seric **
9604796Seric **	Parameters:
9614796Seric **		f -- format
96210175Seric **		m -- the mailer to control formatting.
9634796Seric **		a, b, c -- parameters
9644796Seric **
9654796Seric **	Returns:
9664796Seric **		none.
9674796Seric **
9684796Seric **	Side Effects:
96953740Seric **		writes message to mci->mci_out.
9704796Seric */
9714796Seric 
9724865Seric /*VARARGS1*/
97357642Seric #ifdef __STDC__
97457642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
97557642Seric #else
97657642Seric smtpmessage(f, m, mci, va_alist)
9774796Seric 	char *f;
97810175Seric 	MAILER *m;
97954967Seric 	MCI *mci;
98057642Seric 	va_dcl
98157642Seric #endif
9824796Seric {
98356852Seric 	VA_LOCAL_DECL
98456852Seric 
98557135Seric 	VA_START(mci);
98656852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
98756852Seric 	VA_END;
98858680Seric 
98958120Seric 	if (tTd(18, 1) || Verbose)
99058151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
99163753Seric 	if (TrafficLogFile != NULL)
99263753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
99353740Seric 	if (mci->mci_out != NULL)
99458680Seric 	{
99553740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
99654967Seric 			m == NULL ? "\r\n" : m->m_eol);
99758680Seric 	}
99859149Seric 	else if (tTd(18, 1))
99958725Seric 	{
100059149Seric 		printf("smtpmessage: NULL mci_out\n");
100158725Seric 	}
10024796Seric }
10035182Seric 
100456795Seric # endif /* SMTP */
1005