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