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.13 (Berkeley) 01/19/89 (with SMTP)";
24 #else
25 static char sccsid[] = "@(#)usersmtp.c	5.13 (Berkeley) 01/19/89 (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 	if (SmtpState == SMTP_OPEN)
91 		syserr("smtpinit: already open");
92 
93 	SmtpIn = SmtpOut = NULL;
94 	SmtpState = SMTP_CLOSED;
95 	SmtpError[0] = '\0';
96 	SmtpPhase = "user open";
97 	setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
98 	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
99 	if (SmtpPid < 0)
100 	{
101 		if (tTd(18, 1))
102 			printf("smtpinit: cannot open %s: stat %d errno %d\n",
103 			   pvp[0], ExitStat, errno);
104 		if (CurEnv->e_xfp != NULL)
105 		{
106 			register char *p;
107 			extern char *errstring();
108 			extern char *statstring();
109 
110 			if (errno == 0)
111 			{
112 				p = statstring(ExitStat);
113 				fprintf(CurEnv->e_xfp,
114 					"%.3s %s.%s... %s\n",
115 					p, pvp[1], m->m_name, p);
116 			}
117 			else
118 			{
119 				r = errno;
120 				fprintf(CurEnv->e_xfp,
121 					"421 %s.%s... Deferred: %s\n",
122 					pvp[1], m->m_name, errstring(errno));
123 				errno = r;
124 			}
125 		}
126 		return (ExitStat);
127 	}
128 	SmtpState = SMTP_OPEN;
129 
130 	/*
131 	**  Get the greeting message.
132 	**	This should appear spontaneously.  Give it five minutes to
133 	**	happen.
134 	*/
135 
136 	if (setjmp(CtxGreeting) != 0)
137 		goto tempfail;
138 	gte = setevent((time_t) 300, greettimeout, 0);
139 	SmtpPhase = "greeting wait";
140 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
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 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
154 	r = reply(m);
155 	if (r < 0)
156 		goto tempfail;
157 	else if (REPLYTYPE(r) == 5)
158 		goto unavailable;
159 	else if (REPLYTYPE(r) != 2)
160 		goto tempfail;
161 
162 	/*
163 	**  If this is expected to be another sendmail, send some internal
164 	**  commands.
165 	*/
166 
167 	if (bitnset(M_INTERNAL, m->m_flags))
168 	{
169 		/* tell it to be verbose */
170 		smtpmessage("VERB", m);
171 		r = reply(m);
172 		if (r < 0)
173 			goto tempfail;
174 
175 		/* tell it we will be sending one transaction only */
176 		smtpmessage("ONEX", m);
177 		r = reply(m);
178 		if (r < 0)
179 			goto tempfail;
180 	}
181 
182 	/*
183 	**  Send the MAIL command.
184 	**	Designates the sender.
185 	*/
186 
187 	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
188 	if (CurEnv->e_from.q_mailer == LocalMailer ||
189 	    !bitnset(M_FROMPATH, m->m_flags))
190 	{
191 		smtpmessage("MAIL From:<%s>", m, buf);
192 	}
193 	else
194 	{
195 		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
196 			buf[0] == '@' ? ',' : ':', buf);
197 	}
198 	SmtpPhase = "MAIL wait";
199 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
200 	r = reply(m);
201 	if (r < 0 || REPLYTYPE(r) == 4)
202 		goto tempfail;
203 	else if (r == 250)
204 		return (EX_OK);
205 	else if (r == 552)
206 		goto unavailable;
207 
208 	/* protocol error -- close up */
209 	smtpquit(m);
210 	return (EX_PROTOCOL);
211 
212 	/* signal a temporary failure */
213   tempfail:
214 	smtpquit(m);
215 	return (EX_TEMPFAIL);
216 
217 	/* signal service unavailable */
218   unavailable:
219 	smtpquit(m);
220 	return (EX_UNAVAILABLE);
221 }
222 
223 
224 static
225 greettimeout()
226 {
227 	/* timeout reading the greeting message */
228 	longjmp(CtxGreeting, 1);
229 }
230 /*
231 **  SMTPRCPT -- designate recipient.
232 **
233 **	Parameters:
234 **		to -- address of recipient.
235 **		m -- the mailer we are sending to.
236 **
237 **	Returns:
238 **		exit status corresponding to recipient status.
239 **
240 **	Side Effects:
241 **		Sends the mail via SMTP.
242 */
243 
244 smtprcpt(to, m)
245 	ADDRESS *to;
246 	register MAILER *m;
247 {
248 	register int r;
249 	extern char *remotename();
250 
251 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
252 
253 	SmtpPhase = "RCPT wait";
254 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
255 	r = reply(m);
256 	if (r < 0 || REPLYTYPE(r) == 4)
257 		return (EX_TEMPFAIL);
258 	else if (REPLYTYPE(r) == 2)
259 		return (EX_OK);
260 	else if (r == 550 || r == 551 || r == 553)
261 		return (EX_NOUSER);
262 	else if (r == 552 || r == 554)
263 		return (EX_UNAVAILABLE);
264 	return (EX_PROTOCOL);
265 }
266 /*
267 **  SMTPDATA -- send the data and clean up the transaction.
268 **
269 **	Parameters:
270 **		m -- mailer being sent to.
271 **		e -- the envelope for this message.
272 **
273 **	Returns:
274 **		exit status corresponding to DATA command.
275 **
276 **	Side Effects:
277 **		none.
278 */
279 
280 smtpdata(m, e)
281 	struct mailer *m;
282 	register ENVELOPE *e;
283 {
284 	register int r;
285 
286 	/*
287 	**  Send the data.
288 	**	First send the command and check that it is ok.
289 	**	Then send the data.
290 	**	Follow it up with a dot to terminate.
291 	**	Finally get the results of the transaction.
292 	*/
293 
294 	/* send the command and check ok to proceed */
295 	smtpmessage("DATA", m);
296 	SmtpPhase = "DATA wait";
297 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
298 	r = reply(m);
299 	if (r < 0 || REPLYTYPE(r) == 4)
300 		return (EX_TEMPFAIL);
301 	else if (r == 554)
302 		return (EX_UNAVAILABLE);
303 	else if (r != 354)
304 		return (EX_PROTOCOL);
305 
306 	/* now output the actual message */
307 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
308 	putline("\n", SmtpOut, m);
309 	(*e->e_putbody)(SmtpOut, m, CurEnv);
310 
311 	/* terminate the message */
312 	fprintf(SmtpOut, ".%s", m->m_eol);
313 	if (Verbose && !HoldErrs)
314 		nmessage(Arpa_Info, ">>> .");
315 
316 	/* check for the results of the transaction */
317 	SmtpPhase = "result wait";
318 	setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
319 	r = reply(m);
320 	if (r < 0 || REPLYTYPE(r) == 4)
321 		return (EX_TEMPFAIL);
322 	else if (r == 250)
323 		return (EX_OK);
324 	else if (r == 552 || r == 554)
325 		return (EX_UNAVAILABLE);
326 	return (EX_PROTOCOL);
327 }
328 /*
329 **  SMTPQUIT -- close the SMTP connection.
330 **
331 **	Parameters:
332 **		m -- a pointer to the mailer.
333 **
334 **	Returns:
335 **		none.
336 **
337 **	Side Effects:
338 **		sends the final protocol and closes the connection.
339 */
340 
341 smtpquit(m)
342 	register MAILER *m;
343 {
344 	int i;
345 
346 	/* if the connection is already closed, don't bother */
347 	if (SmtpIn == NULL)
348 		return;
349 
350 	/* send the quit message if not a forced quit */
351 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
352 	{
353 		smtpmessage("QUIT", m);
354 		(void) reply(m);
355 		if (SmtpState == SMTP_CLOSED)
356 			return;
357 	}
358 
359 	/* now actually close the connection */
360 	(void) fclose(SmtpIn);
361 	(void) fclose(SmtpOut);
362 	SmtpIn = SmtpOut = NULL;
363 	SmtpState = SMTP_CLOSED;
364 
365 	/* and pick up the zombie */
366 	i = endmailer(SmtpPid, m->m_argv[0]);
367 	if (i != EX_OK)
368 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
369 }
370 /*
371 **  REPLY -- read arpanet reply
372 **
373 **	Parameters:
374 **		m -- the mailer we are reading the reply from.
375 **
376 **	Returns:
377 **		reply code it reads.
378 **
379 **	Side Effects:
380 **		flushes the mail file.
381 */
382 
383 reply(m)
384 	MAILER *m;
385 {
386 	(void) fflush(SmtpOut);
387 
388 	if (tTd(18, 1))
389 		printf("reply\n");
390 
391 	/*
392 	**  Read the input line, being careful not to hang.
393 	*/
394 
395 	for (;;)
396 	{
397 		register int r;
398 		register char *p;
399 
400 		/* actually do the read */
401 		if (CurEnv->e_xfp != NULL)
402 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
403 
404 		/* if we are in the process of closing just give the code */
405 		if (SmtpState == SMTP_CLOSED)
406 			return (SMTPCLOSING);
407 
408 		/* get the line from the other side */
409 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
410 		if (p == NULL)
411 		{
412 			extern char MsgBuf[];		/* err.c */
413 			extern char Arpa_TSyserr[];	/* conf.c */
414 
415 			/* if the remote end closed early, fake an error */
416 			if (errno == 0)
417 # ifdef ECONNRESET
418 				errno = ECONNRESET;
419 # else ECONNRESET
420 				errno = EPIPE;
421 # endif ECONNRESET
422 
423 			message(Arpa_TSyserr, "reply: read error");
424 			/* if debugging, pause so we can see state */
425 			if (tTd(18, 100))
426 				pause();
427 # ifdef LOG
428 			syslog(LOG_INFO, "%s", &MsgBuf[4]);
429 # endif LOG
430 			SmtpState = SMTP_CLOSED;
431 			smtpquit(m);
432 			return (-1);
433 		}
434 		fixcrlf(SmtpReplyBuffer, TRUE);
435 
436 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
437 		{
438 			/* serious error -- log the previous command */
439 			if (SmtpMsgBuffer[0] != '\0')
440 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
441 			SmtpMsgBuffer[0] = '\0';
442 
443 			/* now log the message as from the other side */
444 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
445 		}
446 
447 		/* display the input for verbose mode */
448 		if (Verbose && !HoldErrs)
449 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
450 
451 		/* if continuation is required, we can go on */
452 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
453 			continue;
454 
455 		/* decode the reply code */
456 		r = atoi(SmtpReplyBuffer);
457 
458 		/* extra semantics: 0xx codes are "informational" */
459 		if (r < 100)
460 			continue;
461 
462 		/* reply code 421 is "Service Shutting Down" */
463 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
464 		{
465 			/* send the quit protocol */
466 			SmtpState = SMTP_SSD;
467 			smtpquit(m);
468 		}
469 
470 		/* save temporary failure messages for posterity */
471 		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
472 			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
473 
474 		return (r);
475 	}
476 }
477 /*
478 **  SMTPMESSAGE -- send message to server
479 **
480 **	Parameters:
481 **		f -- format
482 **		m -- the mailer to control formatting.
483 **		a, b, c -- parameters
484 **
485 **	Returns:
486 **		none.
487 **
488 **	Side Effects:
489 **		writes message to SmtpOut.
490 */
491 
492 /*VARARGS1*/
493 smtpmessage(f, m, a, b, c)
494 	char *f;
495 	MAILER *m;
496 {
497 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
498 	if (tTd(18, 1) || (Verbose && !HoldErrs))
499 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
500 	if (SmtpOut != NULL)
501 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
502 }
503 
504 # endif SMTP
505