122716Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
642831Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822716Sdist 
933731Sbostic # include "sendmail.h"
1022716Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*60210Seric static char sccsid[] = "@(#)usersmtp.c	6.31 (Berkeley) 05/21/93 (with SMTP)";
1433731Sbostic #else
15*60210Seric static char sccsid[] = "@(#)usersmtp.c	6.31 (Berkeley) 05/21/93 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
194684Seric # include <sysexits.h>
2021065Seric # include <errno.h>
214684Seric 
2233731Sbostic # ifdef SMTP
234684Seric 
244684Seric /*
259391Seric **  USERSMTP -- run SMTP protocol from the user end.
269391Seric **
279391Seric **	This protocol is described in RFC821.
289391Seric */
299391Seric 
309391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
319391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
329391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
339391Seric 
3414900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
3510054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
3621065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
3710054Seric int	SmtpPid;			/* pid of mailer */
3858671Seric 
3958671Seric #ifdef __STDC__
4058671Seric extern	smtpmessage(char *f, MAILER *m, MCI *mci, ...);
4158671Seric #endif
429391Seric /*
434865Seric **  SMTPINIT -- initialize SMTP.
444684Seric **
454865Seric **	Opens the connection and sends the initial protocol.
464684Seric **
474684Seric **	Parameters:
484865Seric **		m -- mailer to create connection to.
494865Seric **		pvp -- pointer to parameter vector to pass to
504865Seric **			the mailer.
514684Seric **
524684Seric **	Returns:
5354967Seric **		none.
544684Seric **
554684Seric **	Side Effects:
564865Seric **		creates connection and sends initial protocol.
574684Seric */
584684Seric 
5954967Seric smtpinit(m, mci, e)
604865Seric 	struct mailer *m;
6154967Seric 	register MCI *mci;
6253751Seric 	ENVELOPE *e;
634684Seric {
644865Seric 	register int r;
6558957Seric 	register char *p;
6652107Seric 	extern STAB *stab();
67*60210Seric 	extern void esmtp_check();
6859285Seric 	extern void helo_options();
694684Seric 
7057379Seric 	if (tTd(17, 1))
7157379Seric 	{
7257379Seric 		printf("smtpinit ");
7357379Seric 		mci_dump(mci);
7457379Seric 	}
7557379Seric 
764865Seric 	/*
774865Seric 	**  Open the connection to the mailer.
784865Seric 	*/
794684Seric 
8021065Seric 	SmtpError[0] = '\0';
8157379Seric 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
8254967Seric 	switch (mci->mci_state)
836051Seric 	{
8454967Seric 	  case MCIS_ACTIVE:
8554967Seric 		/* need to clear old information */
8654967Seric 		smtprset(m, mci, e);
8757734Seric 		/* fall through */
8815139Seric 
8954967Seric 	  case MCIS_OPEN:
9054967Seric 		return;
9154967Seric 
9254967Seric 	  case MCIS_ERROR:
9354967Seric 	  case MCIS_SSD:
9454967Seric 		/* shouldn't happen */
9554967Seric 		smtpquit(m, mci, e);
9657734Seric 		/* fall through */
9754967Seric 
9854967Seric 	  case MCIS_CLOSED:
9958151Seric 		syserr("451 smtpinit: state CLOSED");
10054967Seric 		return;
10154967Seric 
10254967Seric 	  case MCIS_OPENING:
10354967Seric 		break;
1046051Seric 	}
1054796Seric 
10654967Seric 	mci->mci_state = MCIS_OPENING;
10754967Seric 
1084865Seric 	/*
1094865Seric 	**  Get the greeting message.
11014913Seric 	**	This should appear spontaneously.  Give it five minutes to
11114886Seric 	**	happen.
1124865Seric 	*/
1134797Seric 
11457379Seric 	SmtpPhase = mci->mci_phase = "greeting wait";
11553751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
116*60210Seric 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check);
1178005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
11852104Seric 		goto tempfail1;
1194684Seric 
1204865Seric 	/*
1214976Seric 	**  Send the HELO command.
1227963Seric 	**	My mother taught me to always introduce myself.
1234976Seric 	*/
1244976Seric 
12559285Seric 	if (bitnset(M_ESMTP, m->m_flags))
12659285Seric 		mci->mci_flags |= MCIF_ESMTP;
12759285Seric 
12859285Seric tryhelo:
12959285Seric 	if (bitset(MCIF_ESMTP, mci->mci_flags))
13059285Seric 	{
13159285Seric 		smtpmessage("EHLO %s", m, mci, MyHostName);
13259285Seric 		SmtpPhase = mci->mci_phase = "EHLO wait";
13359285Seric 	}
13459285Seric 	else
13559285Seric 	{
13659285Seric 		smtpmessage("HELO %s", m, mci, MyHostName);
13759285Seric 		SmtpPhase = mci->mci_phase = "HELO wait";
13859285Seric 	}
13953751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
14059285Seric 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options);
1418005Seric 	if (r < 0)
14252104Seric 		goto tempfail1;
1438005Seric 	else if (REPLYTYPE(r) == 5)
14459285Seric 	{
14559285Seric 		if (bitset(MCIF_ESMTP, mci->mci_flags))
14659285Seric 		{
14759285Seric 			/* try old SMTP instead */
14859285Seric 			mci->mci_flags &= ~MCIF_ESMTP;
14959285Seric 			goto tryhelo;
15059285Seric 		}
15114913Seric 		goto unavailable;
15259285Seric 	}
1537963Seric 	else if (REPLYTYPE(r) != 2)
15452104Seric 		goto tempfail1;
1554976Seric 
1564976Seric 	/*
15758957Seric 	**  Check to see if we actually ended up talking to ourself.
15858957Seric 	**  This means we didn't know about an alias or MX, or we managed
15958957Seric 	**  to connect to an echo server.
16058957Seric 	*/
16158957Seric 
16259026Seric 	p = strchr(&SmtpReplyBuffer[4], ' ');
16358957Seric 	if (p != NULL)
16458957Seric 		*p == '\0';
16559026Seric 	if (strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
16658957Seric 	{
16758957Seric 		syserr("553 %s config error: mail loops back to myself",
16858957Seric 			MyHostName);
16958957Seric 		mci->mci_exitstat = EX_CONFIG;
17058957Seric 		mci->mci_errno = 0;
17158957Seric 		smtpquit(m, mci, e);
17258957Seric 		return;
17358957Seric 	}
17458957Seric 
17558957Seric 	/*
1769315Seric 	**  If this is expected to be another sendmail, send some internal
1779315Seric 	**  commands.
1789315Seric 	*/
1799315Seric 
18010688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1819315Seric 	{
1829315Seric 		/* tell it to be verbose */
18353751Seric 		smtpmessage("VERB", m, mci);
18459285Seric 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
1859315Seric 		if (r < 0)
18652104Seric 			goto tempfail2;
1879315Seric 	}
1889315Seric 
18953751Seric 	mci->mci_state = MCIS_OPEN;
19054967Seric 	return;
19153751Seric 
19253751Seric   tempfail1:
19353751Seric   tempfail2:
19453751Seric 	mci->mci_exitstat = EX_TEMPFAIL;
19557379Seric 	if (mci->mci_errno == 0)
19657379Seric 		mci->mci_errno = errno;
19757379Seric 	if (mci->mci_state != MCIS_CLOSED)
19857379Seric 		smtpquit(m, mci, e);
19954967Seric 	return;
20053751Seric 
20153751Seric   unavailable:
20253751Seric 	mci->mci_exitstat = EX_UNAVAILABLE;
20353751Seric 	mci->mci_errno = errno;
20453751Seric 	smtpquit(m, mci, e);
20554967Seric 	return;
20653751Seric }
20759285Seric /*
208*60210Seric **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
209*60210Seric **
210*60210Seric **
211*60210Seric **	Parameters:
212*60210Seric **		line -- the response line.
213*60210Seric **		m -- the mailer.
214*60210Seric **		mci -- the mailer connection info.
215*60210Seric **		e -- the envelope.
216*60210Seric **
217*60210Seric **	Returns:
218*60210Seric **		none.
219*60210Seric */
220*60210Seric 
221*60210Seric void
222*60210Seric esmtp_check(line, m, mci, e)
223*60210Seric 	char *line;
224*60210Seric 	MAILER *m;
225*60210Seric 	register MCI *mci;
226*60210Seric 	ENVELOPE *e;
227*60210Seric {
228*60210Seric 	if (strlen(line) < 5)
229*60210Seric 		return;
230*60210Seric 	line += 4;
231*60210Seric 	if (strncmp(line, "ESMTP ", 6) == 0)
232*60210Seric 		mci->mci_flags |= MCIF_ESMTP;
233*60210Seric }
234*60210Seric /*
23559285Seric **  HELO_OPTIONS -- process the options on a HELO line.
23659285Seric **
23759285Seric **	Parameters:
23859285Seric **		line -- the response line.
23959285Seric **		m -- the mailer.
24059285Seric **		mci -- the mailer connection info.
24159285Seric **		e -- the envelope.
24259285Seric **
24359285Seric **	Returns:
24459285Seric **		none.
24559285Seric */
24653751Seric 
24759285Seric void
24859285Seric helo_options(line, m, mci, e)
24959285Seric 	char *line;
25059285Seric 	MAILER *m;
25159285Seric 	register MCI *mci;
25259285Seric 	ENVELOPE *e;
25359285Seric {
25459285Seric 	register char *p;
25559285Seric 
25659285Seric 	if (strlen(line) < 5)
25759285Seric 		return;
25859285Seric 	line += 4;
25959285Seric 	p = strchr(line, ' ');
26059285Seric 	if (p != NULL)
26159285Seric 		*p++ = '\0';
26259285Seric 	if (strcasecmp(line, "size") == 0)
26359285Seric 	{
26459285Seric 		mci->mci_flags |= MCIF_SIZE;
26559285Seric 		if (p != NULL)
26659285Seric 			mci->mci_maxsize = atol(p);
26759285Seric 	}
26859285Seric 	else if (strcasecmp(line, "8bitmime") == 0)
26959285Seric 		mci->mci_flags |= MCIF_8BITMIME;
27059285Seric 	else if (strcasecmp(line, "expn") == 0)
27159285Seric 		mci->mci_flags |= MCIF_EXPN;
27259285Seric }
27359285Seric /*
27459285Seric **  SMTPMAILFROM -- send MAIL command
27559285Seric **
27659285Seric **	Parameters:
27759285Seric **		m -- the mailer.
27859285Seric **		mci -- the mailer connection structure.
27959285Seric **		e -- the envelope (including the sender to specify).
28059285Seric */
28159285Seric 
28253751Seric smtpmailfrom(m, mci, e)
28353751Seric 	struct mailer *m;
28454967Seric 	MCI *mci;
28553751Seric 	ENVELOPE *e;
28653751Seric {
28753751Seric 	int r;
28853751Seric 	char buf[MAXNAME];
28959285Seric 	char optbuf[MAXLINE];
29053751Seric 
29157943Seric 	if (tTd(17, 2))
29257943Seric 		printf("smtpmailfrom: CurHost=%s\n", CurHostName);
29357943Seric 
29459285Seric 	/* set up appropriate options to include */
29559285Seric 	if (bitset(MCIF_SIZE, mci->mci_flags))
29659285Seric 		sprintf(optbuf, " SIZE=%ld", e->e_msgsize);
29759285Seric 	else
29859285Seric 		strcpy(optbuf, "");
29959285Seric 
3009315Seric 	/*
3014865Seric 	**  Send the MAIL command.
3024865Seric 	**	Designates the sender.
3034865Seric 	*/
3044796Seric 
30553751Seric 	mci->mci_state = MCIS_ACTIVE;
30653751Seric 
30759540Seric 	if (bitset(EF_RESPONSE, e->e_flags) &&
30859540Seric 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
30958680Seric 		(void) strcpy(buf, "");
31058680Seric 	else
31158680Seric 		expand("\201g", buf, &buf[sizeof buf - 1], e);
31253751Seric 	if (e->e_from.q_mailer == LocalMailer ||
31310688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
3148436Seric 	{
31559285Seric 		smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf);
3168436Seric 	}
3178436Seric 	else
3188436Seric 	{
31959285Seric 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
32059285Seric 			buf[0] == '@' ? ',' : ':', buf, optbuf);
3218436Seric 	}
32257379Seric 	SmtpPhase = mci->mci_phase = "MAIL wait";
32353751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
32459285Seric 	r = reply(m, mci, e, TimeOuts.to_mail, NULL);
3258005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
32653751Seric 	{
32753751Seric 		mci->mci_exitstat = EX_TEMPFAIL;
32853751Seric 		mci->mci_errno = errno;
32953751Seric 		smtpquit(m, mci, e);
33053751Seric 		return EX_TEMPFAIL;
33153751Seric 	}
3327963Seric 	else if (r == 250)
33353751Seric 	{
33453751Seric 		mci->mci_exitstat = EX_OK;
33553751Seric 		return EX_OK;
33653751Seric 	}
3377963Seric 	else if (r == 552)
33853751Seric 	{
33953751Seric 		/* signal service unavailable */
34053751Seric 		mci->mci_exitstat = EX_UNAVAILABLE;
34153751Seric 		smtpquit(m, mci, e);
34253751Seric 		return EX_UNAVAILABLE;
34353751Seric 	}
34414913Seric 
34558008Seric #ifdef LOG
34658020Seric 	if (LogLevel > 1)
34758008Seric 	{
34858008Seric 		syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s",
34958008Seric 			e->e_id, SmtpReplyBuffer);
35058008Seric 	}
35158008Seric #endif
35258008Seric 
35314913Seric 	/* protocol error -- close up */
35453751Seric 	smtpquit(m, mci, e);
35553751Seric 	mci->mci_exitstat = EX_PROTOCOL;
35653751Seric 	return EX_PROTOCOL;
3574684Seric }
3584684Seric /*
3594976Seric **  SMTPRCPT -- designate recipient.
3604797Seric **
3614797Seric **	Parameters:
3624865Seric **		to -- address of recipient.
36310175Seric **		m -- the mailer we are sending to.
36457379Seric **		mci -- the connection info for this transaction.
36557379Seric **		e -- the envelope for this transaction.
3664797Seric **
3674797Seric **	Returns:
3684865Seric **		exit status corresponding to recipient status.
3694797Seric **
3704797Seric **	Side Effects:
3714865Seric **		Sends the mail via SMTP.
3724797Seric */
3734797Seric 
37453751Seric smtprcpt(to, m, mci, e)
3754865Seric 	ADDRESS *to;
37610175Seric 	register MAILER *m;
37754967Seric 	MCI *mci;
37853751Seric 	ENVELOPE *e;
3794797Seric {
3804797Seric 	register int r;
3814797Seric 
38253751Seric 	smtpmessage("RCPT To:<%s>", m, mci, to->q_user);
3834865Seric 
38457379Seric 	SmtpPhase = mci->mci_phase = "RCPT wait";
38553751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
38659285Seric 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL);
3878005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3884865Seric 		return (EX_TEMPFAIL);
3897963Seric 	else if (REPLYTYPE(r) == 2)
3907963Seric 		return (EX_OK);
3917964Seric 	else if (r == 550 || r == 551 || r == 553)
3927964Seric 		return (EX_NOUSER);
3937964Seric 	else if (r == 552 || r == 554)
3947964Seric 		return (EX_UNAVAILABLE);
39558008Seric 
39658008Seric #ifdef LOG
39758020Seric 	if (LogLevel > 1)
39858008Seric 	{
39958008Seric 		syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s",
40058008Seric 			e->e_id, SmtpReplyBuffer);
40158008Seric 	}
40258008Seric #endif
40358008Seric 
4047964Seric 	return (EX_PROTOCOL);
4054797Seric }
4064797Seric /*
40710175Seric **  SMTPDATA -- send the data and clean up the transaction.
4084684Seric **
4094684Seric **	Parameters:
4104865Seric **		m -- mailer being sent to.
4116980Seric **		e -- the envelope for this message.
4124684Seric **
4134684Seric **	Returns:
4144976Seric **		exit status corresponding to DATA command.
4154684Seric **
4164684Seric **	Side Effects:
4174865Seric **		none.
4184684Seric */
4194684Seric 
42053740Seric smtpdata(m, mci, e)
4214865Seric 	struct mailer *m;
42254967Seric 	register MCI *mci;
4236980Seric 	register ENVELOPE *e;
4244684Seric {
4254684Seric 	register int r;
4264684Seric 
4274797Seric 	/*
4284797Seric 	**  Send the data.
42910175Seric 	**	First send the command and check that it is ok.
43010175Seric 	**	Then send the data.
43110175Seric 	**	Follow it up with a dot to terminate.
43210175Seric 	**	Finally get the results of the transaction.
4334797Seric 	*/
4344797Seric 
43510175Seric 	/* send the command and check ok to proceed */
43653751Seric 	smtpmessage("DATA", m, mci);
43757379Seric 	SmtpPhase = mci->mci_phase = "DATA wait";
43853751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
43959285Seric 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL);
4408005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
44157990Seric 	{
44257990Seric 		smtpquit(m, mci, e);
4434797Seric 		return (EX_TEMPFAIL);
44457990Seric 	}
4457963Seric 	else if (r == 554)
44657990Seric 	{
44757990Seric 		smtprset(m, mci, e);
4487963Seric 		return (EX_UNAVAILABLE);
44957990Seric 	}
4507963Seric 	else if (r != 354)
45157990Seric 	{
45258008Seric #ifdef LOG
45358020Seric 		if (LogLevel > 1)
45458008Seric 		{
45558008Seric 			syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s",
45658008Seric 				e->e_id, SmtpReplyBuffer);
45758008Seric 		}
45858008Seric #endif
45957990Seric 		smtprset(m, mci, e);
4607964Seric 		return (EX_PROTOCOL);
46157990Seric 	}
46210175Seric 
46310175Seric 	/* now output the actual message */
46453751Seric 	(*e->e_puthdr)(mci->mci_out, m, e);
46553740Seric 	putline("\n", mci->mci_out, m);
46659730Seric 	(*e->e_putbody)(mci->mci_out, m, e, NULL);
46710175Seric 
46810175Seric 	/* terminate the message */
46953740Seric 	fprintf(mci->mci_out, ".%s", m->m_eol);
47058120Seric 	if (Verbose)
47158151Seric 		nmessage(">>> .");
47210175Seric 
47310175Seric 	/* check for the results of the transaction */
47457379Seric 	SmtpPhase = mci->mci_phase = "result wait";
47553751Seric 	setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase);
47659285Seric 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL);
47753751Seric 	if (r < 0)
47857990Seric 	{
47957990Seric 		smtpquit(m, mci, e);
4804797Seric 		return (EX_TEMPFAIL);
48157990Seric 	}
48253751Seric 	mci->mci_state = MCIS_OPEN;
48358917Seric 	e->e_statmsg = newstr(&SmtpReplyBuffer[4]);
48453751Seric 	if (REPLYTYPE(r) == 4)
48553751Seric 		return (EX_TEMPFAIL);
4867963Seric 	else if (r == 250)
4877963Seric 		return (EX_OK);
4887963Seric 	else if (r == 552 || r == 554)
4897963Seric 		return (EX_UNAVAILABLE);
49058008Seric #ifdef LOG
49158020Seric 	if (LogLevel > 1)
49258008Seric 	{
49358008Seric 		syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s",
49458008Seric 			e->e_id, SmtpReplyBuffer);
49558008Seric 	}
49658008Seric #endif
4977964Seric 	return (EX_PROTOCOL);
4984684Seric }
4994684Seric /*
5004865Seric **  SMTPQUIT -- close the SMTP connection.
5014865Seric **
5024865Seric **	Parameters:
50315535Seric **		m -- a pointer to the mailer.
5044865Seric **
5054865Seric **	Returns:
5064865Seric **		none.
5074865Seric **
5084865Seric **	Side Effects:
5094865Seric **		sends the final protocol and closes the connection.
5104865Seric */
5114865Seric 
51253751Seric smtpquit(m, mci, e)
51353751Seric 	register MAILER *m;
51454967Seric 	register MCI *mci;
51553751Seric 	ENVELOPE *e;
5164865Seric {
5179391Seric 	int i;
5184865Seric 
51954967Seric 	/* send the quit message if we haven't gotten I/O error */
52053751Seric 	if (mci->mci_state != MCIS_ERROR)
5219391Seric 	{
52253751Seric 		smtpmessage("QUIT", m, mci);
52359285Seric 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL);
52453740Seric 		if (mci->mci_state == MCIS_CLOSED)
52510159Seric 			return;
5269391Seric 	}
5279391Seric 
52852676Seric 	/* now actually close the connection and pick up the zombie */
52958846Seric 	i = endmailer(mci, e, m->m_argv);
5309391Seric 	if (i != EX_OK)
53158151Seric 		syserr("451 smtpquit %s: stat %d", m->m_argv[0], i);
5324865Seric }
5334865Seric /*
53454967Seric **  SMTPRSET -- send a RSET (reset) command
53554967Seric */
53654967Seric 
53754967Seric smtprset(m, mci, e)
53854967Seric 	register MAILER *m;
53954967Seric 	register MCI *mci;
54054967Seric 	ENVELOPE *e;
54154967Seric {
54254967Seric 	int r;
54354967Seric 
54454967Seric 	smtpmessage("RSET", m, mci);
54559285Seric 	r = reply(m, mci, e, TimeOuts.to_rset, NULL);
54657734Seric 	if (r < 0)
54757734Seric 		mci->mci_state = MCIS_ERROR;
54854967Seric 	else if (REPLYTYPE(r) == 2)
54957734Seric 	{
55057734Seric 		mci->mci_state = MCIS_OPEN;
55157734Seric 		return;
55257734Seric 	}
55357734Seric 	smtpquit(m, mci, e);
55454967Seric }
55554967Seric /*
55658867Seric **  SMTPPROBE -- check the connection state
55754967Seric */
55854967Seric 
55958867Seric smtpprobe(mci)
56054967Seric 	register MCI *mci;
56154967Seric {
56254967Seric 	int r;
56354967Seric 	MAILER *m = mci->mci_mailer;
56454967Seric 	extern ENVELOPE BlankEnvelope;
56554967Seric 	ENVELOPE *e = &BlankEnvelope;
56654967Seric 
56758867Seric 	smtpmessage("RSET", m, mci);
56859285Seric 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL);
56958061Seric 	if (r < 0 || REPLYTYPE(r) != 2)
57054967Seric 		smtpquit(m, mci, e);
57154967Seric 	return r;
57254967Seric }
57354967Seric /*
5744684Seric **  REPLY -- read arpanet reply
5754684Seric **
5764684Seric **	Parameters:
57710175Seric **		m -- the mailer we are reading the reply from.
57857379Seric **		mci -- the mailer connection info structure.
57957379Seric **		e -- the current envelope.
58057379Seric **		timeout -- the timeout for reads.
58159285Seric **		pfunc -- processing function for second and subsequent
58259285Seric **			lines of response -- if null, no special
58359285Seric **			processing is done.
5844684Seric **
5854684Seric **	Returns:
5864684Seric **		reply code it reads.
5874684Seric **
5884684Seric **	Side Effects:
5894684Seric **		flushes the mail file.
5904684Seric */
5914684Seric 
59259285Seric reply(m, mci, e, timeout, pfunc)
59353751Seric 	MAILER *m;
59454967Seric 	MCI *mci;
59553751Seric 	ENVELOPE *e;
59659285Seric 	time_t timeout;
59759285Seric 	void (*pfunc)();
5984684Seric {
59959014Seric 	register char *bufp;
60059014Seric 	register int r;
60159285Seric 	bool firstline = TRUE;
60258957Seric 	char junkbuf[MAXLINE];
60358957Seric 
60457379Seric 	if (mci->mci_out != NULL)
60557379Seric 		(void) fflush(mci->mci_out);
6064684Seric 
6077677Seric 	if (tTd(18, 1))
6084796Seric 		printf("reply\n");
6094796Seric 
6107356Seric 	/*
6117356Seric 	**  Read the input line, being careful not to hang.
6127356Seric 	*/
6137356Seric 
61459014Seric 	for (bufp = SmtpReplyBuffer;; bufp = junkbuf)
6154684Seric 	{
6167356Seric 		register char *p;
61753751Seric 		extern time_t curtime();
6184684Seric 
6197685Seric 		/* actually do the read */
62053751Seric 		if (e->e_xfp != NULL)
62153751Seric 			(void) fflush(e->e_xfp);	/* for debugging */
6227356Seric 
62310054Seric 		/* if we are in the process of closing just give the code */
62453740Seric 		if (mci->mci_state == MCIS_CLOSED)
62510054Seric 			return (SMTPCLOSING);
62610054Seric 
62758680Seric 		if (mci->mci_out != NULL)
62858680Seric 			fflush(mci->mci_out);
62958680Seric 
63010054Seric 		/* get the line from the other side */
63158957Seric 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout);
63253751Seric 		mci->mci_lastuse = curtime();
63353751Seric 
63410054Seric 		if (p == NULL)
63510131Seric 		{
63610148Seric 			extern char MsgBuf[];		/* err.c */
63710148Seric 
63821065Seric 			/* if the remote end closed early, fake an error */
63921065Seric 			if (errno == 0)
64021065Seric # ifdef ECONNRESET
64121065Seric 				errno = ECONNRESET;
64256795Seric # else /* ECONNRESET */
64321065Seric 				errno = EPIPE;
64456795Seric # endif /* ECONNRESET */
64521065Seric 
64657379Seric 			mci->mci_errno = errno;
64757642Seric 			mci->mci_exitstat = EX_TEMPFAIL;
64858151Seric 			message("451 %s: reply: read error from %s",
64957642Seric 				e->e_id == NULL ? "NOQUEUE" : e->e_id,
65057203Seric 				mci->mci_host);
65110420Seric 			/* if debugging, pause so we can see state */
65210420Seric 			if (tTd(18, 100))
65310420Seric 				pause();
65410148Seric # ifdef LOG
65558020Seric 			if (LogLevel > 1)
65657203Seric 				syslog(LOG_INFO, "%s", &MsgBuf[4]);
65756795Seric # endif /* LOG */
65854967Seric 			mci->mci_state = MCIS_ERROR;
65953751Seric 			smtpquit(m, mci, e);
66010054Seric 			return (-1);
66110131Seric 		}
66258957Seric 		fixcrlf(bufp, TRUE);
66310054Seric 
66459014Seric 		if (e->e_xfp != NULL && strchr("45", bufp[0]) != NULL)
66514900Seric 		{
66614900Seric 			/* serious error -- log the previous command */
66759014Seric 			if (SmtpMsgBuffer[0] != '\0')
66859014Seric 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
66959014Seric 			SmtpMsgBuffer[0] = '\0';
67014900Seric 
67114900Seric 			/* now log the message as from the other side */
67258957Seric 			fprintf(e->e_xfp, "<<< %s\n", bufp);
67314900Seric 		}
67414900Seric 
67514900Seric 		/* display the input for verbose mode */
67658120Seric 		if (Verbose)
67759956Seric 			nmessage("050 %s", bufp);
6787356Seric 
67959285Seric 		/* process the line */
68059285Seric 		if (pfunc != NULL && !firstline)
68159285Seric 			(*pfunc)(bufp, m, mci, e);
68259285Seric 
68359285Seric 		firstline = FALSE;
68459285Seric 
6857356Seric 		/* if continuation is required, we can go on */
68659014Seric 		if (bufp[3] == '-')
6874684Seric 			continue;
6887356Seric 
68959014Seric 		/* ignore improperly formated input */
69059014Seric 		if (!(isascii(bufp[0]) && isdigit(bufp[0])))
69159014Seric 			continue;
69259014Seric 
6937356Seric 		/* decode the reply code */
69459014Seric 		r = atoi(bufp);
6957356Seric 
6967356Seric 		/* extra semantics: 0xx codes are "informational" */
69759014Seric 		if (r >= 100)
69859014Seric 			break;
69959014Seric 	}
7007356Seric 
70159014Seric 	/*
70259014Seric 	**  Now look at SmtpReplyBuffer -- only care about the first
70359014Seric 	**  line of the response from here on out.
70459014Seric 	*/
70558061Seric 
70659014Seric 	/* save temporary failure messages for posterity */
70759014Seric 	if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
70859014Seric 		(void) strcpy(SmtpError, SmtpReplyBuffer);
7099391Seric 
71059014Seric 	/* reply code 421 is "Service Shutting Down" */
71159014Seric 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
71259014Seric 	{
71359014Seric 		/* send the quit protocol */
71459014Seric 		mci->mci_state = MCIS_SSD;
71559014Seric 		smtpquit(m, mci, e);
7164684Seric 	}
71759014Seric 
71859014Seric 	return (r);
7194684Seric }
7204796Seric /*
7214865Seric **  SMTPMESSAGE -- send message to server
7224796Seric **
7234796Seric **	Parameters:
7244796Seric **		f -- format
72510175Seric **		m -- the mailer to control formatting.
7264796Seric **		a, b, c -- parameters
7274796Seric **
7284796Seric **	Returns:
7294796Seric **		none.
7304796Seric **
7314796Seric **	Side Effects:
73253740Seric **		writes message to mci->mci_out.
7334796Seric */
7344796Seric 
7354865Seric /*VARARGS1*/
73657642Seric #ifdef __STDC__
73757642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...)
73857642Seric #else
73957642Seric smtpmessage(f, m, mci, va_alist)
7404796Seric 	char *f;
74110175Seric 	MAILER *m;
74254967Seric 	MCI *mci;
74357642Seric 	va_dcl
74457642Seric #endif
7454796Seric {
74656852Seric 	VA_LOCAL_DECL
74756852Seric 
74857135Seric 	VA_START(mci);
74956852Seric 	(void) vsprintf(SmtpMsgBuffer, f, ap);
75056852Seric 	VA_END;
75158680Seric 
75258120Seric 	if (tTd(18, 1) || Verbose)
75358151Seric 		nmessage(">>> %s", SmtpMsgBuffer);
75453740Seric 	if (mci->mci_out != NULL)
75558680Seric 	{
75653740Seric 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
75754967Seric 			m == NULL ? "\r\n" : m->m_eol);
75858680Seric 	}
75959149Seric 	else if (tTd(18, 1))
76058725Seric 	{
76159149Seric 		printf("smtpmessage: NULL mci_out\n");
76258725Seric 	}
7634796Seric }
7645182Seric 
76556795Seric # endif /* SMTP */
766