122716Sdist /*
222716Sdist **  Sendmail
322716Sdist **  Copyright (c) 1983  Eric P. Allman
422716Sdist **  Berkeley, California
522716Sdist **
622716Sdist **  Copyright (c) 1983 Regents of the University of California.
722716Sdist **  All rights reserved.  The Berkeley software License Agreement
822716Sdist **  specifies the terms and conditions for redistribution.
922716Sdist */
1022716Sdist 
1122716Sdist 
124796Seric # include <ctype.h>
134684Seric # include <sysexits.h>
1421065Seric # include <errno.h>
154865Seric # include "sendmail.h"
164684Seric 
175182Seric # ifndef SMTP
1823124Seric # ifndef lint
19*24945Seric static char	SccsId[] = "@(#)usersmtp.c	5.5 (Berkeley) 09/19/85	(no SMTP)";
2023124Seric # endif not lint
215182Seric # else SMTP
224684Seric 
2323124Seric # ifndef lint
24*24945Seric static char	SccsId[] = "@(#)usersmtp.c	5.5 (Berkeley) 09/19/85";
2523124Seric # endif not lint
265182Seric 
279391Seric 
289391Seric 
294684Seric /*
309391Seric **  USERSMTP -- run SMTP protocol from the user end.
319391Seric **
329391Seric **	This protocol is described in RFC821.
339391Seric */
349391Seric 
359391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
369391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
379391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
389391Seric 
3914900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
4010054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
4121065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
4210054Seric FILE	*SmtpOut;			/* output file */
4310054Seric FILE	*SmtpIn;			/* input file */
4410054Seric int	SmtpPid;			/* pid of mailer */
4511159Seric 
4611159Seric /* following represents the state of the SMTP connection */
4711159Seric int	SmtpState;			/* connection state, see below */
4811159Seric 
4911159Seric #define SMTP_CLOSED	0		/* connection is closed */
5011159Seric #define SMTP_OPEN	1		/* connection is open for business */
5111159Seric #define SMTP_SSD	2		/* service shutting down */
529391Seric /*
534865Seric **  SMTPINIT -- initialize SMTP.
544684Seric **
554865Seric **	Opens the connection and sends the initial protocol.
564684Seric **
574684Seric **	Parameters:
584865Seric **		m -- mailer to create connection to.
594865Seric **		pvp -- pointer to parameter vector to pass to
604865Seric **			the mailer.
614684Seric **
624684Seric **	Returns:
634865Seric **		appropriate exit status -- EX_OK on success.
6414913Seric **		If not EX_OK, it should close the connection.
654684Seric **
664684Seric **	Side Effects:
674865Seric **		creates connection and sends initial protocol.
684684Seric */
694684Seric 
7014886Seric jmp_buf	CtxGreeting;
7114886Seric 
7210175Seric smtpinit(m, pvp)
734865Seric 	struct mailer *m;
744865Seric 	char **pvp;
754684Seric {
764865Seric 	register int r;
7714886Seric 	EVENT *gte;
784865Seric 	char buf[MAXNAME];
7914886Seric 	extern greettimeout();
804684Seric 
814865Seric 	/*
824865Seric 	**  Open the connection to the mailer.
834865Seric 	*/
844684Seric 
8511159Seric #ifdef DEBUG
8611159Seric 	if (SmtpState == SMTP_OPEN)
8711159Seric 		syserr("smtpinit: already open");
8811159Seric #endif DEBUG
8911159Seric 
906051Seric 	SmtpIn = SmtpOut = NULL;
9111159Seric 	SmtpState = SMTP_CLOSED;
9221065Seric 	SmtpError[0] = '\0';
9324944Seric 	SmtpPhase = "user open";
9410175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
956051Seric 	if (SmtpPid < 0)
966051Seric 	{
976051Seric # ifdef DEBUG
987677Seric 		if (tTd(18, 1))
999391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
1009391Seric 			   pvp[0], ExitStat, errno);
1016051Seric # endif DEBUG
10215139Seric 		if (CurEnv->e_xfp != NULL)
10315139Seric 		{
10421065Seric 			register char *p;
10515139Seric 			extern char *errstring();
10621065Seric 			extern char *statstring();
10715139Seric 
10821065Seric 			if (errno == 0)
10921065Seric 			{
11021065Seric 				p = statstring(ExitStat);
11121065Seric 				fprintf(CurEnv->e_xfp,
11221065Seric 					"%.3s %s.%s... %s\n",
11321065Seric 					p, pvp[1], m->m_name, p);
11421065Seric 			}
11521065Seric 			else
11621065Seric 			{
11721065Seric 				fprintf(CurEnv->e_xfp,
11821065Seric 					"421 %s.%s... Deferred: %s\n",
11921065Seric 					pvp[1], m->m_name, errstring(errno));
12021065Seric 			}
12115139Seric 		}
1226051Seric 		return (ExitStat);
1236051Seric 	}
12411159Seric 	SmtpState = SMTP_OPEN;
1254796Seric 
1264865Seric 	/*
1274865Seric 	**  Get the greeting message.
12814913Seric 	**	This should appear spontaneously.  Give it five minutes to
12914886Seric 	**	happen.
1304865Seric 	*/
1314797Seric 
13214886Seric 	if (setjmp(CtxGreeting) != 0)
13314913Seric 		goto tempfail;
13416141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
13524944Seric 	SmtpPhase = "greeting wait";
13610175Seric 	r = reply(m);
13714886Seric 	clrevent(gte);
1388005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
13914913Seric 		goto tempfail;
1404684Seric 
1414865Seric 	/*
1424976Seric 	**  Send the HELO command.
1437963Seric 	**	My mother taught me to always introduce myself.
1444976Seric 	*/
1454976Seric 
14610175Seric 	smtpmessage("HELO %s", m, HostName);
14724944Seric 	SmtpPhase = "HELO wait";
14810175Seric 	r = reply(m);
1498005Seric 	if (r < 0)
15014913Seric 		goto tempfail;
1518005Seric 	else if (REPLYTYPE(r) == 5)
15214913Seric 		goto unavailable;
1537963Seric 	else if (REPLYTYPE(r) != 2)
15414913Seric 		goto tempfail;
1554976Seric 
1564976Seric 	/*
1579315Seric 	**  If this is expected to be another sendmail, send some internal
1589315Seric 	**  commands.
1599315Seric 	*/
1609315Seric 
16110688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1629315Seric 	{
1639315Seric 		/* tell it to be verbose */
16410175Seric 		smtpmessage("VERB", m);
16510175Seric 		r = reply(m);
1669315Seric 		if (r < 0)
16714913Seric 			goto tempfail;
1689315Seric 
1699315Seric 		/* tell it we will be sending one transaction only */
17010175Seric 		smtpmessage("ONEX", m);
17110175Seric 		r = reply(m);
1729315Seric 		if (r < 0)
17314913Seric 			goto tempfail;
1749315Seric 	}
1759315Seric 
1769315Seric 	/*
1774865Seric 	**  Send the MAIL command.
1784865Seric 	**	Designates the sender.
1794865Seric 	*/
1804796Seric 
18116156Seric 	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
1828436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
18310688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1848436Seric 	{
18510308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1868436Seric 	}
1878436Seric 	else
1888436Seric 	{
18910175Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, HostName,
19010308Seric 			buf[0] == '@' ? ',' : ':', buf);
1918436Seric 	}
19224944Seric 	SmtpPhase = "MAIL wait";
19310175Seric 	r = reply(m);
1948005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
19514913Seric 		goto tempfail;
1967963Seric 	else if (r == 250)
1977963Seric 		return (EX_OK);
1987963Seric 	else if (r == 552)
19914913Seric 		goto unavailable;
20014913Seric 
20114913Seric 	/* protocol error -- close up */
20214913Seric 	smtpquit(m);
2037964Seric 	return (EX_PROTOCOL);
20414913Seric 
20514913Seric 	/* signal a temporary failure */
20614913Seric   tempfail:
20714913Seric 	smtpquit(m);
20816891Seric 	CurEnv->e_flags &= ~EF_FATALERRS;
20914913Seric 	return (EX_TEMPFAIL);
21014913Seric 
21114913Seric 	/* signal service unavailable */
21214913Seric   unavailable:
21314913Seric 	smtpquit(m);
21414913Seric 	return (EX_UNAVAILABLE);
2154684Seric }
21614886Seric 
21714886Seric 
21814886Seric static
21914886Seric greettimeout()
22014886Seric {
22114886Seric 	/* timeout reading the greeting message */
22214886Seric 	longjmp(CtxGreeting, 1);
22314886Seric }
2244684Seric /*
2254976Seric **  SMTPRCPT -- designate recipient.
2264797Seric **
2274797Seric **	Parameters:
2284865Seric **		to -- address of recipient.
22910175Seric **		m -- the mailer we are sending to.
2304797Seric **
2314797Seric **	Returns:
2324865Seric **		exit status corresponding to recipient status.
2334797Seric **
2344797Seric **	Side Effects:
2354865Seric **		Sends the mail via SMTP.
2364797Seric */
2374797Seric 
23810175Seric smtprcpt(to, m)
2394865Seric 	ADDRESS *to;
24010175Seric 	register MAILER *m;
2414797Seric {
2424797Seric 	register int r;
24310308Seric 	extern char *remotename();
2444797Seric 
24510308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
2464865Seric 
24724944Seric 	SmtpPhase = "RCPT wait";
24810175Seric 	r = reply(m);
2498005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2504865Seric 		return (EX_TEMPFAIL);
2517963Seric 	else if (REPLYTYPE(r) == 2)
2527963Seric 		return (EX_OK);
2537964Seric 	else if (r == 550 || r == 551 || r == 553)
2547964Seric 		return (EX_NOUSER);
2557964Seric 	else if (r == 552 || r == 554)
2567964Seric 		return (EX_UNAVAILABLE);
2577964Seric 	return (EX_PROTOCOL);
2584797Seric }
2594797Seric /*
26010175Seric **  SMTPDATA -- send the data and clean up the transaction.
2614684Seric **
2624684Seric **	Parameters:
2634865Seric **		m -- mailer being sent to.
2646980Seric **		e -- the envelope for this message.
2654684Seric **
2664684Seric **	Returns:
2674976Seric **		exit status corresponding to DATA command.
2684684Seric **
2694684Seric **	Side Effects:
2704865Seric **		none.
2714684Seric */
2724684Seric 
27310175Seric smtpdata(m, e)
2744865Seric 	struct mailer *m;
2756980Seric 	register ENVELOPE *e;
2764684Seric {
2774684Seric 	register int r;
2784684Seric 
2794797Seric 	/*
2804797Seric 	**  Send the data.
28110175Seric 	**	First send the command and check that it is ok.
28210175Seric 	**	Then send the data.
28310175Seric 	**	Follow it up with a dot to terminate.
28410175Seric 	**	Finally get the results of the transaction.
2854797Seric 	*/
2864797Seric 
28710175Seric 	/* send the command and check ok to proceed */
28810175Seric 	smtpmessage("DATA", m);
28924944Seric 	SmtpPhase = "DATA wait";
29010175Seric 	r = reply(m);
2918005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2924797Seric 		return (EX_TEMPFAIL);
2937963Seric 	else if (r == 554)
2947963Seric 		return (EX_UNAVAILABLE);
2957963Seric 	else if (r != 354)
2967964Seric 		return (EX_PROTOCOL);
29710175Seric 
29810175Seric 	/* now output the actual message */
29910175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
30010175Seric 	putline("\n", SmtpOut, m);
30110175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
30210175Seric 
30310175Seric 	/* terminate the message */
30410328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
30510215Seric 	if (Verbose && !HoldErrs)
30610215Seric 		nmessage(Arpa_Info, ">>> .");
30710175Seric 
30810175Seric 	/* check for the results of the transaction */
30924944Seric 	SmtpPhase = "result wait";
31010175Seric 	r = reply(m);
3118005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3124797Seric 		return (EX_TEMPFAIL);
3137963Seric 	else if (r == 250)
3147963Seric 		return (EX_OK);
3157963Seric 	else if (r == 552 || r == 554)
3167963Seric 		return (EX_UNAVAILABLE);
3177964Seric 	return (EX_PROTOCOL);
3184684Seric }
3194684Seric /*
3204865Seric **  SMTPQUIT -- close the SMTP connection.
3214865Seric **
3224865Seric **	Parameters:
32315535Seric **		m -- a pointer to the mailer.
3244865Seric **
3254865Seric **	Returns:
3264865Seric **		none.
3274865Seric **
3284865Seric **	Side Effects:
3294865Seric **		sends the final protocol and closes the connection.
3304865Seric */
3314865Seric 
33215535Seric smtpquit(m)
33310175Seric 	register MAILER *m;
3344865Seric {
3359391Seric 	int i;
3364865Seric 
33710148Seric 	/* if the connection is already closed, don't bother */
33810148Seric 	if (SmtpIn == NULL)
33910148Seric 		return;
34010148Seric 
34110148Seric 	/* send the quit message if not a forced quit */
34211159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
3439391Seric 	{
34410175Seric 		smtpmessage("QUIT", m);
34510175Seric 		(void) reply(m);
34611159Seric 		if (SmtpState == SMTP_CLOSED)
34710159Seric 			return;
3489391Seric 	}
3499391Seric 
35010148Seric 	/* now actually close the connection */
3517229Seric 	(void) fclose(SmtpIn);
3527229Seric 	(void) fclose(SmtpOut);
35310148Seric 	SmtpIn = SmtpOut = NULL;
35411159Seric 	SmtpState = SMTP_CLOSED;
35510148Seric 
35610148Seric 	/* and pick up the zombie */
35715535Seric 	i = endmailer(SmtpPid, m->m_argv[0]);
3589391Seric 	if (i != EX_OK)
35915535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3604865Seric }
3614865Seric /*
3624684Seric **  REPLY -- read arpanet reply
3634684Seric **
3644684Seric **	Parameters:
36510175Seric **		m -- the mailer we are reading the reply from.
3664684Seric **
3674684Seric **	Returns:
3684684Seric **		reply code it reads.
3694684Seric **
3704684Seric **	Side Effects:
3714684Seric **		flushes the mail file.
3724684Seric */
3734684Seric 
37410175Seric reply(m)
37510688Seric 	MAILER *m;
3764684Seric {
3774865Seric 	(void) fflush(SmtpOut);
3784684Seric 
3797677Seric 	if (tTd(18, 1))
3804796Seric 		printf("reply\n");
3814796Seric 
3827356Seric 	/*
3837356Seric 	**  Read the input line, being careful not to hang.
3847356Seric 	*/
3857356Seric 
3864684Seric 	for (;;)
3874684Seric 	{
3884684Seric 		register int r;
3897356Seric 		register char *p;
3904684Seric 
3917685Seric 		/* actually do the read */
3929547Seric 		if (CurEnv->e_xfp != NULL)
3939547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3947356Seric 
39510054Seric 		/* if we are in the process of closing just give the code */
39611159Seric 		if (SmtpState == SMTP_CLOSED)
39710054Seric 			return (SMTPCLOSING);
39810054Seric 
39910054Seric 		/* get the line from the other side */
40010054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
40110054Seric 		if (p == NULL)
40210131Seric 		{
40310148Seric 			extern char MsgBuf[];		/* err.c */
40410148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
40510148Seric 
40621065Seric 			/* if the remote end closed early, fake an error */
40721065Seric 			if (errno == 0)
40821065Seric # ifdef ECONNRESET
40921065Seric 				errno = ECONNRESET;
41021065Seric # else ECONNRESET
41121065Seric 				errno = EPIPE;
41221065Seric # endif ECONNRESET
41321065Seric 
41410148Seric 			message(Arpa_TSyserr, "reply: read error");
41510420Seric # ifdef DEBUG
41610420Seric 			/* if debugging, pause so we can see state */
41710420Seric 			if (tTd(18, 100))
41810420Seric 				pause();
41910420Seric # endif DEBUG
42010148Seric # ifdef LOG
421*24945Seric 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
42210148Seric # endif LOG
42311159Seric 			SmtpState = SMTP_CLOSED;
42415535Seric 			smtpquit(m);
42510054Seric 			return (-1);
42610131Seric 		}
42710054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
42810054Seric 
42914900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
43014900Seric 		{
43114900Seric 			/* serious error -- log the previous command */
43214900Seric 			if (SmtpMsgBuffer[0] != '\0')
43314900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
43414900Seric 			SmtpMsgBuffer[0] = '\0';
43514900Seric 
43614900Seric 			/* now log the message as from the other side */
43714900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
43814900Seric 		}
43914900Seric 
44014900Seric 		/* display the input for verbose mode */
4417229Seric 		if (Verbose && !HoldErrs)
4429391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4437356Seric 
4447356Seric 		/* if continuation is required, we can go on */
4459391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4464684Seric 			continue;
4477356Seric 
4487356Seric 		/* decode the reply code */
4499391Seric 		r = atoi(SmtpReplyBuffer);
4507356Seric 
4517356Seric 		/* extra semantics: 0xx codes are "informational" */
4524684Seric 		if (r < 100)
4534684Seric 			continue;
4547356Seric 
4559391Seric 		/* reply code 421 is "Service Shutting Down" */
45611159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
4579391Seric 		{
45810054Seric 			/* send the quit protocol */
45911159Seric 			SmtpState = SMTP_SSD;
46015535Seric 			smtpquit(m);
4619391Seric 		}
4629391Seric 
46321065Seric 		/* save temporary failure messages for posterity */
46421065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
46521065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
46621065Seric 
4674684Seric 		return (r);
4684684Seric 	}
4694684Seric }
4704796Seric /*
4714865Seric **  SMTPMESSAGE -- send message to server
4724796Seric **
4734796Seric **	Parameters:
4744796Seric **		f -- format
47510175Seric **		m -- the mailer to control formatting.
4764796Seric **		a, b, c -- parameters
4774796Seric **
4784796Seric **	Returns:
4794796Seric **		none.
4804796Seric **
4814796Seric **	Side Effects:
4824865Seric **		writes message to SmtpOut.
4834796Seric */
4844796Seric 
4854865Seric /*VARARGS1*/
48610175Seric smtpmessage(f, m, a, b, c)
4874796Seric 	char *f;
48810175Seric 	MAILER *m;
4894796Seric {
49014900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4917677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
49214900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
49311159Seric 	if (SmtpOut != NULL)
49414900Seric 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
4954796Seric }
4965182Seric 
4975182Seric # endif SMTP
498