122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
333731Sbostic  * Copyright (c) 1988 Regents of the University of California.
433731Sbostic  * All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822712Sdist 
933731Sbostic # include "sendmail.h"
1022712Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*59747Seric static char sccsid[] = "@(#)srvrsmtp.c	6.52 (Berkeley) 05/05/93 (with SMTP)";
1433731Sbostic #else
15*59747Seric static char sccsid[] = "@(#)srvrsmtp.c	6.52 (Berkeley) 05/05/93 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
2011728Seric # include <signal.h>
214549Seric 
2233731Sbostic # ifdef SMTP
234556Seric 
244549Seric /*
254549Seric **  SMTP -- run the SMTP protocol.
264549Seric **
274549Seric **	Parameters:
284549Seric **		none.
294549Seric **
304549Seric **	Returns:
314549Seric **		never.
324549Seric **
334549Seric **	Side Effects:
344549Seric **		Reads commands from the input channel and processes
354549Seric **			them.
364549Seric */
374549Seric 
384549Seric struct cmd
394549Seric {
404549Seric 	char	*cmdname;	/* command name */
414549Seric 	int	cmdcode;	/* internal code, see below */
424549Seric };
434549Seric 
444549Seric /* values for cmdcode */
454549Seric # define CMDERROR	0	/* bad command */
464549Seric # define CMDMAIL	1	/* mail -- designate sender */
474976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
484549Seric # define CMDDATA	3	/* data -- send message text */
499339Seric # define CMDRSET	4	/* rset -- reset state */
509339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5158092Seric # define CMDEXPN	6	/* expn -- expand address */
529339Seric # define CMDNOOP	7	/* noop -- do nothing */
539339Seric # define CMDQUIT	8	/* quit -- close connection and die */
549339Seric # define CMDHELO	9	/* helo -- be polite */
5558092Seric # define CMDHELP	10	/* help -- give usage info */
5658323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5758092Seric /* non-standard commands */
5858092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5958092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
6036230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6158092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6258092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
634549Seric 
644549Seric static struct cmd	CmdTab[] =
654549Seric {
664549Seric 	"mail",		CMDMAIL,
674976Seric 	"rcpt",		CMDRCPT,
684549Seric 	"data",		CMDDATA,
694549Seric 	"rset",		CMDRSET,
704549Seric 	"vrfy",		CMDVRFY,
7158092Seric 	"expn",		CMDEXPN,
724549Seric 	"help",		CMDHELP,
734549Seric 	"noop",		CMDNOOP,
744549Seric 	"quit",		CMDQUIT,
754976Seric 	"helo",		CMDHELO,
7658323Seric 	"ehlo",		CMDEHLO,
778544Seric 	"verb",		CMDVERB,
789314Seric 	"onex",		CMDONEX,
7936230Skarels 	/*
8036230Skarels 	 * remaining commands are here only
8136230Skarels 	 * to trap and log attempts to use them
8236230Skarels 	 */
839339Seric 	"showq",	CMDDBGQSHOW,
848544Seric 	"debug",	CMDDBGDEBUG,
854549Seric 	NULL,		CMDERROR,
864549Seric };
874549Seric 
889339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
899378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9011146Seric 
919339Seric #define EX_QUIT		22		/* special code for QUIT command */
928544Seric 
9355012Seric smtp(e)
9455012Seric 	register ENVELOPE *e;
954549Seric {
964549Seric 	register char *p;
978544Seric 	register struct cmd *c;
984549Seric 	char *cmd;
9946928Sbostic 	static char *skipword();
1005003Seric 	auto ADDRESS *vrfyqueue;
10112612Seric 	ADDRESS *a;
10258109Seric 	bool gotmail;			/* mail command received */
10358092Seric 	bool gothello;			/* helo command received */
10458092Seric 	bool vrfy;			/* set if this is a vrfy command */
10558323Seric 	char *protocol;			/* sending protocol */
10659016Seric 	char *sendinghost;		/* sending hostname */
10758333Seric 	long msize;			/* approximate maximum message size */
10858333Seric 	auto char *delimptr;
10958714Seric 	char *id;
110*59747Seric 	int nrcpts;			/* number of RCPT commands */
1118544Seric 	char inp[MAXLINE];
11257232Seric 	char cmdbuf[MAXLINE];
1137124Seric 	extern char Version[];
11411151Seric 	extern char *macvalue();
11512612Seric 	extern ADDRESS *recipient();
11624943Seric 	extern ENVELOPE BlankEnvelope;
11724943Seric 	extern ENVELOPE *newenvelope();
11858755Seric 	extern char *anynet_ntoa();
1194549Seric 
12059066Seric 	if (fileno(OutChannel) != fileno(stdout))
1217363Seric 	{
1227363Seric 		/* arrange for debugging output to go to remote host */
12359066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1247363Seric 	}
12555012Seric 	settime(e);
12657642Seric 	CurHostName = RealHostName;
12759060Seric 	setproctitle("srvrsmtp %s startup", CurHostName);
12858050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12958151Seric 	message("220 %s", inp);
13058330Seric 	protocol = NULL;
13159016Seric 	sendinghost = macvalue('s', e);
13258082Seric 	gothello = FALSE;
13358330Seric 	gotmail = FALSE;
1344549Seric 	for (;;)
1354549Seric 	{
13612612Seric 		/* arrange for backout */
13712612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13859058Seric 		{
13959058Seric 			QuickAbort = FALSE;
14059058Seric 			SuprErrs = TRUE;
14112612Seric 			finis();
14259058Seric 		}
14312612Seric 		QuickAbort = FALSE;
14412612Seric 		HoldErrs = FALSE;
14551951Seric 		LogUsrErrs = FALSE;
14658092Seric 		e->e_flags &= ~EF_VRFYONLY;
14712612Seric 
1487356Seric 		/* setup for the read */
14955012Seric 		e->e_to = NULL;
1504577Seric 		Errors = 0;
1517275Seric 		(void) fflush(stdout);
1527356Seric 
1537356Seric 		/* read the input line */
15459060Seric 		SmtpPhase = "srvrsmtp cmd read";
15559060Seric 		setproctitle("srvrsmtp %s cmd read", CurHostName);
15658109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1577356Seric 
1587685Seric 		/* handle errors */
1597356Seric 		if (p == NULL)
1607356Seric 		{
1614549Seric 			/* end of file, just die */
16258151Seric 			message("421 %s Lost input channel from %s",
16325050Seric 				MyHostName, CurHostName);
16455464Seric #ifdef LOG
16558020Seric 			if (LogLevel > 1)
16655464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16755464Seric 					CurHostName);
16855464Seric #endif
16958069Seric 			if (InChild)
17058069Seric 				ExitStat = EX_QUIT;
1714549Seric 			finis();
1724549Seric 		}
1734549Seric 
1744549Seric 		/* clean up end of line */
1754558Seric 		fixcrlf(inp, TRUE);
1764549Seric 
1774713Seric 		/* echo command to transcript */
17855012Seric 		if (e->e_xfp != NULL)
17955012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1804713Seric 
18159060Seric 		if (e->e_id == NULL)
18259060Seric 			setproctitle("%s: %s", CurHostName, inp);
18359060Seric 		else
18459060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
18559060Seric 
1864549Seric 		/* break off command */
18758050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1884549Seric 			continue;
18957232Seric 		cmd = cmdbuf;
19058050Seric 		while (*p != '\0' &&
19158050Seric 		       !(isascii(*p) && isspace(*p)) &&
19258050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19324981Seric 			*cmd++ = *p++;
19424981Seric 		*cmd = '\0';
1954549Seric 
19625691Seric 		/* throw away leading whitespace */
19758050Seric 		while (isascii(*p) && isspace(*p))
19825691Seric 			p++;
19925691Seric 
2004549Seric 		/* decode command */
2014549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2024549Seric 		{
20333725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2044549Seric 				break;
2054549Seric 		}
2064549Seric 
20751954Seric 		/* reset errors */
20851954Seric 		errno = 0;
20951954Seric 
2104549Seric 		/* process command */
2114549Seric 		switch (c->cmdcode)
2124549Seric 		{
2134976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
21458323Seric 		  case CMDEHLO:		/* extended hello */
21558323Seric 			if (c->cmdcode == CMDEHLO)
21658323Seric 			{
21758323Seric 				protocol = "ESMTP";
21858323Seric 				SmtpPhase = "EHLO";
21958323Seric 			}
22058323Seric 			else
22158323Seric 			{
22258323Seric 				protocol = "SMTP";
22358323Seric 				SmtpPhase = "HELO";
22458323Seric 			}
22559016Seric 			sendinghost = newstr(p);
22658308Seric 			if (strcasecmp(p, RealHostName) != 0)
22711146Seric 			{
22858789Seric 				auth_warning(e, "Host %s claimed to be %s",
22958789Seric 					RealHostName, p);
23011146Seric 			}
23158957Seric 			p = macvalue('_', e);
23258957Seric 			if (p == NULL)
23359016Seric 				p = RealHostName;
23458323Seric 
23558323Seric 			/* send ext. message -- old systems must ignore */
23658323Seric 			message("250-%s Hello %s, pleased to meet you",
23758957Seric 				MyHostName, p);
23858323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
23958323Seric 				message("250-EXPN");
24059271Seric 			if (MaxMessageSize > 0)
24159271Seric 				message("250-SIZE %ld", MaxMessageSize);
24259271Seric 			else
24359271Seric 				message("250-SIZE");
24458323Seric 			message("250 HELP");
24558082Seric 			gothello = TRUE;
2464976Seric 			break;
2474976Seric 
2484549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24924943Seric 			SmtpPhase = "MAIL";
25024943Seric 
2519314Seric 			/* check for validity of this command */
25258789Seric 			if (!gothello)
25358082Seric 			{
25458957Seric 				/* set sending host to our known value */
25559016Seric 				if (sendinghost == NULL)
25659016Seric 					sendinghost = RealHostName;
25758957Seric 
25858789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25958821Seric 				{
26058789Seric 					message("503 Polite people say HELO first");
26158821Seric 					break;
26258821Seric 				}
26358789Seric 				else
26458821Seric 				{
26558789Seric 					auth_warning(e,
26658789Seric 						"Host %s didn't use HELO protocol",
26758789Seric 						RealHostName);
26858821Seric 				}
26958082Seric 			}
27058109Seric 			if (gotmail)
2714558Seric 			{
27258151Seric 				message("503 Sender already specified");
2734558Seric 				break;
2744558Seric 			}
2759339Seric 			if (InChild)
2769339Seric 			{
27736230Skarels 				errno = 0;
27858151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
27958069Seric 				finis();
2809339Seric 			}
2819339Seric 
2829339Seric 			/* fork a subprocess to process this command */
28355012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2849339Seric 				break;
28558323Seric 			if (protocol == NULL)
28658323Seric 				protocol = "SMTP";
28758323Seric 			define('r', protocol, e);
28859016Seric 			define('s', sendinghost, e);
28955012Seric 			initsys(e);
290*59747Seric 			nrcpts = 0;
29157389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2929339Seric 
2939339Seric 			/* child -- go do the processing */
2944549Seric 			p = skipword(p, "from");
2954549Seric 			if (p == NULL)
2964549Seric 				break;
29757977Seric 			if (setjmp(TopFrame) > 0)
29858147Seric 			{
29958147Seric 				/* this failed -- undo work */
30058147Seric 				if (InChild)
30159058Seric 				{
30259058Seric 					QuickAbort = FALSE;
30359058Seric 					SuprErrs = TRUE;
30458147Seric 					finis();
30559058Seric 				}
30657977Seric 				break;
30758147Seric 			}
30857977Seric 			QuickAbort = TRUE;
30958333Seric 
31058333Seric 			/* must parse sender first */
31158333Seric 			delimptr = NULL;
31258704Seric 			setsender(p, e, &delimptr, FALSE);
31358333Seric 			p = delimptr;
31458333Seric 			if (p != NULL && *p != '\0')
31558333Seric 				*p++ = '\0';
31658333Seric 
31758333Seric 			/* now parse ESMTP arguments */
31858333Seric 			msize = 0;
31958333Seric 			for (; p != NULL && *p != '\0'; p++)
32058333Seric 			{
32158333Seric 				char *kp;
32258333Seric 				char *vp;
32358333Seric 
32458333Seric 				/* locate the beginning of the keyword */
32558333Seric 				while (isascii(*p) && isspace(*p))
32658333Seric 					p++;
32758333Seric 				if (*p == '\0')
32858333Seric 					break;
32958333Seric 				kp = p;
33058333Seric 
33158333Seric 				/* skip to the value portion */
33258333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
33358333Seric 					p++;
33458333Seric 				if (*p == '=')
33558333Seric 				{
33658333Seric 					*p++ = '\0';
33758333Seric 					vp = p;
33858333Seric 
33958333Seric 					/* skip to the end of the value */
34058333Seric 					while (*p != '\0' && *p != ' ' &&
34158333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
34258333Seric 					       *p != '=')
34358333Seric 						p++;
34458333Seric 				}
34558333Seric 
34658333Seric 				if (*p != '\0')
34758333Seric 					*p++ = '\0';
34858333Seric 
34958333Seric 				if (tTd(19, 1))
35058333Seric 					printf("MAIL: got arg %s=%s\n", kp,
35158333Seric 						vp == NULL ? "<null>" : vp);
35258333Seric 
35358333Seric 				if (strcasecmp(kp, "size") == 0)
35458333Seric 				{
35559093Seric 					if (vp == NULL)
35658333Seric 					{
35758333Seric 						usrerr("501 SIZE requires a value");
35858333Seric 						/* NOTREACHED */
35958333Seric 					}
36058333Seric 					msize = atol(vp);
36158333Seric 				}
36259093Seric 				else if (strcasecmp(kp, "body") == 0)
36359093Seric 				{
36459093Seric 					if (vp == NULL)
36559093Seric 					{
36659093Seric 						usrerr("501 BODY requires a value");
36759093Seric 						/* NOTREACHED */
36859093Seric 					}
36959093Seric # ifdef MIME
37059093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
37159093Seric 					{
37259093Seric 						e->e_bodytype = "8BITMIME";
37359709Seric 						SevenBit = FALSE;
37459093Seric 					}
37559093Seric 					else if (strcasecmp(vp, "7bit") == 0)
37659093Seric 					{
37759093Seric 						e->e_bodytype = "7BIT";
37859709Seric 						SevenBit = TRUE;
37959093Seric 					}
38059093Seric 					else
38159093Seric 					{
38259093Seric 						usrerr("501 Unknown BODY type %s",
38359093Seric 							vp);
38459093Seric 					}
38559093Seric # endif
38659093Seric 				}
38758333Seric 				else
38858333Seric 				{
38958333Seric 					usrerr("501 %s parameter unrecognized", kp);
39058333Seric 					/* NOTREACHED */
39158333Seric 				}
39258333Seric 			}
39359284Seric 
39459284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
39559284Seric 			{
39659284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
39759284Seric 					MaxMessageSize);
39859284Seric 				/* NOTREACHED */
39959284Seric 			}
40058333Seric 
40158333Seric 			if (!enoughspace(msize))
40258333Seric 			{
40358333Seric 				message("452 Insufficient disk space; try again later");
40458333Seric 				break;
40558333Seric 			}
40658151Seric 			message("250 Sender ok");
40758147Seric 			gotmail = TRUE;
4084549Seric 			break;
4094549Seric 
4104976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
41158850Seric 			if (!gotmail)
41258850Seric 			{
41358850Seric 				usrerr("503 Need MAIL before RCPT");
41458850Seric 				break;
41558850Seric 			}
41624943Seric 			SmtpPhase = "RCPT";
41712612Seric 			if (setjmp(TopFrame) > 0)
41814785Seric 			{
41955012Seric 				e->e_flags &= ~EF_FATALERRS;
42012612Seric 				break;
42114785Seric 			}
42212612Seric 			QuickAbort = TRUE;
42351951Seric 			LogUsrErrs = TRUE;
42458093Seric 
42559699Seric 			if (e->e_sendmode != SM_DELIVER)
42659699Seric 				e->e_flags |= EF_VRFYONLY;
42758919Seric 
4284549Seric 			p = skipword(p, "to");
4294549Seric 			if (p == NULL)
4304549Seric 				break;
43158333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
43212612Seric 			if (a == NULL)
43312612Seric 				break;
43416886Seric 			a->q_flags |= QPRIMARY;
43555012Seric 			a = recipient(a, &e->e_sendqueue, e);
43612612Seric 			if (Errors != 0)
43712612Seric 				break;
43812612Seric 
43912612Seric 			/* no errors during parsing, but might be a duplicate */
44055012Seric 			e->e_to = p;
44112612Seric 			if (!bitset(QBADADDR, a->q_flags))
442*59747Seric 			{
44358151Seric 				message("250 Recipient ok");
444*59747Seric 				nrcpts++;
445*59747Seric 			}
44612612Seric 			else
4474549Seric 			{
44812612Seric 				/* punt -- should keep message in ADDRESS.... */
44958151Seric 				message("550 Addressee unknown");
4504549Seric 			}
45155012Seric 			e->e_to = NULL;
4524549Seric 			break;
4534549Seric 
4544549Seric 		  case CMDDATA:		/* data -- text of mail */
45524943Seric 			SmtpPhase = "DATA";
45658109Seric 			if (!gotmail)
4574549Seric 			{
45858151Seric 				message("503 Need MAIL command");
4594976Seric 				break;
4604549Seric 			}
46155012Seric 			else if (e->e_nrcpts <= 0)
4624549Seric 			{
46358151Seric 				message("503 Need RCPT (recipient)");
4644976Seric 				break;
4654549Seric 			}
4664976Seric 
46758929Seric 			/* check to see if we need to re-expand aliases */
46858929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
46958929Seric 			{
47058929Seric 				if (bitset(QVERIFIED, a->q_flags))
47158929Seric 					break;
47258929Seric 			}
47358929Seric 
4744976Seric 			/* collect the text of the message */
47524943Seric 			SmtpPhase = "collect";
47658929Seric 			collect(TRUE, a != NULL, e);
47759730Seric 			e->e_flags &= ~EF_FATALERRS;
4784976Seric 			if (Errors != 0)
479*59747Seric 				goto abortmessage;
4804976Seric 
4818238Seric 			/*
4828238Seric 			**  Arrange to send to everyone.
4838238Seric 			**	If sending to multiple people, mail back
4848238Seric 			**		errors rather than reporting directly.
4858238Seric 			**	In any case, don't mail back errors for
4868238Seric 			**		anything that has happened up to
4878238Seric 			**		now (the other end will do this).
48810197Seric 			**	Truncate our transcript -- the mail has gotten
48910197Seric 			**		to us successfully, and if we have
49010197Seric 			**		to mail this back, it will be easier
49110197Seric 			**		on the reader.
4928238Seric 			**	Then send to everyone.
4938238Seric 			**	Finally give a reply code.  If an error has
4948238Seric 			**		already been given, don't mail a
4958238Seric 			**		message back.
4969339Seric 			**	We goose error returns by clearing error bit.
4978238Seric 			*/
4988238Seric 
49924943Seric 			SmtpPhase = "delivery";
500*59747Seric 			if (nrcpts != 1 && a == NULL)
5019378Seric 			{
5029378Seric 				HoldErrs = TRUE;
50358734Seric 				e->e_errormode = EM_MAIL;
5049378Seric 			}
50555012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
50658714Seric 			id = e->e_id;
5074976Seric 
5084976Seric 			/* send to all recipients */
50958919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
51055012Seric 			e->e_to = NULL;
5114976Seric 
51223516Seric 			/* save statistics */
51355012Seric 			markstats(e, (ADDRESS *) NULL);
51423516Seric 
5158238Seric 			/* issue success if appropriate and reset */
5168238Seric 			if (Errors == 0 || HoldErrs)
51758855Seric 				message("250 %s Message accepted for delivery", id);
518*59747Seric 
519*59747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
52059730Seric 			{
52159730Seric 				/* avoid sending back an extra message */
52259730Seric 				e->e_flags &= ~EF_FATALERRS;
523*59747Seric 				e->e_flags |= EF_CLRQUEUE;
52459730Seric 			}
5258238Seric 			else
52658919Seric 			{
527*59747Seric 				/* from now on, we have to operate silently */
528*59747Seric 				HoldErrs = TRUE;
529*59747Seric 				e->e_errormode = EM_MAIL;
530*59747Seric 
53159730Seric 				/* if we just queued, poke it */
53259730Seric 				if (a != NULL && e->e_sendmode != SM_QUEUE)
53359730Seric 				{
53459730Seric 					unlockqueue(e);
53559730Seric 					dowork(id, TRUE, TRUE, e);
53659730Seric 					e->e_id = NULL;
53759730Seric 				}
53858919Seric 			}
53958883Seric 
540*59747Seric   abortmessage:
5419339Seric 			/* if in a child, pop back to our parent */
5429339Seric 			if (InChild)
5439339Seric 				finis();
54424943Seric 
54524943Seric 			/* clean up a bit */
54658109Seric 			gotmail = FALSE;
54755012Seric 			dropenvelope(e);
54858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
54955012Seric 			e->e_flags = BlankEnvelope.e_flags;
5504549Seric 			break;
5514549Seric 
5524549Seric 		  case CMDRSET:		/* rset -- reset state */
55358151Seric 			message("250 Reset state");
5549339Seric 			if (InChild)
5559339Seric 				finis();
55658109Seric 
55758109Seric 			/* clean up a bit */
55858109Seric 			gotmail = FALSE;
55958109Seric 			dropenvelope(e);
56058179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5619339Seric 			break;
5624549Seric 
5634549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
56458092Seric 		  case CMDEXPN:		/* expn -- expand address */
56558092Seric 			vrfy = c->cmdcode == CMDVRFY;
56658092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
56758092Seric 						PrivacyFlags))
56858082Seric 			{
56958412Seric 				if (vrfy)
57058412Seric 					message("252 Who's to say?");
57158412Seric 				else
57258412Seric 					message("502 That's none of your business");
57358082Seric 				break;
57458082Seric 			}
57558082Seric 			else if (!gothello &&
57658092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
57758092Seric 						PrivacyFlags))
57858082Seric 			{
57958151Seric 				message("503 I demand that you introduce yourself first");
58058082Seric 				break;
58158082Seric 			}
58258092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5839339Seric 				break;
58455173Seric #ifdef LOG
58558020Seric 			if (LogLevel > 5)
58655173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
58755173Seric #endif
5885003Seric 			vrfyqueue = NULL;
5897762Seric 			QuickAbort = TRUE;
59058092Seric 			if (vrfy)
59158092Seric 				e->e_flags |= EF_VRFYONLY;
59258082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5937762Seric 			if (Errors != 0)
5949339Seric 			{
5959339Seric 				if (InChild)
5969339Seric 					finis();
5977762Seric 				break;
5989339Seric 			}
5995003Seric 			while (vrfyqueue != NULL)
6005003Seric 			{
6015003Seric 				register ADDRESS *a = vrfyqueue->q_next;
6025003Seric 				char *code;
6035003Seric 
6047685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
6055003Seric 					a = a->q_next;
6065003Seric 
6077685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
60858151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
6095003Seric 				else if (a == NULL)
61058151Seric 					message("554 Self destructive alias loop");
6115003Seric 				vrfyqueue = a;
6125003Seric 			}
6139339Seric 			if (InChild)
6149339Seric 				finis();
6154549Seric 			break;
6164549Seric 
6174549Seric 		  case CMDHELP:		/* help -- give user info */
6184577Seric 			help(p);
6194549Seric 			break;
6204549Seric 
6214549Seric 		  case CMDNOOP:		/* noop -- do nothing */
62258151Seric 			message("200 OK");
6234549Seric 			break;
6244549Seric 
6254549Seric 		  case CMDQUIT:		/* quit -- leave mail */
62658151Seric 			message("221 %s closing connection", MyHostName);
6279339Seric 			if (InChild)
6289339Seric 				ExitStat = EX_QUIT;
6294549Seric 			finis();
6304549Seric 
6318544Seric 		  case CMDVERB:		/* set verbose mode */
6328544Seric 			Verbose = TRUE;
63358734Seric 			e->e_sendmode = SM_DELIVER;
63458151Seric 			message("200 Verbose mode");
6358544Seric 			break;
6368544Seric 
6379314Seric 		  case CMDONEX:		/* doing one transaction only */
6389378Seric 			OneXact = TRUE;
63958151Seric 			message("200 Only one transaction");
6409314Seric 			break;
6419314Seric 
64236230Skarels # ifdef SMTPDEBUG
6439339Seric 		  case CMDDBGQSHOW:	/* show queues */
6446907Seric 			printf("Send Queue=");
64555012Seric 			printaddr(e->e_sendqueue, TRUE);
6465003Seric 			break;
6477275Seric 
6487275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6497676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6507676Seric 			tTflag(p);
65158151Seric 			message("200 Debug set");
6527275Seric 			break;
6537275Seric 
65436230Skarels # else /* not SMTPDEBUG */
65524945Seric 
65636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
65736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
65836233Skarels # ifdef LOG
65958308Seric 			if (LogLevel > 0)
66036230Skarels 				syslog(LOG_NOTICE,
66158020Seric 				    "\"%s\" command from %s (%s)",
66236230Skarels 				    c->cmdname, RealHostName,
66358755Seric 				    anynet_ntoa(&RealHostAddr));
66436233Skarels # endif
66536230Skarels 			/* FALL THROUGH */
66636230Skarels # endif /* SMTPDEBUG */
66736230Skarels 
6684549Seric 		  case CMDERROR:	/* unknown command */
66958151Seric 			message("500 Command unrecognized");
6704549Seric 			break;
6714549Seric 
6724549Seric 		  default:
67336230Skarels 			errno = 0;
67458151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6754549Seric 			break;
6764549Seric 		}
6774549Seric 	}
6784549Seric }
6794549Seric /*
6804549Seric **  SKIPWORD -- skip a fixed word.
6814549Seric **
6824549Seric **	Parameters:
6834549Seric **		p -- place to start looking.
6844549Seric **		w -- word to skip.
6854549Seric **
6864549Seric **	Returns:
6874549Seric **		p following w.
6884549Seric **		NULL on error.
6894549Seric **
6904549Seric **	Side Effects:
6914549Seric **		clobbers the p data area.
6924549Seric */
6934549Seric 
6944549Seric static char *
6954549Seric skipword(p, w)
6964549Seric 	register char *p;
6974549Seric 	char *w;
6984549Seric {
6994549Seric 	register char *q;
7004549Seric 
7014549Seric 	/* find beginning of word */
70258050Seric 	while (isascii(*p) && isspace(*p))
7034549Seric 		p++;
7044549Seric 	q = p;
7054549Seric 
7064549Seric 	/* find end of word */
70758050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7084549Seric 		p++;
70958050Seric 	while (isascii(*p) && isspace(*p))
7104549Seric 		*p++ = '\0';
7114549Seric 	if (*p != ':')
7124549Seric 	{
7134549Seric 	  syntax:
71458151Seric 		message("501 Syntax error");
7154549Seric 		Errors++;
7164549Seric 		return (NULL);
7174549Seric 	}
7184549Seric 	*p++ = '\0';
71958050Seric 	while (isascii(*p) && isspace(*p))
7204549Seric 		p++;
7214549Seric 
7224549Seric 	/* see if the input word matches desired word */
72333725Sbostic 	if (strcasecmp(q, w))
7244549Seric 		goto syntax;
7254549Seric 
7264549Seric 	return (p);
7274549Seric }
7284577Seric /*
72958151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
73058151Seric **
73158151Seric **	Parameters:
73258151Seric **		a -- the address to print
73358151Seric **		last -- set if this is the last one.
73458151Seric **
73558151Seric **	Returns:
73658151Seric **		none.
73758151Seric **
73858151Seric **	Side Effects:
73958151Seric **		Prints the appropriate 250 codes.
74058151Seric */
74158151Seric 
74258151Seric printvrfyaddr(a, last)
74358151Seric 	register ADDRESS *a;
74458151Seric 	bool last;
74558151Seric {
74658151Seric 	char fmtbuf[20];
74758151Seric 
74858151Seric 	strcpy(fmtbuf, "250");
74958151Seric 	fmtbuf[3] = last ? ' ' : '-';
75058151Seric 
75159746Seric 	if (a->q_fullname == NULL)
75259746Seric 	{
75359746Seric 		if (strchr(a->q_user, '@') == NULL)
75459746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
75559746Seric 		else
75659746Seric 			strcpy(&fmtbuf[4], "<%s>");
75759746Seric 		message(fmtbuf, a->q_user, MyHostName);
75859746Seric 	}
75958151Seric 	else
76058151Seric 	{
76159746Seric 		if (strchr(a->q_user, '@') == NULL)
76259746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
76359746Seric 		else
76459746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
76559746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
76658151Seric 	}
76758151Seric }
76858151Seric /*
7694577Seric **  HELP -- implement the HELP command.
7704577Seric **
7714577Seric **	Parameters:
7724577Seric **		topic -- the topic we want help for.
7734577Seric **
7744577Seric **	Returns:
7754577Seric **		none.
7764577Seric **
7774577Seric **	Side Effects:
7784577Seric **		outputs the help file to message output.
7794577Seric */
7804577Seric 
7814577Seric help(topic)
7824577Seric 	char *topic;
7834577Seric {
7844577Seric 	register FILE *hf;
7854577Seric 	int len;
7864577Seric 	char buf[MAXLINE];
7874577Seric 	bool noinfo;
7884577Seric 
7898269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7904577Seric 	{
7914577Seric 		/* no help */
79211931Seric 		errno = 0;
79358151Seric 		message("502 HELP not implemented");
7944577Seric 		return;
7954577Seric 	}
7964577Seric 
79749669Seric 	if (topic == NULL || *topic == '\0')
79849669Seric 		topic = "smtp";
79949669Seric 	else
80049669Seric 		makelower(topic);
80149669Seric 
8024577Seric 	len = strlen(topic);
8034577Seric 	noinfo = TRUE;
8044577Seric 
8054577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8064577Seric 	{
8074577Seric 		if (strncmp(buf, topic, len) == 0)
8084577Seric 		{
8094577Seric 			register char *p;
8104577Seric 
81156795Seric 			p = strchr(buf, '\t');
8124577Seric 			if (p == NULL)
8134577Seric 				p = buf;
8144577Seric 			else
8154577Seric 				p++;
8164577Seric 			fixcrlf(p, TRUE);
81758151Seric 			message("214-%s", p);
8184577Seric 			noinfo = FALSE;
8194577Seric 		}
8204577Seric 	}
8214577Seric 
8224577Seric 	if (noinfo)
82358151Seric 		message("504 HELP topic unknown");
8244577Seric 	else
82558151Seric 		message("214 End of HELP info");
8264628Seric 	(void) fclose(hf);
8274577Seric }
8288544Seric /*
8299339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8309339Seric **
8319339Seric **	Parameters:
8329339Seric **		label -- a string used in error messages
8339339Seric **
8349339Seric **	Returns:
8359339Seric **		zero in the child
8369339Seric **		one in the parent
8379339Seric **
8389339Seric **	Side Effects:
8399339Seric **		none.
8409339Seric */
8418544Seric 
84255012Seric runinchild(label, e)
8439339Seric 	char *label;
84455012Seric 	register ENVELOPE *e;
8459339Seric {
8469339Seric 	int childpid;
8479339Seric 
84816158Seric 	if (!OneXact)
8499339Seric 	{
85016158Seric 		childpid = dofork();
85116158Seric 		if (childpid < 0)
85216158Seric 		{
85316158Seric 			syserr("%s: cannot fork", label);
85416158Seric 			return (1);
85516158Seric 		}
85616158Seric 		if (childpid > 0)
85716158Seric 		{
85816158Seric 			auto int st;
8599339Seric 
86016158Seric 			/* parent -- wait for child to complete */
86159060Seric 			setproctitle("srvrsmtp %s child wait", CurHostName);
86216158Seric 			st = waitfor(childpid);
86316158Seric 			if (st == -1)
86416158Seric 				syserr("%s: lost child", label);
8659339Seric 
86616158Seric 			/* if we exited on a QUIT command, complete the process */
86716158Seric 			if (st == (EX_QUIT << 8))
86816158Seric 				finis();
8699339Seric 
87016158Seric 			return (1);
87116158Seric 		}
87216158Seric 		else
87316158Seric 		{
87416158Seric 			/* child */
87516158Seric 			InChild = TRUE;
87625050Seric 			QuickAbort = FALSE;
87755012Seric 			clearenvelope(e, FALSE);
87816158Seric 		}
8799339Seric 	}
88015256Seric 
88116158Seric 	/* open alias database */
88259672Seric 	initaliases(FALSE, e);
88316158Seric 
88416158Seric 	return (0);
8859339Seric }
8869339Seric 
88756795Seric # endif /* SMTP */
888