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