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*68884Seric static char sccsid[] = "@(#)usersmtp.c	8.49 (Berkeley) 04/25/95 (with SMTP)";
1433731Sbostic #else
15*68884Seric static char sccsid[] = "@(#)usersmtp.c	8.49 (Berkeley) 04/25/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 
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 */
8268100Seric 	if (CurHostName == NULL)
8368100Seric 		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 **	Parameters:
22260210Seric **		line -- the response line.
22367893Seric **		firstline -- set if this is the first line of the reply.
22460210Seric **		m -- the mailer.
22560210Seric **		mci -- the mailer connection info.
22660210Seric **		e -- the envelope.
22760210Seric **
22860210Seric **	Returns:
22960210Seric **		none.
23060210Seric */
23160210Seric 
23260210Seric void
23367893Seric esmtp_check(line, firstline, m, mci, e)
23460210Seric 	char *line;
23567893Seric 	bool firstline;
23660210Seric 	MAILER *m;
23760210Seric 	register MCI *mci;
23860210Seric 	ENVELOPE *e;
23960210Seric {
24068559Seric 	register char *l;
24168559Seric 
24268559Seric 	for (l = line; (l = strchr(++l, 'E')) != NULL; )
24367893Seric 	{
24468559Seric 		if (strncmp(l, "ESMTP ", 6) == 0)
24567893Seric 		{
24667893Seric 			mci->mci_flags |= MCIF_ESMTP;
24767893Seric 			break;
24867893Seric 		}
24967893Seric 	}
25068559Seric 	for (l = line; (l = strchr(++l, '8')) != NULL; )
25168559Seric 	{
25268559Seric 		if (strncmp(l, "8BIT OK", 7) == 0)
25368559Seric 		{
25468559Seric 			mci->mci_flags |= MCIF_8BITOK;
25568559Seric 			break;
25668559Seric 		}
25768559Seric 	}
25860210Seric }
25960210Seric /*
26059285Seric **  HELO_OPTIONS -- process the options on a HELO line.
26159285Seric **
26259285Seric **	Parameters:
26359285Seric **		line -- the response line.
26467893Seric **		firstline -- set if this is the first line of the reply.
26559285Seric **		m -- the mailer.
26659285Seric **		mci -- the mailer connection info.
26759285Seric **		e -- the envelope.
26859285Seric **
26959285Seric **	Returns:
27059285Seric **		none.
27159285Seric */
27253751Seric 
27359285Seric void
27467893Seric helo_options(line, firstline, m, mci, e)
27559285Seric 	char *line;
27667893Seric 	bool firstline;
27759285Seric 	MAILER *m;
27859285Seric 	register MCI *mci;
27959285Seric 	ENVELOPE *e;
28059285Seric {
28159285Seric 	register char *p;
28259285Seric 
28367971Seric 	if (firstline)
28467893Seric 		return;
28567893Seric 
28668706Seric 	if (strlen(line) < (SIZE_T) 5)
28759285Seric 		return;
28859285Seric 	line += 4;
28959285Seric 	p = strchr(line, ' ');
29059285Seric 	if (p != NULL)
29159285Seric 		*p++ = '\0';
29259285Seric 	if (strcasecmp(line, "size") == 0)
29359285Seric 	{
29459285Seric 		mci->mci_flags |= MCIF_SIZE;
29559285Seric 		if (p != NULL)
29659285Seric 			mci->mci_maxsize = atol(p);
29759285Seric 	}
29859285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
29965870Seric 	{
30059285Seric 		mci->mci_flags |= MCIF_8BITMIME;
30165870Seric 		mci->mci_flags &= ~MCIF_7BIT;
30265870Seric 	}
30359285Seric 	else if (strcasecmp(line, "expn") == 0)
30459285Seric 		mci->mci_flags |= MCIF_EXPN;
30568606Seric 	else if (strcasecmp(line, "x-dsn-03") == 0)
30667880Seric 		mci->mci_flags |= MCIF_DSN;
30759285Seric }
30859285Seric /*
30959285Seric **  SMTPMAILFROM -- send MAIL command
31059285Seric **
31159285Seric **	Parameters:
31259285Seric **		m -- the mailer.
31359285Seric **		mci -- the mailer connection structure.
31459285Seric **		e -- the envelope (including the sender to specify).
31559285Seric */
31659285Seric 
31753751Seric smtpmailfrom(m, mci, e)
31853751Seric 	struct mailer *m;
31954967Seric 	MCI *mci;
32053751Seric 	ENVELOPE *e;
32153751Seric {
32253751Seric 	int r;
32365494Seric 	char *bufp;
32467887Seric 	char *bodytype;
32568528Seric 	char buf[MAXNAME + 1];
32659285Seric 	char optbuf[MAXLINE];
32753751Seric 
32863753Seric 	if (tTd(18, 2))
32957943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
33057943Seric 
33159285Seric 	/* set up appropriate options to include */
33264254Seric 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
33359285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
33459285Seric 	else
33559285Seric 		strcpy(optbuf, "");
33659285Seric 
33767887Seric 	bodytype = e->e_bodytype;
33867887Seric 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
33967417Seric 	{
34067887Seric 		if (bodytype == NULL &&
34167887Seric 		    bitset(MM_MIME8BIT, MimeMode) &&
34267887Seric 		    bitset(EF_HAS8BIT, e->e_flags) &&
34367887Seric 		    !bitnset(M_8BITS, m->m_flags))
34467887Seric 			bodytype = "8BITMIME";
34567887Seric 		if (bodytype != NULL)
34667417Seric 		{
34767417Seric 			strcat(optbuf, " BODY=");
34867887Seric 			strcat(optbuf, bodytype);
34967417Seric 		}
35067417Seric 	}
35167995Seric 	else if (bitnset(M_8BITS, m->m_flags) ||
35267995Seric 		 !bitset(EF_HAS8BIT, e->e_flags) ||
35367995Seric 		 (e->e_bodytype != NULL &&
35467995Seric 		  strcasecmp(e->e_bodytype, "7bit") == 0))
35567887Seric 	{
35667887Seric 		/* just pass it through */
35767887Seric 	}
35867887Seric 	else if (bitset(MM_CVTMIME, MimeMode) &&
359*68884Seric 		 (!bitset(MM_PASS8BIT, MimeMode) ||
360*68884Seric 		  bitset(EF_IS_MIME, e->e_flags)))
36167887Seric 	{
36267887Seric 		/* must convert from 8bit MIME format to 7bit encoded */
36367887Seric 		mci->mci_flags |= MCIF_CVT8TO7;
36467887Seric 	}
36567887Seric 	else if (!bitset(MM_PASS8BIT, MimeMode))
36667887Seric 	{
36767887Seric 		/* cannot just send a 8-bit version */
36867887Seric 		usrerr("%s does not support 8BITMIME", mci->mci_host);
36968857Seric 		mci->mci_status = "5.6.3";
37067887Seric 		return EX_DATAERR;
37167887Seric 	}
37267417Seric 
37367963Seric 	if (bitset(MCIF_DSN, mci->mci_flags))
37467880Seric 	{
37567963Seric 		if (e->e_envid != NULL)
37667963Seric 		{
37767963Seric 			strcat(optbuf, " ENVID=");
37867963Seric 			strcat(optbuf, e->e_envid);
37967963Seric 		}
38068559Seric 
38168559Seric 		/* RET= parameter */
38268559Seric 		if (bitset(EF_RET_PARAM, e->e_flags))
38368559Seric 		{
38468559Seric 			strcat(optbuf, " RET=");
38568559Seric 			if (bitset(EF_NO_BODY_RETN, e->e_flags))
38668559Seric 				strcat(optbuf, "HDRS");
38768559Seric 			else
38868559Seric 				strcat(optbuf, "FULL");
38968559Seric 		}
39067880Seric 	}
39167880Seric 
3929315Seric 	/*
3934865Seric 	**  Send the MAIL command.
3944865Seric 	**	Designates the sender.
3954865Seric 	*/
3964796Seric 
39753751Seric 	mci->mci_state = MCIS_ACTIVE;
39853751Seric 
39959540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
40059540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
40158680Seric 		(void) strcpy(buf, "");
40258680Seric 	else
40368529Seric 		expand("\201g", buf, sizeof buf, e);
40465494Seric 	if (buf[0] == '<')
40565494Seric 	{
40665494Seric 		/* strip off <angle brackets> (put back on below) */
40765494Seric 		bufp = &buf[strlen(buf) - 1];
40865494Seric 		if (*bufp == '>')
40965494Seric 			*bufp = '\0';
41065494Seric 		bufp = &buf[1];
41165494Seric 	}
41265494Seric 	else
41365494Seric 		bufp = buf;
41467472Seric 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
41510688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
4168436Seric 	{
41765494Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
4188436Seric 	}
4198436Seric 	else
4208436Seric 	{
42159285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
42265494Seric 			*bufp == '@' ? ',' : ':', bufp, optbuf);
4238436Seric 	}
42461093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
42553751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
42659285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
42768811Seric 	if (r < 0 || r == 421)
42853751Seric 	{
42968811Seric 		/* communications failure/service shutting down */
43053751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
43153751Seric 		mci->mci_errno = errno;
43253751Seric 		smtpquit(m, mci, e);
43353751Seric 		return EX_TEMPFAIL;
43453751Seric 	}
43568811Seric 	else if (REPLYTYPE(r) == 4)
43668811Seric 	{
43768811Seric 		return EX_TEMPFAIL;
43868811Seric 	}
4397963Seric 	else if (r == 250)
44053751Seric 	{
44153751Seric 		return EX_OK;
44253751Seric 	}
44368857Seric 	else if (r == 501)
44468075Seric 	{
44568857Seric 		/* syntax error in arguments */
44668857Seric 		mci->mci_status = "5.5.2";
44768075Seric 		return EX_DATAERR;
44868075Seric 	}
44968857Seric 	else if (r == 553)
45068857Seric 	{
45168857Seric 		/* mailbox name not allowed */
45268857Seric 		mci->mci_status = "5.1.3";
45368857Seric 		return EX_DATAERR;
45468857Seric 	}
4557963Seric 	else if (r == 552)
45653751Seric 	{
45768811Seric 		/* exceeded storage allocation */
45868857Seric 		mci->mci_status = "5.2.2";
45953751Seric 		return EX_UNAVAILABLE;
46053751Seric 	}
46114913Seric 
46258008Seric #ifdef LOG
46358020Seric 	if (LogLevel > 1)
46458008Seric 	{
46567860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s",
46667860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
46758008Seric 	}
46858008Seric #endif
46958008Seric 
47014913Seric 	/* protocol error -- close up */
47153751Seric 	smtpquit(m, mci, e);
47253751Seric 	return EX_PROTOCOL;
4734684Seric }
4744684Seric /*
4754976Seric **  SMTPRCPT -- designate recipient.
4764797Seric **
4774797Seric **	Parameters:
4784865Seric **		to -- address of recipient.
47910175Seric **		m -- the mailer we are sending to.
48057379Seric **		mci -- the connection info for this transaction.
48157379Seric **		e -- the envelope for this transaction.
4824797Seric **
4834797Seric **	Returns:
4844865Seric **		exit status corresponding to recipient status.
4854797Seric **
4864797Seric **	Side Effects:
4874865Seric **		Sends the mail via SMTP.
4884797Seric */
4894797Seric 
49053751Seric smtprcpt(to, m, mci, e)
4914865Seric 	ADDRESS *to;
49210175Seric 	register MAILER *m;
49354967Seric 	MCI *mci;
49453751Seric 	ENVELOPE *e;
4954797Seric {
4964797Seric 	register int r;
49767880Seric 	char optbuf[MAXLINE];
49868857Seric 	extern char *smtptodsn();
4994797Seric 
50067880Seric 	strcpy(optbuf, "");
50167880Seric 	if (bitset(MCIF_DSN, mci->mci_flags))
50267880Seric 	{
50367987Seric 		/* NOTIFY= parameter */
50468603Seric 		if (bitset(QHASNOTIFY, to->q_flags) &&
50568603Seric 		    bitset(QPRIMARY, to->q_flags))
50667880Seric 		{
50768595Seric 			bool firstone = TRUE;
50868595Seric 
50968595Seric 			strcat(optbuf, " NOTIFY=");
51068595Seric 			if (bitset(QPINGONSUCCESS, to->q_flags))
51168595Seric 			{
51268595Seric 				strcat(optbuf, "SUCCESS");
51368595Seric 				firstone = FALSE;
51468595Seric 			}
51568595Seric 			if (bitset(QPINGONFAILURE, to->q_flags))
51668595Seric 			{
51768595Seric 				if (!firstone)
51868595Seric 					strcat(optbuf, ",");
51968595Seric 				strcat(optbuf, "FAILURE");
52068595Seric 				firstone = FALSE;
52168595Seric 			}
52268595Seric 			if (bitset(QPINGONDELAY, to->q_flags))
52368595Seric 			{
52468595Seric 				if (!firstone)
52568595Seric 					strcat(optbuf, ",");
52668595Seric 				strcat(optbuf, "DELAY");
52768595Seric 				firstone = FALSE;
52868595Seric 			}
52968595Seric 			if (firstone)
53068595Seric 				strcat(optbuf, "NEVER");
53167880Seric 		}
53267963Seric 
53367987Seric 		/* ORCPT= parameter */
53467987Seric 		if (to->q_orcpt != NULL)
53567987Seric 		{
53667987Seric 			strcat(optbuf, " ORCPT=");
53767987Seric 			strcat(optbuf, to->q_orcpt);
53867987Seric 		}
53967880Seric 	}
5404865Seric 
54167880Seric 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
54267880Seric 
54361093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
54453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
54559285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
54668867Seric 	to->q_rstatus = newstr(SmtpReplyBuffer);
54768857Seric 	to->q_status = smtptodsn(r);
5488005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
54968857Seric 		return EX_TEMPFAIL;
5507963Seric 	else if (REPLYTYPE(r) == 2)
55168857Seric 		return EX_OK;
5527964Seric 	else if (r == 550 || r == 551 || r == 553)
55368857Seric 		return EX_NOUSER;
5547964Seric 	else if (r == 552 || r == 554)
55568857Seric 		return EX_UNAVAILABLE;
55658008Seric 
55758008Seric #ifdef LOG
55858020Seric 	if (LogLevel > 1)
55958008Seric 	{
56067860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s",
56167860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
56258008Seric 	}
56358008Seric #endif
56458008Seric 
5657964Seric 	return (EX_PROTOCOL);
5664797Seric }
5674797Seric /*
56810175Seric **  SMTPDATA -- send the data and clean up the transaction.
5694684Seric **
5704684Seric **	Parameters:
5714865Seric **		m -- mailer being sent to.
5726980Seric **		e -- the envelope for this message.
5734684Seric **
5744684Seric **	Returns:
5754976Seric **		exit status corresponding to DATA command.
5764684Seric **
5774684Seric **	Side Effects:
5784865Seric **		none.
5794684Seric */
5804684Seric 
58163753Seric static jmp_buf	CtxDataTimeout;
58268433Seric static void	datatimeout();
58363753Seric 
58453740Seric smtpdata(m, mci, e)
5854865Seric 	struct mailer *m;
58654967Seric 	register MCI *mci;
5876980Seric 	register ENVELOPE *e;
5884684Seric {
5894684Seric 	register int r;
59063753Seric 	register EVENT *ev;
59163753Seric 	time_t timeout;
5924684Seric 
5934797Seric 	/*
5944797Seric 	**  Send the data.
59510175Seric 	**	First send the command and check that it is ok.
59610175Seric 	**	Then send the data.
59710175Seric 	**	Follow it up with a dot to terminate.
59810175Seric 	**	Finally get the results of the transaction.
5994797Seric 	*/
6004797Seric 
60110175Seric 	/* send the command and check ok to proceed */
60253751Seric 	smtpmessage("DATA", m, mci);
60361093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
60453751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
60559285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
6068005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
60757990Seric 	{
60857990Seric 		smtpquit(m, mci, e);
6094797Seric 		return (EX_TEMPFAIL);
61057990Seric 	}
6117963Seric 	else if (r == 554)
61257990Seric 	{
61357990Seric 		smtprset(m, mci, e);
6147963Seric 		return (EX_UNAVAILABLE);
61557990Seric 	}
6167963Seric 	else if (r != 354)
61757990Seric 	{
61858008Seric #ifdef LOG
61958020Seric 		if (LogLevel > 1)
62058008Seric 		{
62167860Seric 			syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s",
62267860Seric 				e->e_id, mci->mci_host, SmtpReplyBuffer);
62358008Seric 		}
62458008Seric #endif
62557990Seric 		smtprset(m, mci, e);
6267964Seric 		return (EX_PROTOCOL);
62757990Seric 	}
62810175Seric 
62963757Seric 	/*
63063757Seric 	**  Set timeout around data writes.  Make it at least large
63163757Seric 	**  enough for DNS timeouts on all recipients plus some fudge
63263757Seric 	**  factor.  The main thing is that it should not be infinite.
63363757Seric 	*/
63463757Seric 
63563753Seric 	if (setjmp(CtxDataTimeout) != 0)
63663753Seric 	{
63763753Seric 		mci->mci_errno = errno;
63863753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
63963753Seric 		mci->mci_state = MCIS_ERROR;
64063753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
64163753Seric 		smtpquit(m, mci, e);
64263753Seric 		return EX_TEMPFAIL;
64363753Seric 	}
64463753Seric 
64563787Seric 	timeout = e->e_msgsize / 16;
64668740Seric 	if (timeout < (time_t) 600)
64768740Seric 		timeout = (time_t) 600;
64868740Seric 	timeout += e->e_nrcpts * 300;
64963753Seric 	ev = setevent(timeout, datatimeout, 0);
65063753Seric 
65167546Seric 	/*
65267546Seric 	**  Output the actual message.
65367546Seric 	*/
65467546Seric 
65568228Seric 	(*e->e_puthdr)(mci, e->e_header, e);
65668228Seric 	(*e->e_putbody)(mci, e, NULL);
65710175Seric 
65867546Seric 	/*
65967546Seric 	**  Cleanup after sending message.
66067546Seric 	*/
66167546Seric 
66263753Seric 	clrevent(ev);
66363753Seric 
66464718Seric 	if (ferror(mci->mci_out))
66564718Seric 	{
66664718Seric 		/* error during processing -- don't send the dot */
66764718Seric 		mci->mci_errno = EIO;
66864718Seric 		mci->mci_exitstat = EX_IOERR;
66964718Seric 		mci->mci_state = MCIS_ERROR;
67064718Seric 		smtpquit(m, mci, e);
67164718Seric 		return EX_IOERR;
67264718Seric 	}
67364718Seric 
67410175Seric 	/* terminate the message */
67553740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
67663753Seric 	if (TrafficLogFile != NULL)
67763753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
67858120Seric 	if (Verbose)
67958151Seric 		nmessage(">>> .");
68010175Seric 
68110175Seric 	/* check for the results of the transaction */
68261093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
68353751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
68459285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
68553751Seric 	if (r < 0)
68657990Seric 	{
68757990Seric 		smtpquit(m, mci, e);
6884797Seric 		return (EX_TEMPFAIL);
68957990Seric 	}
69053751Seric 	mci->mci_state = MCIS_OPEN;
69158917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
69253751Seric 	if (REPLYTYPE(r) == 4)
69353751Seric 		return (EX_TEMPFAIL);
6947963Seric 	else if (r == 250)
6957963Seric 		return (EX_OK);
6967963Seric 	else if (r == 552 || r == 554)
6977963Seric 		return (EX_UNAVAILABLE);
69858008Seric #ifdef LOG
69958020Seric 	if (LogLevel > 1)
70058008Seric 	{
70167860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s",
70267860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
70358008Seric 	}
70458008Seric #endif
7057964Seric 	return (EX_PROTOCOL);
7064684Seric }
70763753Seric 
70863753Seric 
70968433Seric static void
71063753Seric datatimeout()
71163753Seric {
71263753Seric 	longjmp(CtxDataTimeout, 1);
71363753Seric }
7144684Seric /*
7154865Seric **  SMTPQUIT -- close the SMTP connection.
7164865Seric **
7174865Seric **	Parameters:
71815535Seric **		m -- a pointer to the mailer.
7194865Seric **
7204865Seric **	Returns:
7214865Seric **		none.
7224865Seric **
7234865Seric **	Side Effects:
7244865Seric **		sends the final protocol and closes the connection.
7254865Seric */
7264865Seric 
72753751Seric smtpquit(m, mci, e)
72853751Seric 	register MAILER *m;
72954967Seric 	register MCI *mci;
73053751Seric 	ENVELOPE *e;
7314865Seric {
73264822Seric 	bool oldSuprErrs = SuprErrs;
7334865Seric 
73464822Seric 	/*
73564822Seric 	**	Suppress errors here -- we may be processing a different
73664822Seric 	**	job when we do the quit connection, and we don't want the
73764822Seric 	**	new job to be penalized for something that isn't it's
73864822Seric 	**	problem.
73964822Seric 	*/
74064822Seric 
74164822Seric 	SuprErrs = TRUE;
74264822Seric 
74354967Seric 	/* send the quit message if we haven't gotten I/O error */
74453751Seric 	if (mci->mci_state != MCIS_ERROR)
7459391Seric 	{
74661093Seric 		SmtpPhase = "client QUIT";
74753751Seric 		smtpmessage("QUIT", m, mci);
74859285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
74964822Seric 		SuprErrs = oldSuprErrs;
75053740Seric 		if (mci->mci_state == MCIS_CLOSED)
75164822Seric 		{
75264822Seric 			SuprErrs = oldSuprErrs;
75310159Seric 			return;
75464822Seric 		}
7559391Seric 	}
7569391Seric 
75752676Seric 	/* now actually close the connection and pick up the zombie */
75865194Seric 	(void) endmailer(mci, e, NULL);
75964822Seric 
76064822Seric 	SuprErrs = oldSuprErrs;
7614865Seric }
7624865Seric /*
76354967Seric **  SMTPRSET -- send a RSET (reset) command
76454967Seric */
76554967Seric 
76654967Seric smtprset(m, mci, e)
76754967Seric 	register MAILER *m;
76854967Seric 	register MCI *mci;
76954967Seric 	ENVELOPE *e;
77054967Seric {
77154967Seric 	int r;
77254967Seric 
77361093Seric 	SmtpPhase = "client RSET";
77454967Seric 	smtpmessage("RSET", m, mci);
77559285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
77657734Seric 	if (r < 0)
77757734Seric 		mci->mci_state = MCIS_ERROR;
77854967Seric 	else if (REPLYTYPE(r) == 2)
77957734Seric 	{
78057734Seric 		mci->mci_state = MCIS_OPEN;
78157734Seric 		return;
78257734Seric 	}
78357734Seric 	smtpquit(m, mci, e);
78454967Seric }
78554967Seric /*
78658867Seric **  SMTPPROBE -- check the connection state
78754967Seric */
78854967Seric 
78958867Seric smtpprobe(mci)
79054967Seric 	register MCI *mci;
79154967Seric {
79254967Seric 	int r;
79354967Seric 	MAILER *m = mci->mci_mailer;
79454967Seric 	extern ENVELOPE BlankEnvelope;
79554967Seric 	ENVELOPE *e = &BlankEnvelope;
79654967Seric 
79761093Seric 	SmtpPhase = "client probe";
79858867Seric 	smtpmessage("RSET", m, mci);
79959285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
80058061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
80154967Seric 		smtpquit(m, mci, e);
80254967Seric 	return r;
80354967Seric }
80454967Seric /*
8054684Seric **  REPLY -- read arpanet reply
8064684Seric **
8074684Seric **	Parameters:
80810175Seric **		m -- the mailer we are reading the reply from.
80957379Seric **		mci -- the mailer connection info structure.
81057379Seric **		e -- the current envelope.
81157379Seric **		timeout -- the timeout for reads.
81259285Seric **		pfunc -- processing function for second and subsequent
81359285Seric **			lines of response -- if null, no special
81459285Seric **			processing is done.
8154684Seric **
8164684Seric **	Returns:
8174684Seric **		reply code it reads.
8184684Seric **
8194684Seric **	Side Effects:
8204684Seric **		flushes the mail file.
8214684Seric */
8224684Seric 
82359285Seric reply(m, mci, e, timeout, pfunc)
82453751Seric 	MAILER *m;
82554967Seric 	MCI *mci;
82653751Seric 	ENVELOPE *e;
82759285Seric 	time_t timeout;
82859285Seric 	void (*pfunc)();
8294684Seric {
83059014Seric 	register char *bufp;
83159014Seric 	register int r;
83259285Seric 	bool firstline = TRUE;
83358957Seric 	char junkbuf[MAXLINE];
83458957Seric 
83557379Seric 	if (mci->mci_out != NULL)
83657379Seric 		(void) fflush(mci->mci_out);
8374684Seric 
8387677Seric 	if (tTd(18, 1))
8394796Seric 		printf("reply\n");
8404796Seric 
8417356Seric 	/*
8427356Seric 	**  Read the input line, being careful not to hang.
8437356Seric 	*/
8447356Seric 
84559014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
8464684Seric 	{
8477356Seric 		register char *p;
84853751Seric 		extern time_t curtime();
8494684Seric 
8507685Seric 		/* actually do the read */
85153751Seric 		if (e->e_xfp != NULL)
85253751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
8537356Seric 
85410054Seric 		/* if we are in the process of closing just give the code */
85553740Seric 		if (mci->mci_state == MCIS_CLOSED)
85610054Seric 			return (SMTPCLOSING);
85710054Seric 
85858680Seric 		if (mci->mci_out != NULL)
85958680Seric 			fflush(mci->mci_out);
86058680Seric 
86110054Seric 		/* get the line from the other side */
86261093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
86353751Seric 		mci->mci_lastuse = curtime();
86453751Seric 
86510054Seric 		if (p == NULL)
86610131Seric 		{
86763753Seric 			bool oldholderrs;
86810148Seric 			extern char MsgBuf[];		/* err.c */
86910148Seric 
87021065Seric 			/* if the remote end closed early, fake an error */
87121065Seric 			if (errno == 0)
87221065Seric # ifdef ECONNRESET
87321065Seric 				errno = ECONNRESET;
87456795Seric # else /* ECONNRESET */
87521065Seric 				errno = EPIPE;
87656795Seric # endif /* ECONNRESET */
87721065Seric 
87857379Seric 			mci->mci_errno = errno;
87957642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
88063753Seric 			oldholderrs = HoldErrs;
88163753Seric 			HoldErrs = TRUE;
88263753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
88363753Seric 
88410420Seric 			/* if debugging, pause so we can see state */
88510420Seric 			if (tTd(18, 100))
88610420Seric 				pause();
88754967Seric 			mci->mci_state = MCIS_ERROR;
88853751Seric 			smtpquit(m, mci, e);
88963753Seric #ifdef XDEBUG
89063753Seric 			{
89163753Seric 				char wbuf[MAXLINE];
89264082Seric 				char *p = wbuf;
89364082Seric 				if (e->e_to != NULL)
89464082Seric 				{
89564082Seric 					sprintf(p, "%s... ", e->e_to);
89664082Seric 					p += strlen(p);
89764082Seric 				}
89864082Seric 				sprintf(p, "reply(%s) during %s",
89964082Seric 					mci->mci_host, SmtpPhase);
90063753Seric 				checkfd012(wbuf);
90163753Seric 			}
90263753Seric #endif
90363753Seric 			HoldErrs = oldholderrs;
90410054Seric 			return (-1);
90510131Seric 		}
90658957Seric 		fixcrlf(bufp, TRUE);
90710054Seric 
90863753Seric 		/* EHLO failure is not a real error */
90963753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
91063753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
91114900Seric 		{
91214900Seric 			/* serious error -- log the previous command */
91364071Seric 			if (SmtpNeedIntro)
91464071Seric 			{
91564071Seric 				/* inform user who we are chatting with */
91664071Seric 				fprintf(CurEnv->e_xfp,
91764071Seric 					"... while talking to %s:\n",
91864071Seric 					CurHostName);
91964071Seric 				SmtpNeedIntro = FALSE;
92064071Seric 			}
92159014Seric 			if (SmtpMsgBuffer[0] != '\0')
92259014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
92359014Seric 			SmtpMsgBuffer[0] = '\0';
92414900Seric 
92514900Seric 			/* now log the message as from the other side */
92658957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
92714900Seric 		}
92814900Seric 
92914900Seric 		/* display the input for verbose mode */
93058120Seric 		if (Verbose)
93159956Seric 			nmessage("050 %s", bufp);
9327356Seric 
93359285Seric 		/* process the line */
93467893Seric 		if (pfunc != NULL)
93567893Seric 			(*pfunc)(bufp, firstline, m, mci, e);
93659285Seric 
93759285Seric 		firstline = FALSE;
93859285Seric 
9397356Seric 		/* if continuation is required, we can go on */
94059014Seric 		if (bufp[3] == '-')
9414684Seric 			continue;
9427356Seric 
94359014Seric 		/* ignore improperly formated input */
94459014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
94559014Seric 			continue;
94659014Seric 
9477356Seric 		/* decode the reply code */
94859014Seric 		r = atoi(bufp);
9497356Seric 
9507356Seric 		/* extra semantics: 0xx codes are "informational" */
95159014Seric 		if (r >= 100)
95259014Seric 			break;
95359014Seric 	}
9547356Seric 
95559014Seric 	/*
95659014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
95759014Seric 	**  line of the response from here on out.
95859014Seric 	*/
95958061Seric 
96059014Seric 	/* save temporary failure messages for posterity */
96159014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
96259014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
9639391Seric 
96459014Seric 	/* reply code 421 is "Service Shutting Down" */
96559014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
96659014Seric 	{
96759014Seric 		/* send the quit protocol */
96859014Seric 		mci->mci_state = MCIS_SSD;
96959014Seric 		smtpquit(m, mci, e);
9704684Seric 	}
97159014Seric 
97259014Seric 	return (r);
9734684Seric }
9744796Seric /*
9754865Seric **  SMTPMESSAGE -- send message to server
9764796Seric **
9774796Seric **	Parameters:
9784796Seric **		f -- format
97910175Seric **		m -- the mailer to control formatting.
9804796Seric **		a, b, c -- parameters
9814796Seric **
9824796Seric **	Returns:
9834796Seric **		none.
9844796Seric **
9854796Seric **	Side Effects:
98653740Seric **		writes message to mci->mci_out.
9874796Seric */
9884796Seric 
9894865Seric /*VARARGS1*/
99057642Seric #ifdef __STDC__
99157642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
99257642Seric #else
99357642Seric smtpmessage(f, m, mci, va_alist)
9944796Seric 	char *f;
99510175Seric 	MAILER *m;
99654967Seric 	MCI *mci;
99757642Seric 	va_dcl
99857642Seric #endif
9994796Seric {
100056852Seric 	VA_LOCAL_DECL
100156852Seric 
100257135Seric 	VA_START(mci);
100356852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
100456852Seric 	VA_END;
100558680Seric 
100658120Seric 	if (tTd(18, 1) || Verbose)
100758151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
100863753Seric 	if (TrafficLogFile != NULL)
100963753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
101053740Seric 	if (mci->mci_out != NULL)
101158680Seric 	{
101253740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
101354967Seric 			m == NULL ? "\r\n" : m->m_eol);
101458680Seric 	}
101559149Seric 	else if (tTd(18, 1))
101658725Seric 	{
101759149Seric 		printf("smtpmessage: NULL mci_out\n");
101858725Seric 	}
10194796Seric }
10205182Seric 
102156795Seric # endif /* SMTP */
1022