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