1 # include <ctype.h>
2 # include <sysexits.h>
3 # include "sendmail.h"
4 
5 # ifndef SMTP
6 SCCSID(@(#)usersmtp.c	4.7		11/13/83	(no SMTP));
7 # else SMTP
8 
9 SCCSID(@(#)usersmtp.c	4.7		11/13/83);
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(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("$g", 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 	return (EX_TEMPFAIL);
174 
175 	/* signal service unavailable */
176   unavailable:
177 	smtpquit(m);
178 	return (EX_UNAVAILABLE);
179 }
180 
181 
182 static
183 greettimeout()
184 {
185 	/* timeout reading the greeting message */
186 	longjmp(CtxGreeting, 1);
187 }
188 /*
189 **  SMTPRCPT -- designate recipient.
190 **
191 **	Parameters:
192 **		to -- address of recipient.
193 **		m -- the mailer we are sending to.
194 **
195 **	Returns:
196 **		exit status corresponding to recipient status.
197 **
198 **	Side Effects:
199 **		Sends the mail via SMTP.
200 */
201 
202 smtprcpt(to, m)
203 	ADDRESS *to;
204 	register MAILER *m;
205 {
206 	register int r;
207 	extern char *remotename();
208 
209 	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
210 
211 	r = reply(m);
212 	if (r < 0 || REPLYTYPE(r) == 4)
213 		return (EX_TEMPFAIL);
214 	else if (REPLYTYPE(r) == 2)
215 		return (EX_OK);
216 	else if (r == 550 || r == 551 || r == 553)
217 		return (EX_NOUSER);
218 	else if (r == 552 || r == 554)
219 		return (EX_UNAVAILABLE);
220 	return (EX_PROTOCOL);
221 }
222 /*
223 **  SMTPDATA -- send the data and clean up the transaction.
224 **
225 **	Parameters:
226 **		m -- mailer being sent to.
227 **		e -- the envelope for this message.
228 **
229 **	Returns:
230 **		exit status corresponding to DATA command.
231 **
232 **	Side Effects:
233 **		none.
234 */
235 
236 smtpdata(m, e)
237 	struct mailer *m;
238 	register ENVELOPE *e;
239 {
240 	register int r;
241 
242 	/*
243 	**  Send the data.
244 	**	First send the command and check that it is ok.
245 	**	Then send the data.
246 	**	Follow it up with a dot to terminate.
247 	**	Finally get the results of the transaction.
248 	*/
249 
250 	/* send the command and check ok to proceed */
251 	smtpmessage("DATA", m);
252 	r = reply(m);
253 	if (r < 0 || REPLYTYPE(r) == 4)
254 		return (EX_TEMPFAIL);
255 	else if (r == 554)
256 		return (EX_UNAVAILABLE);
257 	else if (r != 354)
258 		return (EX_PROTOCOL);
259 
260 	/* now output the actual message */
261 	(*e->e_puthdr)(SmtpOut, m, CurEnv);
262 	putline("\n", SmtpOut, m);
263 	(*e->e_putbody)(SmtpOut, m, CurEnv);
264 
265 	/* terminate the message */
266 	fprintf(SmtpOut, ".%s", m->m_eol);
267 	if (Verbose && !HoldErrs)
268 		nmessage(Arpa_Info, ">>> .");
269 
270 	/* check for the results of the transaction */
271 	r = reply(m);
272 	if (r < 0 || REPLYTYPE(r) == 4)
273 		return (EX_TEMPFAIL);
274 	else if (r == 250)
275 		return (EX_OK);
276 	else if (r == 552 || r == 554)
277 		return (EX_UNAVAILABLE);
278 	return (EX_PROTOCOL);
279 }
280 /*
281 **  SMTPQUIT -- close the SMTP connection.
282 **
283 **	Parameters:
284 **		m -- a pointer to the mailer.
285 **
286 **	Returns:
287 **		none.
288 **
289 **	Side Effects:
290 **		sends the final protocol and closes the connection.
291 */
292 
293 smtpquit(m)
294 	register MAILER *m;
295 {
296 	int i;
297 
298 	/* if the connection is already closed, don't bother */
299 	if (SmtpIn == NULL)
300 		return;
301 
302 	/* send the quit message if not a forced quit */
303 	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
304 	{
305 		smtpmessage("QUIT", m);
306 		(void) reply(m);
307 		if (SmtpState == SMTP_CLOSED)
308 			return;
309 	}
310 
311 	/* now actually close the connection */
312 	(void) fclose(SmtpIn);
313 	(void) fclose(SmtpOut);
314 	SmtpIn = SmtpOut = NULL;
315 	SmtpState = SMTP_CLOSED;
316 
317 	/* and pick up the zombie */
318 	i = endmailer(SmtpPid, m->m_argv[0]);
319 	if (i != EX_OK)
320 		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
321 }
322 /*
323 **  REPLY -- read arpanet reply
324 **
325 **	Parameters:
326 **		m -- the mailer we are reading the reply from.
327 **
328 **	Returns:
329 **		reply code it reads.
330 **
331 **	Side Effects:
332 **		flushes the mail file.
333 */
334 
335 reply(m)
336 	MAILER *m;
337 {
338 	(void) fflush(SmtpOut);
339 
340 	if (tTd(18, 1))
341 		printf("reply\n");
342 
343 	/*
344 	**  Read the input line, being careful not to hang.
345 	*/
346 
347 	for (;;)
348 	{
349 		register int r;
350 		register char *p;
351 
352 		/* actually do the read */
353 		if (CurEnv->e_xfp != NULL)
354 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
355 
356 		/* if we are in the process of closing just give the code */
357 		if (SmtpState == SMTP_CLOSED)
358 			return (SMTPCLOSING);
359 
360 		/* get the line from the other side */
361 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
362 		if (p == NULL)
363 		{
364 			extern char MsgBuf[];		/* err.c */
365 			extern char Arpa_TSyserr[];	/* conf.c */
366 
367 			message(Arpa_TSyserr, "reply: read error");
368 # ifdef DEBUG
369 			/* if debugging, pause so we can see state */
370 			if (tTd(18, 100))
371 				pause();
372 # endif DEBUG
373 # ifdef LOG
374 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
375 # endif LOG
376 			SmtpState = SMTP_CLOSED;
377 			smtpquit(m);
378 			return (-1);
379 		}
380 		fixcrlf(SmtpReplyBuffer, TRUE);
381 
382 		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
383 		{
384 			/* serious error -- log the previous command */
385 			if (SmtpMsgBuffer[0] != '\0')
386 				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
387 			SmtpMsgBuffer[0] = '\0';
388 
389 			/* now log the message as from the other side */
390 			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
391 		}
392 
393 		/* display the input for verbose mode */
394 		if (Verbose && !HoldErrs)
395 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
396 
397 		/* if continuation is required, we can go on */
398 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
399 			continue;
400 
401 		/* decode the reply code */
402 		r = atoi(SmtpReplyBuffer);
403 
404 		/* extra semantics: 0xx codes are "informational" */
405 		if (r < 100)
406 			continue;
407 
408 		/* reply code 421 is "Service Shutting Down" */
409 		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
410 		{
411 			/* send the quit protocol */
412 			SmtpState = SMTP_SSD;
413 			smtpquit(m);
414 		}
415 
416 		return (r);
417 	}
418 }
419 /*
420 **  SMTPMESSAGE -- send message to server
421 **
422 **	Parameters:
423 **		f -- format
424 **		m -- the mailer to control formatting.
425 **		a, b, c -- parameters
426 **
427 **	Returns:
428 **		none.
429 **
430 **	Side Effects:
431 **		writes message to SmtpOut.
432 */
433 
434 /*VARARGS1*/
435 smtpmessage(f, m, a, b, c)
436 	char *f;
437 	MAILER *m;
438 {
439 	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
440 	if (tTd(18, 1) || (Verbose && !HoldErrs))
441 		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
442 	if (SmtpOut != NULL)
443 		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
444 }
445 
446 # endif SMTP
447