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*52104Seric static char sccsid[] = "@(#)usersmtp.c	5.18 (Berkeley) 01/03/92 (with SMTP)";
1433731Sbostic #else
15*52104Seric static char sccsid[] = "@(#)usersmtp.c	5.18 (Berkeley) 01/03/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 FILE	*SmtpOut;			/* output file */
3810054Seric FILE	*SmtpIn;			/* input file */
3910054Seric int	SmtpPid;			/* pid of mailer */
4011159Seric 
4111159Seric /* following represents the state of the SMTP connection */
4211159Seric int	SmtpState;			/* connection state, see below */
4311159Seric 
4411159Seric #define SMTP_CLOSED	0		/* connection is closed */
4511159Seric #define SMTP_OPEN	1		/* connection is open for business */
4611159Seric #define SMTP_SSD	2		/* service shutting down */
479391Seric /*
484865Seric **  SMTPINIT -- initialize SMTP.
494684Seric **
504865Seric **	Opens the connection and sends the initial protocol.
514684Seric **
524684Seric **	Parameters:
534865Seric **		m -- mailer to create connection to.
544865Seric **		pvp -- pointer to parameter vector to pass to
554865Seric **			the mailer.
564684Seric **
574684Seric **	Returns:
584865Seric **		appropriate exit status -- EX_OK on success.
5914913Seric **		If not EX_OK, it should close the connection.
604684Seric **
614684Seric **	Side Effects:
624865Seric **		creates connection and sends initial protocol.
634684Seric */
644684Seric 
6514886Seric jmp_buf	CtxGreeting;
6614886Seric 
6710175Seric smtpinit(m, pvp)
684865Seric 	struct mailer *m;
694865Seric 	char **pvp;
704684Seric {
714865Seric 	register int r;
7214886Seric 	EVENT *gte;
734865Seric 	char buf[MAXNAME];
7446928Sbostic 	static int greettimeout();
754684Seric 
764865Seric 	/*
774865Seric 	**  Open the connection to the mailer.
784865Seric 	*/
794684Seric 
8011159Seric 	if (SmtpState == SMTP_OPEN)
8111159Seric 		syserr("smtpinit: already open");
8211159Seric 
836051Seric 	SmtpIn = SmtpOut = NULL;
8411159Seric 	SmtpState = SMTP_CLOSED;
8521065Seric 	SmtpError[0] = '\0';
8624944Seric 	SmtpPhase = "user open";
8736584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
8810175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
896051Seric 	if (SmtpPid < 0)
906051Seric 	{
917677Seric 		if (tTd(18, 1))
929391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
939391Seric 			   pvp[0], ExitStat, errno);
9415139Seric 		if (CurEnv->e_xfp != NULL)
9515139Seric 		{
9621065Seric 			register char *p;
9715139Seric 			extern char *errstring();
9821065Seric 			extern char *statstring();
9915139Seric 
10021065Seric 			if (errno == 0)
10121065Seric 			{
10221065Seric 				p = statstring(ExitStat);
10321065Seric 				fprintf(CurEnv->e_xfp,
10421065Seric 					"%.3s %s.%s... %s\n",
10521065Seric 					p, pvp[1], m->m_name, p);
10621065Seric 			}
10721065Seric 			else
10821065Seric 			{
10936584Sbostic 				r = errno;
11021065Seric 				fprintf(CurEnv->e_xfp,
11121065Seric 					"421 %s.%s... Deferred: %s\n",
11221065Seric 					pvp[1], m->m_name, errstring(errno));
11336584Sbostic 				errno = r;
11421065Seric 			}
11515139Seric 		}
1166051Seric 		return (ExitStat);
1176051Seric 	}
11811159Seric 	SmtpState = SMTP_OPEN;
1194796Seric 
1204865Seric 	/*
1214865Seric 	**  Get the greeting message.
12214913Seric 	**	This should appear spontaneously.  Give it five minutes to
12314886Seric 	**	happen.
1244865Seric 	*/
1254797Seric 
12614886Seric 	if (setjmp(CtxGreeting) != 0)
127*52104Seric 		goto tempfail1;
12816141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
12924944Seric 	SmtpPhase = "greeting wait";
13036584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
13110175Seric 	r = reply(m);
13214886Seric 	clrevent(gte);
1338005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
134*52104Seric 		goto tempfail1;
1354684Seric 
1364865Seric 	/*
1374976Seric 	**  Send the HELO command.
1387963Seric 	**	My mother taught me to always introduce myself.
1394976Seric 	*/
1404976Seric 
14125050Seric 	smtpmessage("HELO %s", m, MyHostName);
14224944Seric 	SmtpPhase = "HELO wait";
14336584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
14410175Seric 	r = reply(m);
1458005Seric 	if (r < 0)
146*52104Seric 		goto tempfail1;
1478005Seric 	else if (REPLYTYPE(r) == 5)
14814913Seric 		goto unavailable;
1497963Seric 	else if (REPLYTYPE(r) != 2)
150*52104Seric 		goto tempfail1;
1514976Seric 
1524976Seric 	/*
1539315Seric 	**  If this is expected to be another sendmail, send some internal
1549315Seric 	**  commands.
1559315Seric 	*/
1569315Seric 
15710688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1589315Seric 	{
1599315Seric 		/* tell it to be verbose */
16010175Seric 		smtpmessage("VERB", m);
16110175Seric 		r = reply(m);
1629315Seric 		if (r < 0)
163*52104Seric 			goto tempfail2;
1649315Seric 
1659315Seric 		/* tell it we will be sending one transaction only */
16610175Seric 		smtpmessage("ONEX", m);
16710175Seric 		r = reply(m);
1689315Seric 		if (r < 0)
169*52104Seric 			goto tempfail2;
1709315Seric 	}
1719315Seric 
1729315Seric 	/*
1734865Seric 	**  Send the MAIL command.
1744865Seric 	**	Designates the sender.
1754865Seric 	*/
1764796Seric 
17751951Seric 	expand("\001<", buf, &buf[sizeof buf - 1], CurEnv);
1788436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
17910688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1808436Seric 	{
18110308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1828436Seric 	}
1838436Seric 	else
1848436Seric 	{
18525050Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
18610308Seric 			buf[0] == '@' ? ',' : ':', buf);
1878436Seric 	}
18824944Seric 	SmtpPhase = "MAIL wait";
18936584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
19010175Seric 	r = reply(m);
1918005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
192*52104Seric 		goto tempfail2;
1937963Seric 	else if (r == 250)
1947963Seric 		return (EX_OK);
1957963Seric 	else if (r == 552)
19614913Seric 		goto unavailable;
19714913Seric 
19814913Seric 	/* protocol error -- close up */
19914913Seric 	smtpquit(m);
2007964Seric 	return (EX_PROTOCOL);
20114913Seric 
20214913Seric 	/* signal a temporary failure */
203*52104Seric   tempfail1:
204*52104Seric #ifdef HOSTINFO
205*52104Seric 	{
206*52104Seric 		register STAB *st;
207*52104Seric 		extern STAB *stab();
208*52104Seric 
209*52104Seric 		/* log this as an error to avoid sure-to-be-void connections */
210*52104Seric 		st = stab(CurHostName, ST_HOST, ST_ENTER);
211*52104Seric 		st->s_host.ho_exitstat = EX_TEMPFAIL;
212*52104Seric 		st->s_host.ho_errno = errno;
213*52104Seric 	}
214*52104Seric #endif /* HOSTINFO */
215*52104Seric 
216*52104Seric   tempfail2:
21714913Seric 	smtpquit(m);
21814913Seric 	return (EX_TEMPFAIL);
21914913Seric 
22014913Seric 	/* signal service unavailable */
22114913Seric   unavailable:
22214913Seric 	smtpquit(m);
22314913Seric 	return (EX_UNAVAILABLE);
2244684Seric }
22514886Seric 
22614886Seric 
22714886Seric static
22814886Seric greettimeout()
22914886Seric {
23014886Seric 	/* timeout reading the greeting message */
23114886Seric 	longjmp(CtxGreeting, 1);
23214886Seric }
2334684Seric /*
2344976Seric **  SMTPRCPT -- designate recipient.
2354797Seric **
2364797Seric **	Parameters:
2374865Seric **		to -- address of recipient.
23810175Seric **		m -- the mailer we are sending to.
2394797Seric **
2404797Seric **	Returns:
2414865Seric **		exit status corresponding to recipient status.
2424797Seric **
2434797Seric **	Side Effects:
2444865Seric **		Sends the mail via SMTP.
2454797Seric */
2464797Seric 
24710175Seric smtprcpt(to, m)
2484865Seric 	ADDRESS *to;
24910175Seric 	register MAILER *m;
2504797Seric {
2514797Seric 	register int r;
25210308Seric 	extern char *remotename();
2534797Seric 
25410308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
2554865Seric 
25624944Seric 	SmtpPhase = "RCPT wait";
25736584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
25810175Seric 	r = reply(m);
2598005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2604865Seric 		return (EX_TEMPFAIL);
2617963Seric 	else if (REPLYTYPE(r) == 2)
2627963Seric 		return (EX_OK);
2637964Seric 	else if (r == 550 || r == 551 || r == 553)
2647964Seric 		return (EX_NOUSER);
2657964Seric 	else if (r == 552 || r == 554)
2667964Seric 		return (EX_UNAVAILABLE);
2677964Seric 	return (EX_PROTOCOL);
2684797Seric }
2694797Seric /*
27010175Seric **  SMTPDATA -- send the data and clean up the transaction.
2714684Seric **
2724684Seric **	Parameters:
2734865Seric **		m -- mailer being sent to.
2746980Seric **		e -- the envelope for this message.
2754684Seric **
2764684Seric **	Returns:
2774976Seric **		exit status corresponding to DATA command.
2784684Seric **
2794684Seric **	Side Effects:
2804865Seric **		none.
2814684Seric */
2824684Seric 
28310175Seric smtpdata(m, e)
2844865Seric 	struct mailer *m;
2856980Seric 	register ENVELOPE *e;
2864684Seric {
2874684Seric 	register int r;
2884684Seric 
2894797Seric 	/*
2904797Seric 	**  Send the data.
29110175Seric 	**	First send the command and check that it is ok.
29210175Seric 	**	Then send the data.
29310175Seric 	**	Follow it up with a dot to terminate.
29410175Seric 	**	Finally get the results of the transaction.
2954797Seric 	*/
2964797Seric 
29710175Seric 	/* send the command and check ok to proceed */
29810175Seric 	smtpmessage("DATA", m);
29924944Seric 	SmtpPhase = "DATA wait";
30036584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
30110175Seric 	r = reply(m);
3028005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3034797Seric 		return (EX_TEMPFAIL);
3047963Seric 	else if (r == 554)
3057963Seric 		return (EX_UNAVAILABLE);
3067963Seric 	else if (r != 354)
3077964Seric 		return (EX_PROTOCOL);
30810175Seric 
30910175Seric 	/* now output the actual message */
31010175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
31110175Seric 	putline("\n", SmtpOut, m);
31210175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
31310175Seric 
31410175Seric 	/* terminate the message */
31510328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
31610215Seric 	if (Verbose && !HoldErrs)
31710215Seric 		nmessage(Arpa_Info, ">>> .");
31810175Seric 
31910175Seric 	/* check for the results of the transaction */
32024944Seric 	SmtpPhase = "result wait";
32136584Sbostic 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
32210175Seric 	r = reply(m);
3238005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3244797Seric 		return (EX_TEMPFAIL);
3257963Seric 	else if (r == 250)
3267963Seric 		return (EX_OK);
3277963Seric 	else if (r == 552 || r == 554)
3287963Seric 		return (EX_UNAVAILABLE);
3297964Seric 	return (EX_PROTOCOL);
3304684Seric }
3314684Seric /*
3324865Seric **  SMTPQUIT -- close the SMTP connection.
3334865Seric **
3344865Seric **	Parameters:
33515535Seric **		m -- a pointer to the mailer.
3364865Seric **
3374865Seric **	Returns:
3384865Seric **		none.
3394865Seric **
3404865Seric **	Side Effects:
3414865Seric **		sends the final protocol and closes the connection.
3424865Seric */
3434865Seric 
34415535Seric smtpquit(m)
34510175Seric 	register MAILER *m;
3464865Seric {
3479391Seric 	int i;
3484865Seric 
34910148Seric 	/* if the connection is already closed, don't bother */
35010148Seric 	if (SmtpIn == NULL)
35110148Seric 		return;
35210148Seric 
35310148Seric 	/* send the quit message if not a forced quit */
35411159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
3559391Seric 	{
35610175Seric 		smtpmessage("QUIT", m);
35710175Seric 		(void) reply(m);
35811159Seric 		if (SmtpState == SMTP_CLOSED)
35910159Seric 			return;
3609391Seric 	}
3619391Seric 
36210148Seric 	/* now actually close the connection */
3637229Seric 	(void) fclose(SmtpIn);
3647229Seric 	(void) fclose(SmtpOut);
36510148Seric 	SmtpIn = SmtpOut = NULL;
36611159Seric 	SmtpState = SMTP_CLOSED;
36710148Seric 
36810148Seric 	/* and pick up the zombie */
36915535Seric 	i = endmailer(SmtpPid, m->m_argv[0]);
3709391Seric 	if (i != EX_OK)
37115535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3724865Seric }
3734865Seric /*
3744684Seric **  REPLY -- read arpanet reply
3754684Seric **
3764684Seric **	Parameters:
37710175Seric **		m -- the mailer we are reading the reply from.
3784684Seric **
3794684Seric **	Returns:
3804684Seric **		reply code it reads.
3814684Seric **
3824684Seric **	Side Effects:
3834684Seric **		flushes the mail file.
3844684Seric */
3854684Seric 
38610175Seric reply(m)
38710688Seric 	MAILER *m;
3884684Seric {
3894865Seric 	(void) fflush(SmtpOut);
3904684Seric 
3917677Seric 	if (tTd(18, 1))
3924796Seric 		printf("reply\n");
3934796Seric 
3947356Seric 	/*
3957356Seric 	**  Read the input line, being careful not to hang.
3967356Seric 	*/
3977356Seric 
3984684Seric 	for (;;)
3994684Seric 	{
4004684Seric 		register int r;
4017356Seric 		register char *p;
4024684Seric 
4037685Seric 		/* actually do the read */
4049547Seric 		if (CurEnv->e_xfp != NULL)
4059547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
4067356Seric 
40710054Seric 		/* if we are in the process of closing just give the code */
40811159Seric 		if (SmtpState == SMTP_CLOSED)
40910054Seric 			return (SMTPCLOSING);
41010054Seric 
41110054Seric 		/* get the line from the other side */
41210054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
41310054Seric 		if (p == NULL)
41410131Seric 		{
41510148Seric 			extern char MsgBuf[];		/* err.c */
41610148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
41710148Seric 
41821065Seric 			/* if the remote end closed early, fake an error */
41921065Seric 			if (errno == 0)
42021065Seric # ifdef ECONNRESET
42121065Seric 				errno = ECONNRESET;
42221065Seric # else ECONNRESET
42321065Seric 				errno = EPIPE;
42421065Seric # endif ECONNRESET
42521065Seric 
42610148Seric 			message(Arpa_TSyserr, "reply: read error");
42710420Seric 			/* if debugging, pause so we can see state */
42810420Seric 			if (tTd(18, 100))
42910420Seric 				pause();
43010148Seric # ifdef LOG
43136234Skarels 			syslog(LOG_INFO, "%s", &MsgBuf[4]);
43210148Seric # endif LOG
43311159Seric 			SmtpState = SMTP_CLOSED;
43415535Seric 			smtpquit(m);
43510054Seric 			return (-1);
43610131Seric 		}
43710054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
43810054Seric 
43914900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
44014900Seric 		{
44114900Seric 			/* serious error -- log the previous command */
44214900Seric 			if (SmtpMsgBuffer[0] != '\0')
44314900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
44414900Seric 			SmtpMsgBuffer[0] = '\0';
44514900Seric 
44614900Seric 			/* now log the message as from the other side */
44714900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
44814900Seric 		}
44914900Seric 
45014900Seric 		/* display the input for verbose mode */
4517229Seric 		if (Verbose && !HoldErrs)
4529391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4537356Seric 
4547356Seric 		/* if continuation is required, we can go on */
4559391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4564684Seric 			continue;
4577356Seric 
4587356Seric 		/* decode the reply code */
4599391Seric 		r = atoi(SmtpReplyBuffer);
4607356Seric 
4617356Seric 		/* extra semantics: 0xx codes are "informational" */
4624684Seric 		if (r < 100)
4634684Seric 			continue;
4647356Seric 
4659391Seric 		/* reply code 421 is "Service Shutting Down" */
46611159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
4679391Seric 		{
46810054Seric 			/* send the quit protocol */
46911159Seric 			SmtpState = SMTP_SSD;
47015535Seric 			smtpquit(m);
4719391Seric 		}
4729391Seric 
47321065Seric 		/* save temporary failure messages for posterity */
47421065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
47521065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
47621065Seric 
4774684Seric 		return (r);
4784684Seric 	}
4794684Seric }
4804796Seric /*
4814865Seric **  SMTPMESSAGE -- send message to server
4824796Seric **
4834796Seric **	Parameters:
4844796Seric **		f -- format
48510175Seric **		m -- the mailer to control formatting.
4864796Seric **		a, b, c -- parameters
4874796Seric **
4884796Seric **	Returns:
4894796Seric **		none.
4904796Seric **
4914796Seric **	Side Effects:
4924865Seric **		writes message to SmtpOut.
4934796Seric */
4944796Seric 
4954865Seric /*VARARGS1*/
49610175Seric smtpmessage(f, m, a, b, c)
4974796Seric 	char *f;
49810175Seric 	MAILER *m;
4994796Seric {
50014900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
5017677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
50214900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
50311159Seric 	if (SmtpOut != NULL)
50440997Sbostic 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer,
50540997Sbostic 			m == 0 ? "\r\n" : m->m_eol);
5064796Seric }
5075182Seric 
5085182Seric # endif SMTP
509