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*52676Seric static char sccsid[] = "@(#)usersmtp.c	5.20.1.1 (Berkeley) 02/26/92 (with SMTP)";
1433731Sbostic #else
15*52676Seric static char sccsid[] = "@(#)usersmtp.c	5.20.1.1 (Berkeley) 02/26/92 (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 */
3811159Seric 
3911159Seric /* following represents the state of the SMTP connection */
4011159Seric int	SmtpState;			/* connection state, see below */
4111159Seric 
4211159Seric #define SMTP_CLOSED	0		/* connection is closed */
4311159Seric #define SMTP_OPEN	1		/* connection is open for business */
4411159Seric #define SMTP_SSD	2		/* service shutting down */
459391Seric /*
464865Seric **  SMTPINIT -- initialize SMTP.
474684Seric **
484865Seric **	Opens the connection and sends the initial protocol.
494684Seric **
504684Seric **	Parameters:
514865Seric **		m -- mailer to create connection to.
524865Seric **		pvp -- pointer to parameter vector to pass to
534865Seric **			the mailer.
544684Seric **
554684Seric **	Returns:
564865Seric **		appropriate exit status -- EX_OK on success.
5714913Seric **		If not EX_OK, it should close the connection.
584684Seric **
594684Seric **	Side Effects:
604865Seric **		creates connection and sends initial protocol.
614684Seric */
624684Seric 
6314886Seric jmp_buf	CtxGreeting;
6414886Seric 
6510175Seric smtpinit(m, pvp)
664865Seric 	struct mailer *m;
674865Seric 	char **pvp;
684684Seric {
694865Seric 	register int r;
7014886Seric 	EVENT *gte;
7152107Seric 	register STAB *st;
724865Seric 	char buf[MAXNAME];
7346928Sbostic 	static int greettimeout();
7452107Seric 	extern STAB *stab();
754684Seric 
764865Seric 	/*
774865Seric 	**  Open the connection to the mailer.
784865Seric 	*/
794684Seric 
8011159Seric 	if (SmtpState == SMTP_OPEN)
8111159Seric 		syserr("smtpinit: already open");
8211159Seric 
8311159Seric 	SmtpState = SMTP_CLOSED;
8421065Seric 	SmtpError[0] = '\0';
8524944Seric 	SmtpPhase = "user open";
8636584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
87*52676Seric 	mci = openmailer(m, pvp, (ADDRESS *) NULL, TRUE);
886051Seric 	if (SmtpPid < 0)
896051Seric 	{
907677Seric 		if (tTd(18, 1))
919391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
929391Seric 			   pvp[0], ExitStat, errno);
9315139Seric 		if (CurEnv->e_xfp != NULL)
9415139Seric 		{
9521065Seric 			register char *p;
9615139Seric 			extern char *errstring();
9721065Seric 			extern char *statstring();
9815139Seric 
9921065Seric 			if (errno == 0)
10021065Seric 			{
10121065Seric 				p = statstring(ExitStat);
10221065Seric 				fprintf(CurEnv->e_xfp,
10321065Seric 					"%.3s %s.%s... %s\n",
10421065Seric 					p, pvp[1], m->m_name, p);
10521065Seric 			}
10621065Seric 			else
10721065Seric 			{
10836584Sbostic 				r = errno;
10921065Seric 				fprintf(CurEnv->e_xfp,
11021065Seric 					"421 %s.%s... Deferred: %s\n",
11121065Seric 					pvp[1], m->m_name, errstring(errno));
11236584Sbostic 				errno = r;
11321065Seric 			}
11415139Seric 		}
1156051Seric 		return (ExitStat);
1166051Seric 	}
11711159Seric 	SmtpState = SMTP_OPEN;
1184796Seric 
1194865Seric 	/*
1204865Seric 	**  Get the greeting message.
12114913Seric 	**	This should appear spontaneously.  Give it five minutes to
12214886Seric 	**	happen.
1234865Seric 	*/
1244797Seric 
12514886Seric 	if (setjmp(CtxGreeting) != 0)
12652104Seric 		goto tempfail1;
12716141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
12824944Seric 	SmtpPhase = "greeting wait";
12936584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
13010175Seric 	r = reply(m);
13114886Seric 	clrevent(gte);
1328005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
13352104Seric 		goto tempfail1;
1344684Seric 
1354865Seric 	/*
1364976Seric 	**  Send the HELO command.
1377963Seric 	**	My mother taught me to always introduce myself.
1384976Seric 	*/
1394976Seric 
14025050Seric 	smtpmessage("HELO %s", m, MyHostName);
14124944Seric 	SmtpPhase = "HELO wait";
14236584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
14310175Seric 	r = reply(m);
1448005Seric 	if (r < 0)
14552104Seric 		goto tempfail1;
1468005Seric 	else if (REPLYTYPE(r) == 5)
14714913Seric 		goto unavailable;
1487963Seric 	else if (REPLYTYPE(r) != 2)
14952104Seric 		goto tempfail1;
1504976Seric 
1514976Seric 	/*
1529315Seric 	**  If this is expected to be another sendmail, send some internal
1539315Seric 	**  commands.
1549315Seric 	*/
1559315Seric 
15610688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1579315Seric 	{
1589315Seric 		/* tell it to be verbose */
15910175Seric 		smtpmessage("VERB", m);
16010175Seric 		r = reply(m);
1619315Seric 		if (r < 0)
16252104Seric 			goto tempfail2;
1639315Seric 
1649315Seric 		/* tell it we will be sending one transaction only */
16510175Seric 		smtpmessage("ONEX", m);
16610175Seric 		r = reply(m);
1679315Seric 		if (r < 0)
16852104Seric 			goto tempfail2;
1699315Seric 	}
1709315Seric 
1719315Seric 	/*
1724865Seric 	**  Send the MAIL command.
1734865Seric 	**	Designates the sender.
1744865Seric 	*/
1754796Seric 
17651951Seric 	expand("\001<", buf, &buf[sizeof buf - 1], CurEnv);
1778436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
17810688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1798436Seric 	{
18010308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1818436Seric 	}
1828436Seric 	else
1838436Seric 	{
18425050Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
18510308Seric 			buf[0] == '@' ? ',' : ':', buf);
1868436Seric 	}
18724944Seric 	SmtpPhase = "MAIL wait";
18836584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
18910175Seric 	r = reply(m);
1908005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
19152104Seric 		goto tempfail2;
1927963Seric 	else if (r == 250)
1937963Seric 		return (EX_OK);
1947963Seric 	else if (r == 552)
19514913Seric 		goto unavailable;
19614913Seric 
19714913Seric 	/* protocol error -- close up */
19814913Seric 	smtpquit(m);
1997964Seric 	return (EX_PROTOCOL);
20014913Seric 
20152104Seric   tempfail1:
20252107Seric 	/* log this as an error to avoid sure-to-be-void connections */
203*52676Seric 	st = stab(CurHostName, ST_MCONINFO + m->m_mno, ST_ENTER);
20452107Seric 	st->s_host.ho_exitstat = EX_TEMPFAIL;
20552107Seric 	st->s_host.ho_errno = errno;
20652104Seric 
20752104Seric   tempfail2:
20852107Seric 	/* signal a temporary failure */
20914913Seric 	smtpquit(m);
21014913Seric 	return (EX_TEMPFAIL);
21114913Seric 
21252107Seric   unavailable:
21314913Seric 	/* signal service unavailable */
21414913Seric 	smtpquit(m);
21514913Seric 	return (EX_UNAVAILABLE);
2164684Seric }
21714886Seric 
21814886Seric 
21914886Seric static
22014886Seric greettimeout()
22114886Seric {
22214886Seric 	/* timeout reading the greeting message */
22314886Seric 	longjmp(CtxGreeting, 1);
22414886Seric }
2254684Seric /*
2264976Seric **  SMTPRCPT -- designate recipient.
2274797Seric **
2284797Seric **	Parameters:
2294865Seric **		to -- address of recipient.
23010175Seric **		m -- the mailer we are sending to.
2314797Seric **
2324797Seric **	Returns:
2334865Seric **		exit status corresponding to recipient status.
2344797Seric **
2354797Seric **	Side Effects:
2364865Seric **		Sends the mail via SMTP.
2374797Seric */
2384797Seric 
23910175Seric smtprcpt(to, m)
2404865Seric 	ADDRESS *to;
24110175Seric 	register MAILER *m;
2424797Seric {
2434797Seric 	register int r;
24410308Seric 	extern char *remotename();
2454797Seric 
24652647Seric 	smtpmessage("RCPT To:<%s>", m, to->q_user);
2474865Seric 
24824944Seric 	SmtpPhase = "RCPT wait";
24936584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
25010175Seric 	r = reply(m);
2518005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2524865Seric 		return (EX_TEMPFAIL);
2537963Seric 	else if (REPLYTYPE(r) == 2)
2547963Seric 		return (EX_OK);
2557964Seric 	else if (r == 550 || r == 551 || r == 553)
2567964Seric 		return (EX_NOUSER);
2577964Seric 	else if (r == 552 || r == 554)
2587964Seric 		return (EX_UNAVAILABLE);
2597964Seric 	return (EX_PROTOCOL);
2604797Seric }
2614797Seric /*
26210175Seric **  SMTPDATA -- send the data and clean up the transaction.
2634684Seric **
2644684Seric **	Parameters:
2654865Seric **		m -- mailer being sent to.
2666980Seric **		e -- the envelope for this message.
2674684Seric **
2684684Seric **	Returns:
2694976Seric **		exit status corresponding to DATA command.
2704684Seric **
2714684Seric **	Side Effects:
2724865Seric **		none.
2734684Seric */
2744684Seric 
27510175Seric smtpdata(m, e)
2764865Seric 	struct mailer *m;
2776980Seric 	register ENVELOPE *e;
2784684Seric {
2794684Seric 	register int r;
2804684Seric 
2814797Seric 	/*
2824797Seric 	**  Send the data.
28310175Seric 	**	First send the command and check that it is ok.
28410175Seric 	**	Then send the data.
28510175Seric 	**	Follow it up with a dot to terminate.
28610175Seric 	**	Finally get the results of the transaction.
2874797Seric 	*/
2884797Seric 
28910175Seric 	/* send the command and check ok to proceed */
29010175Seric 	smtpmessage("DATA", m);
29124944Seric 	SmtpPhase = "DATA wait";
29236584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
29310175Seric 	r = reply(m);
2948005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2954797Seric 		return (EX_TEMPFAIL);
2967963Seric 	else if (r == 554)
2977963Seric 		return (EX_UNAVAILABLE);
2987963Seric 	else if (r != 354)
2997964Seric 		return (EX_PROTOCOL);
30010175Seric 
30110175Seric 	/* now output the actual message */
30210175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
30310175Seric 	putline("\n", SmtpOut, m);
30410175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
30510175Seric 
30610175Seric 	/* terminate the message */
30710328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
30810215Seric 	if (Verbose && !HoldErrs)
30910215Seric 		nmessage(Arpa_Info, ">>> .");
31010175Seric 
31110175Seric 	/* check for the results of the transaction */
31224944Seric 	SmtpPhase = "result wait";
31336584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
31410175Seric 	r = reply(m);
3158005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3164797Seric 		return (EX_TEMPFAIL);
3177963Seric 	else if (r == 250)
3187963Seric 		return (EX_OK);
3197963Seric 	else if (r == 552 || r == 554)
3207963Seric 		return (EX_UNAVAILABLE);
3217964Seric 	return (EX_PROTOCOL);
3224684Seric }
3234684Seric /*
3244865Seric **  SMTPQUIT -- close the SMTP connection.
3254865Seric **
3264865Seric **	Parameters:
32715535Seric **		m -- a pointer to the mailer.
3284865Seric **
3294865Seric **	Returns:
3304865Seric **		none.
3314865Seric **
3324865Seric **	Side Effects:
3334865Seric **		sends the final protocol and closes the connection.
3344865Seric */
3354865Seric 
336*52676Seric smtpquit(mci)
337*52676Seric 	register MCONINFO *mci;
3384865Seric {
3399391Seric 	int i;
3404865Seric 
34110148Seric 	/* if the connection is already closed, don't bother */
342*52676Seric 	if (mci->mci_state == MCI_CLOSED)
34310148Seric 		return;
34410148Seric 
34510148Seric 	/* send the quit message if not a forced quit */
34611159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
3479391Seric 	{
34810175Seric 		smtpmessage("QUIT", m);
34910175Seric 		(void) reply(m);
35011159Seric 		if (SmtpState == SMTP_CLOSED)
35110159Seric 			return;
3529391Seric 	}
3539391Seric 
354*52676Seric 	/* now actually close the connection and pick up the zombie */
355*52676Seric 	i = endmailer(mci, m->m_argv[0]);
3569391Seric 	if (i != EX_OK)
35715535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3584865Seric }
3594865Seric /*
3604684Seric **  REPLY -- read arpanet reply
3614684Seric **
3624684Seric **	Parameters:
36310175Seric **		m -- the mailer we are reading the reply from.
3644684Seric **
3654684Seric **	Returns:
3664684Seric **		reply code it reads.
3674684Seric **
3684684Seric **	Side Effects:
3694684Seric **		flushes the mail file.
3704684Seric */
3714684Seric 
37210175Seric reply(m)
37310688Seric 	MAILER *m;
3744684Seric {
3754865Seric 	(void) fflush(SmtpOut);
3764684Seric 
3777677Seric 	if (tTd(18, 1))
3784796Seric 		printf("reply\n");
3794796Seric 
3807356Seric 	/*
3817356Seric 	**  Read the input line, being careful not to hang.
3827356Seric 	*/
3837356Seric 
3844684Seric 	for (;;)
3854684Seric 	{
3864684Seric 		register int r;
3877356Seric 		register char *p;
3884684Seric 
3897685Seric 		/* actually do the read */
3909547Seric 		if (CurEnv->e_xfp != NULL)
3919547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3927356Seric 
39310054Seric 		/* if we are in the process of closing just give the code */
39411159Seric 		if (SmtpState == SMTP_CLOSED)
39510054Seric 			return (SMTPCLOSING);
39610054Seric 
39710054Seric 		/* get the line from the other side */
39810054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
39910054Seric 		if (p == NULL)
40010131Seric 		{
40110148Seric 			extern char MsgBuf[];		/* err.c */
40210148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
40310148Seric 
40421065Seric 			/* if the remote end closed early, fake an error */
40521065Seric 			if (errno == 0)
40621065Seric # ifdef ECONNRESET
40721065Seric 				errno = ECONNRESET;
40821065Seric # else ECONNRESET
40921065Seric 				errno = EPIPE;
41021065Seric # endif ECONNRESET
41121065Seric 
41210148Seric 			message(Arpa_TSyserr, "reply: read error");
41310420Seric 			/* if debugging, pause so we can see state */
41410420Seric 			if (tTd(18, 100))
41510420Seric 				pause();
41610148Seric # ifdef LOG
41736234Skarels 			syslog(LOG_INFO, "%s", &MsgBuf[4]);
41810148Seric # endif LOG
41911159Seric 			SmtpState = SMTP_CLOSED;
42015535Seric 			smtpquit(m);
42110054Seric 			return (-1);
42210131Seric 		}
42310054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
42410054Seric 
42514900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
42614900Seric 		{
42714900Seric 			/* serious error -- log the previous command */
42814900Seric 			if (SmtpMsgBuffer[0] != '\0')
42914900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
43014900Seric 			SmtpMsgBuffer[0] = '\0';
43114900Seric 
43214900Seric 			/* now log the message as from the other side */
43314900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
43414900Seric 		}
43514900Seric 
43614900Seric 		/* display the input for verbose mode */
4377229Seric 		if (Verbose && !HoldErrs)
4389391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4397356Seric 
4407356Seric 		/* if continuation is required, we can go on */
4419391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4424684Seric 			continue;
4437356Seric 
4447356Seric 		/* decode the reply code */
4459391Seric 		r = atoi(SmtpReplyBuffer);
4467356Seric 
4477356Seric 		/* extra semantics: 0xx codes are "informational" */
4484684Seric 		if (r < 100)
4494684Seric 			continue;
4507356Seric 
4519391Seric 		/* reply code 421 is "Service Shutting Down" */
45211159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
4539391Seric 		{
45410054Seric 			/* send the quit protocol */
45511159Seric 			SmtpState = SMTP_SSD;
45615535Seric 			smtpquit(m);
4579391Seric 		}
4589391Seric 
45921065Seric 		/* save temporary failure messages for posterity */
46021065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
46121065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
46221065Seric 
4634684Seric 		return (r);
4644684Seric 	}
4654684Seric }
4664796Seric /*
4674865Seric **  SMTPMESSAGE -- send message to server
4684796Seric **
4694796Seric **	Parameters:
4704796Seric **		f -- format
47110175Seric **		m -- the mailer to control formatting.
4724796Seric **		a, b, c -- parameters
4734796Seric **
4744796Seric **	Returns:
4754796Seric **		none.
4764796Seric **
4774796Seric **	Side Effects:
4784865Seric **		writes message to SmtpOut.
4794796Seric */
4804796Seric 
4814865Seric /*VARARGS1*/
48210175Seric smtpmessage(f, m, a, b, c)
4834796Seric 	char *f;
48410175Seric 	MAILER *m;
4854796Seric {
48614900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4877677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
48814900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
48911159Seric 	if (SmtpOut != NULL)
49040997Sbostic 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer,
49140997Sbostic 			m == 0 ? "\r\n" : m->m_eol);
4924796Seric }
4935182Seric 
4945182Seric # endif SMTP
495