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