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