1 # include <ctype.h>
2 # include <sysexits.h>
3 # include "sendmail.h"
4 
5 # ifndef SMTP
6 SCCSID(@(#)usersmtp.c	3.34		01/05/83	(no SMTP));
7 # else SMTP
8 
9 SCCSID(@(#)usersmtp.c	3.34		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 	}
246 
247 	/* now actually close the connection */
248 	(void) fclose(SmtpIn);
249 	(void) fclose(SmtpOut);
250 	SmtpIn = SmtpOut = NULL;
251 
252 	/* and pick up the zombie */
253 	i = endmailer(SmtpPid, name);
254 	if (i != EX_OK)
255 		syserr("smtpquit %s: stat %d", name, i);
256 }
257 /*
258 **  REPLY -- read arpanet reply
259 **
260 **	Parameters:
261 **		none.
262 **
263 **	Returns:
264 **		reply code it reads.
265 **
266 **	Side Effects:
267 **		flushes the mail file.
268 */
269 
270 reply()
271 {
272 	(void) fflush(SmtpOut);
273 
274 	if (tTd(18, 1))
275 		printf("reply\n");
276 
277 	/*
278 	**  Read the input line, being careful not to hang.
279 	*/
280 
281 	for (;;)
282 	{
283 		register int r;
284 		register char *p;
285 
286 		/* actually do the read */
287 		if (CurEnv->e_xfp != NULL)
288 			(void) fflush(CurEnv->e_xfp);	/* for debugging */
289 
290 		/* if we are in the process of closing just give the code */
291 		if (SmtpClosing)
292 			return (SMTPCLOSING);
293 
294 		/* get the line from the other side */
295 		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
296 		if (p == NULL)
297 		{
298 			extern char MsgBuf[];		/* err.c */
299 			extern char Arpa_TSyserr[];	/* conf.c */
300 
301 			message(Arpa_TSyserr, "reply: read error");
302 # ifdef LOG
303 			syslog(LOG_ERR, "%s", &MsgBuf[4]);
304 # endif LOG
305 			SmtpClosing = TRUE;
306 			smtpquit("reply error");
307 			return (-1);
308 		}
309 		fixcrlf(SmtpReplyBuffer, TRUE);
310 
311 		/* log the input in the transcript for future error returns */
312 		if (Verbose && !HoldErrs)
313 			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
314 		else if (CurEnv->e_xfp != NULL)
315 			fprintf(CurEnv->e_xfp, "%s\n", SmtpReplyBuffer);
316 
317 		/* if continuation is required, we can go on */
318 		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
319 			continue;
320 
321 		/* decode the reply code */
322 		r = atoi(SmtpReplyBuffer);
323 
324 		/* extra semantics: 0xx codes are "informational" */
325 		if (r < 100)
326 			continue;
327 
328 		/* reply code 421 is "Service Shutting Down" */
329 		if (r == SMTPCLOSING)
330 		{
331 			/* send the quit protocol */
332 			smtpquit("SMTP Shutdown");
333 			SmtpClosing = TRUE;
334 		}
335 
336 		return (r);
337 	}
338 }
339 /*
340 **  SMTPMESSAGE -- send message to server
341 **
342 **	Parameters:
343 **		f -- format
344 **		a, b, c -- parameters
345 **
346 **	Returns:
347 **		none.
348 **
349 **	Side Effects:
350 **		writes message to SmtpOut.
351 */
352 
353 /*VARARGS1*/
354 smtpmessage(f, a, b, c)
355 	char *f;
356 {
357 	char buf[100];
358 
359 	(void) sprintf(buf, f, a, b, c);
360 	if (tTd(18, 1) || (Verbose && !HoldErrs))
361 		nmessage(Arpa_Info, ">>> %s", buf);
362 	else if (CurEnv->e_xfp != NULL)
363 		fprintf(CurEnv->e_xfp, ">>> %s\n", buf);
364 	if (!SmtpClosing)
365 		fprintf(SmtpOut, "%s\r\n", buf);
366 }
367 
368 # endif SMTP
369