xref: /freebsd/contrib/sendmail/src/srvrsmtp.c (revision 6e8394b8baa7d5d9153ab90de6824bcd19b3b4e1)
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12 
13 # include "sendmail.h"
14 
15 #ifndef lint
16 #if SMTP
17 static char sccsid[] = "@(#)srvrsmtp.c	8.187 (Berkeley) 10/23/1998 (with SMTP)";
18 #else
19 static char sccsid[] = "@(#)srvrsmtp.c	8.187 (Berkeley) 10/23/1998 (without SMTP)";
20 #endif
21 #endif /* not lint */
22 
23 # include <errno.h>
24 
25 # if SMTP
26 
27 /*
28 **  SMTP -- run the SMTP protocol.
29 **
30 **	Parameters:
31 **		nullserver -- if non-NULL, rejection message for
32 **			all SMTP commands.
33 **		e -- the envelope.
34 **
35 **	Returns:
36 **		never.
37 **
38 **	Side Effects:
39 **		Reads commands from the input channel and processes
40 **			them.
41 */
42 
43 struct cmd
44 {
45 	char	*cmdname;	/* command name */
46 	int	cmdcode;	/* internal code, see below */
47 };
48 
49 /* values for cmdcode */
50 # define CMDERROR	0	/* bad command */
51 # define CMDMAIL	1	/* mail -- designate sender */
52 # define CMDRCPT	2	/* rcpt -- designate recipient */
53 # define CMDDATA	3	/* data -- send message text */
54 # define CMDRSET	4	/* rset -- reset state */
55 # define CMDVRFY	5	/* vrfy -- verify address */
56 # define CMDEXPN	6	/* expn -- expand address */
57 # define CMDNOOP	7	/* noop -- do nothing */
58 # define CMDQUIT	8	/* quit -- close connection and die */
59 # define CMDHELO	9	/* helo -- be polite */
60 # define CMDHELP	10	/* help -- give usage info */
61 # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
62 # define CMDETRN	12	/* etrn -- flush queue */
63 /* non-standard commands */
64 # define CMDONEX	16	/* onex -- sending one transaction only */
65 # define CMDVERB	17	/* verb -- go into verbose mode */
66 # define CMDXUSR	18	/* xusr -- initial (user) submission */
67 /* use this to catch and log "door handle" attempts on your system */
68 # define CMDLOGBOGUS	23	/* bogus command that should be logged */
69 /* debugging-only commands, only enabled if SMTPDEBUG is defined */
70 # define CMDDBGQSHOW	24	/* showq -- show send queue */
71 # define CMDDBGDEBUG	25	/* debug -- set debug mode */
72 
73 static struct cmd	CmdTab[] =
74 {
75 	{ "mail",	CMDMAIL		},
76 	{ "rcpt",	CMDRCPT		},
77 	{ "data",	CMDDATA		},
78 	{ "rset",	CMDRSET		},
79 	{ "vrfy",	CMDVRFY		},
80 	{ "expn",	CMDEXPN		},
81 	{ "help",	CMDHELP		},
82 	{ "noop",	CMDNOOP		},
83 	{ "quit",	CMDQUIT		},
84 	{ "helo",	CMDHELO		},
85 	{ "ehlo",	CMDEHLO		},
86 	{ "etrn",	CMDETRN		},
87 	{ "verb",	CMDVERB		},
88 	{ "onex",	CMDONEX		},
89 	{ "xusr",	CMDXUSR		},
90     /* remaining commands are here only to trap and log attempts to use them */
91 	{ "showq",	CMDDBGQSHOW	},
92 	{ "debug",	CMDDBGDEBUG	},
93 	{ "wiz",	CMDLOGBOGUS	},
94 
95 	{ NULL,		CMDERROR	}
96 };
97 
98 bool	OneXact = FALSE;		/* one xaction only this run */
99 char	*CurSmtpClient;			/* who's at the other end of channel */
100 
101 static char	*skipword __P((char *volatile, char *));
102 
103 
104 #define MAXBADCOMMANDS	25	/* maximum number of bad commands */
105 #define MAXNOOPCOMMANDS	20	/* max "noise" commands before slowdown */
106 #define MAXHELOCOMMANDS	3	/* max HELO/EHLO commands before slowdown */
107 #define MAXVRFYCOMMANDS	6	/* max VRFY/EXPN commands before slowdown */
108 #define MAXETRNCOMMANDS	8	/* max ETRN commands before slowdown */
109 
110 void
111 smtp(nullserver, e)
112 	char *nullserver;
113 	register ENVELOPE *volatile e;
114 {
115 	register char *volatile p;
116 	register struct cmd *c;
117 	char *cmd;
118 	auto ADDRESS *vrfyqueue;
119 	ADDRESS *a;
120 	volatile bool gotmail;		/* mail command received */
121 	volatile bool gothello;		/* helo command received */
122 	bool vrfy;			/* set if this is a vrfy command */
123 	char *volatile protocol;	/* sending protocol */
124 	char *volatile sendinghost;	/* sending hostname */
125 	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
126 	auto char *delimptr;
127 	char *id;
128 	volatile int nrcpts = 0;	/* number of RCPT commands */
129 	bool doublequeue;
130 	volatile bool discard;
131 	volatile int badcommands = 0;	/* count of bad commands */
132 	volatile int nverifies = 0;	/* count of VRFY/EXPN commands */
133 	volatile int n_etrn = 0;	/* count of ETRN commands */
134 	volatile int n_noop = 0;	/* count of NOOP/VERB/ONEX etc cmds */
135 	volatile int n_helo = 0;	/* count of HELO/EHLO commands */
136 	bool ok;
137 	volatile int lognullconnection = TRUE;
138 	register char *q;
139 	QUEUE_CHAR *new;
140 	char inp[MAXLINE];
141 	char cmdbuf[MAXLINE];
142 	extern ENVELOPE BlankEnvelope;
143 	extern void help __P((char *));
144 	extern void settime __P((ENVELOPE *));
145 	extern bool enoughdiskspace __P((long));
146 	extern int runinchild __P((char *, ENVELOPE *));
147 	extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *));
148 
149 	if (fileno(OutChannel) != fileno(stdout))
150 	{
151 		/* arrange for debugging output to go to remote host */
152 		(void) dup2(fileno(OutChannel), fileno(stdout));
153 	}
154 	settime(e);
155 	peerhostname = RealHostName;
156 	if (peerhostname == NULL)
157 		peerhostname = "localhost";
158 	CurHostName = peerhostname;
159 	CurSmtpClient = macvalue('_', e);
160 	if (CurSmtpClient == NULL)
161 		CurSmtpClient = CurHostName;
162 
163 	/* check_relay may have set discard bit, save for later */
164 	discard = bitset(EF_DISCARD, e->e_flags);
165 
166 	sm_setproctitle(TRUE, "server %s startup", CurSmtpClient);
167 #if DAEMON
168 	if (LogLevel > 11)
169 	{
170 		/* log connection information */
171 		sm_syslog(LOG_INFO, NOQID,
172 			"SMTP connect from %.100s (%.100s)",
173 			CurSmtpClient, anynet_ntoa(&RealHostAddr));
174 	}
175 #endif
176 
177 	/* output the first line, inserting "ESMTP" as second word */
178 	expand(SmtpGreeting, inp, sizeof inp, e);
179 	p = strchr(inp, '\n');
180 	if (p != NULL)
181 		*p++ = '\0';
182 	id = strchr(inp, ' ');
183 	if (id == NULL)
184 		id = &inp[strlen(inp)];
185 	cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
186 	message(cmd, id - inp, inp, id);
187 
188 	/* output remaining lines */
189 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
190 	{
191 		*p++ = '\0';
192 		if (isascii(*id) && isspace(*id))
193 			id++;
194 		message("220-%s", id);
195 	}
196 	if (id != NULL)
197 	{
198 		if (isascii(*id) && isspace(*id))
199 			id++;
200 		message("220 %s", id);
201 	}
202 
203 	protocol = NULL;
204 	sendinghost = macvalue('s', e);
205 	gothello = FALSE;
206 	gotmail = FALSE;
207 	for (;;)
208 	{
209 		/* arrange for backout */
210 		(void) setjmp(TopFrame);
211 		QuickAbort = FALSE;
212 		HoldErrs = FALSE;
213 		SuprErrs = FALSE;
214 		LogUsrErrs = FALSE;
215 		OnlyOneError = TRUE;
216 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
217 
218 		/* setup for the read */
219 		e->e_to = NULL;
220 		Errors = 0;
221 		(void) fflush(stdout);
222 
223 		/* read the input line */
224 		SmtpPhase = "server cmd read";
225 		sm_setproctitle(TRUE, "server %s cmd read", CurSmtpClient);
226 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
227 				SmtpPhase);
228 
229 		/* handle errors */
230 		if (p == NULL)
231 		{
232 			/* end of file, just die */
233 			disconnect(1, e);
234 			message("421 %s Lost input channel from %s",
235 				MyHostName, CurSmtpClient);
236 			if (LogLevel > (gotmail ? 1 : 19))
237 				sm_syslog(LOG_NOTICE, e->e_id,
238 					"lost input channel from %.100s",
239 					CurSmtpClient);
240 			if (lognullconnection && LogLevel > 5)
241 				sm_syslog(LOG_INFO, NULL,
242 				"Null connection from %.100s",
243 				CurSmtpClient);
244 
245 			/*
246 			**  If have not accepted mail (DATA), do not bounce
247 			**  bad addresses back to sender.
248 			*/
249 			if (bitset(EF_CLRQUEUE, e->e_flags))
250 				e->e_sendqueue = NULL;
251 
252 			if (InChild)
253 				ExitStat = EX_QUIT;
254 			finis(TRUE, ExitStat);
255 		}
256 
257 		/* clean up end of line */
258 		fixcrlf(inp, TRUE);
259 
260 		/* echo command to transcript */
261 		if (e->e_xfp != NULL)
262 			fprintf(e->e_xfp, "<<< %s\n", inp);
263 
264 		if (LogLevel >= 15)
265 			sm_syslog(LOG_INFO, e->e_id,
266 				"<-- %s",
267 				inp);
268 
269 		if (e->e_id == NULL)
270 			sm_setproctitle(TRUE, "%s: %.80s", CurSmtpClient, inp);
271 		else
272 			sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
273 
274 		/* break off command */
275 		for (p = inp; isascii(*p) && isspace(*p); p++)
276 			continue;
277 		cmd = cmdbuf;
278 		while (*p != '\0' &&
279 		       !(isascii(*p) && isspace(*p)) &&
280 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
281 			*cmd++ = *p++;
282 		*cmd = '\0';
283 
284 		/* throw away leading whitespace */
285 		while (isascii(*p) && isspace(*p))
286 			p++;
287 
288 		/* decode command */
289 		for (c = CmdTab; c->cmdname != NULL; c++)
290 		{
291 			if (!strcasecmp(c->cmdname, cmdbuf))
292 				break;
293 		}
294 
295 		/* reset errors */
296 		errno = 0;
297 
298 		/*
299 		**  Process command.
300 		**
301 		**	If we are running as a null server, return 550
302 		**	to everything.
303 		*/
304 
305 		if (nullserver != NULL)
306 		{
307 			switch (c->cmdcode)
308 			{
309 			  case CMDQUIT:
310 			  case CMDHELO:
311 			  case CMDEHLO:
312 			  case CMDNOOP:
313 				/* process normally */
314 				break;
315 
316 			  default:
317 				if (++badcommands > MAXBADCOMMANDS)
318 					sleep(1);
319 				usrerr("550 %s", nullserver);
320 				continue;
321 			}
322 		}
323 
324 		/* non-null server */
325 		switch (c->cmdcode)
326 		{
327 		  case CMDMAIL:
328 		  case CMDEXPN:
329 		  case CMDVRFY:
330 		  case CMDETRN:
331 			lognullconnection = FALSE;
332 		}
333 
334 		switch (c->cmdcode)
335 		{
336 		  case CMDHELO:		/* hello -- introduce yourself */
337 		  case CMDEHLO:		/* extended hello */
338 			if (c->cmdcode == CMDEHLO)
339 			{
340 				protocol = "ESMTP";
341 				SmtpPhase = "server EHLO";
342 			}
343 			else
344 			{
345 				protocol = "SMTP";
346 				SmtpPhase = "server HELO";
347 			}
348 
349 			/* avoid denial-of-service */
350 			checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e);
351 
352 			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
353 			if (gothello)
354 			{
355 				usrerr("503 %s Duplicate HELO/EHLO",
356 					MyHostName);
357 				break;
358 			}
359 
360 			/* check for valid domain name (re 1123 5.2.5) */
361 			if (*p == '\0' && !AllowBogusHELO)
362 			{
363 				usrerr("501 %s requires domain address",
364 					cmdbuf);
365 				break;
366 			}
367 
368 			/* check for long domain name (hides Received: info) */
369 			if (strlen(p) > MAXNAME)
370 			{
371 				usrerr("501 Invalid domain name");
372 				break;
373 			}
374 
375 			for (q = p; *q != '\0'; q++)
376 			{
377 				if (!isascii(*q))
378 					break;
379 				if (isalnum(*q))
380 					continue;
381 				if (isspace(*q))
382 				{
383 					*q = '\0';
384 					break;
385 				}
386 				if (strchr("[].-_#", *q) == NULL)
387 					break;
388 			}
389 			if (*q == '\0')
390 			{
391 				q = "pleased to meet you";
392 				sendinghost = newstr(p);
393 			}
394 			else if (!AllowBogusHELO)
395 			{
396 				usrerr("501 Invalid domain name");
397 				break;
398 			}
399 			else
400 			{
401 				q = "accepting invalid domain name";
402 			}
403 
404 			gothello = TRUE;
405 
406 			/* print HELO response message */
407 			if (c->cmdcode != CMDEHLO || nullserver != NULL)
408 			{
409 				message("250 %s Hello %s, %s",
410 					MyHostName, CurSmtpClient, q);
411 				break;
412 			}
413 
414 			message("250-%s Hello %s, %s",
415 				MyHostName, CurSmtpClient, q);
416 
417 			/* print EHLO features list */
418 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
419 			{
420 				message("250-EXPN");
421 				if (!bitset(PRIV_NOVERB, PrivacyFlags))
422 					message("250-VERB");
423 			}
424 #if MIME8TO7
425 			message("250-8BITMIME");
426 #endif
427 			if (MaxMessageSize > 0)
428 				message("250-SIZE %ld", MaxMessageSize);
429 			else
430 				message("250-SIZE");
431 #if DSN
432 			if (SendMIMEErrors)
433 				message("250-DSN");
434 #endif
435 			message("250-ONEX");
436 			if (!bitset(PRIV_NOETRN, PrivacyFlags))
437 				message("250-ETRN");
438 			message("250-XUSR");
439 			message("250 HELP");
440 			break;
441 
442 		  case CMDMAIL:		/* mail -- designate sender */
443 			SmtpPhase = "server MAIL";
444 
445 			/* check for validity of this command */
446 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
447 			{
448 				usrerr("503 Polite people say HELO first");
449 				break;
450 			}
451 			if (gotmail)
452 			{
453 				usrerr("503 Sender already specified");
454 				break;
455 			}
456 			if (InChild)
457 			{
458 				errno = 0;
459 				syserr("503 Nested MAIL command: MAIL %s", p);
460 				finis(TRUE, ExitStat);
461 			}
462 
463 			/* make sure we know who the sending host is */
464 			if (sendinghost == NULL)
465 				sendinghost = peerhostname;
466 
467 			p = skipword(p, "from");
468 			if (p == NULL)
469 				break;
470 
471 			/* fork a subprocess to process this command */
472 			if (runinchild("SMTP-MAIL", e) > 0)
473 				break;
474 			if (Errors > 0)
475 				goto undo_subproc_no_pm;
476 			if (!gothello)
477 			{
478 				auth_warning(e,
479 					"%s didn't use HELO protocol",
480 					CurSmtpClient);
481 			}
482 #ifdef PICKY_HELO_CHECK
483 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
484 			    (strcasecmp(peerhostname, "localhost") != 0 ||
485 			     strcasecmp(sendinghost, MyHostName) != 0))
486 			{
487 				auth_warning(e, "Host %s claimed to be %s",
488 					CurSmtpClient, sendinghost);
489 			}
490 #endif
491 
492 			if (protocol == NULL)
493 				protocol = "SMTP";
494 			define('r', protocol, e);
495 			define('s', sendinghost, e);
496 			initsys(e);
497 			if (Errors > 0)
498 				goto undo_subproc_no_pm;
499 			nrcpts = 0;
500 			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
501 			sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
502 
503 			/* child -- go do the processing */
504 			if (setjmp(TopFrame) > 0)
505 			{
506 				/* this failed -- undo work */
507  undo_subproc_no_pm:
508 				e->e_flags &= ~EF_PM_NOTIFY;
509  undo_subproc:
510 				if (InChild)
511 				{
512 					QuickAbort = FALSE;
513 					SuprErrs = TRUE;
514 					e->e_flags &= ~EF_FATALERRS;
515 					finis(TRUE, ExitStat);
516 				}
517 				break;
518 			}
519 			QuickAbort = TRUE;
520 
521 			/* must parse sender first */
522 			delimptr = NULL;
523 			setsender(p, e, &delimptr, ' ', FALSE);
524 			if (delimptr != NULL && *delimptr != '\0')
525 				*delimptr++ = '\0';
526 			if (Errors > 0)
527 				goto undo_subproc_no_pm;
528 
529 			/* do config file checking of the sender */
530 			if (rscheck("check_mail", p, NULL, e) != EX_OK ||
531 			    Errors > 0)
532 				goto undo_subproc_no_pm;
533 
534 			/* check for possible spoofing */
535 			if (RealUid != 0 && OpMode == MD_SMTP &&
536 			    !wordinclass(RealUserName, 't') &&
537 			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
538 			    strcmp(e->e_from.q_user, RealUserName) != 0)
539 			{
540 				auth_warning(e, "%s owned process doing -bs",
541 					RealUserName);
542 			}
543 
544 			/* now parse ESMTP arguments */
545 			e->e_msgsize = 0;
546 			p = delimptr;
547 			while (p != NULL && *p != '\0')
548 			{
549 				char *kp;
550 				char *vp = NULL;
551 				extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
552 
553 				/* locate the beginning of the keyword */
554 				while (isascii(*p) && isspace(*p))
555 					p++;
556 				if (*p == '\0')
557 					break;
558 				kp = p;
559 
560 				/* skip to the value portion */
561 				while ((isascii(*p) && isalnum(*p)) || *p == '-')
562 					p++;
563 				if (*p == '=')
564 				{
565 					*p++ = '\0';
566 					vp = p;
567 
568 					/* skip to the end of the value */
569 					while (*p != '\0' && *p != ' ' &&
570 					       !(isascii(*p) && iscntrl(*p)) &&
571 					       *p != '=')
572 						p++;
573 				}
574 
575 				if (*p != '\0')
576 					*p++ = '\0';
577 
578 				if (tTd(19, 1))
579 					printf("MAIL: got arg %s=\"%s\"\n", kp,
580 						vp == NULL ? "<null>" : vp);
581 
582 				mail_esmtp_args(kp, vp, e);
583 				if (Errors > 0)
584 					goto undo_subproc_no_pm;
585 			}
586 			if (Errors > 0)
587 				goto undo_subproc_no_pm;
588 
589 			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
590 			{
591 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
592 					MaxMessageSize);
593 				goto undo_subproc_no_pm;
594 			}
595 
596 			if (!enoughdiskspace(e->e_msgsize))
597 			{
598 				usrerr("452 Insufficient disk space; try again later");
599 				goto undo_subproc_no_pm;
600 			}
601 			if (Errors > 0)
602 				goto undo_subproc_no_pm;
603 			message("250 Sender ok");
604 			gotmail = TRUE;
605 			break;
606 
607 		  case CMDRCPT:		/* rcpt -- designate recipient */
608 			if (!gotmail)
609 			{
610 				usrerr("503 Need MAIL before RCPT");
611 				break;
612 			}
613 			SmtpPhase = "server RCPT";
614 			if (setjmp(TopFrame) > 0)
615 			{
616 				e->e_flags &= ~EF_FATALERRS;
617 				break;
618 			}
619 			QuickAbort = TRUE;
620 			LogUsrErrs = TRUE;
621 
622 			/* limit flooding of our machine */
623 			if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
624 			{
625 				usrerr("452 Too many recipients");
626 				break;
627 			}
628 
629 			if (e->e_sendmode != SM_DELIVER)
630 				e->e_flags |= EF_VRFYONLY;
631 
632 			p = skipword(p, "to");
633 			if (p == NULL)
634 				break;
635 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
636 			if (Errors > 0)
637 				break;
638 			if (a == NULL)
639 			{
640 				usrerr("501 Missing recipient");
641 				break;
642 			}
643 
644 			if (delimptr != NULL && *delimptr != '\0')
645 				*delimptr++ = '\0';
646 
647 			/* do config file checking of the recipient */
648 			if (rscheck("check_rcpt", p, NULL, e) != EX_OK ||
649 			    Errors > 0)
650 				break;
651 
652 			/* now parse ESMTP arguments */
653 			p = delimptr;
654 			while (p != NULL && *p != '\0')
655 			{
656 				char *kp;
657 				char *vp = NULL;
658 				extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
659 
660 				/* locate the beginning of the keyword */
661 				while (isascii(*p) && isspace(*p))
662 					p++;
663 				if (*p == '\0')
664 					break;
665 				kp = p;
666 
667 				/* skip to the value portion */
668 				while ((isascii(*p) && isalnum(*p)) || *p == '-')
669 					p++;
670 				if (*p == '=')
671 				{
672 					*p++ = '\0';
673 					vp = p;
674 
675 					/* skip to the end of the value */
676 					while (*p != '\0' && *p != ' ' &&
677 					       !(isascii(*p) && iscntrl(*p)) &&
678 					       *p != '=')
679 						p++;
680 				}
681 
682 				if (*p != '\0')
683 					*p++ = '\0';
684 
685 				if (tTd(19, 1))
686 					printf("RCPT: got arg %s=\"%s\"\n", kp,
687 						vp == NULL ? "<null>" : vp);
688 
689 				rcpt_esmtp_args(a, kp, vp, e);
690 				if (Errors > 0)
691 					break;
692 			}
693 			if (Errors > 0)
694 				break;
695 
696 			/* save in recipient list after ESMTP mods */
697 			a = recipient(a, &e->e_sendqueue, 0, e);
698 			if (Errors > 0)
699 				break;
700 
701 			/* no errors during parsing, but might be a duplicate */
702 			e->e_to = a->q_paddr;
703 			if (!bitset(QBADADDR, a->q_flags))
704 			{
705 				message("250 Recipient ok%s",
706 					bitset(QQUEUEUP, a->q_flags) ?
707 						" (will queue)" : "");
708 				nrcpts++;
709 			}
710 			else
711 			{
712 				/* punt -- should keep message in ADDRESS.... */
713 				usrerr("550 Addressee unknown");
714 			}
715 			break;
716 
717 		  case CMDDATA:		/* data -- text of mail */
718 			SmtpPhase = "server DATA";
719 			if (!gotmail)
720 			{
721 				usrerr("503 Need MAIL command");
722 				break;
723 			}
724 			else if (nrcpts <= 0)
725 			{
726 				usrerr("503 Need RCPT (recipient)");
727 				break;
728 			}
729 
730 			/* put back discard bit */
731 			if (discard)
732 				e->e_flags |= EF_DISCARD;
733 
734 			/* check to see if we need to re-expand aliases */
735 			/* also reset QBADADDR on already-diagnosted addrs */
736 			doublequeue = FALSE;
737 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
738 			{
739 				if (bitset(QVERIFIED, a->q_flags) &&
740 				    !bitset(EF_DISCARD, e->e_flags))
741 				{
742 					/* need to re-expand aliases */
743 					doublequeue = TRUE;
744 				}
745 				if (bitset(QBADADDR, a->q_flags))
746 				{
747 					/* make this "go away" */
748 					a->q_flags |= QDONTSEND;
749 					a->q_flags &= ~QBADADDR;
750 				}
751 			}
752 
753 			/* collect the text of the message */
754 			SmtpPhase = "collect";
755 			buffer_errors();
756 			collect(InChannel, TRUE, NULL, e);
757 			if (Errors > 0)
758 			{
759 				flush_errors(TRUE);
760 				buffer_errors();
761 				goto abortmessage;
762 			}
763 
764 			/* make sure we actually do delivery */
765 			e->e_flags &= ~EF_CLRQUEUE;
766 
767 			/* from now on, we have to operate silently */
768 			buffer_errors();
769 			e->e_errormode = EM_MAIL;
770 
771 			/*
772 			**  Arrange to send to everyone.
773 			**	If sending to multiple people, mail back
774 			**		errors rather than reporting directly.
775 			**	In any case, don't mail back errors for
776 			**		anything that has happened up to
777 			**		now (the other end will do this).
778 			**	Truncate our transcript -- the mail has gotten
779 			**		to us successfully, and if we have
780 			**		to mail this back, it will be easier
781 			**		on the reader.
782 			**	Then send to everyone.
783 			**	Finally give a reply code.  If an error has
784 			**		already been given, don't mail a
785 			**		message back.
786 			**	We goose error returns by clearing error bit.
787 			*/
788 
789 			SmtpPhase = "delivery";
790 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
791 			id = e->e_id;
792 
793 			if (doublequeue)
794 			{
795 				/* make sure it is in the queue */
796 				queueup(e, FALSE);
797 			}
798 			else
799 			{
800 				/* send to all recipients */
801 				sendall(e, SM_DEFAULT);
802 			}
803 			e->e_to = NULL;
804 
805 			/* issue success message */
806 			message("250 %s Message accepted for delivery", id);
807 
808 			/* if we just queued, poke it */
809 			if (doublequeue &&
810 			    e->e_sendmode != SM_QUEUE &&
811 			    e->e_sendmode != SM_DEFER)
812 			{
813 				CurrentLA = getla();
814 
815 				if (!shouldqueue(e->e_msgpriority, e->e_ctime))
816 				{
817 					unlockqueue(e);
818 					(void) dowork(id, TRUE, TRUE, e);
819 				}
820 			}
821 
822   abortmessage:
823 			/* if in a child, pop back to our parent */
824 			if (InChild)
825 				finis(TRUE, ExitStat);
826 
827 			/* clean up a bit */
828 			gotmail = FALSE;
829 			dropenvelope(e, TRUE);
830 			CurEnv = e = newenvelope(e, CurEnv);
831 			e->e_flags = BlankEnvelope.e_flags;
832 			break;
833 
834 		  case CMDRSET:		/* rset -- reset state */
835 			if (tTd(94, 100))
836 				message("451 Test failure");
837 			else
838 				message("250 Reset state");
839 
840 			/* arrange to ignore any current send list */
841 			e->e_sendqueue = NULL;
842 			e->e_flags |= EF_CLRQUEUE;
843 			if (InChild)
844 				finis(TRUE, ExitStat);
845 
846 			/* clean up a bit */
847 			gotmail = FALSE;
848 			SuprErrs = TRUE;
849 			dropenvelope(e, TRUE);
850 			CurEnv = e = newenvelope(e, CurEnv);
851 			break;
852 
853 		  case CMDVRFY:		/* vrfy -- verify address */
854 		  case CMDEXPN:		/* expn -- expand address */
855 			checksmtpattack(&nverifies, MAXVRFYCOMMANDS,
856 				c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e);
857 			vrfy = c->cmdcode == CMDVRFY;
858 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
859 						PrivacyFlags))
860 			{
861 				if (vrfy)
862 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
863 				else
864 					message("502 Sorry, we do not allow this operation");
865 				if (LogLevel > 5)
866 					sm_syslog(LOG_INFO, e->e_id,
867 						"%.100s: %s [rejected]",
868 						CurSmtpClient,
869 						shortenstring(inp, MAXSHORTSTR));
870 				break;
871 			}
872 			else if (!gothello &&
873 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
874 						PrivacyFlags))
875 			{
876 				usrerr("503 I demand that you introduce yourself first");
877 				break;
878 			}
879 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
880 				break;
881 			if (Errors > 0)
882 				goto undo_subproc;
883 			if (LogLevel > 5)
884 				sm_syslog(LOG_INFO, e->e_id,
885 					"%.100s: %s",
886 					CurSmtpClient,
887 					shortenstring(inp, MAXSHORTSTR));
888 			if (setjmp(TopFrame) > 0)
889 				goto undo_subproc;
890 			QuickAbort = TRUE;
891 			vrfyqueue = NULL;
892 			if (vrfy)
893 				e->e_flags |= EF_VRFYONLY;
894 			while (*p != '\0' && isascii(*p) && isspace(*p))
895 				p++;
896 			if (*p == '\0')
897 			{
898 				usrerr("501 Argument required");
899 			}
900 			else
901 			{
902 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
903 			}
904 			if (Errors > 0)
905 				goto undo_subproc;
906 			if (vrfyqueue == NULL)
907 			{
908 				usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
909 			}
910 			while (vrfyqueue != NULL)
911 			{
912 				extern void printvrfyaddr __P((ADDRESS *, bool, bool));
913 
914 				a = vrfyqueue;
915 				while ((a = a->q_next) != NULL &&
916 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
917 					continue;
918 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
919 					printvrfyaddr(vrfyqueue, a == NULL, vrfy);
920 				vrfyqueue = vrfyqueue->q_next;
921 			}
922 			if (InChild)
923 				finis(TRUE, ExitStat);
924 			break;
925 
926 		  case CMDETRN:		/* etrn -- force queue flush */
927 			if (bitset(PRIV_NOETRN, PrivacyFlags))
928 			{
929 				message("502 Sorry, we do not allow this operation");
930 				if (LogLevel > 5)
931 					sm_syslog(LOG_INFO, e->e_id,
932 						"%.100s: %s [rejected]",
933 						CurSmtpClient,
934 						shortenstring(inp, MAXSHORTSTR));
935 				break;
936 			}
937 
938 			if (strlen(p) <= 0)
939 			{
940 				usrerr("500 Parameter required");
941 				break;
942 			}
943 
944 			/* crude way to avoid denial-of-service attacks */
945 			checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e);
946 
947 			if (LogLevel > 5)
948 				sm_syslog(LOG_INFO, e->e_id,
949 					"%.100s: ETRN %s",
950 					CurSmtpClient,
951 					shortenstring(p, MAXSHORTSTR));
952 
953 			id = p;
954 			if (*id == '@')
955 				id++;
956 			else
957 				*--id = '@';
958 
959 			if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
960 			{
961 				syserr("500 ETRN out of memory");
962 				break;
963 			}
964 			new->queue_match = id;
965 			new->queue_next = NULL;
966 			QueueLimitRecipient = new;
967 			ok = runqueue(TRUE, TRUE);
968 			free(QueueLimitRecipient);
969 			QueueLimitRecipient = NULL;
970 			if (ok && Errors == 0)
971 				message("250 Queuing for node %s started", p);
972 			break;
973 
974 		  case CMDHELP:		/* help -- give user info */
975 			help(p);
976 			break;
977 
978 		  case CMDNOOP:		/* noop -- do nothing */
979 			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e);
980 			message("250 OK");
981 			break;
982 
983 		  case CMDQUIT:		/* quit -- leave mail */
984 			message("221 %s closing connection", MyHostName);
985 
986 doquit:
987 			/* arrange to ignore any current send list */
988 			e->e_sendqueue = NULL;
989 
990 			/* avoid future 050 messages */
991 			disconnect(1, e);
992 
993 			if (InChild)
994 				ExitStat = EX_QUIT;
995 			if (lognullconnection && LogLevel > 5)
996 				sm_syslog(LOG_INFO, NULL,
997 					"Null connection from %.100s",
998 					CurSmtpClient);
999 			finis(TRUE, ExitStat);
1000 
1001 		  case CMDVERB:		/* set verbose mode */
1002 			if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
1003 			    bitset(PRIV_NOVERB, PrivacyFlags))
1004 			{
1005 				/* this would give out the same info */
1006 				message("502 Verbose unavailable");
1007 				break;
1008 			}
1009 			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e);
1010 			Verbose = 1;
1011 			e->e_sendmode = SM_DELIVER;
1012 			message("250 Verbose mode");
1013 			break;
1014 
1015 		  case CMDONEX:		/* doing one transaction only */
1016 			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e);
1017 			OneXact = TRUE;
1018 			message("250 Only one transaction");
1019 			break;
1020 
1021 		  case CMDXUSR:		/* initial (user) submission */
1022 			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e);
1023 			UserSubmission = TRUE;
1024 			message("250 Initial submission");
1025 			break;
1026 
1027 # if SMTPDEBUG
1028 		  case CMDDBGQSHOW:	/* show queues */
1029 			printf("Send Queue=");
1030 			printaddr(e->e_sendqueue, TRUE);
1031 			break;
1032 
1033 		  case CMDDBGDEBUG:	/* set debug mode */
1034 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
1035 			tTflag(p);
1036 			message("200 Debug set");
1037 			break;
1038 
1039 # else /* not SMTPDEBUG */
1040 		  case CMDDBGQSHOW:	/* show queues */
1041 		  case CMDDBGDEBUG:	/* set debug mode */
1042 # endif /* SMTPDEBUG */
1043 		  case CMDLOGBOGUS:	/* bogus command */
1044 			if (LogLevel > 0)
1045 				sm_syslog(LOG_CRIT, e->e_id,
1046 				    "\"%s\" command from %.100s (%.100s)",
1047 				    c->cmdname, CurSmtpClient,
1048 				    anynet_ntoa(&RealHostAddr));
1049 			/* FALL THROUGH */
1050 
1051 		  case CMDERROR:	/* unknown command */
1052 			if (++badcommands > MAXBADCOMMANDS)
1053 			{
1054 				message("421 %s Too many bad commands; closing connection",
1055 					MyHostName);
1056 				goto doquit;
1057 			}
1058 
1059 			usrerr("500 Command unrecognized: \"%s\"",
1060 				shortenstring(inp, MAXSHORTSTR));
1061 			break;
1062 
1063 		  default:
1064 			errno = 0;
1065 			syserr("500 smtp: unknown code %d", c->cmdcode);
1066 			break;
1067 		}
1068 	}
1069 }
1070 /*
1071 **  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
1072 **
1073 **	Parameters:
1074 **		pcounter -- pointer to a counter for this command.
1075 **		maxcount -- maximum value for this counter before we
1076 **			slow down.
1077 **		cname -- command name for logging.
1078 **		e -- the current envelope.
1079 **
1080 **	Returns:
1081 **		none.
1082 **
1083 **	Side Effects:
1084 **		Slows down if we seem to be under attack.
1085 */
1086 
1087 void
1088 checksmtpattack(pcounter, maxcount, cname, e)
1089 	volatile int *pcounter;
1090 	int maxcount;
1091 	char *cname;
1092 	ENVELOPE *e;
1093 {
1094 	if (++(*pcounter) >= maxcount)
1095 	{
1096 		if (*pcounter == maxcount && LogLevel > 5)
1097 		{
1098 			sm_syslog(LOG_INFO, e->e_id,
1099 				"%.100s: %.40s attack?",
1100 			       CurSmtpClient, cname);
1101 		}
1102 		sleep(*pcounter / maxcount);
1103 	}
1104 }
1105 /*
1106 **  SKIPWORD -- skip a fixed word.
1107 **
1108 **	Parameters:
1109 **		p -- place to start looking.
1110 **		w -- word to skip.
1111 **
1112 **	Returns:
1113 **		p following w.
1114 **		NULL on error.
1115 **
1116 **	Side Effects:
1117 **		clobbers the p data area.
1118 */
1119 
1120 static char *
1121 skipword(p, w)
1122 	register char *volatile p;
1123 	char *w;
1124 {
1125 	register char *q;
1126 	char *firstp = p;
1127 
1128 	/* find beginning of word */
1129 	while (isascii(*p) && isspace(*p))
1130 		p++;
1131 	q = p;
1132 
1133 	/* find end of word */
1134 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
1135 		p++;
1136 	while (isascii(*p) && isspace(*p))
1137 		*p++ = '\0';
1138 	if (*p != ':')
1139 	{
1140 	  syntax:
1141 		usrerr("501 Syntax error in parameters scanning \"%s\"",
1142 			shortenstring(firstp, MAXSHORTSTR));
1143 		return (NULL);
1144 	}
1145 	*p++ = '\0';
1146 	while (isascii(*p) && isspace(*p))
1147 		p++;
1148 
1149 	if (*p == '\0')
1150 		goto syntax;
1151 
1152 	/* see if the input word matches desired word */
1153 	if (strcasecmp(q, w))
1154 		goto syntax;
1155 
1156 	return (p);
1157 }
1158 /*
1159 **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
1160 **
1161 **	Parameters:
1162 **		kp -- the parameter key.
1163 **		vp -- the value of that parameter.
1164 **		e -- the envelope.
1165 **
1166 **	Returns:
1167 **		none.
1168 */
1169 
1170 void
1171 mail_esmtp_args(kp, vp, e)
1172 	char *kp;
1173 	char *vp;
1174 	ENVELOPE *e;
1175 {
1176 	if (strcasecmp(kp, "size") == 0)
1177 	{
1178 		if (vp == NULL)
1179 		{
1180 			usrerr("501 SIZE requires a value");
1181 			/* NOTREACHED */
1182 		}
1183 # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
1184 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
1185 # else
1186 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
1187 # endif
1188 	}
1189 	else if (strcasecmp(kp, "body") == 0)
1190 	{
1191 		if (vp == NULL)
1192 		{
1193 			usrerr("501 BODY requires a value");
1194 			/* NOTREACHED */
1195 		}
1196 		else if (strcasecmp(vp, "8bitmime") == 0)
1197 		{
1198 			SevenBitInput = FALSE;
1199 		}
1200 		else if (strcasecmp(vp, "7bit") == 0)
1201 		{
1202 			SevenBitInput = TRUE;
1203 		}
1204 		else
1205 		{
1206 			usrerr("501 Unknown BODY type %s",
1207 				vp);
1208 			/* NOTREACHED */
1209 		}
1210 		e->e_bodytype = newstr(vp);
1211 	}
1212 	else if (strcasecmp(kp, "envid") == 0)
1213 	{
1214 		if (vp == NULL)
1215 		{
1216 			usrerr("501 ENVID requires a value");
1217 			/* NOTREACHED */
1218 		}
1219 		if (!xtextok(vp))
1220 		{
1221 			usrerr("501 Syntax error in ENVID parameter value");
1222 			/* NOTREACHED */
1223 		}
1224 		if (e->e_envid != NULL)
1225 		{
1226 			usrerr("501 Duplicate ENVID parameter");
1227 			/* NOTREACHED */
1228 		}
1229 		e->e_envid = newstr(vp);
1230 	}
1231 	else if (strcasecmp(kp, "ret") == 0)
1232 	{
1233 		if (vp == NULL)
1234 		{
1235 			usrerr("501 RET requires a value");
1236 			/* NOTREACHED */
1237 		}
1238 		if (bitset(EF_RET_PARAM, e->e_flags))
1239 		{
1240 			usrerr("501 Duplicate RET parameter");
1241 			/* NOTREACHED */
1242 		}
1243 		e->e_flags |= EF_RET_PARAM;
1244 		if (strcasecmp(vp, "hdrs") == 0)
1245 			e->e_flags |= EF_NO_BODY_RETN;
1246 		else if (strcasecmp(vp, "full") != 0)
1247 		{
1248 			usrerr("501 Bad argument \"%s\" to RET", vp);
1249 			/* NOTREACHED */
1250 		}
1251 	}
1252 	else
1253 	{
1254 		usrerr("501 %s parameter unrecognized", kp);
1255 		/* NOTREACHED */
1256 	}
1257 }
1258 /*
1259 **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
1260 **
1261 **	Parameters:
1262 **		a -- the address corresponding to the To: parameter.
1263 **		kp -- the parameter key.
1264 **		vp -- the value of that parameter.
1265 **		e -- the envelope.
1266 **
1267 **	Returns:
1268 **		none.
1269 */
1270 
1271 void
1272 rcpt_esmtp_args(a, kp, vp, e)
1273 	ADDRESS *a;
1274 	char *kp;
1275 	char *vp;
1276 	ENVELOPE *e;
1277 {
1278 	if (strcasecmp(kp, "notify") == 0)
1279 	{
1280 		char *p;
1281 
1282 		if (vp == NULL)
1283 		{
1284 			usrerr("501 NOTIFY requires a value");
1285 			/* NOTREACHED */
1286 		}
1287 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
1288 		a->q_flags |= QHASNOTIFY;
1289 		if (strcasecmp(vp, "never") == 0)
1290 			return;
1291 		for (p = vp; p != NULL; vp = p)
1292 		{
1293 			p = strchr(p, ',');
1294 			if (p != NULL)
1295 				*p++ = '\0';
1296 			if (strcasecmp(vp, "success") == 0)
1297 				a->q_flags |= QPINGONSUCCESS;
1298 			else if (strcasecmp(vp, "failure") == 0)
1299 				a->q_flags |= QPINGONFAILURE;
1300 			else if (strcasecmp(vp, "delay") == 0)
1301 				a->q_flags |= QPINGONDELAY;
1302 			else
1303 			{
1304 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
1305 					vp);
1306 				/* NOTREACHED */
1307 			}
1308 		}
1309 	}
1310 	else if (strcasecmp(kp, "orcpt") == 0)
1311 	{
1312 		if (vp == NULL)
1313 		{
1314 			usrerr("501 ORCPT requires a value");
1315 			/* NOTREACHED */
1316 		}
1317 		if (strchr(vp, ';') == NULL || !xtextok(vp))
1318 		{
1319 			usrerr("501 Syntax error in ORCPT parameter value");
1320 			/* NOTREACHED */
1321 		}
1322 		if (a->q_orcpt != NULL)
1323 		{
1324 			usrerr("501 Duplicate ORCPT parameter");
1325 			/* NOTREACHED */
1326 		}
1327 		a->q_orcpt = newstr(vp);
1328 	}
1329 	else
1330 	{
1331 		usrerr("501 %s parameter unrecognized", kp);
1332 		/* NOTREACHED */
1333 	}
1334 }
1335 /*
1336 **  PRINTVRFYADDR -- print an entry in the verify queue
1337 **
1338 **	Parameters:
1339 **		a -- the address to print
1340 **		last -- set if this is the last one.
1341 **		vrfy -- set if this is a VRFY command.
1342 **
1343 **	Returns:
1344 **		none.
1345 **
1346 **	Side Effects:
1347 **		Prints the appropriate 250 codes.
1348 */
1349 
1350 void
1351 printvrfyaddr(a, last, vrfy)
1352 	register ADDRESS *a;
1353 	bool last;
1354 	bool vrfy;
1355 {
1356 	char fmtbuf[20];
1357 
1358 	if (vrfy && a->q_mailer != NULL &&
1359 	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
1360 		strcpy(fmtbuf, "252");
1361 	else
1362 		strcpy(fmtbuf, "250");
1363 	fmtbuf[3] = last ? ' ' : '-';
1364 
1365 	if (a->q_fullname == NULL)
1366 	{
1367 		if (strchr(a->q_user, '@') == NULL)
1368 			strcpy(&fmtbuf[4], "<%s@%s>");
1369 		else
1370 			strcpy(&fmtbuf[4], "<%s>");
1371 		message(fmtbuf, a->q_user, MyHostName);
1372 	}
1373 	else
1374 	{
1375 		if (strchr(a->q_user, '@') == NULL)
1376 			strcpy(&fmtbuf[4], "%s <%s@%s>");
1377 		else
1378 			strcpy(&fmtbuf[4], "%s <%s>");
1379 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
1380 	}
1381 }
1382 /*
1383 **  RUNINCHILD -- return twice -- once in the child, then in the parent again
1384 **
1385 **	Parameters:
1386 **		label -- a string used in error messages
1387 **
1388 **	Returns:
1389 **		zero in the child
1390 **		one in the parent
1391 **
1392 **	Side Effects:
1393 **		none.
1394 */
1395 
1396 int
1397 runinchild(label, e)
1398 	char *label;
1399 	register ENVELOPE *e;
1400 {
1401 	pid_t childpid;
1402 
1403 	if (!OneXact)
1404 	{
1405 		/*
1406 		**  Disable child process reaping, in case ETRN has preceeded
1407 		**  MAIL command, and then fork.
1408 		*/
1409 
1410 		(void) blocksignal(SIGCHLD);
1411 
1412 		childpid = dofork();
1413 		if (childpid < 0)
1414 		{
1415 			syserr("451 %s: cannot fork", label);
1416 			(void) releasesignal(SIGCHLD);
1417 			return (1);
1418 		}
1419 		if (childpid > 0)
1420 		{
1421 			auto int st;
1422 
1423 			/* parent -- wait for child to complete */
1424 			sm_setproctitle(TRUE, "server %s child wait", CurSmtpClient);
1425 			st = waitfor(childpid);
1426 			if (st == -1)
1427 				syserr("451 %s: lost child", label);
1428 			else if (!WIFEXITED(st))
1429 				syserr("451 %s: died on signal %d",
1430 					label, st & 0177);
1431 
1432 			/* if we exited on a QUIT command, complete the process */
1433 			if (WEXITSTATUS(st) == EX_QUIT)
1434 			{
1435 				disconnect(1, e);
1436 				finis(TRUE, ExitStat);
1437 			}
1438 
1439 			/* restore the child signal */
1440 			(void) releasesignal(SIGCHLD);
1441 
1442 			return (1);
1443 		}
1444 		else
1445 		{
1446 			/* child */
1447 			InChild = TRUE;
1448 			QuickAbort = FALSE;
1449 			clearenvelope(e, FALSE);
1450 			(void) setsignal(SIGCHLD, SIG_DFL);
1451 			(void) releasesignal(SIGCHLD);
1452 		}
1453 	}
1454 	return (0);
1455 }
1456 
1457 # endif /* SMTP */
1458 /*
1459 **  HELP -- implement the HELP command.
1460 **
1461 **	Parameters:
1462 **		topic -- the topic we want help for.
1463 **
1464 **	Returns:
1465 **		none.
1466 **
1467 **	Side Effects:
1468 **		outputs the help file to message output.
1469 */
1470 
1471 void
1472 help(topic)
1473 	char *topic;
1474 {
1475 	register FILE *hf;
1476 	int len;
1477 	bool noinfo;
1478 	int sff = SFF_OPENASROOT|SFF_REGONLY;
1479 	char buf[MAXLINE];
1480 	extern char Version[];
1481 
1482 	if (DontLockReadFiles)
1483 		sff |= SFF_NOLOCK;
1484 	if (!bitset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
1485 		sff |= SFF_SAFEDIRPATH;
1486 
1487 	if (HelpFile == NULL ||
1488 	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
1489 	{
1490 		/* no help */
1491 		errno = 0;
1492 		message("502 Sendmail %s -- HELP not implemented", Version);
1493 		return;
1494 	}
1495 
1496 	if (topic == NULL || *topic == '\0')
1497 	{
1498 		topic = "smtp";
1499 		message("214-This is Sendmail version %s", Version);
1500 		noinfo = FALSE;
1501 	}
1502 	else
1503 	{
1504 		makelower(topic);
1505 		noinfo = TRUE;
1506 	}
1507 
1508 	len = strlen(topic);
1509 
1510 	while (fgets(buf, sizeof buf, hf) != NULL)
1511 	{
1512 		if (strncmp(buf, topic, len) == 0)
1513 		{
1514 			register char *p;
1515 
1516 			p = strchr(buf, '\t');
1517 			if (p == NULL)
1518 				p = buf;
1519 			else
1520 				p++;
1521 			fixcrlf(p, TRUE);
1522 			message("214-%s", p);
1523 			noinfo = FALSE;
1524 		}
1525 	}
1526 
1527 	if (noinfo)
1528 		message("504 HELP topic \"%.10s\" unknown", topic);
1529 	else
1530 		message("214 End of HELP info");
1531 	(void) fclose(hf);
1532 }
1533