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