122716Sdist /*
2*33731Sbostic  * Copyright (c) 1988 Regents of the University of California.
3*33731Sbostic  * All rights reserved.
4*33731Sbostic  *
5*33731Sbostic  * Redistribution and use in source and binary forms are permitted
6*33731Sbostic  * provided that this notice is preserved and that due credit is given
7*33731Sbostic  * to the University of California at Berkeley. The name of the University
8*33731Sbostic  * may not be used to endorse or promote products derived from this
9*33731Sbostic  * software without specific prior written permission. This software
10*33731Sbostic  * is provided ``as is'' without express or implied warranty.
11*33731Sbostic  *
12*33731Sbostic  *  Sendmail
13*33731Sbostic  *  Copyright (c) 1983  Eric P. Allman
14*33731Sbostic  *  Berkeley, California
15*33731Sbostic  */
1622716Sdist 
17*33731Sbostic # include "sendmail.h"
1822716Sdist 
19*33731Sbostic #ifndef lint
20*33731Sbostic #ifdef SMTP
21*33731Sbostic static char sccsid[] = "@(#)usersmtp.c	5.9 (Berkeley) 03/13/88 (with SMTP)";
22*33731Sbostic #else
23*33731Sbostic static char sccsid[] = "@(#)usersmtp.c	5.9 (Berkeley) 03/13/88 (without SMTP)";
24*33731Sbostic #endif
25*33731Sbostic #endif /* not lint */
26*33731Sbostic 
274684Seric # include <sysexits.h>
2821065Seric # include <errno.h>
294684Seric 
30*33731Sbostic # ifdef SMTP
314684Seric 
324684Seric /*
339391Seric **  USERSMTP -- run SMTP protocol from the user end.
349391Seric **
359391Seric **	This protocol is described in RFC821.
369391Seric */
379391Seric 
389391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
399391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
409391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
419391Seric 
4214900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
4310054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
4421065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
4510054Seric FILE	*SmtpOut;			/* output file */
4610054Seric FILE	*SmtpIn;			/* input file */
4710054Seric int	SmtpPid;			/* pid of mailer */
4811159Seric 
4911159Seric /* following represents the state of the SMTP connection */
5011159Seric int	SmtpState;			/* connection state, see below */
5111159Seric 
5211159Seric #define SMTP_CLOSED	0		/* connection is closed */
5311159Seric #define SMTP_OPEN	1		/* connection is open for business */
5411159Seric #define SMTP_SSD	2		/* service shutting down */
559391Seric /*
564865Seric **  SMTPINIT -- initialize SMTP.
574684Seric **
584865Seric **	Opens the connection and sends the initial protocol.
594684Seric **
604684Seric **	Parameters:
614865Seric **		m -- mailer to create connection to.
624865Seric **		pvp -- pointer to parameter vector to pass to
634865Seric **			the mailer.
644684Seric **
654684Seric **	Returns:
664865Seric **		appropriate exit status -- EX_OK on success.
6714913Seric **		If not EX_OK, it should close the connection.
684684Seric **
694684Seric **	Side Effects:
704865Seric **		creates connection and sends initial protocol.
714684Seric */
724684Seric 
7314886Seric jmp_buf	CtxGreeting;
7414886Seric 
7510175Seric smtpinit(m, pvp)
764865Seric 	struct mailer *m;
774865Seric 	char **pvp;
784684Seric {
794865Seric 	register int r;
8014886Seric 	EVENT *gte;
814865Seric 	char buf[MAXNAME];
8214886Seric 	extern greettimeout();
834684Seric 
844865Seric 	/*
854865Seric 	**  Open the connection to the mailer.
864865Seric 	*/
874684Seric 
8811159Seric #ifdef DEBUG
8911159Seric 	if (SmtpState == SMTP_OPEN)
9011159Seric 		syserr("smtpinit: already open");
9111159Seric #endif DEBUG
9211159Seric 
936051Seric 	SmtpIn = SmtpOut = NULL;
9411159Seric 	SmtpState = SMTP_CLOSED;
9521065Seric 	SmtpError[0] = '\0';
9624944Seric 	SmtpPhase = "user open";
9710175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
986051Seric 	if (SmtpPid < 0)
996051Seric 	{
1006051Seric # ifdef DEBUG
1017677Seric 		if (tTd(18, 1))
1029391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
1039391Seric 			   pvp[0], ExitStat, errno);
1046051Seric # endif DEBUG
10515139Seric 		if (CurEnv->e_xfp != NULL)
10615139Seric 		{
10721065Seric 			register char *p;
10815139Seric 			extern char *errstring();
10921065Seric 			extern char *statstring();
11015139Seric 
11121065Seric 			if (errno == 0)
11221065Seric 			{
11321065Seric 				p = statstring(ExitStat);
11421065Seric 				fprintf(CurEnv->e_xfp,
11521065Seric 					"%.3s %s.%s... %s\n",
11621065Seric 					p, pvp[1], m->m_name, p);
11721065Seric 			}
11821065Seric 			else
11921065Seric 			{
12021065Seric 				fprintf(CurEnv->e_xfp,
12121065Seric 					"421 %s.%s... Deferred: %s\n",
12221065Seric 					pvp[1], m->m_name, errstring(errno));
12321065Seric 			}
12415139Seric 		}
1256051Seric 		return (ExitStat);
1266051Seric 	}
12711159Seric 	SmtpState = SMTP_OPEN;
1284796Seric 
1294865Seric 	/*
1304865Seric 	**  Get the greeting message.
13114913Seric 	**	This should appear spontaneously.  Give it five minutes to
13214886Seric 	**	happen.
1334865Seric 	*/
1344797Seric 
13514886Seric 	if (setjmp(CtxGreeting) != 0)
13614913Seric 		goto tempfail;
13716141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
13824944Seric 	SmtpPhase = "greeting wait";
13910175Seric 	r = reply(m);
14014886Seric 	clrevent(gte);
1418005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
14214913Seric 		goto tempfail;
1434684Seric 
1444865Seric 	/*
1454976Seric 	**  Send the HELO command.
1467963Seric 	**	My mother taught me to always introduce myself.
1474976Seric 	*/
1484976Seric 
14925050Seric 	smtpmessage("HELO %s", m, MyHostName);
15024944Seric 	SmtpPhase = "HELO wait";
15110175Seric 	r = reply(m);
1528005Seric 	if (r < 0)
15314913Seric 		goto tempfail;
1548005Seric 	else if (REPLYTYPE(r) == 5)
15514913Seric 		goto unavailable;
1567963Seric 	else if (REPLYTYPE(r) != 2)
15714913Seric 		goto tempfail;
1584976Seric 
1594976Seric 	/*
1609315Seric 	**  If this is expected to be another sendmail, send some internal
1619315Seric 	**  commands.
1629315Seric 	*/
1639315Seric 
16410688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1659315Seric 	{
1669315Seric 		/* tell it to be verbose */
16710175Seric 		smtpmessage("VERB", m);
16810175Seric 		r = reply(m);
1699315Seric 		if (r < 0)
17014913Seric 			goto tempfail;
1719315Seric 
1729315Seric 		/* tell it we will be sending one transaction only */
17310175Seric 		smtpmessage("ONEX", m);
17410175Seric 		r = reply(m);
1759315Seric 		if (r < 0)
17614913Seric 			goto tempfail;
1779315Seric 	}
1789315Seric 
1799315Seric 	/*
1804865Seric 	**  Send the MAIL command.
1814865Seric 	**	Designates the sender.
1824865Seric 	*/
1834796Seric 
18416156Seric 	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
1858436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
18610688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1878436Seric 	{
18810308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1898436Seric 	}
1908436Seric 	else
1918436Seric 	{
19225050Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
19310308Seric 			buf[0] == '@' ? ',' : ':', buf);
1948436Seric 	}
19524944Seric 	SmtpPhase = "MAIL wait";
19610175Seric 	r = reply(m);
1978005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
19814913Seric 		goto tempfail;
1997963Seric 	else if (r == 250)
2007963Seric 		return (EX_OK);
2017963Seric 	else if (r == 552)
20214913Seric 		goto unavailable;
20314913Seric 
20414913Seric 	/* protocol error -- close up */
20514913Seric 	smtpquit(m);
2067964Seric 	return (EX_PROTOCOL);
20714913Seric 
20814913Seric 	/* signal a temporary failure */
20914913Seric   tempfail:
21014913Seric 	smtpquit(m);
21114913Seric 	return (EX_TEMPFAIL);
21214913Seric 
21314913Seric 	/* signal service unavailable */
21414913Seric   unavailable:
21514913Seric 	smtpquit(m);
21614913Seric 	return (EX_UNAVAILABLE);
2174684Seric }
21814886Seric 
21914886Seric 
22014886Seric static
22114886Seric greettimeout()
22214886Seric {
22314886Seric 	/* timeout reading the greeting message */
22414886Seric 	longjmp(CtxGreeting, 1);
22514886Seric }
2264684Seric /*
2274976Seric **  SMTPRCPT -- designate recipient.
2284797Seric **
2294797Seric **	Parameters:
2304865Seric **		to -- address of recipient.
23110175Seric **		m -- the mailer we are sending to.
2324797Seric **
2334797Seric **	Returns:
2344865Seric **		exit status corresponding to recipient status.
2354797Seric **
2364797Seric **	Side Effects:
2374865Seric **		Sends the mail via SMTP.
2384797Seric */
2394797Seric 
24010175Seric smtprcpt(to, m)
2414865Seric 	ADDRESS *to;
24210175Seric 	register MAILER *m;
2434797Seric {
2444797Seric 	register int r;
24510308Seric 	extern char *remotename();
2464797Seric 
24710308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
2484865Seric 
24924944Seric 	SmtpPhase = "RCPT wait";
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";
29210175Seric 	r = reply(m);
2938005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2944797Seric 		return (EX_TEMPFAIL);
2957963Seric 	else if (r == 554)
2967963Seric 		return (EX_UNAVAILABLE);
2977963Seric 	else if (r != 354)
2987964Seric 		return (EX_PROTOCOL);
29910175Seric 
30010175Seric 	/* now output the actual message */
30110175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
30210175Seric 	putline("\n", SmtpOut, m);
30310175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
30410175Seric 
30510175Seric 	/* terminate the message */
30610328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
30710215Seric 	if (Verbose && !HoldErrs)
30810215Seric 		nmessage(Arpa_Info, ">>> .");
30910175Seric 
31010175Seric 	/* check for the results of the transaction */
31124944Seric 	SmtpPhase = "result wait";
31210175Seric 	r = reply(m);
3138005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
3144797Seric 		return (EX_TEMPFAIL);
3157963Seric 	else if (r == 250)
3167963Seric 		return (EX_OK);
3177963Seric 	else if (r == 552 || r == 554)
3187963Seric 		return (EX_UNAVAILABLE);
3197964Seric 	return (EX_PROTOCOL);
3204684Seric }
3214684Seric /*
3224865Seric **  SMTPQUIT -- close the SMTP connection.
3234865Seric **
3244865Seric **	Parameters:
32515535Seric **		m -- a pointer to the mailer.
3264865Seric **
3274865Seric **	Returns:
3284865Seric **		none.
3294865Seric **
3304865Seric **	Side Effects:
3314865Seric **		sends the final protocol and closes the connection.
3324865Seric */
3334865Seric 
33415535Seric smtpquit(m)
33510175Seric 	register MAILER *m;
3364865Seric {
3379391Seric 	int i;
3384865Seric 
33910148Seric 	/* if the connection is already closed, don't bother */
34010148Seric 	if (SmtpIn == NULL)
34110148Seric 		return;
34210148Seric 
34310148Seric 	/* send the quit message if not a forced quit */
34411159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
3459391Seric 	{
34610175Seric 		smtpmessage("QUIT", m);
34710175Seric 		(void) reply(m);
34811159Seric 		if (SmtpState == SMTP_CLOSED)
34910159Seric 			return;
3509391Seric 	}
3519391Seric 
35210148Seric 	/* now actually close the connection */
3537229Seric 	(void) fclose(SmtpIn);
3547229Seric 	(void) fclose(SmtpOut);
35510148Seric 	SmtpIn = SmtpOut = NULL;
35611159Seric 	SmtpState = SMTP_CLOSED;
35710148Seric 
35810148Seric 	/* and pick up the zombie */
35915535Seric 	i = endmailer(SmtpPid, m->m_argv[0]);
3609391Seric 	if (i != EX_OK)
36115535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3624865Seric }
3634865Seric /*
3644684Seric **  REPLY -- read arpanet reply
3654684Seric **
3664684Seric **	Parameters:
36710175Seric **		m -- the mailer we are reading the reply from.
3684684Seric **
3694684Seric **	Returns:
3704684Seric **		reply code it reads.
3714684Seric **
3724684Seric **	Side Effects:
3734684Seric **		flushes the mail file.
3744684Seric */
3754684Seric 
37610175Seric reply(m)
37710688Seric 	MAILER *m;
3784684Seric {
3794865Seric 	(void) fflush(SmtpOut);
3804684Seric 
3817677Seric 	if (tTd(18, 1))
3824796Seric 		printf("reply\n");
3834796Seric 
3847356Seric 	/*
3857356Seric 	**  Read the input line, being careful not to hang.
3867356Seric 	*/
3877356Seric 
3884684Seric 	for (;;)
3894684Seric 	{
3904684Seric 		register int r;
3917356Seric 		register char *p;
3924684Seric 
3937685Seric 		/* actually do the read */
3949547Seric 		if (CurEnv->e_xfp != NULL)
3959547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3967356Seric 
39710054Seric 		/* if we are in the process of closing just give the code */
39811159Seric 		if (SmtpState == SMTP_CLOSED)
39910054Seric 			return (SMTPCLOSING);
40010054Seric 
40110054Seric 		/* get the line from the other side */
40210054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
40310054Seric 		if (p == NULL)
40410131Seric 		{
40510148Seric 			extern char MsgBuf[];		/* err.c */
40610148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
40710148Seric 
40821065Seric 			/* if the remote end closed early, fake an error */
40921065Seric 			if (errno == 0)
41021065Seric # ifdef ECONNRESET
41121065Seric 				errno = ECONNRESET;
41221065Seric # else ECONNRESET
41321065Seric 				errno = EPIPE;
41421065Seric # endif ECONNRESET
41521065Seric 
41610148Seric 			message(Arpa_TSyserr, "reply: read error");
41710420Seric # ifdef DEBUG
41810420Seric 			/* if debugging, pause so we can see state */
41910420Seric 			if (tTd(18, 100))
42010420Seric 				pause();
42110420Seric # endif DEBUG
42210148Seric # ifdef LOG
42324945Seric 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
42410148Seric # endif LOG
42511159Seric 			SmtpState = SMTP_CLOSED;
42615535Seric 			smtpquit(m);
42710054Seric 			return (-1);
42810131Seric 		}
42910054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
43010054Seric 
43114900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
43214900Seric 		{
43314900Seric 			/* serious error -- log the previous command */
43414900Seric 			if (SmtpMsgBuffer[0] != '\0')
43514900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
43614900Seric 			SmtpMsgBuffer[0] = '\0';
43714900Seric 
43814900Seric 			/* now log the message as from the other side */
43914900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
44014900Seric 		}
44114900Seric 
44214900Seric 		/* display the input for verbose mode */
4437229Seric 		if (Verbose && !HoldErrs)
4449391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4457356Seric 
4467356Seric 		/* if continuation is required, we can go on */
4479391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4484684Seric 			continue;
4497356Seric 
4507356Seric 		/* decode the reply code */
4519391Seric 		r = atoi(SmtpReplyBuffer);
4527356Seric 
4537356Seric 		/* extra semantics: 0xx codes are "informational" */
4544684Seric 		if (r < 100)
4554684Seric 			continue;
4567356Seric 
4579391Seric 		/* reply code 421 is "Service Shutting Down" */
45811159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
4599391Seric 		{
46010054Seric 			/* send the quit protocol */
46111159Seric 			SmtpState = SMTP_SSD;
46215535Seric 			smtpquit(m);
4639391Seric 		}
4649391Seric 
46521065Seric 		/* save temporary failure messages for posterity */
46621065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
46721065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
46821065Seric 
4694684Seric 		return (r);
4704684Seric 	}
4714684Seric }
4724796Seric /*
4734865Seric **  SMTPMESSAGE -- send message to server
4744796Seric **
4754796Seric **	Parameters:
4764796Seric **		f -- format
47710175Seric **		m -- the mailer to control formatting.
4784796Seric **		a, b, c -- parameters
4794796Seric **
4804796Seric **	Returns:
4814796Seric **		none.
4824796Seric **
4834796Seric **	Side Effects:
4844865Seric **		writes message to SmtpOut.
4854796Seric */
4864796Seric 
4874865Seric /*VARARGS1*/
48810175Seric smtpmessage(f, m, a, b, c)
4894796Seric 	char *f;
49010175Seric 	MAILER *m;
4914796Seric {
49214900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4937677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
49414900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
49511159Seric 	if (SmtpOut != NULL)
49614900Seric 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
4974796Seric }
4985182Seric 
4995182Seric # endif SMTP
500