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