14796Seric # include <ctype.h>
24684Seric # include <sysexits.h>
34865Seric # include "sendmail.h"
44684Seric 
55182Seric # ifndef SMTP
6*14900Seric SCCSID(@(#)usersmtp.c	4.3		09/05/83	(no SMTP));
75182Seric # else SMTP
84684Seric 
9*14900Seric SCCSID(@(#)usersmtp.c	4.3		09/05/83);
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 
23*14900Seric 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.
474684Seric **
484684Seric **	Side Effects:
494865Seric **		creates connection and sends initial protocol.
504684Seric */
514684Seric 
5214886Seric jmp_buf	CtxGreeting;
5314886Seric 
5410175Seric smtpinit(m, pvp)
554865Seric 	struct mailer *m;
564865Seric 	char **pvp;
574684Seric {
584865Seric 	register int r;
5914886Seric 	EVENT *gte;
604865Seric 	char buf[MAXNAME];
6114886Seric 	extern greettimeout();
624684Seric 
634865Seric 	/*
644865Seric 	**  Open the connection to the mailer.
654865Seric 	*/
664684Seric 
6711159Seric #ifdef DEBUG
6811159Seric 	if (SmtpState == SMTP_OPEN)
6911159Seric 		syserr("smtpinit: already open");
7011159Seric #endif DEBUG
7111159Seric 
726051Seric 	SmtpIn = SmtpOut = NULL;
7311159Seric 	SmtpState = SMTP_CLOSED;
7410175Seric 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
756051Seric 	if (SmtpPid < 0)
766051Seric 	{
776051Seric # ifdef DEBUG
787677Seric 		if (tTd(18, 1))
799391Seric 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
809391Seric 			   pvp[0], ExitStat, errno);
816051Seric # endif DEBUG
826051Seric 		return (ExitStat);
836051Seric 	}
8411159Seric 	SmtpState = SMTP_OPEN;
854796Seric 
864865Seric 	/*
874865Seric 	**  Get the greeting message.
8814886Seric 	**	This should appear spontaneously.  Give it two minutes to
8914886Seric 	**	happen.
904865Seric 	*/
914797Seric 
9214886Seric 	if (setjmp(CtxGreeting) != 0)
9314886Seric 		return (EX_TEMPFAIL);
9414886Seric 	gte = setevent(120, greettimeout, 0);
9510175Seric 	r = reply(m);
9614886Seric 	clrevent(gte);
978005Seric 	if (r < 0 || REPLYTYPE(r) != 2)
984865Seric 		return (EX_TEMPFAIL);
994684Seric 
1004865Seric 	/*
1014976Seric 	**  Send the HELO command.
1027963Seric 	**	My mother taught me to always introduce myself.
1034976Seric 	*/
1044976Seric 
10510175Seric 	smtpmessage("HELO %s", m, HostName);
10610175Seric 	r = reply(m);
1078005Seric 	if (r < 0)
1088005Seric 		return (EX_TEMPFAIL);
1098005Seric 	else if (REPLYTYPE(r) == 5)
1104976Seric 		return (EX_UNAVAILABLE);
1117963Seric 	else if (REPLYTYPE(r) != 2)
1124976Seric 		return (EX_TEMPFAIL);
1134976Seric 
1144976Seric 	/*
1159315Seric 	**  If this is expected to be another sendmail, send some internal
1169315Seric 	**  commands.
1179315Seric 	*/
1189315Seric 
11910688Seric 	if (bitnset(M_INTERNAL, m->m_flags))
1209315Seric 	{
1219315Seric 		/* tell it to be verbose */
12210175Seric 		smtpmessage("VERB", m);
12310175Seric 		r = reply(m);
1249315Seric 		if (r < 0)
1259315Seric 			return (EX_TEMPFAIL);
1269315Seric 
1279315Seric 		/* tell it we will be sending one transaction only */
12810175Seric 		smtpmessage("ONEX", m);
12910175Seric 		r = reply(m);
1309315Seric 		if (r < 0)
1319315Seric 			return (EX_TEMPFAIL);
1329315Seric 	}
1339315Seric 
1349315Seric 	/*
1354865Seric 	**  Send the MAIL command.
1364865Seric 	**	Designates the sender.
1374865Seric 	*/
1384796Seric 
1396980Seric 	expand("$g", buf, &buf[sizeof buf - 1], CurEnv);
1408436Seric 	if (CurEnv->e_from.q_mailer == LocalMailer ||
14110688Seric 	    !bitnset(M_FROMPATH, m->m_flags))
1428436Seric 	{
14310308Seric 		smtpmessage("MAIL From:<%s>", m, buf);
1448436Seric 	}
1458436Seric 	else
1468436Seric 	{
14710175Seric 		smtpmessage("MAIL From:<@%s%c%s>", m, HostName,
14810308Seric 			buf[0] == '@' ? ',' : ':', buf);
1498436Seric 	}
15010175Seric 	r = reply(m);
1518005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1524865Seric 		return (EX_TEMPFAIL);
1537963Seric 	else if (r == 250)
1547963Seric 		return (EX_OK);
1557963Seric 	else if (r == 552)
1567963Seric 		return (EX_UNAVAILABLE);
1577964Seric 	return (EX_PROTOCOL);
1584684Seric }
15914886Seric 
16014886Seric 
16114886Seric static
16214886Seric greettimeout()
16314886Seric {
16414886Seric 	/* timeout reading the greeting message */
16514886Seric 	longjmp(CtxGreeting, 1);
16614886Seric }
1674684Seric /*
1684976Seric **  SMTPRCPT -- designate recipient.
1694797Seric **
1704797Seric **	Parameters:
1714865Seric **		to -- address of recipient.
17210175Seric **		m -- the mailer we are sending to.
1734797Seric **
1744797Seric **	Returns:
1754865Seric **		exit status corresponding to recipient status.
1764797Seric **
1774797Seric **	Side Effects:
1784865Seric **		Sends the mail via SMTP.
1794797Seric */
1804797Seric 
18110175Seric smtprcpt(to, m)
1824865Seric 	ADDRESS *to;
18310175Seric 	register MAILER *m;
1844797Seric {
1854797Seric 	register int r;
18610308Seric 	extern char *remotename();
1874797Seric 
18810308Seric 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
1894865Seric 
19010175Seric 	r = reply(m);
1918005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
1924865Seric 		return (EX_TEMPFAIL);
1937963Seric 	else if (REPLYTYPE(r) == 2)
1947963Seric 		return (EX_OK);
1957964Seric 	else if (r == 550 || r == 551 || r == 553)
1967964Seric 		return (EX_NOUSER);
1977964Seric 	else if (r == 552 || r == 554)
1987964Seric 		return (EX_UNAVAILABLE);
1997964Seric 	return (EX_PROTOCOL);
2004797Seric }
2014797Seric /*
20210175Seric **  SMTPDATA -- send the data and clean up the transaction.
2034684Seric **
2044684Seric **	Parameters:
2054865Seric **		m -- mailer being sent to.
2066980Seric **		e -- the envelope for this message.
2074684Seric **
2084684Seric **	Returns:
2094976Seric **		exit status corresponding to DATA command.
2104684Seric **
2114684Seric **	Side Effects:
2124865Seric **		none.
2134684Seric */
2144684Seric 
21510175Seric smtpdata(m, e)
2164865Seric 	struct mailer *m;
2176980Seric 	register ENVELOPE *e;
2184684Seric {
2194684Seric 	register int r;
2204684Seric 
2214797Seric 	/*
2224797Seric 	**  Send the data.
22310175Seric 	**	First send the command and check that it is ok.
22410175Seric 	**	Then send the data.
22510175Seric 	**	Follow it up with a dot to terminate.
22610175Seric 	**	Finally get the results of the transaction.
2274797Seric 	*/
2284797Seric 
22910175Seric 	/* send the command and check ok to proceed */
23010175Seric 	smtpmessage("DATA", m);
23110175Seric 	r = reply(m);
2328005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2334797Seric 		return (EX_TEMPFAIL);
2347963Seric 	else if (r == 554)
2357963Seric 		return (EX_UNAVAILABLE);
2367963Seric 	else if (r != 354)
2377964Seric 		return (EX_PROTOCOL);
23810175Seric 
23910175Seric 	/* now output the actual message */
24010175Seric 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
24110175Seric 	putline("\n", SmtpOut, m);
24210175Seric 	(*e->e_putbody)(SmtpOut, m, CurEnv);
24310175Seric 
24410175Seric 	/* terminate the message */
24510328Seric 	fprintf(SmtpOut, ".%s", m->m_eol);
24610215Seric 	if (Verbose && !HoldErrs)
24710215Seric 		nmessage(Arpa_Info, ">>> .");
24810175Seric 
24910175Seric 	/* check for the results of the transaction */
25010175Seric 	r = reply(m);
2518005Seric 	if (r < 0 || REPLYTYPE(r) == 4)
2524797Seric 		return (EX_TEMPFAIL);
2537963Seric 	else if (r == 250)
2547963Seric 		return (EX_OK);
2557963Seric 	else if (r == 552 || r == 554)
2567963Seric 		return (EX_UNAVAILABLE);
2577964Seric 	return (EX_PROTOCOL);
2584684Seric }
2594684Seric /*
2604865Seric **  SMTPQUIT -- close the SMTP connection.
2614865Seric **
2624865Seric **	Parameters:
2634865Seric **		name -- name of mailer we are quitting.
2644865Seric **
2654865Seric **	Returns:
2664865Seric **		none.
2674865Seric **
2684865Seric **	Side Effects:
2694865Seric **		sends the final protocol and closes the connection.
2704865Seric */
2714865Seric 
27210175Seric smtpquit(name, m)
2734865Seric 	char *name;
27410175Seric 	register MAILER *m;
2754865Seric {
2769391Seric 	int i;
2774865Seric 
27810148Seric 	/* if the connection is already closed, don't bother */
27910148Seric 	if (SmtpIn == NULL)
28010148Seric 		return;
28110148Seric 
28210148Seric 	/* send the quit message if not a forced quit */
28311159Seric 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
2849391Seric 	{
28510175Seric 		smtpmessage("QUIT", m);
28610175Seric 		(void) reply(m);
28711159Seric 		if (SmtpState == SMTP_CLOSED)
28810159Seric 			return;
2899391Seric 	}
2909391Seric 
29110148Seric 	/* now actually close the connection */
2927229Seric 	(void) fclose(SmtpIn);
2937229Seric 	(void) fclose(SmtpOut);
29410148Seric 	SmtpIn = SmtpOut = NULL;
29511159Seric 	SmtpState = SMTP_CLOSED;
29610148Seric 
29710148Seric 	/* and pick up the zombie */
2987229Seric 	i = endmailer(SmtpPid, name);
2999391Seric 	if (i != EX_OK)
3009391Seric 		syserr("smtpquit %s: stat %d", name, i);
3014865Seric }
3024865Seric /*
3034684Seric **  REPLY -- read arpanet reply
3044684Seric **
3054684Seric **	Parameters:
30610175Seric **		m -- the mailer we are reading the reply from.
3074684Seric **
3084684Seric **	Returns:
3094684Seric **		reply code it reads.
3104684Seric **
3114684Seric **	Side Effects:
3124684Seric **		flushes the mail file.
3134684Seric */
3144684Seric 
31510175Seric reply(m)
31610688Seric 	MAILER *m;
3174684Seric {
3184865Seric 	(void) fflush(SmtpOut);
3194684Seric 
3207677Seric 	if (tTd(18, 1))
3214796Seric 		printf("reply\n");
3224796Seric 
3237356Seric 	/*
3247356Seric 	**  Read the input line, being careful not to hang.
3257356Seric 	*/
3267356Seric 
3274684Seric 	for (;;)
3284684Seric 	{
3294684Seric 		register int r;
3307356Seric 		register char *p;
3314684Seric 
3327685Seric 		/* actually do the read */
3339547Seric 		if (CurEnv->e_xfp != NULL)
3349547Seric 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
3357356Seric 
33610054Seric 		/* if we are in the process of closing just give the code */
33711159Seric 		if (SmtpState == SMTP_CLOSED)
33810054Seric 			return (SMTPCLOSING);
33910054Seric 
34010054Seric 		/* get the line from the other side */
34110054Seric 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
34210054Seric 		if (p == NULL)
34310131Seric 		{
34410148Seric 			extern char MsgBuf[];		/* err.c */
34510148Seric 			extern char Arpa_TSyserr[];	/* conf.c */
34610148Seric 
34710148Seric 			message(Arpa_TSyserr, "reply: read error");
34810420Seric # ifdef DEBUG
34910420Seric 			/* if debugging, pause so we can see state */
35010420Seric 			if (tTd(18, 100))
35110420Seric 				pause();
35210420Seric # endif DEBUG
35310148Seric # ifdef LOG
35410148Seric 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
35510148Seric # endif LOG
35611159Seric 			SmtpState = SMTP_CLOSED;
35710175Seric 			smtpquit("reply error", m);
35810054Seric 			return (-1);
35910131Seric 		}
36010054Seric 		fixcrlf(SmtpReplyBuffer, TRUE);
36110054Seric 
362*14900Seric 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
363*14900Seric 		{
364*14900Seric 			/* serious error -- log the previous command */
365*14900Seric 			if (SmtpMsgBuffer[0] != '\0')
366*14900Seric 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
367*14900Seric 			SmtpMsgBuffer[0] = '\0';
368*14900Seric 
369*14900Seric 			/* now log the message as from the other side */
370*14900Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
371*14900Seric 		}
372*14900Seric 
373*14900Seric 		/* display the input for verbose mode */
3747229Seric 		if (Verbose && !HoldErrs)
3759391Seric 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
3767356Seric 
3777356Seric 		/* if continuation is required, we can go on */
3789391Seric 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
3794684Seric 			continue;
3807356Seric 
3817356Seric 		/* decode the reply code */
3829391Seric 		r = atoi(SmtpReplyBuffer);
3837356Seric 
3847356Seric 		/* extra semantics: 0xx codes are "informational" */
3854684Seric 		if (r < 100)
3864684Seric 			continue;
3877356Seric 
3889391Seric 		/* reply code 421 is "Service Shutting Down" */
38911159Seric 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
3909391Seric 		{
39110054Seric 			/* send the quit protocol */
39211159Seric 			SmtpState = SMTP_SSD;
39310175Seric 			smtpquit("SMTP Shutdown", m);
3949391Seric 		}
3959391Seric 
3964684Seric 		return (r);
3974684Seric 	}
3984684Seric }
3994796Seric /*
4004865Seric **  SMTPMESSAGE -- send message to server
4014796Seric **
4024796Seric **	Parameters:
4034796Seric **		f -- format
40410175Seric **		m -- the mailer to control formatting.
4054796Seric **		a, b, c -- parameters
4064796Seric **
4074796Seric **	Returns:
4084796Seric **		none.
4094796Seric **
4104796Seric **	Side Effects:
4114865Seric **		writes message to SmtpOut.
4124796Seric */
4134796Seric 
4144865Seric /*VARARGS1*/
41510175Seric smtpmessage(f, m, a, b, c)
4164796Seric 	char *f;
41710175Seric 	MAILER *m;
4184796Seric {
419*14900Seric 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
4207677Seric 	if (tTd(18, 1) || (Verbose && !HoldErrs))
421*14900Seric 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
42211159Seric 	if (SmtpOut != NULL)
423*14900Seric 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
4244796Seric }
4255182Seric 
4265182Seric # endif SMTP
427