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