122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
362532Sbostic  * Copyright (c) 1988, 1993
462532Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822712Sdist 
933731Sbostic # include "sendmail.h"
1022712Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*63937Seric static char sccsid[] = "@(#)srvrsmtp.c	8.6 (Berkeley) 07/19/93 (with SMTP)";
1433731Sbostic #else
15*63937Seric static char sccsid[] = "@(#)srvrsmtp.c	8.6 (Berkeley) 07/19/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 
93*63937Seric static char	*skipword();
94*63937Seric 
9555012Seric smtp(e)
9655012Seric 	register ENVELOPE *e;
974549Seric {
984549Seric 	register char *p;
998544Seric 	register struct cmd *c;
1004549Seric 	char *cmd;
1015003Seric 	auto ADDRESS *vrfyqueue;
10212612Seric 	ADDRESS *a;
10358109Seric 	bool gotmail;			/* mail command received */
10458092Seric 	bool gothello;			/* helo command received */
10558092Seric 	bool vrfy;			/* set if this is a vrfy command */
10658323Seric 	char *protocol;			/* sending protocol */
10759016Seric 	char *sendinghost;		/* sending hostname */
10858333Seric 	long msize;			/* approximate maximum message size */
10958333Seric 	auto char *delimptr;
11058714Seric 	char *id;
11159747Seric 	int nrcpts;			/* number of RCPT commands */
11263787Seric 	bool doublequeue;
1138544Seric 	char inp[MAXLINE];
11457232Seric 	char cmdbuf[MAXLINE];
1157124Seric 	extern char Version[];
11624943Seric 	extern ENVELOPE BlankEnvelope;
1174549Seric 
11859066Seric 	if (fileno(OutChannel) != fileno(stdout))
1197363Seric 	{
1207363Seric 		/* arrange for debugging output to go to remote host */
12159066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1227363Seric 	}
12355012Seric 	settime(e);
12457642Seric 	CurHostName = RealHostName;
12561093Seric 	setproctitle("server %s startup", CurHostName);
12658050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12760210Seric 	message("220-%s", inp);
12860210Seric 	message("220 ESMTP spoken here");
12958330Seric 	protocol = NULL;
13059016Seric 	sendinghost = macvalue('s', e);
13158082Seric 	gothello = FALSE;
13258330Seric 	gotmail = FALSE;
1334549Seric 	for (;;)
1344549Seric 	{
13512612Seric 		/* arrange for backout */
13612612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13759058Seric 		{
13859058Seric 			QuickAbort = FALSE;
13959058Seric 			SuprErrs = TRUE;
14012612Seric 			finis();
14159058Seric 		}
14212612Seric 		QuickAbort = FALSE;
14312612Seric 		HoldErrs = FALSE;
14451951Seric 		LogUsrErrs = FALSE;
14563843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
14612612Seric 
1477356Seric 		/* setup for the read */
14855012Seric 		e->e_to = NULL;
1494577Seric 		Errors = 0;
1507275Seric 		(void) fflush(stdout);
1517356Seric 
1527356Seric 		/* read the input line */
15361093Seric 		SmtpPhase = "server cmd read";
15461093Seric 		setproctitle("server %s cmd read", CurHostName);
15561093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
15661093Seric 				SmtpPhase);
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
16563843Seric 			if (LogLevel > (gotmail ? 1 : 19))
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";
21861093Seric 				SmtpPhase = "server EHLO";
21958323Seric 			}
22058323Seric 			else
22158323Seric 			{
22258323Seric 				protocol = "SMTP";
22361093Seric 				SmtpPhase = "server 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 
23560210Seric 			gothello = TRUE;
23660210Seric 			if (c->cmdcode != CMDEHLO)
23760239Seric 			{
23860239Seric 				/* print old message and be done with it */
23960239Seric 				message("250 %s Hello %s, pleased to meet you",
24060239Seric 					MyHostName, p);
24160210Seric 				break;
24260239Seric 			}
24360239Seric 
24460239Seric 			/* print extended message and brag */
24560239Seric 			message("250-%s Hello %s, pleased to meet you",
24660239Seric 				MyHostName, p);
24758323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24858323Seric 				message("250-EXPN");
24959271Seric 			if (MaxMessageSize > 0)
25059271Seric 				message("250-SIZE %ld", MaxMessageSize);
25159271Seric 			else
25259271Seric 				message("250-SIZE");
25358323Seric 			message("250 HELP");
2544976Seric 			break;
2554976Seric 
2564549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25761093Seric 			SmtpPhase = "server MAIL";
25824943Seric 
2599314Seric 			/* check for validity of this command */
26058789Seric 			if (!gothello)
26158082Seric 			{
26258957Seric 				/* set sending host to our known value */
26359016Seric 				if (sendinghost == NULL)
26459016Seric 					sendinghost = RealHostName;
26558957Seric 
26658789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
26758821Seric 				{
26858789Seric 					message("503 Polite people say HELO first");
26958821Seric 					break;
27058821Seric 				}
27158082Seric 			}
27258109Seric 			if (gotmail)
2734558Seric 			{
27458151Seric 				message("503 Sender already specified");
27563843Seric 				if (InChild)
27663843Seric 					finis();
2774558Seric 				break;
2784558Seric 			}
2799339Seric 			if (InChild)
2809339Seric 			{
28136230Skarels 				errno = 0;
28258151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
28358069Seric 				finis();
2849339Seric 			}
2859339Seric 
2869339Seric 			/* fork a subprocess to process this command */
28755012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2889339Seric 				break;
28963753Seric 			if (!gothello)
29063753Seric 			{
29163753Seric 				auth_warning(e,
29263753Seric 					"Host %s didn't use HELO protocol",
29363753Seric 					RealHostName);
29463753Seric 			}
29558323Seric 			if (protocol == NULL)
29658323Seric 				protocol = "SMTP";
29758323Seric 			define('r', protocol, e);
29859016Seric 			define('s', sendinghost, e);
29955012Seric 			initsys(e);
30059747Seric 			nrcpts = 0;
30157389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
3029339Seric 
3039339Seric 			/* child -- go do the processing */
3044549Seric 			p = skipword(p, "from");
3054549Seric 			if (p == NULL)
3064549Seric 				break;
30757977Seric 			if (setjmp(TopFrame) > 0)
30858147Seric 			{
30958147Seric 				/* this failed -- undo work */
31058147Seric 				if (InChild)
31159058Seric 				{
31259058Seric 					QuickAbort = FALSE;
31359058Seric 					SuprErrs = TRUE;
31463787Seric 					e->e_flags &= ~EF_FATALERRS;
31558147Seric 					finis();
31659058Seric 				}
31757977Seric 				break;
31858147Seric 			}
31957977Seric 			QuickAbort = TRUE;
32058333Seric 
32158333Seric 			/* must parse sender first */
32258333Seric 			delimptr = NULL;
32358704Seric 			setsender(p, e, &delimptr, FALSE);
32458333Seric 			p = delimptr;
32558333Seric 			if (p != NULL && *p != '\0')
32658333Seric 				*p++ = '\0';
32758333Seric 
32858333Seric 			/* now parse ESMTP arguments */
32958333Seric 			msize = 0;
33058333Seric 			for (; p != NULL && *p != '\0'; p++)
33158333Seric 			{
33258333Seric 				char *kp;
33358333Seric 				char *vp;
33458333Seric 
33558333Seric 				/* locate the beginning of the keyword */
33658333Seric 				while (isascii(*p) && isspace(*p))
33758333Seric 					p++;
33858333Seric 				if (*p == '\0')
33958333Seric 					break;
34058333Seric 				kp = p;
34158333Seric 
34258333Seric 				/* skip to the value portion */
34358333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
34458333Seric 					p++;
34558333Seric 				if (*p == '=')
34658333Seric 				{
34758333Seric 					*p++ = '\0';
34858333Seric 					vp = p;
34958333Seric 
35058333Seric 					/* skip to the end of the value */
35158333Seric 					while (*p != '\0' && *p != ' ' &&
35258333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
35358333Seric 					       *p != '=')
35458333Seric 						p++;
35558333Seric 				}
35658333Seric 
35758333Seric 				if (*p != '\0')
35858333Seric 					*p++ = '\0';
35958333Seric 
36058333Seric 				if (tTd(19, 1))
36158333Seric 					printf("MAIL: got arg %s=%s\n", kp,
36258333Seric 						vp == NULL ? "<null>" : vp);
36358333Seric 
36458333Seric 				if (strcasecmp(kp, "size") == 0)
36558333Seric 				{
36659093Seric 					if (vp == NULL)
36758333Seric 					{
36858333Seric 						usrerr("501 SIZE requires a value");
36958333Seric 						/* NOTREACHED */
37058333Seric 					}
37158333Seric 					msize = atol(vp);
37258333Seric 				}
37359093Seric 				else if (strcasecmp(kp, "body") == 0)
37459093Seric 				{
37559093Seric 					if (vp == NULL)
37659093Seric 					{
37759093Seric 						usrerr("501 BODY requires a value");
37859093Seric 						/* NOTREACHED */
37959093Seric 					}
38059093Seric # ifdef MIME
38159093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
38259093Seric 					{
38359093Seric 						e->e_bodytype = "8BITMIME";
38459709Seric 						SevenBit = FALSE;
38559093Seric 					}
38659093Seric 					else if (strcasecmp(vp, "7bit") == 0)
38759093Seric 					{
38859093Seric 						e->e_bodytype = "7BIT";
38959709Seric 						SevenBit = TRUE;
39059093Seric 					}
39159093Seric 					else
39259093Seric 					{
39359093Seric 						usrerr("501 Unknown BODY type %s",
39459093Seric 							vp);
39559093Seric 					}
39659093Seric # endif
39759093Seric 				}
39858333Seric 				else
39958333Seric 				{
40058333Seric 					usrerr("501 %s parameter unrecognized", kp);
40158333Seric 					/* NOTREACHED */
40258333Seric 				}
40358333Seric 			}
40459284Seric 
40559284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
40659284Seric 			{
40759284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
40859284Seric 					MaxMessageSize);
40959284Seric 				/* NOTREACHED */
41059284Seric 			}
41158333Seric 
41258333Seric 			if (!enoughspace(msize))
41358333Seric 			{
41458333Seric 				message("452 Insufficient disk space; try again later");
41558333Seric 				break;
41658333Seric 			}
41758151Seric 			message("250 Sender ok");
41858147Seric 			gotmail = TRUE;
4194549Seric 			break;
4204549Seric 
4214976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
42258850Seric 			if (!gotmail)
42358850Seric 			{
42458850Seric 				usrerr("503 Need MAIL before RCPT");
42558850Seric 				break;
42658850Seric 			}
42761093Seric 			SmtpPhase = "server RCPT";
42812612Seric 			if (setjmp(TopFrame) > 0)
42914785Seric 			{
43055012Seric 				e->e_flags &= ~EF_FATALERRS;
43112612Seric 				break;
43214785Seric 			}
43312612Seric 			QuickAbort = TRUE;
43451951Seric 			LogUsrErrs = TRUE;
43558093Seric 
43659699Seric 			if (e->e_sendmode != SM_DELIVER)
43759699Seric 				e->e_flags |= EF_VRFYONLY;
43858919Seric 
4394549Seric 			p = skipword(p, "to");
4404549Seric 			if (p == NULL)
4414549Seric 				break;
44258333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
44312612Seric 			if (a == NULL)
44412612Seric 				break;
44516886Seric 			a->q_flags |= QPRIMARY;
44655012Seric 			a = recipient(a, &e->e_sendqueue, e);
44712612Seric 			if (Errors != 0)
44812612Seric 				break;
44912612Seric 
45012612Seric 			/* no errors during parsing, but might be a duplicate */
45155012Seric 			e->e_to = p;
45212612Seric 			if (!bitset(QBADADDR, a->q_flags))
45359747Seric 			{
45458151Seric 				message("250 Recipient ok");
45559747Seric 				nrcpts++;
45659747Seric 			}
45712612Seric 			else
4584549Seric 			{
45912612Seric 				/* punt -- should keep message in ADDRESS.... */
46058151Seric 				message("550 Addressee unknown");
4614549Seric 			}
46255012Seric 			e->e_to = NULL;
4634549Seric 			break;
4644549Seric 
4654549Seric 		  case CMDDATA:		/* data -- text of mail */
46661093Seric 			SmtpPhase = "server DATA";
46758109Seric 			if (!gotmail)
4684549Seric 			{
46958151Seric 				message("503 Need MAIL command");
4704976Seric 				break;
4714549Seric 			}
47255012Seric 			else if (e->e_nrcpts <= 0)
4734549Seric 			{
47458151Seric 				message("503 Need RCPT (recipient)");
4754976Seric 				break;
4764549Seric 			}
4774976Seric 
47858929Seric 			/* check to see if we need to re-expand aliases */
47963787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
48063787Seric 			doublequeue = FALSE;
48158929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
48258929Seric 			{
48358929Seric 				if (bitset(QVERIFIED, a->q_flags))
48463787Seric 				{
48563787Seric 					/* need to re-expand aliases */
48663787Seric 					doublequeue = TRUE;
48763787Seric 				}
48863787Seric 				if (bitset(QBADADDR, a->q_flags))
48963787Seric 				{
49063787Seric 					/* make this "go away" */
49163787Seric 					a->q_flags |= QDONTSEND;
49263787Seric 					a->q_flags &= ~QBADADDR;
49363787Seric 				}
49458929Seric 			}
49558929Seric 
4964976Seric 			/* collect the text of the message */
49724943Seric 			SmtpPhase = "collect";
49863843Seric 			SuprErrs = TRUE;
49963787Seric 			collect(TRUE, doublequeue, e);
5004976Seric 			if (Errors != 0)
50159747Seric 				goto abortmessage;
5024976Seric 
5038238Seric 			/*
5048238Seric 			**  Arrange to send to everyone.
5058238Seric 			**	If sending to multiple people, mail back
5068238Seric 			**		errors rather than reporting directly.
5078238Seric 			**	In any case, don't mail back errors for
5088238Seric 			**		anything that has happened up to
5098238Seric 			**		now (the other end will do this).
51010197Seric 			**	Truncate our transcript -- the mail has gotten
51110197Seric 			**		to us successfully, and if we have
51210197Seric 			**		to mail this back, it will be easier
51310197Seric 			**		on the reader.
5148238Seric 			**	Then send to everyone.
5158238Seric 			**	Finally give a reply code.  If an error has
5168238Seric 			**		already been given, don't mail a
5178238Seric 			**		message back.
5189339Seric 			**	We goose error returns by clearing error bit.
5198238Seric 			*/
5208238Seric 
52124943Seric 			SmtpPhase = "delivery";
52263787Seric 			if (nrcpts != 1 && !doublequeue)
5239378Seric 			{
5249378Seric 				HoldErrs = TRUE;
52558734Seric 				e->e_errormode = EM_MAIL;
5269378Seric 			}
52755012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
52858714Seric 			id = e->e_id;
5294976Seric 
5304976Seric 			/* send to all recipients */
53163787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
53255012Seric 			e->e_to = NULL;
53363843Seric 			SuprErrs = FALSE;
5344976Seric 
53523516Seric 			/* save statistics */
53655012Seric 			markstats(e, (ADDRESS *) NULL);
53723516Seric 
5388238Seric 			/* issue success if appropriate and reset */
5398238Seric 			if (Errors == 0 || HoldErrs)
54058855Seric 				message("250 %s Message accepted for delivery", id);
54159747Seric 
54259747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
54359730Seric 			{
54459730Seric 				/* avoid sending back an extra message */
54559730Seric 				e->e_flags &= ~EF_FATALERRS;
54659747Seric 				e->e_flags |= EF_CLRQUEUE;
54759730Seric 			}
5488238Seric 			else
54958919Seric 			{
55059747Seric 				/* from now on, we have to operate silently */
55159747Seric 				HoldErrs = TRUE;
55259747Seric 				e->e_errormode = EM_MAIL;
55359747Seric 
55459730Seric 				/* if we just queued, poke it */
55563787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
55659730Seric 				{
55759730Seric 					unlockqueue(e);
55859730Seric 					dowork(id, TRUE, TRUE, e);
55959730Seric 					e->e_id = NULL;
56059730Seric 				}
56158919Seric 			}
56258883Seric 
56359747Seric   abortmessage:
5649339Seric 			/* if in a child, pop back to our parent */
5659339Seric 			if (InChild)
5669339Seric 				finis();
56724943Seric 
56824943Seric 			/* clean up a bit */
56958109Seric 			gotmail = FALSE;
57055012Seric 			dropenvelope(e);
57158179Seric 			CurEnv = e = newenvelope(e, CurEnv);
57255012Seric 			e->e_flags = BlankEnvelope.e_flags;
5734549Seric 			break;
5744549Seric 
5754549Seric 		  case CMDRSET:		/* rset -- reset state */
57658151Seric 			message("250 Reset state");
5779339Seric 			if (InChild)
5789339Seric 				finis();
57958109Seric 
58058109Seric 			/* clean up a bit */
58158109Seric 			gotmail = FALSE;
58258109Seric 			dropenvelope(e);
58358179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5849339Seric 			break;
5854549Seric 
5864549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
58758092Seric 		  case CMDEXPN:		/* expn -- expand address */
58858092Seric 			vrfy = c->cmdcode == CMDVRFY;
58958092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
59058092Seric 						PrivacyFlags))
59158082Seric 			{
59258412Seric 				if (vrfy)
59358412Seric 					message("252 Who's to say?");
59458412Seric 				else
59558412Seric 					message("502 That's none of your business");
59658082Seric 				break;
59758082Seric 			}
59858082Seric 			else if (!gothello &&
59958092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
60058092Seric 						PrivacyFlags))
60158082Seric 			{
60258151Seric 				message("503 I demand that you introduce yourself first");
60358082Seric 				break;
60458082Seric 			}
60558092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6069339Seric 				break;
60755173Seric #ifdef LOG
60858020Seric 			if (LogLevel > 5)
60955173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
61055173Seric #endif
6115003Seric 			vrfyqueue = NULL;
6127762Seric 			QuickAbort = TRUE;
61358092Seric 			if (vrfy)
61458092Seric 				e->e_flags |= EF_VRFYONLY;
61562373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
61662373Seric 				*p++;
61762373Seric 			if (*p == '\0')
61862373Seric 			{
61962373Seric 				message("501 Argument required");
62062373Seric 				Errors++;
62162373Seric 			}
62262373Seric 			else
62362373Seric 			{
62462373Seric 				(void) sendtolist(p, (ADDRESS *) NULL,
62562373Seric 						  &vrfyqueue, e);
62662373Seric 			}
6277762Seric 			if (Errors != 0)
6289339Seric 			{
6299339Seric 				if (InChild)
6309339Seric 					finis();
6317762Seric 				break;
6329339Seric 			}
63362373Seric 			if (vrfyqueue == NULL)
63462373Seric 			{
63562373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
63662373Seric 			}
6375003Seric 			while (vrfyqueue != NULL)
6385003Seric 			{
6397685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
64058151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
64163847Seric 				vrfyqueue = vrfyqueue->q_next;
6425003Seric 			}
6439339Seric 			if (InChild)
6449339Seric 				finis();
6454549Seric 			break;
6464549Seric 
6474549Seric 		  case CMDHELP:		/* help -- give user info */
6484577Seric 			help(p);
6494549Seric 			break;
6504549Seric 
6514549Seric 		  case CMDNOOP:		/* noop -- do nothing */
65258151Seric 			message("200 OK");
6534549Seric 			break;
6544549Seric 
6554549Seric 		  case CMDQUIT:		/* quit -- leave mail */
65658151Seric 			message("221 %s closing connection", MyHostName);
65761051Seric 
65861051Seric 			/* avoid future 050 messages */
65961051Seric 			Verbose = FALSE;
66061051Seric 
6619339Seric 			if (InChild)
6629339Seric 				ExitStat = EX_QUIT;
6634549Seric 			finis();
6644549Seric 
6658544Seric 		  case CMDVERB:		/* set verbose mode */
66659957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
66759957Seric 			{
66859957Seric 				/* this would give out the same info */
66959957Seric 				message("502 Verbose unavailable");
67059957Seric 				break;
67159957Seric 			}
6728544Seric 			Verbose = TRUE;
67358734Seric 			e->e_sendmode = SM_DELIVER;
67459957Seric 			message("250 Verbose mode");
6758544Seric 			break;
6768544Seric 
6779314Seric 		  case CMDONEX:		/* doing one transaction only */
6789378Seric 			OneXact = TRUE;
67959957Seric 			message("250 Only one transaction");
6809314Seric 			break;
6819314Seric 
68236230Skarels # ifdef SMTPDEBUG
6839339Seric 		  case CMDDBGQSHOW:	/* show queues */
6846907Seric 			printf("Send Queue=");
68555012Seric 			printaddr(e->e_sendqueue, TRUE);
6865003Seric 			break;
6877275Seric 
6887275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6897676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6907676Seric 			tTflag(p);
69158151Seric 			message("200 Debug set");
6927275Seric 			break;
6937275Seric 
69436230Skarels # else /* not SMTPDEBUG */
69524945Seric 
69636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
69736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
69836233Skarels # ifdef LOG
69958308Seric 			if (LogLevel > 0)
70036230Skarels 				syslog(LOG_NOTICE,
70158020Seric 				    "\"%s\" command from %s (%s)",
70236230Skarels 				    c->cmdname, RealHostName,
70358755Seric 				    anynet_ntoa(&RealHostAddr));
70436233Skarels # endif
70536230Skarels 			/* FALL THROUGH */
70636230Skarels # endif /* SMTPDEBUG */
70736230Skarels 
7084549Seric 		  case CMDERROR:	/* unknown command */
70958151Seric 			message("500 Command unrecognized");
7104549Seric 			break;
7114549Seric 
7124549Seric 		  default:
71336230Skarels 			errno = 0;
71458151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7154549Seric 			break;
7164549Seric 		}
7174549Seric 	}
7184549Seric }
7194549Seric /*
7204549Seric **  SKIPWORD -- skip a fixed word.
7214549Seric **
7224549Seric **	Parameters:
7234549Seric **		p -- place to start looking.
7244549Seric **		w -- word to skip.
7254549Seric **
7264549Seric **	Returns:
7274549Seric **		p following w.
7284549Seric **		NULL on error.
7294549Seric **
7304549Seric **	Side Effects:
7314549Seric **		clobbers the p data area.
7324549Seric */
7334549Seric 
7344549Seric static char *
7354549Seric skipword(p, w)
7364549Seric 	register char *p;
7374549Seric 	char *w;
7384549Seric {
7394549Seric 	register char *q;
7404549Seric 
7414549Seric 	/* find beginning of word */
74258050Seric 	while (isascii(*p) && isspace(*p))
7434549Seric 		p++;
7444549Seric 	q = p;
7454549Seric 
7464549Seric 	/* find end of word */
74758050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7484549Seric 		p++;
74958050Seric 	while (isascii(*p) && isspace(*p))
7504549Seric 		*p++ = '\0';
7514549Seric 	if (*p != ':')
7524549Seric 	{
7534549Seric 	  syntax:
75462373Seric 		message("501 Syntax error in parameters");
7554549Seric 		Errors++;
7564549Seric 		return (NULL);
7574549Seric 	}
7584549Seric 	*p++ = '\0';
75958050Seric 	while (isascii(*p) && isspace(*p))
7604549Seric 		p++;
7614549Seric 
76262373Seric 	if (*p == '\0')
76362373Seric 		goto syntax;
76462373Seric 
7654549Seric 	/* see if the input word matches desired word */
76633725Sbostic 	if (strcasecmp(q, w))
7674549Seric 		goto syntax;
7684549Seric 
7694549Seric 	return (p);
7704549Seric }
7714577Seric /*
77258151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
77358151Seric **
77458151Seric **	Parameters:
77558151Seric **		a -- the address to print
77658151Seric **		last -- set if this is the last one.
77758151Seric **
77858151Seric **	Returns:
77958151Seric **		none.
78058151Seric **
78158151Seric **	Side Effects:
78258151Seric **		Prints the appropriate 250 codes.
78358151Seric */
78458151Seric 
78558151Seric printvrfyaddr(a, last)
78658151Seric 	register ADDRESS *a;
78758151Seric 	bool last;
78858151Seric {
78958151Seric 	char fmtbuf[20];
79058151Seric 
79158151Seric 	strcpy(fmtbuf, "250");
79258151Seric 	fmtbuf[3] = last ? ' ' : '-';
79358151Seric 
79459746Seric 	if (a->q_fullname == NULL)
79559746Seric 	{
79659746Seric 		if (strchr(a->q_user, '@') == NULL)
79759746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
79859746Seric 		else
79959746Seric 			strcpy(&fmtbuf[4], "<%s>");
80059746Seric 		message(fmtbuf, a->q_user, MyHostName);
80159746Seric 	}
80258151Seric 	else
80358151Seric 	{
80459746Seric 		if (strchr(a->q_user, '@') == NULL)
80559746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
80659746Seric 		else
80759746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
80859746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
80958151Seric 	}
81058151Seric }
81158151Seric /*
8124577Seric **  HELP -- implement the HELP command.
8134577Seric **
8144577Seric **	Parameters:
8154577Seric **		topic -- the topic we want help for.
8164577Seric **
8174577Seric **	Returns:
8184577Seric **		none.
8194577Seric **
8204577Seric **	Side Effects:
8214577Seric **		outputs the help file to message output.
8224577Seric */
8234577Seric 
8244577Seric help(topic)
8254577Seric 	char *topic;
8264577Seric {
8274577Seric 	register FILE *hf;
8284577Seric 	int len;
8294577Seric 	char buf[MAXLINE];
8304577Seric 	bool noinfo;
8314577Seric 
8328269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8334577Seric 	{
8344577Seric 		/* no help */
83511931Seric 		errno = 0;
83658151Seric 		message("502 HELP not implemented");
8374577Seric 		return;
8384577Seric 	}
8394577Seric 
84049669Seric 	if (topic == NULL || *topic == '\0')
84149669Seric 		topic = "smtp";
84249669Seric 	else
84349669Seric 		makelower(topic);
84449669Seric 
8454577Seric 	len = strlen(topic);
8464577Seric 	noinfo = TRUE;
8474577Seric 
8484577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8494577Seric 	{
8504577Seric 		if (strncmp(buf, topic, len) == 0)
8514577Seric 		{
8524577Seric 			register char *p;
8534577Seric 
85456795Seric 			p = strchr(buf, '\t');
8554577Seric 			if (p == NULL)
8564577Seric 				p = buf;
8574577Seric 			else
8584577Seric 				p++;
8594577Seric 			fixcrlf(p, TRUE);
86058151Seric 			message("214-%s", p);
8614577Seric 			noinfo = FALSE;
8624577Seric 		}
8634577Seric 	}
8644577Seric 
8654577Seric 	if (noinfo)
86658151Seric 		message("504 HELP topic unknown");
8674577Seric 	else
86858151Seric 		message("214 End of HELP info");
8694628Seric 	(void) fclose(hf);
8704577Seric }
8718544Seric /*
8729339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8739339Seric **
8749339Seric **	Parameters:
8759339Seric **		label -- a string used in error messages
8769339Seric **
8779339Seric **	Returns:
8789339Seric **		zero in the child
8799339Seric **		one in the parent
8809339Seric **
8819339Seric **	Side Effects:
8829339Seric **		none.
8839339Seric */
8848544Seric 
88555012Seric runinchild(label, e)
8869339Seric 	char *label;
88755012Seric 	register ENVELOPE *e;
8889339Seric {
8899339Seric 	int childpid;
8909339Seric 
89116158Seric 	if (!OneXact)
8929339Seric 	{
89316158Seric 		childpid = dofork();
89416158Seric 		if (childpid < 0)
89516158Seric 		{
89616158Seric 			syserr("%s: cannot fork", label);
89716158Seric 			return (1);
89816158Seric 		}
89916158Seric 		if (childpid > 0)
90016158Seric 		{
90116158Seric 			auto int st;
9029339Seric 
90316158Seric 			/* parent -- wait for child to complete */
90461093Seric 			setproctitle("server %s child wait", CurHostName);
90516158Seric 			st = waitfor(childpid);
90616158Seric 			if (st == -1)
90716158Seric 				syserr("%s: lost child", label);
9089339Seric 
90916158Seric 			/* if we exited on a QUIT command, complete the process */
91016158Seric 			if (st == (EX_QUIT << 8))
91116158Seric 				finis();
9129339Seric 
91316158Seric 			return (1);
91416158Seric 		}
91516158Seric 		else
91616158Seric 		{
91716158Seric 			/* child */
91816158Seric 			InChild = TRUE;
91925050Seric 			QuickAbort = FALSE;
92055012Seric 			clearenvelope(e, FALSE);
92116158Seric 		}
9229339Seric 	}
92315256Seric 
92416158Seric 	/* open alias database */
92560537Seric 	initmaps(FALSE, e);
92616158Seric 
92716158Seric 	return (0);
9289339Seric }
9299339Seric 
93056795Seric # endif /* SMTP */
931