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