1 # include "sendmail.h"
2 
3 # ifndef SMTP
4 SCCSID(@(#)srvrsmtp.c	3.31		09/06/82	(no SMTP));
5 # else SMTP
6 
7 SCCSID(@(#)srvrsmtp.c	3.31		09/06/82);
8 
9 /*
10 **  SMTP -- run the SMTP protocol.
11 **
12 **	Parameters:
13 **		none.
14 **
15 **	Returns:
16 **		never.
17 **
18 **	Side Effects:
19 **		Reads commands from the input channel and processes
20 **			them.
21 */
22 
23 struct cmd
24 {
25 	char	*cmdname;	/* command name */
26 	int	cmdcode;	/* internal code, see below */
27 };
28 
29 /* values for cmdcode */
30 # define CMDERROR	0	/* bad command */
31 # define CMDMAIL	1	/* mail -- designate sender */
32 # define CMDRCPT	2	/* rcpt -- designate recipient */
33 # define CMDDATA	3	/* data -- send message text */
34 # define CMDRSET	5	/* rset -- reset state */
35 # define CMDVRFY	6	/* vrfy -- verify address */
36 # define CMDHELP	7	/* help -- give usage info */
37 # define CMDNOOP	8	/* noop -- do nothing */
38 # define CMDQUIT	9	/* quit -- close connection and die */
39 # define CMDMRSQ	10	/* mrsq -- for old mtp compat only */
40 # define CMDHELO	11	/* helo -- be polite */
41 # define CMDDBGSHOWQ	12	/* _showq -- show send queue (DEBUG) */
42 # define CMDDBGDEBUG	13	/* _debug -- set debug mode */
43 # define CMDDBGVERBOSE	14	/* _verbose -- go into verbose mode */
44 # define CMDDBGKILL	15	/* _kill -- kill sendmail */
45 
46 static struct cmd	CmdTab[] =
47 {
48 	"mail",		CMDMAIL,
49 	"rcpt",		CMDRCPT,
50 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
51 	"data",		CMDDATA,
52 	"rset",		CMDRSET,
53 	"vrfy",		CMDVRFY,
54 	"expn",		CMDVRFY,
55 	"help",		CMDHELP,
56 	"noop",		CMDNOOP,
57 	"quit",		CMDQUIT,
58 	"mrsq",		CMDMRSQ,
59 	"helo",		CMDHELO,
60 # ifdef DEBUG
61 	"_showq",	CMDDBGSHOWQ,
62 	"_debug",	CMDDBGDEBUG,
63 	"_verbose",	CMDDBGVERBOSE,
64 	"_kill",	CMDDBGKILL,
65 # endif DEBUG
66 	NULL,		CMDERROR,
67 };
68 
69 smtp()
70 {
71 	char inp[MAXLINE];
72 	register char *p;
73 	struct cmd *c;
74 	char *cmd;
75 	extern char *skipword();
76 	extern bool sameword();
77 	bool hasmail;			/* mail command received */
78 	int rcps;			/* number of recipients */
79 	auto ADDRESS *vrfyqueue;
80 	extern char Version[];
81 	extern tick();
82 
83 	hasmail = FALSE;
84 	rcps = 0;
85 	if (OutChannel != stdout)
86 	{
87 		/* arrange for debugging output to go to remote host */
88 		(void) close(1);
89 		(void) dup(fileno(OutChannel));
90 	}
91 	message("220", "%s Sendmail %s ready at %s", HostName,
92 			Version, arpadate(NULL));
93 	(void) setjmp(TopFrame);
94 	QuickAbort = FALSE;
95 	for (;;)
96 	{
97 		/* setup for the read */
98 		CurEnv->e_to = NULL;
99 		Errors = 0;
100 		(void) fflush(stdout);
101 
102 		/* read the input line */
103 		p = sfgets(inp, sizeof inp, InChannel);
104 
105 		/* handle errors */
106 		if (p == NULL)
107 		{
108 			/* end of file, just die */
109 			message("421", "%s Lost input channel", HostName);
110 			finis();
111 		}
112 
113 		/* clean up end of line */
114 		fixcrlf(inp, TRUE);
115 
116 		/* echo command to transcript */
117 		fprintf(Xscript, "<<< %s\n", inp);
118 
119 		/* break off command */
120 		for (p = inp; isspace(*p); p++)
121 			continue;
122 		cmd = p;
123 		while (*++p != '\0' && !isspace(*p))
124 			continue;
125 		if (*p != '\0')
126 			*p++ = '\0';
127 
128 		/* decode command */
129 		for (c = CmdTab; c->cmdname != NULL; c++)
130 		{
131 			if (sameword(c->cmdname, cmd))
132 				break;
133 		}
134 
135 		/* process command */
136 		switch (c->cmdcode)
137 		{
138 		  case CMDHELO:		/* hello -- introduce yourself */
139 			define('s', newstr(p));
140 			message("250", "%s Hello %s, pleased to meet you",
141 				HostName, p);
142 			break;
143 
144 		  case CMDMAIL:		/* mail -- designate sender */
145 			if (hasmail)
146 			{
147 				message("503", "Sender already specified");
148 				break;
149 			}
150 			p = skipword(p, "from");
151 			if (p == NULL)
152 				break;
153 			if (index(p, ',') != NULL)
154 			{
155 				message("501", "Source routing not implemented");
156 				Errors++;
157 				break;
158 			}
159 			setsender(p);
160 			if (Errors == 0)
161 			{
162 				message("250", "Sender ok");
163 				hasmail = TRUE;
164 			}
165 			break;
166 
167 		  case CMDRCPT:		/* rcpt -- designate recipient */
168 			p = skipword(p, "to");
169 			if (p == NULL)
170 				break;
171 			sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
172 			if (Errors == 0)
173 			{
174 				message("250", "%s... Recipient ok", p);
175 				rcps++;
176 			}
177 			break;
178 
179 		  case CMDDATA:		/* data -- text of mail */
180 			if (!hasmail)
181 			{
182 				message("503", "Need MAIL command");
183 				break;
184 			}
185 			else if (rcps <= 0)
186 			{
187 				message("503", "Need RCPT (recipient)");
188 				break;
189 			}
190 
191 			/* collect the text of the message */
192 			collect(TRUE);
193 			if (Errors != 0)
194 				break;
195 
196 			/* if sending to multiple people, mail back errors */
197 			if (rcps != 1)
198 				HoldErrs = MailBack = TRUE;
199 
200 			/* send to all recipients */
201 			sendall(CurEnv, FALSE);
202 
203 			/* reset strange modes */
204 			HoldErrs = FALSE;
205 			CurEnv->e_to = NULL;
206 
207 			/* issue success if appropriate */
208 			if (Errors == 0 || rcps != 1)
209 				message("250", "Sent");
210 			break;
211 
212 		  case CMDRSET:		/* rset -- reset state */
213 			message("250", "Reset state");
214 			finis();
215 
216 		  case CMDVRFY:		/* vrfy -- verify address */
217 			vrfyqueue = NULL;
218 			QuickAbort = TRUE;
219 			sendto(p, (ADDRESS *) NULL, &vrfyqueue);
220 			if (Errors != 0)
221 				break;
222 			while (vrfyqueue != NULL)
223 			{
224 				register ADDRESS *a = vrfyqueue->q_next;
225 				char *code;
226 
227 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
228 					a = a->q_next;
229 
230 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
231 				{
232 					if (a != NULL)
233 						code = "250-";
234 					else
235 						code = "250";
236 					if (vrfyqueue->q_fullname == NULL)
237 						message(code, "<%s>", vrfyqueue->q_paddr);
238 					else
239 						message(code, "%s <%s>",
240 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
241 				}
242 				else if (a == NULL)
243 					message("554", "Self destructive alias loop");
244 				vrfyqueue = a;
245 			}
246 			break;
247 
248 		  case CMDHELP:		/* help -- give user info */
249 			if (*p == '\0')
250 				p = "SMTP";
251 			help(p);
252 			break;
253 
254 		  case CMDNOOP:		/* noop -- do nothing */
255 			message("200", "OK");
256 			break;
257 
258 		  case CMDQUIT:		/* quit -- leave mail */
259 			message("221", "%s closing connection", HostName);
260 			finis();
261 
262 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
263 			if (*p == 'R' || *p == 'T')
264 			{
265 				/* recipients first or text first */
266 				message("200", "%c ok, please continue", *p);
267 			}
268 			else if (*p == '?')
269 			{
270 				/* what do I prefer?  anything, anytime */
271 				message("215", "R Recipients first is my choice");
272 			}
273 			else if (*p == '\0')
274 			{
275 				/* no meaningful scheme */
276 				message("200", "okey dokie boobie");
277 			}
278 			else
279 			{
280 				/* bad argument */
281 				message("504", "Scheme unknown");
282 			}
283 			break;
284 
285 # ifdef DEBUG
286 		  case CMDDBGSHOWQ:	/* show queues */
287 			printf("Send Queue=");
288 			printaddr(CurEnv->e_sendqueue, TRUE);
289 			break;
290 
291 		  case CMDDBGDEBUG:	/* set debug mode */
292 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
293 			tTflag(p);
294 			message("200", "Debug set");
295 			break;
296 
297 		  case CMDDBGVERBOSE:	/* set verbose mode */
298 			Verbose = TRUE;
299 			message("200", "Verbose mode");
300 			break;
301 
302 		  case CMDDBGKILL:	/* kill the parent */
303 			if (kill(MotherPid, SIGTERM) >= 0)
304 				message("200", "Mother is dead");
305 			else
306 				message("500", "Can't kill Mom");
307 			break;
308 # endif DEBUG
309 
310 		  case CMDERROR:	/* unknown command */
311 			message("500", "Command unrecognized");
312 			break;
313 
314 		  default:
315 			syserr("smtp: unknown code %d", c->cmdcode);
316 			break;
317 		}
318 	}
319 }
320 /*
321 **  SKIPWORD -- skip a fixed word.
322 **
323 **	Parameters:
324 **		p -- place to start looking.
325 **		w -- word to skip.
326 **
327 **	Returns:
328 **		p following w.
329 **		NULL on error.
330 **
331 **	Side Effects:
332 **		clobbers the p data area.
333 */
334 
335 static char *
336 skipword(p, w)
337 	register char *p;
338 	char *w;
339 {
340 	register char *q;
341 	extern bool sameword();
342 
343 	/* find beginning of word */
344 	while (isspace(*p))
345 		p++;
346 	q = p;
347 
348 	/* find end of word */
349 	while (*p != '\0' && *p != ':' && !isspace(*p))
350 		p++;
351 	while (isspace(*p))
352 		*p++ = '\0';
353 	if (*p != ':')
354 	{
355 	  syntax:
356 		message("501", "Syntax error");
357 		Errors++;
358 		return (NULL);
359 	}
360 	*p++ = '\0';
361 	while (isspace(*p))
362 		p++;
363 
364 	/* see if the input word matches desired word */
365 	if (!sameword(q, w))
366 		goto syntax;
367 
368 	return (p);
369 }
370 /*
371 **  HELP -- implement the HELP command.
372 **
373 **	Parameters:
374 **		topic -- the topic we want help for.
375 **
376 **	Returns:
377 **		none.
378 **
379 **	Side Effects:
380 **		outputs the help file to message output.
381 */
382 
383 help(topic)
384 	char *topic;
385 {
386 	register FILE *hf;
387 	int len;
388 	char buf[MAXLINE];
389 	bool noinfo;
390 	extern char *HelpFile;
391 
392 	hf = fopen(HelpFile, "r");
393 	if (hf == NULL)
394 	{
395 		/* no help */
396 		message("502", "HELP not implemented");
397 		return;
398 	}
399 
400 	len = strlen(topic);
401 	makelower(topic);
402 	noinfo = TRUE;
403 
404 	while (fgets(buf, sizeof buf, hf) != NULL)
405 	{
406 		if (strncmp(buf, topic, len) == 0)
407 		{
408 			register char *p;
409 
410 			p = index(buf, '\t');
411 			if (p == NULL)
412 				p = buf;
413 			else
414 				p++;
415 			fixcrlf(p, TRUE);
416 			message("214-", p);
417 			noinfo = FALSE;
418 		}
419 	}
420 
421 	if (noinfo)
422 		message("504", "HELP topic unknown");
423 	else
424 		message("214", "End of HELP info");
425 	(void) fclose(hf);
426 }
427 
428 # endif SMTP
429