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*67971Seric static char sccsid[] = "@(#)usersmtp.c	8.29 (Berkeley) 11/22/94 (with SMTP)";
1433731Sbostic #else
15*67971Seric static char sccsid[] = "@(#)usersmtp.c	8.29 (Berkeley) 11/22/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 */
8264071Seric 	SmtpNeedIntro = TRUE;
8354967Seric 	switch (mci->mci_state)
846051Seric 	{
8554967Seric 	  case MCIS_ACTIVE:
8654967Seric 		/* need to clear old information */
8754967Seric 		smtprset(m, mci, e);
8857734Seric 		/* fall through */
8915139Seric 
9054967Seric 	  case MCIS_OPEN:
9154967Seric 		return;
9254967Seric 
9354967Seric 	  case MCIS_ERROR:
9454967Seric 	  case MCIS_SSD:
9554967Seric 		/* shouldn't happen */
9654967Seric 		smtpquit(m, mci, e);
9757734Seric 		/* fall through */
9854967Seric 
9954967Seric 	  case MCIS_CLOSED:
10058151Seric 		syserr("451 smtpinit: state CLOSED");
10154967Seric 		return;
10254967Seric 
10354967Seric 	  case MCIS_OPENING:
10454967Seric 		break;
1056051Seric 	}
1064796Seric 
10754967Seric 	mci->mci_state = MCIS_OPENING;
10854967Seric 
1094865Seric 	/*
1104865Seric 	**  Get the greeting message.
11114913Seric 	**	This should appear spontaneously.  Give it five minutes to
11214886Seric 	**	happen.
1134865Seric 	*/
1144797Seric 
11561093Seric 	SmtpPhase = mci->mci_phase = "client greeting";
11653751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
11760210Seric 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
11864750Seric 	if (r < 0 || REPLYTYPE(r) == 4)
11952104Seric 		goto tempfail1;
12064750Seric 	if (REPLYTYPE(r) != 2)
12164750Seric 		goto unavailable;
1224684Seric 
1234865Seric 	/*
1244976Seric 	**  Send the HELO command.
1257963Seric 	**	My mother taught me to always introduce myself.
1264976Seric 	*/
1274976Seric 
12859285Seric 	if (bitnset(M_ESMTP, m->m_flags))
12959285Seric 		mci->mci_flags |= MCIF_ESMTP;
13059285Seric 
13159285Seric tryhelo:
13259285Seric 	if (bitset(MCIF_ESMTP, mci->mci_flags))
13359285Seric 	{
13459285Seric 		smtpmessage("EHLO %s", m, mci, MyHostName);
13561093Seric 		SmtpPhase = mci->mci_phase = "client EHLO";
13659285Seric 	}
13759285Seric 	else
13859285Seric 	{
13959285Seric 		smtpmessage("HELO %s", m, mci, MyHostName);
14061093Seric 		SmtpPhase = mci->mci_phase = "client HELO";
14159285Seric 	}
14253751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
14359285Seric 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1448005Seric 	if (r < 0)
14552104Seric 		goto tempfail1;
1468005Seric 	else if (REPLYTYPE(r) == 5)
14759285Seric 	{
14859285Seric 		if (bitset(MCIF_ESMTP, mci->mci_flags))
14959285Seric 		{
15059285Seric 			/* try old SMTP instead */
15159285Seric 			mci->mci_flags &= ~MCIF_ESMTP;
15259285Seric 			goto tryhelo;
15359285Seric 		}
15414913Seric 		goto unavailable;
15559285Seric 	}
1567963Seric 	else if (REPLYTYPE(r) != 2)
15752104Seric 		goto tempfail1;
1584976Seric 
1594976Seric 	/*
16058957Seric 	**  Check to see if we actually ended up talking to ourself.
16158957Seric 	**  This means we didn't know about an alias or MX, or we managed
16258957Seric 	**  to connect to an echo server.
16358957Seric 	*/
16458957Seric 
16559026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16658957Seric 	if (p != NULL)
16761707Seric 		*p = '\0';
16867472Seric 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
16967472Seric 	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
17058957Seric 	{
17158957Seric 		syserr("553 %s config error: mail loops back to myself",
17258957Seric 			MyHostName);
17358957Seric 		mci->mci_exitstat = EX_CONFIG;
17458957Seric 		mci->mci_errno = 0;
17558957Seric 		smtpquit(m, mci, e);
17658957Seric 		return;
17758957Seric 	}
17858957Seric 
17958957Seric 	/*
1809315Seric 	**  If this is expected to be another sendmail, send some internal
1819315Seric 	**  commands.
1829315Seric 	*/
1839315Seric 
18410688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1859315Seric 	{
1869315Seric 		/* tell it to be verbose */
18753751Seric 		smtpmessage("VERB", m, mci);
18859285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1899315Seric 		if (r < 0)
19052104Seric 			goto tempfail2;
1919315Seric 	}
1929315Seric 
19365057Seric 	if (mci->mci_state != MCIS_CLOSED)
19465057Seric 	{
19565057Seric 		mci->mci_state = MCIS_OPEN;
19665057Seric 		return;
19765057Seric 	}
19853751Seric 
19965057Seric 	/* got a 421 error code during startup */
20065057Seric 
20153751Seric   tempfail1:
20253751Seric   tempfail2:
20353751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
20457379Seric 	if (mci->mci_errno == 0)
20557379Seric 		mci->mci_errno = errno;
20657379Seric 	if (mci->mci_state != MCIS_CLOSED)
20757379Seric 		smtpquit(m, mci, e);
20854967Seric 	return;
20953751Seric 
21053751Seric   unavailable:
21153751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
21253751Seric 	mci->mci_errno = errno;
21353751Seric 	smtpquit(m, mci, e);
21454967Seric 	return;
21553751Seric }
21659285Seric /*
21760210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
21860210Seric **
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
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 {
23967893Seric 	while ((line = strchr(++line, 'E')) != NULL)
24067893Seric 	{
24167893Seric 		if (strncmp(line, "ESMTP ", 6) == 0)
24267893Seric 		{
24367893Seric 			mci->mci_flags |= MCIF_ESMTP;
24467893Seric 			break;
24567893Seric 		}
24667893Seric 	}
24760210Seric }
24860210Seric /*
24959285Seric **  HELO_OPTIONS -- process the options on a HELO line.
25059285Seric **
25159285Seric **	Parameters:
25259285Seric **		line -- the response line.
25367893Seric **		firstline -- set if this is the first line of the reply.
25459285Seric **		m -- the mailer.
25559285Seric **		mci -- the mailer connection info.
25659285Seric **		e -- the envelope.
25759285Seric **
25859285Seric **	Returns:
25959285Seric **		none.
26059285Seric */
26153751Seric 
26259285Seric void
26367893Seric helo_options(line, firstline, m, mci, e)
26459285Seric 	char *line;
26567893Seric 	bool firstline;
26659285Seric 	MAILER *m;
26759285Seric 	register MCI *mci;
26859285Seric 	ENVELOPE *e;
26959285Seric {
27059285Seric 	register char *p;
27159285Seric 
272*67971Seric 	if (firstline)
27367893Seric 		return;
27467893Seric 
27559285Seric 	if (strlen(line) < 5)
27659285Seric 		return;
27759285Seric 	line += 4;
27859285Seric 	p = strchr(line, ' ');
27959285Seric 	if (p != NULL)
28059285Seric 		*p++ = '\0';
28159285Seric 	if (strcasecmp(line, "size") == 0)
28259285Seric 	{
28359285Seric 		mci->mci_flags |= MCIF_SIZE;
28459285Seric 		if (p != NULL)
28559285Seric 			mci->mci_maxsize = atol(p);
28659285Seric 	}
28759285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
28865870Seric 	{
28959285Seric 		mci->mci_flags |= MCIF_8BITMIME;
29065870Seric 		mci->mci_flags &= ~MCIF_7BIT;
29165870Seric 	}
29259285Seric 	else if (strcasecmp(line, "expn") == 0)
29359285Seric 		mci->mci_flags |= MCIF_EXPN;
29467963Seric 	else if (strcasecmp(line, "x-dsn-1") == 0)
29567880Seric 		mci->mci_flags |= MCIF_DSN;
29659285Seric }
29759285Seric /*
29859285Seric **  SMTPMAILFROM -- send MAIL command
29959285Seric **
30059285Seric **	Parameters:
30159285Seric **		m -- the mailer.
30259285Seric **		mci -- the mailer connection structure.
30359285Seric **		e -- the envelope (including the sender to specify).
30459285Seric */
30559285Seric 
30653751Seric smtpmailfrom(m, mci, e)
30753751Seric 	struct mailer *m;
30854967Seric 	MCI *mci;
30953751Seric 	ENVELOPE *e;
31053751Seric {
31153751Seric 	int r;
31265494Seric 	char *bufp;
31367887Seric 	char *bodytype;
31453751Seric 	char buf[MAXNAME];
31559285Seric 	char optbuf[MAXLINE];
31653751Seric 
31763753Seric 	if (tTd(18, 2))
31857943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
31957943Seric 
32059285Seric 	/* set up appropriate options to include */
32164254Seric 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
32259285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
32359285Seric 	else
32459285Seric 		strcpy(optbuf, "");
32559285Seric 
32667887Seric 	bodytype = e->e_bodytype;
32767887Seric 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
32867417Seric 	{
32967887Seric 		if (bodytype == NULL &&
33067887Seric 		    bitset(MM_MIME8BIT, MimeMode) &&
33167887Seric 		    bitset(EF_HAS8BIT, e->e_flags) &&
33267887Seric 		    !bitnset(M_8BITS, m->m_flags))
33367887Seric 			bodytype = "8BITMIME";
33467887Seric 		if (bodytype != NULL)
33567417Seric 		{
33667417Seric 			strcat(optbuf, " BODY=");
33767887Seric 			strcat(optbuf, bodytype);
33867417Seric 		}
33967417Seric 	}
34067887Seric 	else if (bitnset(M_8BITS, m->m_flags))
34167887Seric 	{
34267887Seric 		/* just pass it through */
34367887Seric 	}
34467887Seric 	else if (bitset(MM_CVTMIME, MimeMode) &&
34567887Seric 		 (e->e_bodytype == NULL ? !bitset(MM_PASS8BIT, MimeMode)
34667887Seric 					: strcasecmp(e->e_bodytype, "7bit") != 0))
34767887Seric 	{
34867887Seric 		/* must convert from 8bit MIME format to 7bit encoded */
34967887Seric 		mci->mci_flags |= MCIF_CVT8TO7;
35067887Seric 	}
35167887Seric 	else if (!bitset(MM_PASS8BIT, MimeMode))
35267887Seric 	{
35367887Seric 		/* cannot just send a 8-bit version */
35467887Seric 		usrerr("%s does not support 8BITMIME", mci->mci_host);
35567887Seric 		return EX_DATAERR;
35667887Seric 	}
35767417Seric 
35867963Seric 	if (bitset(MCIF_DSN, mci->mci_flags))
35967880Seric 	{
36067963Seric 		if (e->e_envid != NULL)
36167963Seric 		{
36267963Seric 			strcat(optbuf, " ENVID=");
36367963Seric 			strcat(optbuf, e->e_envid);
36467963Seric 		}
36567963Seric 		if (e->e_omts != NULL)
36667963Seric 		{
36767963Seric 			strcat(optbuf, " OMTS=");
36867963Seric 			strcat(optbuf, e->e_omts);
36967963Seric 		}
37067880Seric 	}
37167880Seric 
3729315Seric 	/*
3734865Seric 	**  Send the MAIL command.
3744865Seric 	**	Designates the sender.
3754865Seric 	*/
3764796Seric 
37753751Seric 	mci->mci_state = MCIS_ACTIVE;
37853751Seric 
37959540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
38059540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
38158680Seric 		(void) strcpy(buf, "");
38258680Seric 	else
38358680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
38465494Seric 	if (buf[0] == '<')
38565494Seric 	{
38665494Seric 		/* strip off <angle brackets> (put back on below) */
38765494Seric 		bufp = &buf[strlen(buf) - 1];
38865494Seric 		if (*bufp == '>')
38965494Seric 			*bufp = '\0';
39065494Seric 		bufp = &buf[1];
39165494Seric 	}
39265494Seric 	else
39365494Seric 		bufp = buf;
39467472Seric 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
39510688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
3968436Seric 	{
39765494Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
3988436Seric 	}
3998436Seric 	else
4008436Seric 	{
40159285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
40265494Seric 			*bufp == '@' ? ',' : ':', bufp, optbuf);
4038436Seric 	}
40461093Seric 	SmtpPhase = mci->mci_phase = "client MAIL";
40553751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
40659285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
4078005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
40853751Seric 	{
40953751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
41053751Seric 		mci->mci_errno = errno;
41153751Seric 		smtpquit(m, mci, e);
41253751Seric 		return EX_TEMPFAIL;
41353751Seric 	}
4147963Seric 	else if (r == 250)
41553751Seric 	{
41653751Seric 		mci->mci_exitstat = EX_OK;
41753751Seric 		return EX_OK;
41853751Seric 	}
4197963Seric 	else if (r == 552)
42053751Seric 	{
42153751Seric 		/* signal service unavailable */
42253751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
42353751Seric 		smtpquit(m, mci, e);
42453751Seric 		return EX_UNAVAILABLE;
42553751Seric 	}
42614913Seric 
42758008Seric #ifdef LOG
42858020Seric 	if (LogLevel > 1)
42958008Seric 	{
43067860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP MAIL protocol error: %s",
43167860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
43258008Seric 	}
43358008Seric #endif
43458008Seric 
43514913Seric 	/* protocol error -- close up */
43653751Seric 	smtpquit(m, mci, e);
43753751Seric 	mci->mci_exitstat = EX_PROTOCOL;
43853751Seric 	return EX_PROTOCOL;
4394684Seric }
4404684Seric /*
4414976Seric **  SMTPRCPT -- designate recipient.
4424797Seric **
4434797Seric **	Parameters:
4444865Seric **		to -- address of recipient.
44510175Seric **		m -- the mailer we are sending to.
44657379Seric **		mci -- the connection info for this transaction.
44757379Seric **		e -- the envelope for this transaction.
4484797Seric **
4494797Seric **	Returns:
4504865Seric **		exit status corresponding to recipient status.
4514797Seric **
4524797Seric **	Side Effects:
4534865Seric **		Sends the mail via SMTP.
4544797Seric */
4554797Seric 
45653751Seric smtprcpt(to, m, mci, e)
4574865Seric 	ADDRESS *to;
45810175Seric 	register MAILER *m;
45954967Seric 	MCI *mci;
46053751Seric 	ENVELOPE *e;
4614797Seric {
4624797Seric 	register int r;
46367880Seric 	char optbuf[MAXLINE];
4644797Seric 
46567880Seric 	strcpy(optbuf, "");
46667880Seric 	if (bitset(MCIF_DSN, mci->mci_flags))
46767880Seric 	{
46867963Seric 		bool firstone = TRUE;
46967963Seric 
47067880Seric 		strcat(optbuf, " NOTIFY=");
47167880Seric 		if (bitset(QPINGONFAILURE, to->q_flags))
47267880Seric 		{
47367963Seric 			strcat(optbuf, "FAILURE");
47467963Seric 			firstone = FALSE;
47567880Seric 		}
47667963Seric 		if (bitset(QPINGONSUCCESS, to->q_flags))
47767880Seric 		{
478*67971Seric 			if (!firstone)
47967963Seric 				strcat(optbuf, ",");
48067963Seric 			strcat(optbuf, "SUCCESS");
48167963Seric 			firstone = FALSE;
48267880Seric 		}
48367963Seric 		if (bitset(QPINGONDELAY, to->q_flags))
48467880Seric 		{
485*67971Seric 			if (!firstone)
48667963Seric 				strcat(optbuf, ",");
48767963Seric 			strcat(optbuf, "DELAY");
48867963Seric 			firstone = FALSE;
48967963Seric 		}
49067963Seric 		if (firstone)
49167963Seric 			strcat(optbuf, "NEVER");
49267963Seric 
49367963Seric 		if (bitset(QHAS_RET_PARAM, to->q_flags))
49467963Seric 		{
49567880Seric 			strcat(optbuf, " RET=");
49667963Seric 			if (bitset(QRET_HDRS, to->q_flags))
49767963Seric 				strcat(optbuf, "HDRS");
49867880Seric 			else
49967963Seric 				strcat(optbuf, "FULL");
50067880Seric 		}
50167880Seric 	}
50267880Seric 	else if (bitset(QPINGONSUCCESS, to->q_flags))
50367880Seric 	{
50467880Seric 		to->q_flags |= QRELAYED;
50567880Seric 		fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n",
50667880Seric 			to->q_paddr);
50767880Seric 	}
5084865Seric 
50967880Seric 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
51067880Seric 
51161093Seric 	SmtpPhase = mci->mci_phase = "client RCPT";
51253751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
51359285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
51467880Seric 	setstatus(to, SmtpReplyBuffer);
5158005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
5164865Seric 		return (EX_TEMPFAIL);
5177963Seric 	else if (REPLYTYPE(r) == 2)
5187963Seric 		return (EX_OK);
5197964Seric 	else if (r == 550 || r == 551 || r == 553)
5207964Seric 		return (EX_NOUSER);
5217964Seric 	else if (r == 552 || r == 554)
5227964Seric 		return (EX_UNAVAILABLE);
52358008Seric 
52458008Seric #ifdef LOG
52558020Seric 	if (LogLevel > 1)
52658008Seric 	{
52767860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP RCPT protocol error: %s",
52867860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
52958008Seric 	}
53058008Seric #endif
53158008Seric 
5327964Seric 	return (EX_PROTOCOL);
5334797Seric }
5344797Seric /*
53510175Seric **  SMTPDATA -- send the data and clean up the transaction.
5364684Seric **
5374684Seric **	Parameters:
5384865Seric **		m -- mailer being sent to.
5396980Seric **		e -- the envelope for this message.
5404684Seric **
5414684Seric **	Returns:
5424976Seric **		exit status corresponding to DATA command.
5434684Seric **
5444684Seric **	Side Effects:
5454865Seric **		none.
5464684Seric */
5474684Seric 
54863753Seric static jmp_buf	CtxDataTimeout;
54963937Seric static int	datatimeout();
55063753Seric 
55153740Seric smtpdata(m, mci, e)
5524865Seric 	struct mailer *m;
55354967Seric 	register MCI *mci;
5546980Seric 	register ENVELOPE *e;
5554684Seric {
5564684Seric 	register int r;
55763753Seric 	register EVENT *ev;
55863753Seric 	time_t timeout;
5594684Seric 
5604797Seric 	/*
5614797Seric 	**  Send the data.
56210175Seric 	**	First send the command and check that it is ok.
56310175Seric 	**	Then send the data.
56410175Seric 	**	Follow it up with a dot to terminate.
56510175Seric 	**	Finally get the results of the transaction.
5664797Seric 	*/
5674797Seric 
56810175Seric 	/* send the command and check ok to proceed */
56953751Seric 	smtpmessage("DATA", m, mci);
57061093Seric 	SmtpPhase = mci->mci_phase = "client DATA 354";
57153751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
57259285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
5738005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
57457990Seric 	{
57557990Seric 		smtpquit(m, mci, e);
5764797Seric 		return (EX_TEMPFAIL);
57757990Seric 	}
5787963Seric 	else if (r == 554)
57957990Seric 	{
58057990Seric 		smtprset(m, mci, e);
5817963Seric 		return (EX_UNAVAILABLE);
58257990Seric 	}
5837963Seric 	else if (r != 354)
58457990Seric 	{
58558008Seric #ifdef LOG
58658020Seric 		if (LogLevel > 1)
58758008Seric 		{
58867860Seric 			syslog(LOG_CRIT, "%s: %s: SMTP DATA-1 protocol error: %s",
58967860Seric 				e->e_id, mci->mci_host, SmtpReplyBuffer);
59058008Seric 		}
59158008Seric #endif
59257990Seric 		smtprset(m, mci, e);
5937964Seric 		return (EX_PROTOCOL);
59457990Seric 	}
59510175Seric 
59663757Seric 	/*
59763757Seric 	**  Set timeout around data writes.  Make it at least large
59863757Seric 	**  enough for DNS timeouts on all recipients plus some fudge
59963757Seric 	**  factor.  The main thing is that it should not be infinite.
60063757Seric 	*/
60163757Seric 
60263753Seric 	if (setjmp(CtxDataTimeout) != 0)
60363753Seric 	{
60463753Seric 		mci->mci_errno = errno;
60563753Seric 		mci->mci_exitstat = EX_TEMPFAIL;
60663753Seric 		mci->mci_state = MCIS_ERROR;
60763753Seric 		syserr("451 timeout writing message to %s", mci->mci_host);
60863753Seric 		smtpquit(m, mci, e);
60963753Seric 		return EX_TEMPFAIL;
61063753Seric 	}
61163753Seric 
61263787Seric 	timeout = e->e_msgsize / 16;
61363787Seric 	if (timeout < (time_t) 60)
61463787Seric 		timeout = (time_t) 60;
61563787Seric 	timeout += e->e_nrcpts * 90;
61663753Seric 	ev = setevent(timeout, datatimeout, 0);
61763753Seric 
61867546Seric 	/*
61967546Seric 	**  Output the actual message.
62067546Seric 	*/
62167546Seric 
62267936Seric 	(*e->e_puthdr)(mci, e->e_header, e, 0);
62367936Seric 	(*e->e_putbody)(mci, e, NULL, 0);
62410175Seric 
62567546Seric 	/*
62667546Seric 	**  Cleanup after sending message.
62767546Seric 	*/
62867546Seric 
62963753Seric 	clrevent(ev);
63063753Seric 
63164718Seric 	if (ferror(mci->mci_out))
63264718Seric 	{
63364718Seric 		/* error during processing -- don't send the dot */
63464718Seric 		mci->mci_errno = EIO;
63564718Seric 		mci->mci_exitstat = EX_IOERR;
63664718Seric 		mci->mci_state = MCIS_ERROR;
63764718Seric 		smtpquit(m, mci, e);
63864718Seric 		return EX_IOERR;
63964718Seric 	}
64064718Seric 
64110175Seric 	/* terminate the message */
64253740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
64363753Seric 	if (TrafficLogFile != NULL)
64463753Seric 		fprintf(TrafficLogFile, "%05d >>> .\n", getpid());
64558120Seric 	if (Verbose)
64658151Seric 		nmessage(">>> .");
64710175Seric 
64810175Seric 	/* check for the results of the transaction */
64961093Seric 	SmtpPhase = mci->mci_phase = "client DATA 250";
65053751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
65159285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
65253751Seric 	if (r < 0)
65357990Seric 	{
65457990Seric 		smtpquit(m, mci, e);
6554797Seric 		return (EX_TEMPFAIL);
65657990Seric 	}
65753751Seric 	mci->mci_state = MCIS_OPEN;
65858917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
65953751Seric 	if (REPLYTYPE(r) == 4)
66053751Seric 		return (EX_TEMPFAIL);
6617963Seric 	else if (r == 250)
6627963Seric 		return (EX_OK);
6637963Seric 	else if (r == 552 || r == 554)
6647963Seric 		return (EX_UNAVAILABLE);
66558008Seric #ifdef LOG
66658020Seric 	if (LogLevel > 1)
66758008Seric 	{
66867860Seric 		syslog(LOG_CRIT, "%s: %s: SMTP DATA-2 protocol error: %s",
66967860Seric 			e->e_id, mci->mci_host, SmtpReplyBuffer);
67058008Seric 	}
67158008Seric #endif
6727964Seric 	return (EX_PROTOCOL);
6734684Seric }
67463753Seric 
67563753Seric 
67663753Seric static int
67763753Seric datatimeout()
67863753Seric {
67963753Seric 	longjmp(CtxDataTimeout, 1);
68063753Seric }
6814684Seric /*
6824865Seric **  SMTPQUIT -- close the SMTP connection.
6834865Seric **
6844865Seric **	Parameters:
68515535Seric **		m -- a pointer to the mailer.
6864865Seric **
6874865Seric **	Returns:
6884865Seric **		none.
6894865Seric **
6904865Seric **	Side Effects:
6914865Seric **		sends the final protocol and closes the connection.
6924865Seric */
6934865Seric 
69453751Seric smtpquit(m, mci, e)
69553751Seric 	register MAILER *m;
69654967Seric 	register MCI *mci;
69753751Seric 	ENVELOPE *e;
6984865Seric {
69964822Seric 	bool oldSuprErrs = SuprErrs;
7004865Seric 
70164822Seric 	/*
70264822Seric 	**	Suppress errors here -- we may be processing a different
70364822Seric 	**	job when we do the quit connection, and we don't want the
70464822Seric 	**	new job to be penalized for something that isn't it's
70564822Seric 	**	problem.
70664822Seric 	*/
70764822Seric 
70864822Seric 	SuprErrs = TRUE;
70964822Seric 
71054967Seric 	/* send the quit message if we haven't gotten I/O error */
71153751Seric 	if (mci->mci_state != MCIS_ERROR)
7129391Seric 	{
71361093Seric 		SmtpPhase = "client QUIT";
71453751Seric 		smtpmessage("QUIT", m, mci);
71559285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
71664822Seric 		SuprErrs = oldSuprErrs;
71753740Seric 		if (mci->mci_state == MCIS_CLOSED)
71864822Seric 		{
71964822Seric 			SuprErrs = oldSuprErrs;
72010159Seric 			return;
72164822Seric 		}
7229391Seric 	}
7239391Seric 
72452676Seric 	/* now actually close the connection and pick up the zombie */
72565194Seric 	(void) endmailer(mci, e, NULL);
72664822Seric 
72764822Seric 	SuprErrs = oldSuprErrs;
7284865Seric }
7294865Seric /*
73054967Seric **  SMTPRSET -- send a RSET (reset) command
73154967Seric */
73254967Seric 
73354967Seric smtprset(m, mci, e)
73454967Seric 	register MAILER *m;
73554967Seric 	register MCI *mci;
73654967Seric 	ENVELOPE *e;
73754967Seric {
73854967Seric 	int r;
73954967Seric 
74061093Seric 	SmtpPhase = "client RSET";
74154967Seric 	smtpmessage("RSET", m, mci);
74259285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
74357734Seric 	if (r < 0)
74457734Seric 		mci->mci_state = MCIS_ERROR;
74554967Seric 	else if (REPLYTYPE(r) == 2)
74657734Seric 	{
74757734Seric 		mci->mci_state = MCIS_OPEN;
74857734Seric 		return;
74957734Seric 	}
75057734Seric 	smtpquit(m, mci, e);
75154967Seric }
75254967Seric /*
75358867Seric **  SMTPPROBE -- check the connection state
75454967Seric */
75554967Seric 
75658867Seric smtpprobe(mci)
75754967Seric 	register MCI *mci;
75854967Seric {
75954967Seric 	int r;
76054967Seric 	MAILER *m = mci->mci_mailer;
76154967Seric 	extern ENVELOPE BlankEnvelope;
76254967Seric 	ENVELOPE *e = &BlankEnvelope;
76354967Seric 
76461093Seric 	SmtpPhase = "client probe";
76558867Seric 	smtpmessage("RSET", m, mci);
76659285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
76758061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
76854967Seric 		smtpquit(m, mci, e);
76954967Seric 	return r;
77054967Seric }
77154967Seric /*
7724684Seric **  REPLY -- read arpanet reply
7734684Seric **
7744684Seric **	Parameters:
77510175Seric **		m -- the mailer we are reading the reply from.
77657379Seric **		mci -- the mailer connection info structure.
77757379Seric **		e -- the current envelope.
77857379Seric **		timeout -- the timeout for reads.
77959285Seric **		pfunc -- processing function for second and subsequent
78059285Seric **			lines of response -- if null, no special
78159285Seric **			processing is done.
7824684Seric **
7834684Seric **	Returns:
7844684Seric **		reply code it reads.
7854684Seric **
7864684Seric **	Side Effects:
7874684Seric **		flushes the mail file.
7884684Seric */
7894684Seric 
79059285Seric reply(m, mci, e, timeout, pfunc)
79153751Seric 	MAILER *m;
79254967Seric 	MCI *mci;
79353751Seric 	ENVELOPE *e;
79459285Seric 	time_t timeout;
79559285Seric 	void (*pfunc)();
7964684Seric {
79759014Seric 	register char *bufp;
79859014Seric 	register int r;
79959285Seric 	bool firstline = TRUE;
80058957Seric 	char junkbuf[MAXLINE];
80158957Seric 
80257379Seric 	if (mci->mci_out != NULL)
80357379Seric 		(void) fflush(mci->mci_out);
8044684Seric 
8057677Seric 	if (tTd(18, 1))
8064796Seric 		printf("reply\n");
8074796Seric 
8087356Seric 	/*
8097356Seric 	**  Read the input line, being careful not to hang.
8107356Seric 	*/
8117356Seric 
81259014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
8134684Seric 	{
8147356Seric 		register char *p;
81553751Seric 		extern time_t curtime();
8164684Seric 
8177685Seric 		/* actually do the read */
81853751Seric 		if (e->e_xfp != NULL)
81953751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
8207356Seric 
82110054Seric 		/* if we are in the process of closing just give the code */
82253740Seric 		if (mci->mci_state == MCIS_CLOSED)
82310054Seric 			return (SMTPCLOSING);
82410054Seric 
82558680Seric 		if (mci->mci_out != NULL)
82658680Seric 			fflush(mci->mci_out);
82758680Seric 
82810054Seric 		/* get the line from the other side */
82961093Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
83053751Seric 		mci->mci_lastuse = curtime();
83153751Seric 
83210054Seric 		if (p == NULL)
83310131Seric 		{
83463753Seric 			bool oldholderrs;
83510148Seric 			extern char MsgBuf[];		/* err.c */
83610148Seric 
83721065Seric 			/* if the remote end closed early, fake an error */
83821065Seric 			if (errno == 0)
83921065Seric # ifdef ECONNRESET
84021065Seric 				errno = ECONNRESET;
84156795Seric # else /* ECONNRESET */
84221065Seric 				errno = EPIPE;
84356795Seric # endif /* ECONNRESET */
84421065Seric 
84557379Seric 			mci->mci_errno = errno;
84657642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
84763753Seric 			oldholderrs = HoldErrs;
84863753Seric 			HoldErrs = TRUE;
84963753Seric 			usrerr("451 reply: read error from %s", mci->mci_host);
85063753Seric 
85110420Seric 			/* if debugging, pause so we can see state */
85210420Seric 			if (tTd(18, 100))
85310420Seric 				pause();
85454967Seric 			mci->mci_state = MCIS_ERROR;
85553751Seric 			smtpquit(m, mci, e);
85663753Seric #ifdef XDEBUG
85763753Seric 			{
85863753Seric 				char wbuf[MAXLINE];
85964082Seric 				char *p = wbuf;
86064082Seric 				if (e->e_to != NULL)
86164082Seric 				{
86264082Seric 					sprintf(p, "%s... ", e->e_to);
86364082Seric 					p += strlen(p);
86464082Seric 				}
86564082Seric 				sprintf(p, "reply(%s) during %s",
86664082Seric 					mci->mci_host, SmtpPhase);
86763753Seric 				checkfd012(wbuf);
86863753Seric 			}
86963753Seric #endif
87063753Seric 			HoldErrs = oldholderrs;
87110054Seric 			return (-1);
87210131Seric 		}
87358957Seric 		fixcrlf(bufp, TRUE);
87410054Seric 
87563753Seric 		/* EHLO failure is not a real error */
87663753Seric 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
87763753Seric 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
87814900Seric 		{
87914900Seric 			/* serious error -- log the previous command */
88064071Seric 			if (SmtpNeedIntro)
88164071Seric 			{
88264071Seric 				/* inform user who we are chatting with */
88364071Seric 				fprintf(CurEnv->e_xfp,
88464071Seric 					"... while talking to %s:\n",
88564071Seric 					CurHostName);
88664071Seric 				SmtpNeedIntro = FALSE;
88764071Seric 			}
88859014Seric 			if (SmtpMsgBuffer[0] != '\0')
88959014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
89059014Seric 			SmtpMsgBuffer[0] = '\0';
89114900Seric 
89214900Seric 			/* now log the message as from the other side */
89358957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
89414900Seric 		}
89514900Seric 
89614900Seric 		/* display the input for verbose mode */
89758120Seric 		if (Verbose)
89859956Seric 			nmessage("050 %s", bufp);
8997356Seric 
90059285Seric 		/* process the line */
90167893Seric 		if (pfunc != NULL)
90267893Seric 			(*pfunc)(bufp, firstline, m, mci, e);
90359285Seric 
90459285Seric 		firstline = FALSE;
90559285Seric 
9067356Seric 		/* if continuation is required, we can go on */
90759014Seric 		if (bufp[3] == '-')
9084684Seric 			continue;
9097356Seric 
91059014Seric 		/* ignore improperly formated input */
91159014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
91259014Seric 			continue;
91359014Seric 
9147356Seric 		/* decode the reply code */
91559014Seric 		r = atoi(bufp);
9167356Seric 
9177356Seric 		/* extra semantics: 0xx codes are "informational" */
91859014Seric 		if (r >= 100)
91959014Seric 			break;
92059014Seric 	}
9217356Seric 
92259014Seric 	/*
92359014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
92459014Seric 	**  line of the response from here on out.
92559014Seric 	*/
92658061Seric 
92759014Seric 	/* save temporary failure messages for posterity */
92859014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
92959014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
9309391Seric 
93159014Seric 	/* reply code 421 is "Service Shutting Down" */
93259014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
93359014Seric 	{
93459014Seric 		/* send the quit protocol */
93559014Seric 		mci->mci_state = MCIS_SSD;
93659014Seric 		smtpquit(m, mci, e);
9374684Seric 	}
93859014Seric 
93959014Seric 	return (r);
9404684Seric }
9414796Seric /*
9424865Seric **  SMTPMESSAGE -- send message to server
9434796Seric **
9444796Seric **	Parameters:
9454796Seric **		f -- format
94610175Seric **		m -- the mailer to control formatting.
9474796Seric **		a, b, c -- parameters
9484796Seric **
9494796Seric **	Returns:
9504796Seric **		none.
9514796Seric **
9524796Seric **	Side Effects:
95353740Seric **		writes message to mci->mci_out.
9544796Seric */
9554796Seric 
9564865Seric /*VARARGS1*/
95757642Seric #ifdef __STDC__
95857642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
95957642Seric #else
96057642Seric smtpmessage(f, m, mci, va_alist)
9614796Seric 	char *f;
96210175Seric 	MAILER *m;
96354967Seric 	MCI *mci;
96457642Seric 	va_dcl
96557642Seric #endif
9664796Seric {
96756852Seric 	VA_LOCAL_DECL
96856852Seric 
96957135Seric 	VA_START(mci);
97056852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
97156852Seric 	VA_END;
97258680Seric 
97358120Seric 	if (tTd(18, 1) || Verbose)
97458151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
97563753Seric 	if (TrafficLogFile != NULL)
97663753Seric 		fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer);
97753740Seric 	if (mci->mci_out != NULL)
97858680Seric 	{
97953740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
98054967Seric 			m == NULL ? "\r\n" : m->m_eol);
98158680Seric 	}
98259149Seric 	else if (tTd(18, 1))
98358725Seric 	{
98459149Seric 		printf("smtpmessage: NULL mci_out\n");
98558725Seric 	}
9864796Seric }
9875182Seric 
98856795Seric # endif /* SMTP */
989