1 # include "sendmail.h"
2 
3 # ifndef SMTP
4 SCCSID(@(#)srvrsmtp.c	3.34		10/16/82	(no SMTP));
5 # else SMTP
6 
7 SCCSID(@(#)srvrsmtp.c	3.34		10/16/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 CMDVERB	14	/* verb -- go into verbose mode */
44 # define CMDDBGKILL	15	/* kill -- kill sendmail */
45 # define CMDDBGWIZ	16	/* wiz -- become a wizard */
46 
47 static struct cmd	CmdTab[] =
48 {
49 	"mail",		CMDMAIL,
50 	"rcpt",		CMDRCPT,
51 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
52 	"data",		CMDDATA,
53 	"rset",		CMDRSET,
54 	"vrfy",		CMDVRFY,
55 	"expn",		CMDVRFY,
56 	"help",		CMDHELP,
57 	"noop",		CMDNOOP,
58 	"quit",		CMDQUIT,
59 	"mrsq",		CMDMRSQ,
60 	"helo",		CMDHELO,
61 	"verb",		CMDVERB,
62 # ifdef DEBUG
63 	"showq",	CMDDBGSHOWQ,
64 	"debug",	CMDDBGDEBUG,
65 	"kill",		CMDDBGKILL,
66 	"wiz",		CMDDBGWIZ,
67 # endif DEBUG
68 	NULL,		CMDERROR,
69 };
70 
71 # ifdef DEBUG
72 bool	IsWiz = FALSE;			/* set if we are a wizard */
73 char	*WizWord = NULL;		/* the wizard word to compare against */
74 # endif DEBUG
75 
76 smtp()
77 {
78 	register char *p;
79 	register struct cmd *c;
80 	char *cmd;
81 	extern char *skipword();
82 	extern bool sameword();
83 	bool hasmail;			/* mail command received */
84 	int rcps;			/* number of recipients */
85 	auto ADDRESS *vrfyqueue;
86 	char inp[MAXLINE];
87 	extern char Version[];
88 	extern tick();
89 	extern bool iswiz();
90 
91 	hasmail = FALSE;
92 	rcps = 0;
93 	if (OutChannel != stdout)
94 	{
95 		/* arrange for debugging output to go to remote host */
96 		(void) close(1);
97 		(void) dup(fileno(OutChannel));
98 	}
99 	message("220", "%s Sendmail %s ready at %s", HostName,
100 			Version, arpadate(NULL));
101 	(void) setjmp(TopFrame);
102 	QuickAbort = FALSE;
103 	for (;;)
104 	{
105 		/* setup for the read */
106 		CurEnv->e_to = NULL;
107 		Errors = 0;
108 		(void) fflush(stdout);
109 
110 		/* read the input line */
111 		p = sfgets(inp, sizeof inp, InChannel);
112 
113 		/* handle errors */
114 		if (p == NULL)
115 		{
116 			/* end of file, just die */
117 			message("421", "%s Lost input channel", HostName);
118 			finis();
119 		}
120 
121 		/* clean up end of line */
122 		fixcrlf(inp, TRUE);
123 
124 		/* echo command to transcript */
125 		fprintf(Xscript, "<<< %s\n", inp);
126 
127 		/* break off command */
128 		for (p = inp; isspace(*p); p++)
129 			continue;
130 		cmd = p;
131 		while (*++p != '\0' && !isspace(*p))
132 			continue;
133 		if (*p != '\0')
134 			*p++ = '\0';
135 
136 		/* decode command */
137 		for (c = CmdTab; c->cmdname != NULL; c++)
138 		{
139 			if (sameword(c->cmdname, cmd))
140 				break;
141 		}
142 
143 		/* process command */
144 		switch (c->cmdcode)
145 		{
146 		  case CMDHELO:		/* hello -- introduce yourself */
147 			define('s', newstr(p));
148 			message("250", "%s Hello %s, pleased to meet you",
149 				HostName, p);
150 			break;
151 
152 		  case CMDMAIL:		/* mail -- designate sender */
153 			if (hasmail)
154 			{
155 				message("503", "Sender already specified");
156 				break;
157 			}
158 			p = skipword(p, "from");
159 			if (p == NULL)
160 				break;
161 			if (index(p, ',') != NULL)
162 			{
163 				message("501", "Source routing not implemented");
164 				Errors++;
165 				break;
166 			}
167 			setsender(p);
168 			if (Errors == 0)
169 			{
170 				message("250", "Sender ok");
171 				hasmail = TRUE;
172 			}
173 			break;
174 
175 		  case CMDRCPT:		/* rcpt -- designate recipient */
176 			p = skipword(p, "to");
177 			if (p == NULL)
178 				break;
179 			sendto(p, (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 			/*
205 			**  Arrange to send to everyone.
206 			**	If sending to multiple people, mail back
207 			**		errors rather than reporting directly.
208 			**	In any case, don't mail back errors for
209 			**		anything that has happened up to
210 			**		now (the other end will do this).
211 			**	Then send to everyone.
212 			**	Finally give a reply code.  If an error has
213 			**		already been given, don't mail a
214 			**		message back.
215 			**	We goose error returns by clearing FatalErrors.
216 			*/
217 
218 			if (rcps != 1)
219 				HoldErrs = MailBack = TRUE;
220 			FatalErrors = FALSE;
221 
222 			/* send to all recipients */
223 			sendall(CurEnv, FALSE);
224 			CurEnv->e_to = NULL;
225 
226 			/* issue success if appropriate and reset */
227 			if (Errors == 0 || HoldErrs)
228 			{
229 				HoldErrs = FALSE;
230 				message("250", "Sent");
231 			}
232 			else
233 				FatalErrors = FALSE;
234 			break;
235 
236 		  case CMDRSET:		/* rset -- reset state */
237 			message("250", "Reset state");
238 			finis();
239 
240 		  case CMDVRFY:		/* vrfy -- verify address */
241 			vrfyqueue = NULL;
242 			QuickAbort = TRUE;
243 			sendto(p, (ADDRESS *) NULL, &vrfyqueue);
244 			if (Errors != 0)
245 				break;
246 			while (vrfyqueue != NULL)
247 			{
248 				register ADDRESS *a = vrfyqueue->q_next;
249 				char *code;
250 
251 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
252 					a = a->q_next;
253 
254 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
255 				{
256 					if (a != NULL)
257 						code = "250-";
258 					else
259 						code = "250";
260 					if (vrfyqueue->q_fullname == NULL)
261 						message(code, "<%s>", vrfyqueue->q_paddr);
262 					else
263 						message(code, "%s <%s>",
264 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
265 				}
266 				else if (a == NULL)
267 					message("554", "Self destructive alias loop");
268 				vrfyqueue = a;
269 			}
270 			break;
271 
272 		  case CMDHELP:		/* help -- give user info */
273 			if (*p == '\0')
274 				p = "SMTP";
275 			help(p);
276 			break;
277 
278 		  case CMDNOOP:		/* noop -- do nothing */
279 			message("200", "OK");
280 			break;
281 
282 		  case CMDQUIT:		/* quit -- leave mail */
283 			message("221", "%s closing connection", HostName);
284 			finis();
285 
286 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
287 			if (*p == 'R' || *p == 'T')
288 			{
289 				/* recipients first or text first */
290 				message("200", "%c ok, please continue", *p);
291 			}
292 			else if (*p == '?')
293 			{
294 				/* what do I prefer?  anything, anytime */
295 				message("215", "R Recipients first is my choice");
296 			}
297 			else if (*p == '\0')
298 			{
299 				/* no meaningful scheme */
300 				message("200", "okey dokie boobie");
301 			}
302 			else
303 			{
304 				/* bad argument */
305 				message("504", "Scheme unknown");
306 			}
307 			break;
308 
309 		  case CMDVERB:		/* set verbose mode */
310 			Verbose = TRUE;
311 			message("200", "Verbose mode");
312 			break;
313 
314 # ifdef DEBUG
315 		  case CMDDBGSHOWQ:	/* show queues */
316 			printf("Send Queue=");
317 			printaddr(CurEnv->e_sendqueue, TRUE);
318 			break;
319 
320 		  case CMDDBGDEBUG:	/* set debug mode */
321 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
322 			tTflag(p);
323 			message("200", "Debug set");
324 			break;
325 
326 		  case CMDDBGKILL:	/* kill the parent */
327 			if (!iswiz())
328 				break;
329 			if (kill(MotherPid, SIGTERM) >= 0)
330 				message("200", "Mother is dead");
331 			else
332 				message("500", "Can't kill Mom");
333 			break;
334 
335 		  case CMDDBGWIZ:	/* become a wizard */
336 			if (WizWord != NULL)
337 			{
338 				char seed[3];
339 				extern char *crypt();
340 
341 				strncpy(seed, WizWord, 2);
342 				if (strcmp(WizWord, crypt(p, seed)) != 0)
343 				{
344 					message("500", "You are no wizard!");
345 					break;
346 				}
347 			}
348 			IsWiz = TRUE;
349 			message("200", "Please pass, oh mighty wizard");
350 			break;
351 # endif DEBUG
352 
353 		  case CMDERROR:	/* unknown command */
354 			message("500", "Command unrecognized");
355 			break;
356 
357 		  default:
358 			syserr("smtp: unknown code %d", c->cmdcode);
359 			break;
360 		}
361 	}
362 }
363 /*
364 **  SKIPWORD -- skip a fixed word.
365 **
366 **	Parameters:
367 **		p -- place to start looking.
368 **		w -- word to skip.
369 **
370 **	Returns:
371 **		p following w.
372 **		NULL on error.
373 **
374 **	Side Effects:
375 **		clobbers the p data area.
376 */
377 
378 static char *
379 skipword(p, w)
380 	register char *p;
381 	char *w;
382 {
383 	register char *q;
384 	extern bool sameword();
385 
386 	/* find beginning of word */
387 	while (isspace(*p))
388 		p++;
389 	q = p;
390 
391 	/* find end of word */
392 	while (*p != '\0' && *p != ':' && !isspace(*p))
393 		p++;
394 	while (isspace(*p))
395 		*p++ = '\0';
396 	if (*p != ':')
397 	{
398 	  syntax:
399 		message("501", "Syntax error");
400 		Errors++;
401 		return (NULL);
402 	}
403 	*p++ = '\0';
404 	while (isspace(*p))
405 		p++;
406 
407 	/* see if the input word matches desired word */
408 	if (!sameword(q, w))
409 		goto syntax;
410 
411 	return (p);
412 }
413 /*
414 **  HELP -- implement the HELP command.
415 **
416 **	Parameters:
417 **		topic -- the topic we want help for.
418 **
419 **	Returns:
420 **		none.
421 **
422 **	Side Effects:
423 **		outputs the help file to message output.
424 */
425 
426 help(topic)
427 	char *topic;
428 {
429 	register FILE *hf;
430 	int len;
431 	char buf[MAXLINE];
432 	bool noinfo;
433 
434 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
435 	{
436 		/* no help */
437 		message("502", "HELP not implemented");
438 		return;
439 	}
440 
441 	len = strlen(topic);
442 	makelower(topic);
443 	noinfo = TRUE;
444 
445 	while (fgets(buf, sizeof buf, hf) != NULL)
446 	{
447 		if (strncmp(buf, topic, len) == 0)
448 		{
449 			register char *p;
450 
451 			p = index(buf, '\t');
452 			if (p == NULL)
453 				p = buf;
454 			else
455 				p++;
456 			fixcrlf(p, TRUE);
457 			message("214-", p);
458 			noinfo = FALSE;
459 		}
460 	}
461 
462 	if (noinfo)
463 		message("504", "HELP topic unknown");
464 	else
465 		message("214", "End of HELP info");
466 	(void) fclose(hf);
467 }
468 /*
469 **  ISWIZ -- tell us if we are a wizard
470 **
471 **	If not, print a nasty message.
472 **
473 **	Parameters:
474 **		none.
475 **
476 **	Returns:
477 **		TRUE if we are a wizard.
478 **		FALSE if we are not a wizard.
479 **
480 **	Side Effects:
481 **		Prints a 500 exit stat if we are not a wizard.
482 */
483 
484 bool
485 iswiz()
486 {
487 	if (!IsWiz)
488 		message("500", "Mere mortals musn't mutter that mantra");
489 	return (IsWiz);
490 }
491 
492 # endif SMTP
493