1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 # include "sendmail.h"
10 
11 #ifndef lint
12 #ifdef SMTP
13 static char sccsid[] = "@(#)srvrsmtp.c	6.39 (Berkeley) 04/04/93 (with SMTP)";
14 #else
15 static char sccsid[] = "@(#)srvrsmtp.c	6.39 (Berkeley) 04/04/93 (without SMTP)";
16 #endif
17 #endif /* not lint */
18 
19 # include <errno.h>
20 # include <signal.h>
21 
22 # ifdef SMTP
23 
24 /*
25 **  SMTP -- run the SMTP protocol.
26 **
27 **	Parameters:
28 **		none.
29 **
30 **	Returns:
31 **		never.
32 **
33 **	Side Effects:
34 **		Reads commands from the input channel and processes
35 **			them.
36 */
37 
38 struct cmd
39 {
40 	char	*cmdname;	/* command name */
41 	int	cmdcode;	/* internal code, see below */
42 };
43 
44 /* values for cmdcode */
45 # define CMDERROR	0	/* bad command */
46 # define CMDMAIL	1	/* mail -- designate sender */
47 # define CMDRCPT	2	/* rcpt -- designate recipient */
48 # define CMDDATA	3	/* data -- send message text */
49 # define CMDRSET	4	/* rset -- reset state */
50 # define CMDVRFY	5	/* vrfy -- verify address */
51 # define CMDEXPN	6	/* expn -- expand address */
52 # define CMDNOOP	7	/* noop -- do nothing */
53 # define CMDQUIT	8	/* quit -- close connection and die */
54 # define CMDHELO	9	/* helo -- be polite */
55 # define CMDHELP	10	/* help -- give usage info */
56 # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
57 /* non-standard commands */
58 # define CMDONEX	16	/* onex -- sending one transaction only */
59 # define CMDVERB	17	/* verb -- go into verbose mode */
60 /* debugging-only commands, only enabled if SMTPDEBUG is defined */
61 # define CMDDBGQSHOW	24	/* showq -- show send queue */
62 # define CMDDBGDEBUG	25	/* debug -- set debug mode */
63 
64 static struct cmd	CmdTab[] =
65 {
66 	"mail",		CMDMAIL,
67 	"rcpt",		CMDRCPT,
68 	"data",		CMDDATA,
69 	"rset",		CMDRSET,
70 	"vrfy",		CMDVRFY,
71 	"expn",		CMDEXPN,
72 	"help",		CMDHELP,
73 	"noop",		CMDNOOP,
74 	"quit",		CMDQUIT,
75 	"helo",		CMDHELO,
76 	"ehlo",		CMDEHLO,
77 	"verb",		CMDVERB,
78 	"onex",		CMDONEX,
79 	/*
80 	 * remaining commands are here only
81 	 * to trap and log attempts to use them
82 	 */
83 	"showq",	CMDDBGQSHOW,
84 	"debug",	CMDDBGDEBUG,
85 	NULL,		CMDERROR,
86 };
87 
88 bool	InChild = FALSE;		/* true if running in a subprocess */
89 bool	OneXact = FALSE;		/* one xaction only this run */
90 
91 #define EX_QUIT		22		/* special code for QUIT command */
92 
93 smtp(e)
94 	register ENVELOPE *e;
95 {
96 	register char *p;
97 	register struct cmd *c;
98 	char *cmd;
99 	static char *skipword();
100 	auto ADDRESS *vrfyqueue;
101 	ADDRESS *a;
102 	bool gotmail;			/* mail command received */
103 	bool gothello;			/* helo command received */
104 	bool vrfy;			/* set if this is a vrfy command */
105 	char *protocol;			/* sending protocol */
106 	long msize;			/* approximate maximum message size */
107 	auto char *delimptr;
108 	char *id;
109 	char inp[MAXLINE];
110 	char cmdbuf[MAXLINE];
111 	extern char Version[];
112 	extern char *macvalue();
113 	extern ADDRESS *recipient();
114 	extern ENVELOPE BlankEnvelope;
115 	extern ENVELOPE *newenvelope();
116 	extern char *anynet_ntoa();
117 
118 	if (OutChannel != stdout)
119 	{
120 		/* arrange for debugging output to go to remote host */
121 		(void) close(1);
122 		(void) dup(fileno(OutChannel));
123 	}
124 	settime(e);
125 	CurHostName = RealHostName;
126 	setproctitle("srvrsmtp %s", CurHostName);
127 	expand("\201e", inp, &inp[sizeof inp], e);
128 	message("220 %s", inp);
129 	SmtpPhase = "startup";
130 	protocol = NULL;
131 	gothello = FALSE;
132 	gotmail = FALSE;
133 	for (;;)
134 	{
135 		/* arrange for backout */
136 		if (setjmp(TopFrame) > 0 && InChild)
137 			finis();
138 		QuickAbort = FALSE;
139 		HoldErrs = FALSE;
140 		LogUsrErrs = FALSE;
141 		e->e_flags &= ~EF_VRFYONLY;
142 
143 		/* setup for the read */
144 		e->e_to = NULL;
145 		Errors = 0;
146 		(void) fflush(stdout);
147 
148 		/* read the input line */
149 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
150 
151 		/* handle errors */
152 		if (p == NULL)
153 		{
154 			/* end of file, just die */
155 			message("421 %s Lost input channel from %s",
156 				MyHostName, CurHostName);
157 #ifdef LOG
158 			if (LogLevel > 1)
159 				syslog(LOG_NOTICE, "lost input channel from %s",
160 					CurHostName);
161 #endif
162 			if (InChild)
163 				ExitStat = EX_QUIT;
164 			finis();
165 		}
166 
167 		/* clean up end of line */
168 		fixcrlf(inp, TRUE);
169 
170 		/* echo command to transcript */
171 		if (e->e_xfp != NULL)
172 			fprintf(e->e_xfp, "<<< %s\n", inp);
173 
174 		/* break off command */
175 		for (p = inp; isascii(*p) && isspace(*p); p++)
176 			continue;
177 		cmd = cmdbuf;
178 		while (*p != '\0' &&
179 		       !(isascii(*p) && isspace(*p)) &&
180 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
181 			*cmd++ = *p++;
182 		*cmd = '\0';
183 
184 		/* throw away leading whitespace */
185 		while (isascii(*p) && isspace(*p))
186 			p++;
187 
188 		/* decode command */
189 		for (c = CmdTab; c->cmdname != NULL; c++)
190 		{
191 			if (!strcasecmp(c->cmdname, cmdbuf))
192 				break;
193 		}
194 
195 		/* reset errors */
196 		errno = 0;
197 
198 		/* process command */
199 		switch (c->cmdcode)
200 		{
201 		  case CMDHELO:		/* hello -- introduce yourself */
202 		  case CMDEHLO:		/* extended hello */
203 			if (c->cmdcode == CMDEHLO)
204 			{
205 				protocol = "ESMTP";
206 				SmtpPhase = "EHLO";
207 			}
208 			else
209 			{
210 				protocol = "SMTP";
211 				SmtpPhase = "HELO";
212 			}
213 			setproctitle("%s: %s", CurHostName, inp);
214 			define('s', newstr(p), e);
215 			if (strcasecmp(p, RealHostName) != 0)
216 			{
217 				auth_warning(e, "Host %s claimed to be %s",
218 					RealHostName, p);
219 			}
220 			p = macvalue('_', e);
221 			if (p == NULL)
222 				p = macvalue('s', e);
223 
224 			/* send ext. message -- old systems must ignore */
225 			message("250-%s Hello %s, pleased to meet you",
226 				MyHostName, p);
227 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
228 				message("250-EXPN");
229 			message("250-SIZE");
230 			message("250 HELP");
231 			gothello = TRUE;
232 			break;
233 
234 		  case CMDMAIL:		/* mail -- designate sender */
235 			SmtpPhase = "MAIL";
236 
237 			/* check for validity of this command */
238 			if (!gothello)
239 			{
240 				/* set sending host to our known value */
241 				if (macvalue('s', e) == NULL)
242 					define('s', RealHostName, e);
243 
244 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
245 				{
246 					message("503 Polite people say HELO first");
247 					break;
248 				}
249 				else
250 				{
251 					auth_warning(e,
252 						"Host %s didn't use HELO protocol",
253 						RealHostName);
254 				}
255 			}
256 			if (gotmail)
257 			{
258 				message("503 Sender already specified");
259 				break;
260 			}
261 			if (InChild)
262 			{
263 				errno = 0;
264 				syserr("503 Nested MAIL command: MAIL %s", p);
265 				finis();
266 			}
267 
268 			/* fork a subprocess to process this command */
269 			if (runinchild("SMTP-MAIL", e) > 0)
270 				break;
271 			if (protocol == NULL)
272 				protocol = "SMTP";
273 			define('r', protocol, e);
274 			initsys(e);
275 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
276 
277 			/* child -- go do the processing */
278 			p = skipword(p, "from");
279 			if (p == NULL)
280 				break;
281 			if (setjmp(TopFrame) > 0)
282 			{
283 				/* this failed -- undo work */
284 				if (InChild)
285 					finis();
286 				break;
287 			}
288 			QuickAbort = TRUE;
289 
290 			/* must parse sender first */
291 			delimptr = NULL;
292 			setsender(p, e, &delimptr, FALSE);
293 			p = delimptr;
294 			if (p != NULL && *p != '\0')
295 				*p++ = '\0';
296 
297 			/* now parse ESMTP arguments */
298 			msize = 0;
299 			for (; p != NULL && *p != '\0'; p++)
300 			{
301 				char *kp;
302 				char *vp;
303 
304 				/* locate the beginning of the keyword */
305 				while (isascii(*p) && isspace(*p))
306 					p++;
307 				if (*p == '\0')
308 					break;
309 				kp = p;
310 
311 				/* skip to the value portion */
312 				while (isascii(*p) && isalnum(*p) || *p == '-')
313 					p++;
314 				if (*p == '=')
315 				{
316 					*p++ = '\0';
317 					vp = p;
318 
319 					/* skip to the end of the value */
320 					while (*p != '\0' && *p != ' ' &&
321 					       !(isascii(*p) && iscntrl(*p)) &&
322 					       *p != '=')
323 						p++;
324 				}
325 
326 				if (*p != '\0')
327 					*p++ = '\0';
328 
329 				if (tTd(19, 1))
330 					printf("MAIL: got arg %s=%s\n", kp,
331 						vp == NULL ? "<null>" : vp);
332 
333 				if (strcasecmp(kp, "size") == 0)
334 				{
335 					if (kp == NULL)
336 					{
337 						usrerr("501 SIZE requires a value");
338 						/* NOTREACHED */
339 					}
340 					msize = atol(vp);
341 				}
342 				else
343 				{
344 					usrerr("501 %s parameter unrecognized", kp);
345 					/* NOTREACHED */
346 				}
347 			}
348 
349 			if (!enoughspace(msize))
350 			{
351 				message("452 Insufficient disk space; try again later");
352 				break;
353 			}
354 			message("250 Sender ok");
355 			gotmail = TRUE;
356 			break;
357 
358 		  case CMDRCPT:		/* rcpt -- designate recipient */
359 			if (!gotmail)
360 			{
361 				usrerr("503 Need MAIL before RCPT");
362 				break;
363 			}
364 			SmtpPhase = "RCPT";
365 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
366 			if (setjmp(TopFrame) > 0)
367 			{
368 				e->e_flags &= ~EF_FATALERRS;
369 				break;
370 			}
371 			QuickAbort = TRUE;
372 			LogUsrErrs = TRUE;
373 
374 			e->e_flags |= EF_VRFYONLY;
375 
376 			p = skipword(p, "to");
377 			if (p == NULL)
378 				break;
379 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
380 			if (a == NULL)
381 				break;
382 			a->q_flags |= QPRIMARY;
383 			a = recipient(a, &e->e_sendqueue, e);
384 			if (Errors != 0)
385 				break;
386 
387 			/* no errors during parsing, but might be a duplicate */
388 			e->e_to = p;
389 			if (!bitset(QBADADDR, a->q_flags))
390 				message("250 Recipient ok");
391 			else
392 			{
393 				/* punt -- should keep message in ADDRESS.... */
394 				message("550 Addressee unknown");
395 			}
396 			e->e_to = NULL;
397 			break;
398 
399 		  case CMDDATA:		/* data -- text of mail */
400 			SmtpPhase = "DATA";
401 			if (!gotmail)
402 			{
403 				message("503 Need MAIL command");
404 				break;
405 			}
406 			else if (e->e_nrcpts <= 0)
407 			{
408 				message("503 Need RCPT (recipient)");
409 				break;
410 			}
411 
412 			/* check to see if we need to re-expand aliases */
413 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
414 			{
415 				if (bitset(QVERIFIED, a->q_flags))
416 					break;
417 			}
418 
419 			/* collect the text of the message */
420 			SmtpPhase = "collect";
421 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
422 			collect(TRUE, a != NULL, e);
423 			if (Errors != 0)
424 				break;
425 
426 			/*
427 			**  Arrange to send to everyone.
428 			**	If sending to multiple people, mail back
429 			**		errors rather than reporting directly.
430 			**	In any case, don't mail back errors for
431 			**		anything that has happened up to
432 			**		now (the other end will do this).
433 			**	Truncate our transcript -- the mail has gotten
434 			**		to us successfully, and if we have
435 			**		to mail this back, it will be easier
436 			**		on the reader.
437 			**	Then send to everyone.
438 			**	Finally give a reply code.  If an error has
439 			**		already been given, don't mail a
440 			**		message back.
441 			**	We goose error returns by clearing error bit.
442 			*/
443 
444 			SmtpPhase = "delivery";
445 			if (e->e_nrcpts != 1)
446 			{
447 				HoldErrs = TRUE;
448 				e->e_errormode = EM_MAIL;
449 			}
450 			e->e_flags &= ~EF_FATALERRS;
451 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
452 			id = e->e_id;
453 
454 			/* send to all recipients */
455 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
456 			e->e_to = NULL;
457 
458 			/* save statistics */
459 			markstats(e, (ADDRESS *) NULL);
460 
461 			/* issue success if appropriate and reset */
462 			if (Errors == 0 || HoldErrs)
463 				message("250 %s Message accepted for delivery", id);
464 			else
465 				e->e_flags &= ~EF_FATALERRS;
466 
467 			/* if we just queued, poke it */
468 			if (a != NULL && e->e_sendmode != SM_QUEUE)
469 			{
470 				unlockqueue(e);
471 				dowork(id, TRUE, TRUE, e);
472 				e->e_id = NULL;
473 			}
474 
475 			/* if in a child, pop back to our parent */
476 			if (InChild)
477 				finis();
478 
479 			/* clean up a bit */
480 			gotmail = FALSE;
481 			dropenvelope(e);
482 			CurEnv = e = newenvelope(e, CurEnv);
483 			e->e_flags = BlankEnvelope.e_flags;
484 			break;
485 
486 		  case CMDRSET:		/* rset -- reset state */
487 			message("250 Reset state");
488 			if (InChild)
489 				finis();
490 
491 			/* clean up a bit */
492 			gotmail = FALSE;
493 			dropenvelope(e);
494 			CurEnv = e = newenvelope(e, CurEnv);
495 			break;
496 
497 		  case CMDVRFY:		/* vrfy -- verify address */
498 		  case CMDEXPN:		/* expn -- expand address */
499 			vrfy = c->cmdcode == CMDVRFY;
500 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
501 						PrivacyFlags))
502 			{
503 				if (vrfy)
504 					message("252 Who's to say?");
505 				else
506 					message("502 That's none of your business");
507 				break;
508 			}
509 			else if (!gothello &&
510 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
511 						PrivacyFlags))
512 			{
513 				message("503 I demand that you introduce yourself first");
514 				break;
515 			}
516 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
517 				break;
518 			setproctitle("%s: %s", CurHostName, inp);
519 #ifdef LOG
520 			if (LogLevel > 5)
521 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
522 #endif
523 			vrfyqueue = NULL;
524 			QuickAbort = TRUE;
525 			if (vrfy)
526 				e->e_flags |= EF_VRFYONLY;
527 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
528 			if (Errors != 0)
529 			{
530 				if (InChild)
531 					finis();
532 				break;
533 			}
534 			while (vrfyqueue != NULL)
535 			{
536 				register ADDRESS *a = vrfyqueue->q_next;
537 				char *code;
538 
539 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
540 					a = a->q_next;
541 
542 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
543 					printvrfyaddr(vrfyqueue, a == NULL);
544 				else if (a == NULL)
545 					message("554 Self destructive alias loop");
546 				vrfyqueue = a;
547 			}
548 			if (InChild)
549 				finis();
550 			break;
551 
552 		  case CMDHELP:		/* help -- give user info */
553 			help(p);
554 			break;
555 
556 		  case CMDNOOP:		/* noop -- do nothing */
557 			message("200 OK");
558 			break;
559 
560 		  case CMDQUIT:		/* quit -- leave mail */
561 			message("221 %s closing connection", MyHostName);
562 			if (InChild)
563 				ExitStat = EX_QUIT;
564 			finis();
565 
566 		  case CMDVERB:		/* set verbose mode */
567 			Verbose = TRUE;
568 			e->e_sendmode = SM_DELIVER;
569 			message("200 Verbose mode");
570 			break;
571 
572 		  case CMDONEX:		/* doing one transaction only */
573 			OneXact = TRUE;
574 			message("200 Only one transaction");
575 			break;
576 
577 # ifdef SMTPDEBUG
578 		  case CMDDBGQSHOW:	/* show queues */
579 			printf("Send Queue=");
580 			printaddr(e->e_sendqueue, TRUE);
581 			break;
582 
583 		  case CMDDBGDEBUG:	/* set debug mode */
584 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
585 			tTflag(p);
586 			message("200 Debug set");
587 			break;
588 
589 # else /* not SMTPDEBUG */
590 
591 		  case CMDDBGQSHOW:	/* show queues */
592 		  case CMDDBGDEBUG:	/* set debug mode */
593 # ifdef LOG
594 			if (LogLevel > 0)
595 				syslog(LOG_NOTICE,
596 				    "\"%s\" command from %s (%s)",
597 				    c->cmdname, RealHostName,
598 				    anynet_ntoa(&RealHostAddr));
599 # endif
600 			/* FALL THROUGH */
601 # endif /* SMTPDEBUG */
602 
603 		  case CMDERROR:	/* unknown command */
604 			message("500 Command unrecognized");
605 			break;
606 
607 		  default:
608 			errno = 0;
609 			syserr("500 smtp: unknown code %d", c->cmdcode);
610 			break;
611 		}
612 	}
613 }
614 /*
615 **  SKIPWORD -- skip a fixed word.
616 **
617 **	Parameters:
618 **		p -- place to start looking.
619 **		w -- word to skip.
620 **
621 **	Returns:
622 **		p following w.
623 **		NULL on error.
624 **
625 **	Side Effects:
626 **		clobbers the p data area.
627 */
628 
629 static char *
630 skipword(p, w)
631 	register char *p;
632 	char *w;
633 {
634 	register char *q;
635 
636 	/* find beginning of word */
637 	while (isascii(*p) && isspace(*p))
638 		p++;
639 	q = p;
640 
641 	/* find end of word */
642 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
643 		p++;
644 	while (isascii(*p) && isspace(*p))
645 		*p++ = '\0';
646 	if (*p != ':')
647 	{
648 	  syntax:
649 		message("501 Syntax error");
650 		Errors++;
651 		return (NULL);
652 	}
653 	*p++ = '\0';
654 	while (isascii(*p) && isspace(*p))
655 		p++;
656 
657 	/* see if the input word matches desired word */
658 	if (strcasecmp(q, w))
659 		goto syntax;
660 
661 	return (p);
662 }
663 /*
664 **  PRINTVRFYADDR -- print an entry in the verify queue
665 **
666 **	Parameters:
667 **		a -- the address to print
668 **		last -- set if this is the last one.
669 **
670 **	Returns:
671 **		none.
672 **
673 **	Side Effects:
674 **		Prints the appropriate 250 codes.
675 */
676 
677 printvrfyaddr(a, last)
678 	register ADDRESS *a;
679 	bool last;
680 {
681 	char fmtbuf[20];
682 
683 	strcpy(fmtbuf, "250");
684 	fmtbuf[3] = last ? ' ' : '-';
685 
686 	if (strchr(a->q_paddr, '<') != NULL)
687 		strcpy(&fmtbuf[4], "%s");
688 	else if (a->q_fullname == NULL)
689 		strcpy(&fmtbuf[4], "<%s>");
690 	else
691 	{
692 		strcpy(&fmtbuf[4], "%s <%s>");
693 		message(fmtbuf, a->q_fullname, a->q_paddr);
694 		return;
695 	}
696 	message(fmtbuf, a->q_paddr);
697 }
698 /*
699 **  HELP -- implement the HELP command.
700 **
701 **	Parameters:
702 **		topic -- the topic we want help for.
703 **
704 **	Returns:
705 **		none.
706 **
707 **	Side Effects:
708 **		outputs the help file to message output.
709 */
710 
711 help(topic)
712 	char *topic;
713 {
714 	register FILE *hf;
715 	int len;
716 	char buf[MAXLINE];
717 	bool noinfo;
718 
719 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
720 	{
721 		/* no help */
722 		errno = 0;
723 		message("502 HELP not implemented");
724 		return;
725 	}
726 
727 	if (topic == NULL || *topic == '\0')
728 		topic = "smtp";
729 	else
730 		makelower(topic);
731 
732 	len = strlen(topic);
733 	noinfo = TRUE;
734 
735 	while (fgets(buf, sizeof buf, hf) != NULL)
736 	{
737 		if (strncmp(buf, topic, len) == 0)
738 		{
739 			register char *p;
740 
741 			p = strchr(buf, '\t');
742 			if (p == NULL)
743 				p = buf;
744 			else
745 				p++;
746 			fixcrlf(p, TRUE);
747 			message("214-%s", p);
748 			noinfo = FALSE;
749 		}
750 	}
751 
752 	if (noinfo)
753 		message("504 HELP topic unknown");
754 	else
755 		message("214 End of HELP info");
756 	(void) fclose(hf);
757 }
758 /*
759 **  RUNINCHILD -- return twice -- once in the child, then in the parent again
760 **
761 **	Parameters:
762 **		label -- a string used in error messages
763 **
764 **	Returns:
765 **		zero in the child
766 **		one in the parent
767 **
768 **	Side Effects:
769 **		none.
770 */
771 
772 runinchild(label, e)
773 	char *label;
774 	register ENVELOPE *e;
775 {
776 	int childpid;
777 
778 	if (!OneXact)
779 	{
780 		childpid = dofork();
781 		if (childpid < 0)
782 		{
783 			syserr("%s: cannot fork", label);
784 			return (1);
785 		}
786 		if (childpid > 0)
787 		{
788 			auto int st;
789 
790 			/* parent -- wait for child to complete */
791 			st = waitfor(childpid);
792 			if (st == -1)
793 				syserr("%s: lost child", label);
794 
795 			/* if we exited on a QUIT command, complete the process */
796 			if (st == (EX_QUIT << 8))
797 				finis();
798 
799 			return (1);
800 		}
801 		else
802 		{
803 			/* child */
804 			InChild = TRUE;
805 			QuickAbort = FALSE;
806 			clearenvelope(e, FALSE);
807 		}
808 	}
809 
810 	/* open alias database */
811 	initaliases(AliasFile, FALSE, e);
812 
813 	return (0);
814 }
815 
816 # endif /* SMTP */
817