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