1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  *
12  *  Sendmail
13  *  Copyright (c) 1983  Eric P. Allman
14  *  Berkeley, California
15  */
16 
17 # include "sendmail.h"
18 
19 #ifndef lint
20 #ifdef SMTP
21 static char sccsid[] = "@(#)usersmtp.c	5.9 (Berkeley) 03/13/88 (with SMTP)";
22 #else
23 static char sccsid[] = "@(#)usersmtp.c	5.9 (Berkeley) 03/13/88 (without SMTP)";
24 #endif
25 #endif /* not lint */
26 
27 # include <sysexits.h>
28 # include <errno.h>
29 
30 # ifdef SMTP
31 
32 /*
33 **  USERSMTP -- run SMTP protocol from the user end.
34 **
35 **	This protocol is described in RFC821.
36 */
37 
38 #define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
39 #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
40 #define SMTPCLOSING	421			/* "Service Shutting Down" */
41 
42 char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
43 char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
44 char	SmtpError[MAXLINE] = "";	/* save failure error messages */
45 FILE	*SmtpOut;			/* output file */
46 FILE	*SmtpIn;			/* input file */
47 int	SmtpPid;			/* pid of mailer */
48 
49 /* following represents the state of the SMTP connection */
50 int	SmtpState;			/* connection state, see below */
51 
52 #define SMTP_CLOSED	0		/* connection is closed */
53 #define SMTP_OPEN	1		/* connection is open for business */
54 #define SMTP_SSD	2		/* service shutting down */
55 /*
56 **  SMTPINIT -- initialize SMTP.
57 **
58 **	Opens the connection and sends the initial protocol.
59 **
60 **	Parameters:
61 **		m -- mailer to create connection to.
62 **		pvp -- pointer to parameter vector to pass to
63 **			the mailer.
64 **
65 **	Returns:
66 **		appropriate exit status -- EX_OK on success.
67 **		If not EX_OK, it should close the connection.
68 **
69 **	Side Effects:
70 **		creates connection and sends initial protocol.
71 */
72 
73 jmp_buf	CtxGreeting;
74 
75 smtpinit(m, pvp)
76 	struct mailer *m;
77 	char **pvp;
78 {
79 	register int r;
80 	EVENT *gte;
81 	char buf[MAXNAME];
82 	extern greettimeout();
83 
84 	/*
85 	**  Open the connection to the mailer.
86 	*/
87 
88 #ifdef DEBUG
89 	if (SmtpState == SMTP_OPEN)
90 		syserr("smtpinit: already open");
91 #endif DEBUG
92 
93 	SmtpIn = SmtpOut = NULL;
94 	SmtpState = SMTP_CLOSED;
95 	SmtpError[0] = '\0';
96 	SmtpPhase = "user open";
97 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
98 	if (SmtpPid < 0)
99 	{
100 # ifdef DEBUG
101 		if (tTd(18, 1))
102 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
103 			   pvp[0], ExitStat, errno);
104 # endif DEBUG
105 		if (CurEnv->e_xfp != NULL)
106 		{
107 			register char *p;
108 			extern char *errstring();
109 			extern char *statstring();
110 
111 			if (errno == 0)
112 			{
113 				p = statstring(ExitStat);
114 				fprintf(CurEnv->e_xfp,
115 					"%.3s %s.%s... %s\n",
116 					p, pvp[1], m->m_name, p);
117 			}
118 			else
119 			{
120 				fprintf(CurEnv->e_xfp,
121 					"421 %s.%s... Deferred: %s\n",
122 					pvp[1], m->m_name, errstring(errno));
123 			}
124 		}
125 		return (ExitStat);
126 	}
127 	SmtpState = SMTP_OPEN;
128 
129 	/*
130 	**  Get the greeting message.
131 	**	This should appear spontaneously.  Give it five minutes to
132 	**	happen.
133 	*/
134 
135 	if (setjmp(CtxGreeting) != 0)
136 		goto tempfail;
137 	gte = setevent((time_t) 300, greettimeout, 0);
138 	SmtpPhase = "greeting wait";
139 	r = reply(m);
140 	clrevent(gte);
141 	if (r < 0 || REPLYTYPE(r) != 2)
142 		goto tempfail;
143 
144 	/*
145 	**  Send the HELO command.
146 	**	My mother taught me to always introduce myself.
147 	*/
148 
149 	smtpmessage("HELO %s", m, MyHostName);
150 	SmtpPhase = "HELO wait";
151 	r = reply(m);
152 	if (r < 0)
153 		goto tempfail;
154 	else if (REPLYTYPE(r) == 5)
155 		goto unavailable;
156 	else if (REPLYTYPE(r) != 2)
157 		goto tempfail;
158 
159 	/*
160 	**  If this is expected to be another sendmail, send some internal
161 	**  commands.
162 	*/
163 
164 	if (bitnset(M_INTERNAL, m->m_flags))
165 	{
166 		/* tell it to be verbose */
167 		smtpmessage("VERB", m);
168 		r = reply(m);
169 		if (r < 0)
170 			goto tempfail;
171 
172 		/* tell it we will be sending one transaction only */
173 		smtpmessage("ONEX", m);
174 		r = reply(m);
175 		if (r < 0)
176 			goto tempfail;
177 	}
178 
179 	/*
180 	**  Send the MAIL command.
181 	**	Designates the sender.
182 	*/
183 
184 	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
185 	if (CurEnv->e_from.q_mailer == LocalMailer ||
186 	    !bitnset(M_FROMPATH, m->m_flags))
187 	{
188 		smtpmessage("MAIL From:<%s>", m, buf);
189 	}
190 	else
191 	{
192 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
193 			buf[0] == '@' ? ',' : ':', buf);
194 	}
195 	SmtpPhase = "MAIL wait";
196 	r = reply(m);
197 	if (r < 0 || REPLYTYPE(r) == 4)
198 		goto tempfail;
199 	else if (r == 250)
200 		return (EX_OK);
201 	else if (r == 552)
202 		goto unavailable;
203 
204 	/* protocol error -- close up */
205 	smtpquit(m);
206 	return (EX_PROTOCOL);
207 
208 	/* signal a temporary failure */
209   tempfail:
210 	smtpquit(m);
211 	return (EX_TEMPFAIL);
212 
213 	/* signal service unavailable */
214   unavailable:
215 	smtpquit(m);
216 	return (EX_UNAVAILABLE);
217 }
218 
219 
220 static
221 greettimeout()
222 {
223 	/* timeout reading the greeting message */
224 	longjmp(CtxGreeting, 1);
225 }
226 /*
227 **  SMTPRCPT -- designate recipient.
228 **
229 **	Parameters:
230 **		to -- address of recipient.
231 **		m -- the mailer we are sending to.
232 **
233 **	Returns:
234 **		exit status corresponding to recipient status.
235 **
236 **	Side Effects:
237 **		Sends the mail via SMTP.
238 */
239 
240 smtprcpt(to, m)
241 	ADDRESS *to;
242 	register MAILER *m;
243 {
244 	register int r;
245 	extern char *remotename();
246 
247 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
248 
249 	SmtpPhase = "RCPT wait";
250 	r = reply(m);
251 	if (r < 0 || REPLYTYPE(r) == 4)
252 		return (EX_TEMPFAIL);
253 	else if (REPLYTYPE(r) == 2)
254 		return (EX_OK);
255 	else if (r == 550 || r == 551 || r == 553)
256 		return (EX_NOUSER);
257 	else if (r == 552 || r == 554)
258 		return (EX_UNAVAILABLE);
259 	return (EX_PROTOCOL);
260 }
261 /*
262 **  SMTPDATA -- send the data and clean up the transaction.
263 **
264 **	Parameters:
265 **		m -- mailer being sent to.
266 **		e -- the envelope for this message.
267 **
268 **	Returns:
269 **		exit status corresponding to DATA command.
270 **
271 **	Side Effects:
272 **		none.
273 */
274 
275 smtpdata(m, e)
276 	struct mailer *m;
277 	register ENVELOPE *e;
278 {
279 	register int r;
280 
281 	/*
282 	**  Send the data.
283 	**	First send the command and check that it is ok.
284 	**	Then send the data.
285 	**	Follow it up with a dot to terminate.
286 	**	Finally get the results of the transaction.
287 	*/
288 
289 	/* send the command and check ok to proceed */
290 	smtpmessage("DATA", m);
291 	SmtpPhase = "DATA wait";
292 	r = reply(m);
293 	if (r < 0 || REPLYTYPE(r) == 4)
294 		return (EX_TEMPFAIL);
295 	else if (r == 554)
296 		return (EX_UNAVAILABLE);
297 	else if (r != 354)
298 		return (EX_PROTOCOL);
299 
300 	/* now output the actual message */
301 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
302 	putline("\n", SmtpOut, m);
303 	(*e->e_putbody)(SmtpOut, m, CurEnv);
304 
305 	/* terminate the message */
306 	fprintf(SmtpOut, ".%s", m->m_eol);
307 	if (Verbose && !HoldErrs)
308 		nmessage(Arpa_Info, ">>> .");
309 
310 	/* check for the results of the transaction */
311 	SmtpPhase = "result wait";
312 	r = reply(m);
313 	if (r < 0 || REPLYTYPE(r) == 4)
314 		return (EX_TEMPFAIL);
315 	else if (r == 250)
316 		return (EX_OK);
317 	else if (r == 552 || r == 554)
318 		return (EX_UNAVAILABLE);
319 	return (EX_PROTOCOL);
320 }
321 /*
322 **  SMTPQUIT -- close the SMTP connection.
323 **
324 **	Parameters:
325 **		m -- a pointer to the mailer.
326 **
327 **	Returns:
328 **		none.
329 **
330 **	Side Effects:
331 **		sends the final protocol and closes the connection.
332 */
333 
334 smtpquit(m)
335 	register MAILER *m;
336 {
337 	int i;
338 
339 	/* if the connection is already closed, don't bother */
340 	if (SmtpIn == NULL)
341 		return;
342 
343 	/* send the quit message if not a forced quit */
344 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
345 	{
346 		smtpmessage("QUIT", m);
347 		(void) reply(m);
348 		if (SmtpState == SMTP_CLOSED)
349 			return;
350 	}
351 
352 	/* now actually close the connection */
353 	(void) fclose(SmtpIn);
354 	(void) fclose(SmtpOut);
355 	SmtpIn = SmtpOut = NULL;
356 	SmtpState = SMTP_CLOSED;
357 
358 	/* and pick up the zombie */
359 	i = endmailer(SmtpPid, m->m_argv[0]);
360 	if (i != EX_OK)
361 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
362 }
363 /*
364 **  REPLY -- read arpanet reply
365 **
366 **	Parameters:
367 **		m -- the mailer we are reading the reply from.
368 **
369 **	Returns:
370 **		reply code it reads.
371 **
372 **	Side Effects:
373 **		flushes the mail file.
374 */
375 
376 reply(m)
377 	MAILER *m;
378 {
379 	(void) fflush(SmtpOut);
380 
381 	if (tTd(18, 1))
382 		printf("reply\n");
383 
384 	/*
385 	**  Read the input line, being careful not to hang.
386 	*/
387 
388 	for (;;)
389 	{
390 		register int r;
391 		register char *p;
392 
393 		/* actually do the read */
394 		if (CurEnv->e_xfp != NULL)
395 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
396 
397 		/* if we are in the process of closing just give the code */
398 		if (SmtpState == SMTP_CLOSED)
399 			return (SMTPCLOSING);
400 
401 		/* get the line from the other side */
402 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
403 		if (p == NULL)
404 		{
405 			extern char MsgBuf[];		/* err.c */
406 			extern char Arpa_TSyserr[];	/* conf.c */
407 
408 			/* if the remote end closed early, fake an error */
409 			if (errno == 0)
410 # ifdef ECONNRESET
411 				errno = ECONNRESET;
412 # else ECONNRESET
413 				errno = EPIPE;
414 # endif ECONNRESET
415 
416 			message(Arpa_TSyserr, "reply: read error");
417 # ifdef DEBUG
418 			/* if debugging, pause so we can see state */
419 			if (tTd(18, 100))
420 				pause();
421 # endif DEBUG
422 # ifdef LOG
423 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
424 # endif LOG
425 			SmtpState = SMTP_CLOSED;
426 			smtpquit(m);
427 			return (-1);
428 		}
429 		fixcrlf(SmtpReplyBuffer, TRUE);
430 
431 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
432 		{
433 			/* serious error -- log the previous command */
434 			if (SmtpMsgBuffer[0] != '\0')
435 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
436 			SmtpMsgBuffer[0] = '\0';
437 
438 			/* now log the message as from the other side */
439 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
440 		}
441 
442 		/* display the input for verbose mode */
443 		if (Verbose && !HoldErrs)
444 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
445 
446 		/* if continuation is required, we can go on */
447 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
448 			continue;
449 
450 		/* decode the reply code */
451 		r = atoi(SmtpReplyBuffer);
452 
453 		/* extra semantics: 0xx codes are "informational" */
454 		if (r < 100)
455 			continue;
456 
457 		/* reply code 421 is "Service Shutting Down" */
458 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
459 		{
460 			/* send the quit protocol */
461 			SmtpState = SMTP_SSD;
462 			smtpquit(m);
463 		}
464 
465 		/* save temporary failure messages for posterity */
466 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
467 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
468 
469 		return (r);
470 	}
471 }
472 /*
473 **  SMTPMESSAGE -- send message to server
474 **
475 **	Parameters:
476 **		f -- format
477 **		m -- the mailer to control formatting.
478 **		a, b, c -- parameters
479 **
480 **	Returns:
481 **		none.
482 **
483 **	Side Effects:
484 **		writes message to SmtpOut.
485 */
486 
487 /*VARARGS1*/
488 smtpmessage(f, m, a, b, c)
489 	char *f;
490 	MAILER *m;
491 {
492 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
493 	if (tTd(18, 1) || (Verbose && !HoldErrs))
494 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
495 	if (SmtpOut != NULL)
496 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
497 }
498 
499 # endif SMTP
500