14796Seric # include <ctype.h>
24684Seric # include <sysexits.h>
34865Seric # include "sendmail.h"
44684Seric 
55182Seric # ifndef SMTP
6*18573Smiriam SCCSID(@(#)usersmtp.c	4.11		04/04/85	(no SMTP));
75182Seric # else SMTP
84684Seric 
9*18573Smiriam SCCSID(@(#)usersmtp.c	4.11		04/04/85);
105182Seric 
119391Seric 
129391Seric 
134684Seric /*
149391Seric **  USERSMTP -- run SMTP protocol from the user end.
159391Seric **
169391Seric **	This protocol is described in RFC821.
179391Seric */
189391Seric 
199391Seric #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
209391Seric #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
219391Seric #define SMTPCLOSING	421			/* "Service Shutting Down" */
229391Seric 
2314900Seric char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
2410054Seric char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
2510054Seric FILE	*SmtpOut;			/* output file */
2610054Seric FILE	*SmtpIn;			/* input file */
2710054Seric int	SmtpPid;			/* pid of mailer */
2811159Seric 
2911159Seric /* following represents the state of the SMTP connection */
3011159Seric int	SmtpState;			/* connection state, see below */
3111159Seric 
3211159Seric #define SMTP_CLOSED	0		/* connection is closed */
3311159Seric #define SMTP_OPEN	1		/* connection is open for business */
3411159Seric #define SMTP_SSD	2		/* service shutting down */
359391Seric /*
364865Seric **  SMTPINIT -- initialize SMTP.
374684Seric **
384865Seric **	Opens the connection and sends the initial protocol.
394684Seric **
404684Seric **	Parameters:
414865Seric **		m -- mailer to create connection to.
424865Seric **		pvp -- pointer to parameter vector to pass to
434865Seric **			the mailer.
444684Seric **
454684Seric **	Returns:
464865Seric **		appropriate exit status -- EX_OK on success.
4714913Seric **		If not EX_OK, it should close the connection.
484684Seric **
494684Seric **	Side Effects:
504865Seric **		creates connection and sends initial protocol.
514684Seric */
524684Seric 
5314886Seric jmp_buf	CtxGreeting;
5414886Seric 
5510175Seric smtpinit(m, pvp)
564865Seric 	struct mailer *m;
574865Seric 	char **pvp;
584684Seric {
594865Seric 	register int r;
6014886Seric 	EVENT *gte;
614865Seric 	char buf[MAXNAME];
6214886Seric 	extern greettimeout();
634684Seric 
644865Seric 	/*
654865Seric 	**  Open the connection to the mailer.
664865Seric 	*/
674684Seric 
6811159Seric #ifdef DEBUG
6911159Seric 	if (SmtpState == SMTP_OPEN)
7011159Seric 		syserr("smtpinit: already open");
7111159Seric #endif DEBUG
7211159Seric 
736051Seric 	SmtpIn = SmtpOut = NULL;
7411159Seric 	SmtpState = SMTP_CLOSED;
7510175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
766051Seric 	if (SmtpPid < 0)
776051Seric 	{
786051Seric # ifdef DEBUG
797677Seric 		if (tTd(18, 1))
809391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
819391Seric 			   pvp[0], ExitStat, errno);
826051Seric # endif DEBUG
8315139Seric 		if (CurEnv->e_xfp != NULL)
8415139Seric 		{
8515139Seric 			extern char *errstring();
8615139Seric 
8715323Seric 			fprintf(CurEnv->e_xfp, "421 %s.%s... Deferred: %s\n",
8815139Seric 				pvp[1], m->m_name, errstring(errno));
8915139Seric 		}
906051Seric 		return (ExitStat);
916051Seric 	}
9211159Seric 	SmtpState = SMTP_OPEN;
934796Seric 
944865Seric 	/*
954865Seric 	**  Get the greeting message.
9614913Seric 	**	This should appear spontaneously.  Give it five minutes to
9714886Seric 	**	happen.
984865Seric 	*/
994797Seric 
10014886Seric 	if (setjmp(CtxGreeting) != 0)
10114913Seric 		goto tempfail;
10216141Seric 	gte = setevent((time_t) 300, greettimeout, 0);
10310175Seric 	r = reply(m);
10414886Seric 	clrevent(gte);
1058005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
10614913Seric 		goto tempfail;
1074684Seric 
1084865Seric 	/*
1094976Seric 	**  Send the HELO command.
1107963Seric 	**	My mother taught me to always introduce myself.
1114976Seric 	*/
1124976Seric 
11310175Seric 	smtpmessage("HELO %s", m, HostName);
11410175Seric 	r = reply(m);
1158005Seric 	if (r < 0)
11614913Seric 		goto tempfail;
1178005Seric 	else if (REPLYTYPE(r) == 5)
11814913Seric 		goto unavailable;
1197963Seric 	else if (REPLYTYPE(r) != 2)
12014913Seric 		goto tempfail;
1214976Seric 
1224976Seric 	/*
1239315Seric 	**  If this is expected to be another sendmail, send some internal
1249315Seric 	**  commands.
1259315Seric 	*/
1269315Seric 
12710688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1289315Seric 	{
1299315Seric 		/* tell it to be verbose */
13010175Seric 		smtpmessage("VERB", m);
13110175Seric 		r = reply(m);
1329315Seric 		if (r < 0)
13314913Seric 			goto tempfail;
1349315Seric 
1359315Seric 		/* tell it we will be sending one transaction only */
13610175Seric 		smtpmessage("ONEX", m);
13710175Seric 		r = reply(m);
1389315Seric 		if (r < 0)
13914913Seric 			goto tempfail;
1409315Seric 	}
1419315Seric 
1429315Seric 	/*
1434865Seric 	**  Send the MAIL command.
1444865Seric 	**	Designates the sender.
1454865Seric 	*/
1464796Seric 
14716156Seric 	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
1488436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
14910688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1508436Seric 	{
15110308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1528436Seric 	}
1538436Seric 	else
1548436Seric 	{
15510175Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, HostName,
15610308Seric 			buf[0] == '@' ? ',' : ':', buf);
1578436Seric 	}
15810175Seric 	r = reply(m);
1598005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
16014913Seric 		goto tempfail;
1617963Seric 	else if (r == 250)
1627963Seric 		return (EX_OK);
1637963Seric 	else if (r == 552)
16414913Seric 		goto unavailable;
16514913Seric 
16614913Seric 	/* protocol error -- close up */
16714913Seric 	smtpquit(m);
1687964Seric 	return (EX_PROTOCOL);
16914913Seric 
17014913Seric 	/* signal a temporary failure */
17114913Seric   tempfail:
17214913Seric 	smtpquit(m);
17316891Seric 	CurEnv->e_flags &= ~EF_FATALERRS;
17414913Seric 	return (EX_TEMPFAIL);
17514913Seric 
17614913Seric 	/* signal service unavailable */
17714913Seric   unavailable:
17814913Seric 	smtpquit(m);
17914913Seric 	return (EX_UNAVAILABLE);
1804684Seric }
18114886Seric 
18214886Seric 
18314886Seric static
18414886Seric greettimeout()
18514886Seric {
18614886Seric 	/* timeout reading the greeting message */
18714886Seric 	longjmp(CtxGreeting, 1);
18814886Seric }
1894684Seric /*
1904976Seric **  SMTPRCPT -- designate recipient.
1914797Seric **
1924797Seric **	Parameters:
1934865Seric **		to -- address of recipient.
19410175Seric **		m -- the mailer we are sending to.
1954797Seric **
1964797Seric **	Returns:
1974865Seric **		exit status corresponding to recipient status.
1984797Seric **
1994797Seric **	Side Effects:
2004865Seric **		Sends the mail via SMTP.
2014797Seric */
2024797Seric 
20310175Seric smtprcpt(to, m)
2044865Seric 	ADDRESS *to;
20510175Seric 	register MAILER *m;
2064797Seric {
2074797Seric 	register int r;
20810308Seric 	extern char *remotename();
2094797Seric 
21010308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
2114865Seric 
21210175Seric 	r = reply(m);
2138005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2144865Seric 		return (EX_TEMPFAIL);
2157963Seric 	else if (REPLYTYPE(r) == 2)
2167963Seric 		return (EX_OK);
2177964Seric 	else if (r == 550 || r == 551 || r == 553)
2187964Seric 		return (EX_NOUSER);
2197964Seric 	else if (r == 552 || r == 554)
2207964Seric 		return (EX_UNAVAILABLE);
2217964Seric 	return (EX_PROTOCOL);
2224797Seric }
2234797Seric /*
22410175Seric **  SMTPDATA -- send the data and clean up the transaction.
2254684Seric **
2264684Seric **	Parameters:
2274865Seric **		m -- mailer being sent to.
2286980Seric **		e -- the envelope for this message.
2294684Seric **
2304684Seric **	Returns:
2314976Seric **		exit status corresponding to DATA command.
2324684Seric **
2334684Seric **	Side Effects:
2344865Seric **		none.
2354684Seric */
2364684Seric 
23710175Seric smtpdata(m, e)
2384865Seric 	struct mailer *m;
2396980Seric 	register ENVELOPE *e;
2404684Seric {
2414684Seric 	register int r;
2424684Seric 
2434797Seric 	/*
2444797Seric 	**  Send the data.
24510175Seric 	**	First send the command and check that it is ok.
24610175Seric 	**	Then send the data.
24710175Seric 	**	Follow it up with a dot to terminate.
24810175Seric 	**	Finally get the results of the transaction.
2494797Seric 	*/
2504797Seric 
25110175Seric 	/* send the command and check ok to proceed */
25210175Seric 	smtpmessage("DATA", m);
25310175Seric 	r = reply(m);
2548005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2554797Seric 		return (EX_TEMPFAIL);
2567963Seric 	else if (r == 554)
2577963Seric 		return (EX_UNAVAILABLE);
2587963Seric 	else if (r != 354)
2597964Seric 		return (EX_PROTOCOL);
26010175Seric 
26110175Seric 	/* now output the actual message */
26210175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
26310175Seric 	putline("\n", SmtpOut, m);
26410175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
26510175Seric 
26610175Seric 	/* terminate the message */
26710328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
26810215Seric 	if (Verbose && !HoldErrs)
26910215Seric 		nmessage(Arpa_Info, ">>> .");
27010175Seric 
27110175Seric 	/* check for the results of the transaction */
27210175Seric 	r = reply(m);
2738005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2744797Seric 		return (EX_TEMPFAIL);
2757963Seric 	else if (r == 250)
2767963Seric 		return (EX_OK);
2777963Seric 	else if (r == 552 || r == 554)
2787963Seric 		return (EX_UNAVAILABLE);
2797964Seric 	return (EX_PROTOCOL);
2804684Seric }
2814684Seric /*
2824865Seric **  SMTPQUIT -- close the SMTP connection.
2834865Seric **
2844865Seric **	Parameters:
28515535Seric **		m -- a pointer to the mailer.
2864865Seric **
2874865Seric **	Returns:
2884865Seric **		none.
2894865Seric **
2904865Seric **	Side Effects:
2914865Seric **		sends the final protocol and closes the connection.
2924865Seric */
2934865Seric 
29415535Seric smtpquit(m)
29510175Seric 	register MAILER *m;
2964865Seric {
2979391Seric 	int i;
2984865Seric 
29910148Seric 	/* if the connection is already closed, don't bother */
30010148Seric 	if (SmtpIn == NULL)
30110148Seric 		return;
30210148Seric 
30310148Seric 	/* send the quit message if not a forced quit */
30411159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
3059391Seric 	{
30610175Seric 		smtpmessage("QUIT", m);
30710175Seric 		(void) reply(m);
30811159Seric 		if (SmtpState == SMTP_CLOSED)
30910159Seric 			return;
3109391Seric 	}
3119391Seric 
31210148Seric 	/* now actually close the connection */
3137229Seric 	(void) fclose(SmtpIn);
3147229Seric 	(void) fclose(SmtpOut);
31510148Seric 	SmtpIn = SmtpOut = NULL;
31611159Seric 	SmtpState = SMTP_CLOSED;
31710148Seric 
31810148Seric 	/* and pick up the zombie */
31915535Seric 	i = endmailer(SmtpPid, m->m_argv[0]);
3209391Seric 	if (i != EX_OK)
32115535Seric 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
3224865Seric }
3234865Seric /*
3244684Seric **  REPLY -- read arpanet reply
3254684Seric **
3264684Seric **	Parameters:
32710175Seric **		m -- the mailer we are reading the reply from.
3284684Seric **
3294684Seric **	Returns:
3304684Seric **		reply code it reads.
3314684Seric **
3324684Seric **	Side Effects:
3334684Seric **		flushes the mail file.
3344684Seric */
3354684Seric 
33610175Seric reply(m)
33710688Seric 	MAILER *m;
3384684Seric {
3394865Seric 	(void) fflush(SmtpOut);
3404684Seric 
3417677Seric 	if (tTd(18, 1))
3424796Seric 		printf("reply\n");
3434796Seric 
3447356Seric 	/*
3457356Seric 	**  Read the input line, being careful not to hang.
3467356Seric 	*/
3477356Seric 
3484684Seric 	for (;;)
3494684Seric 	{
3504684Seric 		register int r;
3517356Seric 		register char *p;
3524684Seric 
3537685Seric 		/* actually do the read */
3549547Seric 		if (CurEnv->e_xfp != NULL)
3559547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3567356Seric 
35710054Seric 		/* if we are in the process of closing just give the code */
35811159Seric 		if (SmtpState == SMTP_CLOSED)
35910054Seric 			return (SMTPCLOSING);
36010054Seric 
36110054Seric 		/* get the line from the other side */
36210054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
36310054Seric 		if (p == NULL)
36410131Seric 		{
36510148Seric 			extern char MsgBuf[];		/* err.c */
36610148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
36710148Seric 
36810148Seric 			message(Arpa_TSyserr, "reply: read error");
36910420Seric # ifdef DEBUG
37010420Seric 			/* if debugging, pause so we can see state */
37110420Seric 			if (tTd(18, 100))
37210420Seric 				pause();
37310420Seric # endif DEBUG
37410148Seric # ifdef LOG
375*18573Smiriam 			syslog(LOG_MAIL, "%s", &MsgBuf[4]);
37610148Seric # endif LOG
37711159Seric 			SmtpState = SMTP_CLOSED;
37815535Seric 			smtpquit(m);
37910054Seric 			return (-1);
38010131Seric 		}
38110054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
38210054Seric 
38314900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
38414900Seric 		{
38514900Seric 			/* serious error -- log the previous command */
38614900Seric 			if (SmtpMsgBuffer[0] != '\0')
38714900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
38814900Seric 			SmtpMsgBuffer[0] = '\0';
38914900Seric 
39014900Seric 			/* now log the message as from the other side */
39114900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
39214900Seric 		}
39314900Seric 
39414900Seric 		/* display the input for verbose mode */
3957229Seric 		if (Verbose && !HoldErrs)
3969391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
3977356Seric 
3987356Seric 		/* if continuation is required, we can go on */
3999391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
4004684Seric 			continue;
4017356Seric 
4027356Seric 		/* decode the reply code */
4039391Seric 		r = atoi(SmtpReplyBuffer);
4047356Seric 
4057356Seric 		/* extra semantics: 0xx codes are "informational" */
4064684Seric 		if (r < 100)
4074684Seric 			continue;
4087356Seric 
4099391Seric 		/* reply code 421 is "Service Shutting Down" */
41011159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
4119391Seric 		{
41210054Seric 			/* send the quit protocol */
41311159Seric 			SmtpState = SMTP_SSD;
41415535Seric 			smtpquit(m);
4159391Seric 		}
4169391Seric 
4174684Seric 		return (r);
4184684Seric 	}
4194684Seric }
4204796Seric /*
4214865Seric **  SMTPMESSAGE -- send message to server
4224796Seric **
4234796Seric **	Parameters:
4244796Seric **		f -- format
42510175Seric **		m -- the mailer to control formatting.
4264796Seric **		a, b, c -- parameters
4274796Seric **
4284796Seric **	Returns:
4294796Seric **		none.
4304796Seric **
4314796Seric **	Side Effects:
4324865Seric **		writes message to SmtpOut.
4334796Seric */
4344796Seric 
4354865Seric /*VARARGS1*/
43610175Seric smtpmessage(f, m, a, b, c)
4374796Seric 	char *f;
43810175Seric 	MAILER *m;
4394796Seric {
44014900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4417677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
44214900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
44311159Seric 	if (SmtpOut != NULL)
44414900Seric 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
4454796Seric }
4465182Seric 
4475182Seric # endif SMTP
448