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*59957Seric static char sccsid[] = "@(#)srvrsmtp.c	6.53 (Berkeley) 05/12/93 (with SMTP)";
1433731Sbostic #else
15*59957Seric static char sccsid[] = "@(#)srvrsmtp.c	6.53 (Berkeley) 05/12/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;
11059747Seric 	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);
29059747Seric 			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))
44259747Seric 			{
44358151Seric 				message("250 Recipient ok");
44459747Seric 				nrcpts++;
44559747Seric 			}
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)
47959747Seric 				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";
50059747Seric 			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);
51859747Seric 
51959747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
52059730Seric 			{
52159730Seric 				/* avoid sending back an extra message */
52259730Seric 				e->e_flags &= ~EF_FATALERRS;
52359747Seric 				e->e_flags |= EF_CLRQUEUE;
52459730Seric 			}
5258238Seric 			else
52658919Seric 			{
52759747Seric 				/* from now on, we have to operate silently */
52859747Seric 				HoldErrs = TRUE;
52959747Seric 				e->e_errormode = EM_MAIL;
53059747Seric 
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 
54059747Seric   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 */
632*59957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
633*59957Seric 			{
634*59957Seric 				/* this would give out the same info */
635*59957Seric 				message("502 Verbose unavailable");
636*59957Seric 				break;
637*59957Seric 			}
6388544Seric 			Verbose = TRUE;
63958734Seric 			e->e_sendmode = SM_DELIVER;
640*59957Seric 			message("250 Verbose mode");
6418544Seric 			break;
6428544Seric 
6439314Seric 		  case CMDONEX:		/* doing one transaction only */
6449378Seric 			OneXact = TRUE;
645*59957Seric 			message("250 Only one transaction");
6469314Seric 			break;
6479314Seric 
64836230Skarels # ifdef SMTPDEBUG
6499339Seric 		  case CMDDBGQSHOW:	/* show queues */
6506907Seric 			printf("Send Queue=");
65155012Seric 			printaddr(e->e_sendqueue, TRUE);
6525003Seric 			break;
6537275Seric 
6547275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6557676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6567676Seric 			tTflag(p);
65758151Seric 			message("200 Debug set");
6587275Seric 			break;
6597275Seric 
66036230Skarels # else /* not SMTPDEBUG */
66124945Seric 
66236230Skarels 		  case CMDDBGQSHOW:	/* show queues */
66336230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
66436233Skarels # ifdef LOG
66558308Seric 			if (LogLevel > 0)
66636230Skarels 				syslog(LOG_NOTICE,
66758020Seric 				    "\"%s\" command from %s (%s)",
66836230Skarels 				    c->cmdname, RealHostName,
66958755Seric 				    anynet_ntoa(&RealHostAddr));
67036233Skarels # endif
67136230Skarels 			/* FALL THROUGH */
67236230Skarels # endif /* SMTPDEBUG */
67336230Skarels 
6744549Seric 		  case CMDERROR:	/* unknown command */
67558151Seric 			message("500 Command unrecognized");
6764549Seric 			break;
6774549Seric 
6784549Seric 		  default:
67936230Skarels 			errno = 0;
68058151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6814549Seric 			break;
6824549Seric 		}
6834549Seric 	}
6844549Seric }
6854549Seric /*
6864549Seric **  SKIPWORD -- skip a fixed word.
6874549Seric **
6884549Seric **	Parameters:
6894549Seric **		p -- place to start looking.
6904549Seric **		w -- word to skip.
6914549Seric **
6924549Seric **	Returns:
6934549Seric **		p following w.
6944549Seric **		NULL on error.
6954549Seric **
6964549Seric **	Side Effects:
6974549Seric **		clobbers the p data area.
6984549Seric */
6994549Seric 
7004549Seric static char *
7014549Seric skipword(p, w)
7024549Seric 	register char *p;
7034549Seric 	char *w;
7044549Seric {
7054549Seric 	register char *q;
7064549Seric 
7074549Seric 	/* find beginning of word */
70858050Seric 	while (isascii(*p) && isspace(*p))
7094549Seric 		p++;
7104549Seric 	q = p;
7114549Seric 
7124549Seric 	/* find end of word */
71358050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7144549Seric 		p++;
71558050Seric 	while (isascii(*p) && isspace(*p))
7164549Seric 		*p++ = '\0';
7174549Seric 	if (*p != ':')
7184549Seric 	{
7194549Seric 	  syntax:
72058151Seric 		message("501 Syntax error");
7214549Seric 		Errors++;
7224549Seric 		return (NULL);
7234549Seric 	}
7244549Seric 	*p++ = '\0';
72558050Seric 	while (isascii(*p) && isspace(*p))
7264549Seric 		p++;
7274549Seric 
7284549Seric 	/* see if the input word matches desired word */
72933725Sbostic 	if (strcasecmp(q, w))
7304549Seric 		goto syntax;
7314549Seric 
7324549Seric 	return (p);
7334549Seric }
7344577Seric /*
73558151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
73658151Seric **
73758151Seric **	Parameters:
73858151Seric **		a -- the address to print
73958151Seric **		last -- set if this is the last one.
74058151Seric **
74158151Seric **	Returns:
74258151Seric **		none.
74358151Seric **
74458151Seric **	Side Effects:
74558151Seric **		Prints the appropriate 250 codes.
74658151Seric */
74758151Seric 
74858151Seric printvrfyaddr(a, last)
74958151Seric 	register ADDRESS *a;
75058151Seric 	bool last;
75158151Seric {
75258151Seric 	char fmtbuf[20];
75358151Seric 
75458151Seric 	strcpy(fmtbuf, "250");
75558151Seric 	fmtbuf[3] = last ? ' ' : '-';
75658151Seric 
75759746Seric 	if (a->q_fullname == NULL)
75859746Seric 	{
75959746Seric 		if (strchr(a->q_user, '@') == NULL)
76059746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
76159746Seric 		else
76259746Seric 			strcpy(&fmtbuf[4], "<%s>");
76359746Seric 		message(fmtbuf, a->q_user, MyHostName);
76459746Seric 	}
76558151Seric 	else
76658151Seric 	{
76759746Seric 		if (strchr(a->q_user, '@') == NULL)
76859746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
76959746Seric 		else
77059746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
77159746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
77258151Seric 	}
77358151Seric }
77458151Seric /*
7754577Seric **  HELP -- implement the HELP command.
7764577Seric **
7774577Seric **	Parameters:
7784577Seric **		topic -- the topic we want help for.
7794577Seric **
7804577Seric **	Returns:
7814577Seric **		none.
7824577Seric **
7834577Seric **	Side Effects:
7844577Seric **		outputs the help file to message output.
7854577Seric */
7864577Seric 
7874577Seric help(topic)
7884577Seric 	char *topic;
7894577Seric {
7904577Seric 	register FILE *hf;
7914577Seric 	int len;
7924577Seric 	char buf[MAXLINE];
7934577Seric 	bool noinfo;
7944577Seric 
7958269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7964577Seric 	{
7974577Seric 		/* no help */
79811931Seric 		errno = 0;
79958151Seric 		message("502 HELP not implemented");
8004577Seric 		return;
8014577Seric 	}
8024577Seric 
80349669Seric 	if (topic == NULL || *topic == '\0')
80449669Seric 		topic = "smtp";
80549669Seric 	else
80649669Seric 		makelower(topic);
80749669Seric 
8084577Seric 	len = strlen(topic);
8094577Seric 	noinfo = TRUE;
8104577Seric 
8114577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8124577Seric 	{
8134577Seric 		if (strncmp(buf, topic, len) == 0)
8144577Seric 		{
8154577Seric 			register char *p;
8164577Seric 
81756795Seric 			p = strchr(buf, '\t');
8184577Seric 			if (p == NULL)
8194577Seric 				p = buf;
8204577Seric 			else
8214577Seric 				p++;
8224577Seric 			fixcrlf(p, TRUE);
82358151Seric 			message("214-%s", p);
8244577Seric 			noinfo = FALSE;
8254577Seric 		}
8264577Seric 	}
8274577Seric 
8284577Seric 	if (noinfo)
82958151Seric 		message("504 HELP topic unknown");
8304577Seric 	else
83158151Seric 		message("214 End of HELP info");
8324628Seric 	(void) fclose(hf);
8334577Seric }
8348544Seric /*
8359339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8369339Seric **
8379339Seric **	Parameters:
8389339Seric **		label -- a string used in error messages
8399339Seric **
8409339Seric **	Returns:
8419339Seric **		zero in the child
8429339Seric **		one in the parent
8439339Seric **
8449339Seric **	Side Effects:
8459339Seric **		none.
8469339Seric */
8478544Seric 
84855012Seric runinchild(label, e)
8499339Seric 	char *label;
85055012Seric 	register ENVELOPE *e;
8519339Seric {
8529339Seric 	int childpid;
8539339Seric 
85416158Seric 	if (!OneXact)
8559339Seric 	{
85616158Seric 		childpid = dofork();
85716158Seric 		if (childpid < 0)
85816158Seric 		{
85916158Seric 			syserr("%s: cannot fork", label);
86016158Seric 			return (1);
86116158Seric 		}
86216158Seric 		if (childpid > 0)
86316158Seric 		{
86416158Seric 			auto int st;
8659339Seric 
86616158Seric 			/* parent -- wait for child to complete */
86759060Seric 			setproctitle("srvrsmtp %s child wait", CurHostName);
86816158Seric 			st = waitfor(childpid);
86916158Seric 			if (st == -1)
87016158Seric 				syserr("%s: lost child", label);
8719339Seric 
87216158Seric 			/* if we exited on a QUIT command, complete the process */
87316158Seric 			if (st == (EX_QUIT << 8))
87416158Seric 				finis();
8759339Seric 
87616158Seric 			return (1);
87716158Seric 		}
87816158Seric 		else
87916158Seric 		{
88016158Seric 			/* child */
88116158Seric 			InChild = TRUE;
88225050Seric 			QuickAbort = FALSE;
88355012Seric 			clearenvelope(e, FALSE);
88416158Seric 		}
8859339Seric 	}
88615256Seric 
88716158Seric 	/* open alias database */
88859672Seric 	initaliases(FALSE, e);
88916158Seric 
89016158Seric 	return (0);
8919339Seric }
8929339Seric 
89356795Seric # endif /* SMTP */
894