xref: /freebsd/contrib/sendmail/src/srvrsmtp.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
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.181 (Berkeley) 6/15/98 (with SMTP)";
18 #else
19 static char sccsid[] = "@(#)srvrsmtp.c	8.181 (Berkeley) 6/15/98 (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 	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 	setproctitle("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 		setproctitle("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();
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 			setproctitle("%s: %.80s", CurSmtpClient, inp);
271 		else
272 			setproctitle("%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();
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 			setproctitle("%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();
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 (a == NULL || Errors > 0)
637 				break;
638 			if (delimptr != NULL && *delimptr != '\0')
639 				*delimptr++ = '\0';
640 
641 			/* do config file checking of the recipient */
642 			if (rscheck("check_rcpt", p, NULL, e) != EX_OK ||
643 			    Errors > 0)
644 				break;
645 
646 			/* now parse ESMTP arguments */
647 			p = delimptr;
648 			while (p != NULL && *p != '\0')
649 			{
650 				char *kp;
651 				char *vp = NULL;
652 				extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
653 
654 				/* locate the beginning of the keyword */
655 				while (isascii(*p) && isspace(*p))
656 					p++;
657 				if (*p == '\0')
658 					break;
659 				kp = p;
660 
661 				/* skip to the value portion */
662 				while ((isascii(*p) && isalnum(*p)) || *p == '-')
663 					p++;
664 				if (*p == '=')
665 				{
666 					*p++ = '\0';
667 					vp = p;
668 
669 					/* skip to the end of the value */
670 					while (*p != '\0' && *p != ' ' &&
671 					       !(isascii(*p) && iscntrl(*p)) &&
672 					       *p != '=')
673 						p++;
674 				}
675 
676 				if (*p != '\0')
677 					*p++ = '\0';
678 
679 				if (tTd(19, 1))
680 					printf("RCPT: got arg %s=\"%s\"\n", kp,
681 						vp == NULL ? "<null>" : vp);
682 
683 				rcpt_esmtp_args(a, kp, vp, e);
684 				if (Errors > 0)
685 					break;
686 			}
687 			if (Errors > 0)
688 				break;
689 
690 			/* save in recipient list after ESMTP mods */
691 			a = recipient(a, &e->e_sendqueue, 0, e);
692 			if (Errors > 0)
693 				break;
694 
695 			/* no errors during parsing, but might be a duplicate */
696 			e->e_to = a->q_paddr;
697 			if (!bitset(QBADADDR, a->q_flags))
698 			{
699 				message("250 Recipient ok%s",
700 					bitset(QQUEUEUP, a->q_flags) ?
701 						" (will queue)" : "");
702 				nrcpts++;
703 			}
704 			else
705 			{
706 				/* punt -- should keep message in ADDRESS.... */
707 				usrerr("550 Addressee unknown");
708 			}
709 			break;
710 
711 		  case CMDDATA:		/* data -- text of mail */
712 			SmtpPhase = "server DATA";
713 			if (!gotmail)
714 			{
715 				usrerr("503 Need MAIL command");
716 				break;
717 			}
718 			else if (nrcpts <= 0)
719 			{
720 				usrerr("503 Need RCPT (recipient)");
721 				break;
722 			}
723 
724 			/* put back discard bit */
725 			if (discard)
726 				e->e_flags |= EF_DISCARD;
727 
728 			/* check to see if we need to re-expand aliases */
729 			/* also reset QBADADDR on already-diagnosted addrs */
730 			doublequeue = FALSE;
731 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
732 			{
733 				if (bitset(QVERIFIED, a->q_flags) &&
734 				    !bitset(EF_DISCARD, e->e_flags))
735 				{
736 					/* need to re-expand aliases */
737 					doublequeue = TRUE;
738 				}
739 				if (bitset(QBADADDR, a->q_flags))
740 				{
741 					/* make this "go away" */
742 					a->q_flags |= QDONTSEND;
743 					a->q_flags &= ~QBADADDR;
744 				}
745 			}
746 
747 			/* collect the text of the message */
748 			SmtpPhase = "collect";
749 			buffer_errors();
750 			collect(InChannel, TRUE, NULL, e);
751 			if (Errors > 0)
752 			{
753 				flush_errors(TRUE);
754 				buffer_errors();
755 				goto abortmessage;
756 			}
757 
758 			/* make sure we actually do delivery */
759 			e->e_flags &= ~EF_CLRQUEUE;
760 
761 			/* from now on, we have to operate silently */
762 			buffer_errors();
763 			e->e_errormode = EM_MAIL;
764 
765 			/*
766 			**  Arrange to send to everyone.
767 			**	If sending to multiple people, mail back
768 			**		errors rather than reporting directly.
769 			**	In any case, don't mail back errors for
770 			**		anything that has happened up to
771 			**		now (the other end will do this).
772 			**	Truncate our transcript -- the mail has gotten
773 			**		to us successfully, and if we have
774 			**		to mail this back, it will be easier
775 			**		on the reader.
776 			**	Then send to everyone.
777 			**	Finally give a reply code.  If an error has
778 			**		already been given, don't mail a
779 			**		message back.
780 			**	We goose error returns by clearing error bit.
781 			*/
782 
783 			SmtpPhase = "delivery";
784 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
785 			id = e->e_id;
786 
787 			if (doublequeue)
788 			{
789 				/* make sure it is in the queue */
790 				queueup(e, FALSE);
791 			}
792 			else
793 			{
794 				/* send to all recipients */
795 				sendall(e, SM_DEFAULT);
796 			}
797 			e->e_to = NULL;
798 
799 			/* issue success message */
800 			message("250 %s Message accepted for delivery", id);
801 
802 			/* if we just queued, poke it */
803 			if (doublequeue &&
804 			    e->e_sendmode != SM_QUEUE &&
805 			    e->e_sendmode != SM_DEFER)
806 			{
807 				CurrentLA = getla();
808 
809 				if (!shouldqueue(e->e_msgpriority, e->e_ctime))
810 				{
811 					extern pid_t dowork __P((char *, bool, bool, ENVELOPE *));
812 
813 					unlockqueue(e);
814 					(void) dowork(id, TRUE, TRUE, e);
815 				}
816 			}
817 
818   abortmessage:
819 			/* if in a child, pop back to our parent */
820 			if (InChild)
821 				finis();
822 
823 			/* clean up a bit */
824 			gotmail = FALSE;
825 			dropenvelope(e, TRUE);
826 			CurEnv = e = newenvelope(e, CurEnv);
827 			e->e_flags = BlankEnvelope.e_flags;
828 			break;
829 
830 		  case CMDRSET:		/* rset -- reset state */
831 			if (tTd(94, 100))
832 				message("451 Test failure");
833 			else
834 				message("250 Reset state");
835 
836 			/* arrange to ignore any current send list */
837 			e->e_sendqueue = NULL;
838 			e->e_flags |= EF_CLRQUEUE;
839 			if (InChild)
840 				finis();
841 
842 			/* clean up a bit */
843 			gotmail = FALSE;
844 			SuprErrs = TRUE;
845 			dropenvelope(e, TRUE);
846 			CurEnv = e = newenvelope(e, CurEnv);
847 			break;
848 
849 		  case CMDVRFY:		/* vrfy -- verify address */
850 		  case CMDEXPN:		/* expn -- expand address */
851 			checksmtpattack(&nverifies, MAXVRFYCOMMANDS,
852 				c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e);
853 			vrfy = c->cmdcode == CMDVRFY;
854 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
855 						PrivacyFlags))
856 			{
857 				if (vrfy)
858 					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
859 				else
860 					message("502 Sorry, we do not allow this operation");
861 				if (LogLevel > 5)
862 					sm_syslog(LOG_INFO, e->e_id,
863 						"%.100s: %s [rejected]",
864 						CurSmtpClient,
865 						shortenstring(inp, MAXSHORTSTR));
866 				break;
867 			}
868 			else if (!gothello &&
869 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
870 						PrivacyFlags))
871 			{
872 				usrerr("503 I demand that you introduce yourself first");
873 				break;
874 			}
875 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
876 				break;
877 			if (Errors > 0)
878 				goto undo_subproc;
879 			if (LogLevel > 5)
880 				sm_syslog(LOG_INFO, e->e_id,
881 					"%.100s: %s",
882 					CurSmtpClient,
883 					shortenstring(inp, MAXSHORTSTR));
884 			if (setjmp(TopFrame) > 0)
885 				goto undo_subproc;
886 			QuickAbort = TRUE;
887 			vrfyqueue = NULL;
888 			if (vrfy)
889 				e->e_flags |= EF_VRFYONLY;
890 			while (*p != '\0' && isascii(*p) && isspace(*p))
891 				p++;
892 			if (*p == '\0')
893 			{
894 				usrerr("501 Argument required");
895 			}
896 			else
897 			{
898 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
899 			}
900 			if (Errors > 0)
901 				goto undo_subproc;
902 			if (vrfyqueue == NULL)
903 			{
904 				usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
905 			}
906 			while (vrfyqueue != NULL)
907 			{
908 				extern void printvrfyaddr __P((ADDRESS *, bool, bool));
909 
910 				a = vrfyqueue;
911 				while ((a = a->q_next) != NULL &&
912 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
913 					continue;
914 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
915 					printvrfyaddr(vrfyqueue, a == NULL, vrfy);
916 				vrfyqueue = vrfyqueue->q_next;
917 			}
918 			if (InChild)
919 				finis();
920 			break;
921 
922 		  case CMDETRN:		/* etrn -- force queue flush */
923 			if (bitset(PRIV_NOETRN, PrivacyFlags))
924 			{
925 				message("502 Sorry, we do not allow this operation");
926 				if (LogLevel > 5)
927 					sm_syslog(LOG_INFO, e->e_id,
928 						"%.100s: %s [rejected]",
929 						CurSmtpClient,
930 						shortenstring(inp, MAXSHORTSTR));
931 				break;
932 			}
933 
934 			if (strlen(p) <= 0)
935 			{
936 				usrerr("500 Parameter required");
937 				break;
938 			}
939 
940 			/* crude way to avoid denial-of-service attacks */
941 			checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e);
942 
943 			if (LogLevel > 5)
944 				sm_syslog(LOG_INFO, e->e_id,
945 					"%.100s: ETRN %s",
946 					CurSmtpClient,
947 					shortenstring(p, MAXSHORTSTR));
948 
949 			id = p;
950 			if (*id == '@')
951 				id++;
952 			else
953 				*--id = '@';
954 
955 			if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
956 			{
957 				syserr("500 ETRN out of memory");
958 				break;
959 			}
960 			new->queue_match = id;
961 			new->queue_next = NULL;
962 			QueueLimitRecipient = new;
963 			ok = runqueue(TRUE, TRUE);
964 			free(QueueLimitRecipient);
965 			QueueLimitRecipient = NULL;
966 			if (ok && Errors == 0)
967 				message("250 Queuing for node %s started", p);
968 			break;
969 
970 		  case CMDHELP:		/* help -- give user info */
971 			help(p);
972 			break;
973 
974 		  case CMDNOOP:		/* noop -- do nothing */
975 			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e);
976 			message("250 OK");
977 			break;
978 
979 		  case CMDQUIT:		/* quit -- leave mail */
980 			message("221 %s closing connection", MyHostName);
981 
982 doquit:
983 			/* arrange to ignore any current send list */
984 			e->e_sendqueue = NULL;
985 
986 			/* avoid future 050 messages */
987 			disconnect(1, e);
988 
989 			if (InChild)
990 				ExitStat = EX_QUIT;
991 			if (lognullconnection && LogLevel > 5)
992 				sm_syslog(LOG_INFO, NULL,
993 					"Null connection from %.100s",
994 					CurSmtpClient);
995 			finis();
996 
997 		  case CMDVERB:		/* set verbose mode */
998 			if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
999 			    bitset(PRIV_NOVERB, PrivacyFlags))
1000 			{
1001 				/* this would give out the same info */
1002 				message("502 Verbose unavailable");
1003 				break;
1004 			}
1005 			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e);
1006 			Verbose = 1;
1007 			e->e_sendmode = SM_DELIVER;
1008 			message("250 Verbose mode");
1009 			break;
1010 
1011 		  case CMDONEX:		/* doing one transaction only */
1012 			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e);
1013 			OneXact = TRUE;
1014 			message("250 Only one transaction");
1015 			break;
1016 
1017 		  case CMDXUSR:		/* initial (user) submission */
1018 			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e);
1019 			UserSubmission = TRUE;
1020 			message("250 Initial submission");
1021 			break;
1022 
1023 # if SMTPDEBUG
1024 		  case CMDDBGQSHOW:	/* show queues */
1025 			printf("Send Queue=");
1026 			printaddr(e->e_sendqueue, TRUE);
1027 			break;
1028 
1029 		  case CMDDBGDEBUG:	/* set debug mode */
1030 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
1031 			tTflag(p);
1032 			message("200 Debug set");
1033 			break;
1034 
1035 # else /* not SMTPDEBUG */
1036 		  case CMDDBGQSHOW:	/* show queues */
1037 		  case CMDDBGDEBUG:	/* set debug mode */
1038 # endif /* SMTPDEBUG */
1039 		  case CMDLOGBOGUS:	/* bogus command */
1040 			if (LogLevel > 0)
1041 				sm_syslog(LOG_CRIT, e->e_id,
1042 				    "\"%s\" command from %.100s (%.100s)",
1043 				    c->cmdname, CurSmtpClient,
1044 				    anynet_ntoa(&RealHostAddr));
1045 			/* FALL THROUGH */
1046 
1047 		  case CMDERROR:	/* unknown command */
1048 			if (++badcommands > MAXBADCOMMANDS)
1049 			{
1050 				message("421 %s Too many bad commands; closing connection",
1051 					MyHostName);
1052 				goto doquit;
1053 			}
1054 
1055 			usrerr("500 Command unrecognized: \"%s\"",
1056 				shortenstring(inp, MAXSHORTSTR));
1057 			break;
1058 
1059 		  default:
1060 			errno = 0;
1061 			syserr("500 smtp: unknown code %d", c->cmdcode);
1062 			break;
1063 		}
1064 	}
1065 }
1066 /*
1067 **  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
1068 **
1069 **	Parameters:
1070 **		pcounter -- pointer to a counter for this command.
1071 **		maxcount -- maximum value for this counter before we
1072 **			slow down.
1073 **		cname -- command name for logging.
1074 **		e -- the current envelope.
1075 **
1076 **	Returns:
1077 **		none.
1078 **
1079 **	Side Effects:
1080 **		Slows down if we seem to be under attack.
1081 */
1082 
1083 void
1084 checksmtpattack(pcounter, maxcount, cname, e)
1085 	volatile int *pcounter;
1086 	int maxcount;
1087 	char *cname;
1088 	ENVELOPE *e;
1089 {
1090 	if (++(*pcounter) >= maxcount)
1091 	{
1092 		if (*pcounter == maxcount && LogLevel > 5)
1093 		{
1094 			sm_syslog(LOG_INFO, e->e_id,
1095 				"%.100s: %.40s attack?",
1096 			       CurSmtpClient, cname);
1097 		}
1098 		sleep(*pcounter / maxcount);
1099 	}
1100 }
1101 /*
1102 **  SKIPWORD -- skip a fixed word.
1103 **
1104 **	Parameters:
1105 **		p -- place to start looking.
1106 **		w -- word to skip.
1107 **
1108 **	Returns:
1109 **		p following w.
1110 **		NULL on error.
1111 **
1112 **	Side Effects:
1113 **		clobbers the p data area.
1114 */
1115 
1116 static char *
1117 skipword(p, w)
1118 	register char *volatile p;
1119 	char *w;
1120 {
1121 	register char *q;
1122 	char *firstp = p;
1123 
1124 	/* find beginning of word */
1125 	while (isascii(*p) && isspace(*p))
1126 		p++;
1127 	q = p;
1128 
1129 	/* find end of word */
1130 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
1131 		p++;
1132 	while (isascii(*p) && isspace(*p))
1133 		*p++ = '\0';
1134 	if (*p != ':')
1135 	{
1136 	  syntax:
1137 		usrerr("501 Syntax error in parameters scanning \"%s\"",
1138 			shortenstring(firstp, MAXSHORTSTR));
1139 		return (NULL);
1140 	}
1141 	*p++ = '\0';
1142 	while (isascii(*p) && isspace(*p))
1143 		p++;
1144 
1145 	if (*p == '\0')
1146 		goto syntax;
1147 
1148 	/* see if the input word matches desired word */
1149 	if (strcasecmp(q, w))
1150 		goto syntax;
1151 
1152 	return (p);
1153 }
1154 /*
1155 **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
1156 **
1157 **	Parameters:
1158 **		kp -- the parameter key.
1159 **		vp -- the value of that parameter.
1160 **		e -- the envelope.
1161 **
1162 **	Returns:
1163 **		none.
1164 */
1165 
1166 void
1167 mail_esmtp_args(kp, vp, e)
1168 	char *kp;
1169 	char *vp;
1170 	ENVELOPE *e;
1171 {
1172 	if (strcasecmp(kp, "size") == 0)
1173 	{
1174 		if (vp == NULL)
1175 		{
1176 			usrerr("501 SIZE requires a value");
1177 			/* NOTREACHED */
1178 		}
1179 # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
1180 		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
1181 # else
1182 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
1183 # endif
1184 	}
1185 	else if (strcasecmp(kp, "body") == 0)
1186 	{
1187 		if (vp == NULL)
1188 		{
1189 			usrerr("501 BODY requires a value");
1190 			/* NOTREACHED */
1191 		}
1192 		else if (strcasecmp(vp, "8bitmime") == 0)
1193 		{
1194 			SevenBitInput = FALSE;
1195 		}
1196 		else if (strcasecmp(vp, "7bit") == 0)
1197 		{
1198 			SevenBitInput = TRUE;
1199 		}
1200 		else
1201 		{
1202 			usrerr("501 Unknown BODY type %s",
1203 				vp);
1204 			/* NOTREACHED */
1205 		}
1206 		e->e_bodytype = newstr(vp);
1207 	}
1208 	else if (strcasecmp(kp, "envid") == 0)
1209 	{
1210 		if (vp == NULL)
1211 		{
1212 			usrerr("501 ENVID requires a value");
1213 			/* NOTREACHED */
1214 		}
1215 		if (!xtextok(vp))
1216 		{
1217 			usrerr("501 Syntax error in ENVID parameter value");
1218 			/* NOTREACHED */
1219 		}
1220 		if (e->e_envid != NULL)
1221 		{
1222 			usrerr("501 Duplicate ENVID parameter");
1223 			/* NOTREACHED */
1224 		}
1225 		e->e_envid = newstr(vp);
1226 	}
1227 	else if (strcasecmp(kp, "ret") == 0)
1228 	{
1229 		if (vp == NULL)
1230 		{
1231 			usrerr("501 RET requires a value");
1232 			/* NOTREACHED */
1233 		}
1234 		if (bitset(EF_RET_PARAM, e->e_flags))
1235 		{
1236 			usrerr("501 Duplicate RET parameter");
1237 			/* NOTREACHED */
1238 		}
1239 		e->e_flags |= EF_RET_PARAM;
1240 		if (strcasecmp(vp, "hdrs") == 0)
1241 			e->e_flags |= EF_NO_BODY_RETN;
1242 		else if (strcasecmp(vp, "full") != 0)
1243 		{
1244 			usrerr("501 Bad argument \"%s\" to RET", vp);
1245 			/* NOTREACHED */
1246 		}
1247 	}
1248 	else
1249 	{
1250 		usrerr("501 %s parameter unrecognized", kp);
1251 		/* NOTREACHED */
1252 	}
1253 }
1254 /*
1255 **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
1256 **
1257 **	Parameters:
1258 **		a -- the address corresponding to the To: parameter.
1259 **		kp -- the parameter key.
1260 **		vp -- the value of that parameter.
1261 **		e -- the envelope.
1262 **
1263 **	Returns:
1264 **		none.
1265 */
1266 
1267 void
1268 rcpt_esmtp_args(a, kp, vp, e)
1269 	ADDRESS *a;
1270 	char *kp;
1271 	char *vp;
1272 	ENVELOPE *e;
1273 {
1274 	if (strcasecmp(kp, "notify") == 0)
1275 	{
1276 		char *p;
1277 
1278 		if (vp == NULL)
1279 		{
1280 			usrerr("501 NOTIFY requires a value");
1281 			/* NOTREACHED */
1282 		}
1283 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
1284 		a->q_flags |= QHASNOTIFY;
1285 		if (strcasecmp(vp, "never") == 0)
1286 			return;
1287 		for (p = vp; p != NULL; vp = p)
1288 		{
1289 			p = strchr(p, ',');
1290 			if (p != NULL)
1291 				*p++ = '\0';
1292 			if (strcasecmp(vp, "success") == 0)
1293 				a->q_flags |= QPINGONSUCCESS;
1294 			else if (strcasecmp(vp, "failure") == 0)
1295 				a->q_flags |= QPINGONFAILURE;
1296 			else if (strcasecmp(vp, "delay") == 0)
1297 				a->q_flags |= QPINGONDELAY;
1298 			else
1299 			{
1300 				usrerr("501 Bad argument \"%s\"  to NOTIFY",
1301 					vp);
1302 				/* NOTREACHED */
1303 			}
1304 		}
1305 	}
1306 	else if (strcasecmp(kp, "orcpt") == 0)
1307 	{
1308 		if (vp == NULL)
1309 		{
1310 			usrerr("501 ORCPT requires a value");
1311 			/* NOTREACHED */
1312 		}
1313 		if (strchr(vp, ';') == NULL || !xtextok(vp))
1314 		{
1315 			usrerr("501 Syntax error in ORCPT parameter value");
1316 			/* NOTREACHED */
1317 		}
1318 		if (a->q_orcpt != NULL)
1319 		{
1320 			usrerr("501 Duplicate ORCPT parameter");
1321 			/* NOTREACHED */
1322 		}
1323 		a->q_orcpt = newstr(vp);
1324 	}
1325 	else
1326 	{
1327 		usrerr("501 %s parameter unrecognized", kp);
1328 		/* NOTREACHED */
1329 	}
1330 }
1331 /*
1332 **  PRINTVRFYADDR -- print an entry in the verify queue
1333 **
1334 **	Parameters:
1335 **		a -- the address to print
1336 **		last -- set if this is the last one.
1337 **		vrfy -- set if this is a VRFY command.
1338 **
1339 **	Returns:
1340 **		none.
1341 **
1342 **	Side Effects:
1343 **		Prints the appropriate 250 codes.
1344 */
1345 
1346 void
1347 printvrfyaddr(a, last, vrfy)
1348 	register ADDRESS *a;
1349 	bool last;
1350 	bool vrfy;
1351 {
1352 	char fmtbuf[20];
1353 
1354 	if (vrfy && a->q_mailer != NULL &&
1355 	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
1356 		strcpy(fmtbuf, "252");
1357 	else
1358 		strcpy(fmtbuf, "250");
1359 	fmtbuf[3] = last ? ' ' : '-';
1360 
1361 	if (a->q_fullname == NULL)
1362 	{
1363 		if (strchr(a->q_user, '@') == NULL)
1364 			strcpy(&fmtbuf[4], "<%s@%s>");
1365 		else
1366 			strcpy(&fmtbuf[4], "<%s>");
1367 		message(fmtbuf, a->q_user, MyHostName);
1368 	}
1369 	else
1370 	{
1371 		if (strchr(a->q_user, '@') == NULL)
1372 			strcpy(&fmtbuf[4], "%s <%s@%s>");
1373 		else
1374 			strcpy(&fmtbuf[4], "%s <%s>");
1375 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
1376 	}
1377 }
1378 /*
1379 **  RUNINCHILD -- return twice -- once in the child, then in the parent again
1380 **
1381 **	Parameters:
1382 **		label -- a string used in error messages
1383 **
1384 **	Returns:
1385 **		zero in the child
1386 **		one in the parent
1387 **
1388 **	Side Effects:
1389 **		none.
1390 */
1391 
1392 int
1393 runinchild(label, e)
1394 	char *label;
1395 	register ENVELOPE *e;
1396 {
1397 	pid_t childpid;
1398 
1399 	if (!OneXact)
1400 	{
1401 		/*
1402 		**  Disable child process reaping, in case ETRN has preceeded
1403 		**  MAIL command, and then fork.
1404 		*/
1405 
1406 		(void) blocksignal(SIGCHLD);
1407 
1408 		childpid = dofork();
1409 		if (childpid < 0)
1410 		{
1411 			syserr("451 %s: cannot fork", label);
1412 			(void) releasesignal(SIGCHLD);
1413 			return (1);
1414 		}
1415 		if (childpid > 0)
1416 		{
1417 			auto int st;
1418 
1419 			/* parent -- wait for child to complete */
1420 			setproctitle("server %s child wait", CurSmtpClient);
1421 			st = waitfor(childpid);
1422 			if (st == -1)
1423 				syserr("451 %s: lost child", label);
1424 			else if (!WIFEXITED(st))
1425 				syserr("451 %s: died on signal %d",
1426 					label, st & 0177);
1427 
1428 			/* if we exited on a QUIT command, complete the process */
1429 			if (WEXITSTATUS(st) == EX_QUIT)
1430 			{
1431 				disconnect(1, e);
1432 				finis();
1433 			}
1434 
1435 			/* restore the child signal */
1436 			(void) releasesignal(SIGCHLD);
1437 
1438 			return (1);
1439 		}
1440 		else
1441 		{
1442 			/* child */
1443 			InChild = TRUE;
1444 			QuickAbort = FALSE;
1445 			clearenvelope(e, FALSE);
1446 			(void) setsignal(SIGCHLD, SIG_DFL);
1447 			(void) releasesignal(SIGCHLD);
1448 		}
1449 	}
1450 
1451 	/* open alias database */
1452 	initmaps(FALSE, e);
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