122716Sdist /*
2*34921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
633731Sbostic  * Redistribution and use in source and binary forms are permitted
7*34921Sbostic  * provided that the above copyright notice and this paragraph are
8*34921Sbostic  * duplicated in all such forms and that any documentation,
9*34921Sbostic  * advertising materials, and other materials related to such
10*34921Sbostic  * distribution and use acknowledge that the software was developed
11*34921Sbostic  * by the University of California, Berkeley.  The name of the
12*34921Sbostic  * University may not be used to endorse or promote products derived
13*34921Sbostic  * from this software without specific prior written permission.
14*34921Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15*34921Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16*34921Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1733731Sbostic  */
1822716Sdist 
1933731Sbostic # include "sendmail.h"
2022716Sdist 
2133731Sbostic #ifndef lint
2233731Sbostic #ifdef SMTP
23*34921Sbostic static char sccsid[] = "@(#)usersmtp.c	5.10 (Berkeley) 06/30/88 (with SMTP)";
2433731Sbostic #else
25*34921Sbostic static char sccsid[] = "@(#)usersmtp.c	5.10 (Berkeley) 06/30/88 (without SMTP)";
2633731Sbostic #endif
2733731Sbostic #endif /* not lint */
2833731Sbostic 
294684Seric # include <sysexits.h>
3021065Seric # include <errno.h>
314684Seric 
3233731Sbostic # ifdef SMTP
334684Seric 
344684Seric /*
359391Seric **  USERSMTP -- run SMTP protocol from the user end.
369391Seric **
379391Seric **	This protocol is described in RFC821.
389391Seric */
399391Seric 
409391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
419391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
429391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
439391Seric 
4414900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
4510054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
4621065Seric char	SmtpError[MAXLINE] = "";	/* save failure error messages */
4710054Seric FILE	*SmtpOut;			/* output file */
4810054Seric FILE	*SmtpIn;			/* input file */
4910054Seric int	SmtpPid;			/* pid of mailer */
5011159Seric 
5111159Seric /* following represents the state of the SMTP connection */
5211159Seric int	SmtpState;			/* connection state, see below */
5311159Seric 
5411159Seric #define SMTP_CLOSED	0		/* connection is closed */
5511159Seric #define SMTP_OPEN	1		/* connection is open for business */
5611159Seric #define SMTP_SSD	2		/* service shutting down */
579391Seric /*
584865Seric **  SMTPINIT -- initialize SMTP.
594684Seric **
604865Seric **	Opens the connection and sends the initial protocol.
614684Seric **
624684Seric **	Parameters:
634865Seric **		m -- mailer to create connection to.
644865Seric **		pvp -- pointer to parameter vector to pass to
654865Seric **			the mailer.
664684Seric **
674684Seric **	Returns:
684865Seric **		appropriate exit status -- EX_OK on success.
6914913Seric **		If not EX_OK, it should close the connection.
704684Seric **
714684Seric **	Side Effects:
724865Seric **		creates connection and sends initial protocol.
734684Seric */
744684Seric 
7514886Seric jmp_buf	CtxGreeting;
7614886Seric 
7710175Seric smtpinit(m, pvp)
784865Seric 	struct mailer *m;
794865Seric 	char **pvp;
804684Seric {
814865Seric 	register int r;
8214886Seric 	EVENT *gte;
834865Seric 	char buf[MAXNAME];
8414886Seric 	extern greettimeout();
854684Seric 
864865Seric 	/*
874865Seric 	**  Open the connection to the mailer.
884865Seric 	*/
894684Seric 
9011159Seric #ifdef DEBUG
9111159Seric 	if (SmtpState == SMTP_OPEN)
9211159Seric 		syserr("smtpinit: already open");
9311159Seric #endif DEBUG
9411159Seric 
956051Seric 	SmtpIn = SmtpOut = NULL;
9611159Seric 	SmtpState = SMTP_CLOSED;
9721065Seric 	SmtpError[0] = '\0';
9824944Seric 	SmtpPhase = "user open";
9910175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
1006051Seric 	if (SmtpPid < 0)
1016051Seric 	{
1026051Seric # ifdef DEBUG
1037677Seric 		if (tTd(18, 1))
1049391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
1059391Seric 			   pvp[0], ExitStat, errno);
1066051Seric # endif DEBUG
10715139Seric 		if (CurEnv->e_xfp != NULL)
10815139Seric 		{
10921065Seric 			register char *p;
11015139Seric 			extern char *errstring();
11121065Seric 			extern char *statstring();
11215139Seric 
11321065Seric 			if (errno == 0)
11421065Seric 			{
11521065Seric 				p = statstring(ExitStat);
11621065Seric 				fprintf(CurEnv->e_xfp,
11721065Seric 					"%.3s %s.%s... %s\n",
11821065Seric 					p, pvp[1], m->m_name, p);
11921065Seric 			}
12021065Seric 			else
12121065Seric 			{
12221065Seric 				fprintf(CurEnv->e_xfp,
12321065Seric 					"421 %s.%s... Deferred: %s\n",
12421065Seric 					pvp[1], m->m_name, errstring(errno));
12521065Seric 			}
12615139Seric 		}
1276051Seric 		return (ExitStat);
1286051Seric 	}
12911159Seric 	SmtpState = SMTP_OPEN;
1304796Seric 
1314865Seric 	/*
1324865Seric 	**  Get the greeting message.
13314913Seric 	**	This should appear spontaneously.  Give it five minutes to
13414886Seric 	**	happen.
1354865Seric 	*/
1364797Seric 
13714886Seric 	if (setjmp(CtxGreeting) != 0)
13814913Seric 		goto tempfail;
13916141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
14024944Seric 	SmtpPhase = "greeting wait";
14110175Seric 	r = reply(m);
14214886Seric 	clrevent(gte);
1438005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
14414913Seric 		goto tempfail;
1454684Seric 
1464865Seric 	/*
1474976Seric 	**  Send the HELO command.
1487963Seric 	**	My mother taught me to always introduce myself.
1494976Seric 	*/
1504976Seric 
15125050Seric 	smtpmessage("HELO %s", m, MyHostName);
15224944Seric 	SmtpPhase = "HELO wait";
15310175Seric 	r = reply(m);
1548005Seric 	if (r < 0)
15514913Seric 		goto tempfail;
1568005Seric 	else if (REPLYTYPE(r) == 5)
15714913Seric 		goto unavailable;
1587963Seric 	else if (REPLYTYPE(r) != 2)
15914913Seric 		goto tempfail;
1604976Seric 
1614976Seric 	/*
1629315Seric 	**  If this is expected to be another sendmail, send some internal
1639315Seric 	**  commands.
1649315Seric 	*/
1659315Seric 
16610688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1679315Seric 	{
1689315Seric 		/* tell it to be verbose */
16910175Seric 		smtpmessage("VERB", m);
17010175Seric 		r = reply(m);
1719315Seric 		if (r < 0)
17214913Seric 			goto tempfail;
1739315Seric 
1749315Seric 		/* tell it we will be sending one transaction only */
17510175Seric 		smtpmessage("ONEX", m);
17610175Seric 		r = reply(m);
1779315Seric 		if (r < 0)
17814913Seric 			goto tempfail;
1799315Seric 	}
1809315Seric 
1819315Seric 	/*
1824865Seric 	**  Send the MAIL command.
1834865Seric 	**	Designates the sender.
1844865Seric 	*/
1854796Seric 
18616156Seric 	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
1878436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
18810688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1898436Seric 	{
19010308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1918436Seric 	}
1928436Seric 	else
1938436Seric 	{
19425050Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
19510308Seric 			buf[0] == '@' ? ',' : ':', buf);
1968436Seric 	}
19724944Seric 	SmtpPhase = "MAIL wait";
19810175Seric 	r = reply(m);
1998005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
20014913Seric 		goto tempfail;
2017963Seric 	else if (r == 250)
2027963Seric 		return (EX_OK);
2037963Seric 	else if (r == 552)
20414913Seric 		goto unavailable;
20514913Seric 
20614913Seric 	/* protocol error -- close up */
20714913Seric 	smtpquit(m);
2087964Seric 	return (EX_PROTOCOL);
20914913Seric 
21014913Seric 	/* signal a temporary failure */
21114913Seric   tempfail:
21214913Seric 	smtpquit(m);
21314913Seric 	return (EX_TEMPFAIL);
21414913Seric 
21514913Seric 	/* signal service unavailable */
21614913Seric   unavailable:
21714913Seric 	smtpquit(m);
21814913Seric 	return (EX_UNAVAILABLE);
2194684Seric }
22014886Seric 
22114886Seric 
22214886Seric static
22314886Seric greettimeout()
22414886Seric {
22514886Seric 	/* timeout reading the greeting message */
22614886Seric 	longjmp(CtxGreeting, 1);
22714886Seric }
2284684Seric /*
2294976Seric **  SMTPRCPT -- designate recipient.
2304797Seric **
2314797Seric **	Parameters:
2324865Seric **		to -- address of recipient.
23310175Seric **		m -- the mailer we are sending to.
2344797Seric **
2354797Seric **	Returns:
2364865Seric **		exit status corresponding to recipient status.
2374797Seric **
2384797Seric **	Side Effects:
2394865Seric **		Sends the mail via SMTP.
2404797Seric */
2414797Seric 
24210175Seric smtprcpt(to, m)
2434865Seric 	ADDRESS *to;
24410175Seric 	register MAILER *m;
2454797Seric {
2464797Seric 	register int r;
24710308Seric 	extern char *remotename();
2484797Seric 
24910308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
2504865Seric 
25124944Seric 	SmtpPhase = "RCPT wait";
25210175Seric 	r = reply(m);
2538005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2544865Seric 		return (EX_TEMPFAIL);
2557963Seric 	else if (REPLYTYPE(r) == 2)
2567963Seric 		return (EX_OK);
2577964Seric 	else if (r == 550 || r == 551 || r == 553)
2587964Seric 		return (EX_NOUSER);
2597964Seric 	else if (r == 552 || r == 554)
2607964Seric 		return (EX_UNAVAILABLE);
2617964Seric 	return (EX_PROTOCOL);
2624797Seric }
2634797Seric /*
26410175Seric **  SMTPDATA -- send the data and clean up the transaction.
2654684Seric **
2664684Seric **	Parameters:
2674865Seric **		m -- mailer being sent to.
2686980Seric **		e -- the envelope for this message.
2694684Seric **
2704684Seric **	Returns:
2714976Seric **		exit status corresponding to DATA command.
2724684Seric **
2734684Seric **	Side Effects:
2744865Seric **		none.
2754684Seric */
2764684Seric 
27710175Seric smtpdata(m, e)
2784865Seric 	struct mailer *m;
2796980Seric 	register ENVELOPE *e;
2804684Seric {
2814684Seric 	register int r;
2824684Seric 
2834797Seric 	/*
2844797Seric 	**  Send the data.
28510175Seric 	**	First send the command and check that it is ok.
28610175Seric 	**	Then send the data.
28710175Seric 	**	Follow it up with a dot to terminate.
28810175Seric 	**	Finally get the results of the transaction.
2894797Seric 	*/
2904797Seric 
29110175Seric 	/* send the command and check ok to proceed */
29210175Seric 	smtpmessage("DATA", m);
29324944Seric 	SmtpPhase = "DATA wait";
29410175Seric 	r = reply(m);
2958005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2964797Seric 		return (EX_TEMPFAIL);
2977963Seric 	else if (r == 554)
2987963Seric 		return (EX_UNAVAILABLE);
2997963Seric 	else if (r != 354)
3007964Seric 		return (EX_PROTOCOL);
30110175Seric 
30210175Seric 	/* now output the actual message */
30310175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
30410175Seric 	putline("\n", SmtpOut, m);
30510175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
30610175Seric 
30710175Seric 	/* terminate the message */
30810328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
30910215Seric 	if (Verbose && !HoldErrs)
31010215Seric 		nmessage(Arpa_Info, ">>> .");
31110175Seric 
31210175Seric 	/* check for the results of the transaction */
31324944Seric 	SmtpPhase = "result wait";
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 
33615535Seric smtpquit(m)
33710175Seric 	register MAILER *m;
3384865Seric {
3399391Seric 	int i;
3404865Seric 
34110148Seric 	/* if the connection is already closed, don't bother */
34210148Seric 	if (SmtpIn == NULL)
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 
35410148Seric 	/* now actually close the connection */
3557229Seric 	(void) fclose(SmtpIn);
3567229Seric 	(void) fclose(SmtpOut);
35710148Seric 	SmtpIn = SmtpOut = NULL;
35811159Seric 	SmtpState = SMTP_CLOSED;
35910148Seric 
36010148Seric 	/* and pick up the zombie */
36115535Seric 	i = endmailer(SmtpPid, m->m_argv[0]);
3629391Seric 	if (i != EX_OK)
36315535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3644865Seric }
3654865Seric /*
3664684Seric **  REPLY -- read arpanet reply
3674684Seric **
3684684Seric **	Parameters:
36910175Seric **		m -- the mailer we are reading the reply from.
3704684Seric **
3714684Seric **	Returns:
3724684Seric **		reply code it reads.
3734684Seric **
3744684Seric **	Side Effects:
3754684Seric **		flushes the mail file.
3764684Seric */
3774684Seric 
37810175Seric reply(m)
37910688Seric 	MAILER *m;
3804684Seric {
3814865Seric 	(void) fflush(SmtpOut);
3824684Seric 
3837677Seric 	if (tTd(18, 1))
3844796Seric 		printf("reply\n");
3854796Seric 
3867356Seric 	/*
3877356Seric 	**  Read the input line, being careful not to hang.
3887356Seric 	*/
3897356Seric 
3904684Seric 	for (;;)
3914684Seric 	{
3924684Seric 		register int r;
3937356Seric 		register char *p;
3944684Seric 
3957685Seric 		/* actually do the read */
3969547Seric 		if (CurEnv->e_xfp != NULL)
3979547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3987356Seric 
39910054Seric 		/* if we are in the process of closing just give the code */
40011159Seric 		if (SmtpState == SMTP_CLOSED)
40110054Seric 			return (SMTPCLOSING);
40210054Seric 
40310054Seric 		/* get the line from the other side */
40410054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
40510054Seric 		if (p == NULL)
40610131Seric 		{
40710148Seric 			extern char MsgBuf[];		/* err.c */
40810148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
40910148Seric 
41021065Seric 			/* if the remote end closed early, fake an error */
41121065Seric 			if (errno == 0)
41221065Seric # ifdef ECONNRESET
41321065Seric 				errno = ECONNRESET;
41421065Seric # else ECONNRESET
41521065Seric 				errno = EPIPE;
41621065Seric # endif ECONNRESET
41721065Seric 
41810148Seric 			message(Arpa_TSyserr, "reply: read error");
41910420Seric # ifdef DEBUG
42010420Seric 			/* if debugging, pause so we can see state */
42110420Seric 			if (tTd(18, 100))
42210420Seric 				pause();
42310420Seric # endif DEBUG
42410148Seric # ifdef LOG
42524945Seric 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
42610148Seric # endif LOG
42711159Seric 			SmtpState = SMTP_CLOSED;
42815535Seric 			smtpquit(m);
42910054Seric 			return (-1);
43010131Seric 		}
43110054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
43210054Seric 
43314900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
43414900Seric 		{
43514900Seric 			/* serious error -- log the previous command */
43614900Seric 			if (SmtpMsgBuffer[0] != '\0')
43714900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
43814900Seric 			SmtpMsgBuffer[0] = '\0';
43914900Seric 
44014900Seric 			/* now log the message as from the other side */
44114900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
44214900Seric 		}
44314900Seric 
44414900Seric 		/* display the input for verbose mode */
4457229Seric 		if (Verbose && !HoldErrs)
4469391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
4477356Seric 
4487356Seric 		/* if continuation is required, we can go on */
4499391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4504684Seric 			continue;
4517356Seric 
4527356Seric 		/* decode the reply code */
4539391Seric 		r = atoi(SmtpReplyBuffer);
4547356Seric 
4557356Seric 		/* extra semantics: 0xx codes are "informational" */
4564684Seric 		if (r < 100)
4574684Seric 			continue;
4587356Seric 
4599391Seric 		/* reply code 421 is "Service Shutting Down" */
46011159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
4619391Seric 		{
46210054Seric 			/* send the quit protocol */
46311159Seric 			SmtpState = SMTP_SSD;
46415535Seric 			smtpquit(m);
4659391Seric 		}
4669391Seric 
46721065Seric 		/* save temporary failure messages for posterity */
46821065Seric 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
46921065Seric 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
47021065Seric 
4714684Seric 		return (r);
4724684Seric 	}
4734684Seric }
4744796Seric /*
4754865Seric **  SMTPMESSAGE -- send message to server
4764796Seric **
4774796Seric **	Parameters:
4784796Seric **		f -- format
47910175Seric **		m -- the mailer to control formatting.
4804796Seric **		a, b, c -- parameters
4814796Seric **
4824796Seric **	Returns:
4834796Seric **		none.
4844796Seric **
4854796Seric **	Side Effects:
4864865Seric **		writes message to SmtpOut.
4874796Seric */
4884796Seric 
4894865Seric /*VARARGS1*/
49010175Seric smtpmessage(f, m, a, b, c)
4914796Seric 	char *f;
49210175Seric 	MAILER *m;
4934796Seric {
49414900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4957677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
49614900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
49711159Seric 	if (SmtpOut != NULL)
49814900Seric 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
4994796Seric }
5005182Seric 
5015182Seric # endif SMTP
502