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*59746Seric static char sccsid[] = "@(#)srvrsmtp.c	6.51 (Berkeley) 05/05/93 (with SMTP)";
1433731Sbostic #else
15*59746Seric static char sccsid[] = "@(#)srvrsmtp.c	6.51 (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;
1108544Seric 	char inp[MAXLINE];
11157232Seric 	char cmdbuf[MAXLINE];
1127124Seric 	extern char Version[];
11311151Seric 	extern char *macvalue();
11412612Seric 	extern ADDRESS *recipient();
11524943Seric 	extern ENVELOPE BlankEnvelope;
11624943Seric 	extern ENVELOPE *newenvelope();
11758755Seric 	extern char *anynet_ntoa();
1184549Seric 
11959066Seric 	if (fileno(OutChannel) != fileno(stdout))
1207363Seric 	{
1217363Seric 		/* arrange for debugging output to go to remote host */
12259066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1237363Seric 	}
12455012Seric 	settime(e);
12557642Seric 	CurHostName = RealHostName;
12659060Seric 	setproctitle("srvrsmtp %s startup", CurHostName);
12758050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12858151Seric 	message("220 %s", inp);
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;
14558092Seric 		e->e_flags &= ~EF_VRFYONLY;
14612612Seric 
1477356Seric 		/* setup for the read */
14855012Seric 		e->e_to = NULL;
1494577Seric 		Errors = 0;
1507275Seric 		(void) fflush(stdout);
1517356Seric 
1527356Seric 		/* read the input line */
15359060Seric 		SmtpPhase = "srvrsmtp cmd read";
15459060Seric 		setproctitle("srvrsmtp %s cmd read", CurHostName);
15558109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1567356Seric 
1577685Seric 		/* handle errors */
1587356Seric 		if (p == NULL)
1597356Seric 		{
1604549Seric 			/* end of file, just die */
16158151Seric 			message("421 %s Lost input channel from %s",
16225050Seric 				MyHostName, CurHostName);
16355464Seric #ifdef LOG
16458020Seric 			if (LogLevel > 1)
16555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16655464Seric 					CurHostName);
16755464Seric #endif
16858069Seric 			if (InChild)
16958069Seric 				ExitStat = EX_QUIT;
1704549Seric 			finis();
1714549Seric 		}
1724549Seric 
1734549Seric 		/* clean up end of line */
1744558Seric 		fixcrlf(inp, TRUE);
1754549Seric 
1764713Seric 		/* echo command to transcript */
17755012Seric 		if (e->e_xfp != NULL)
17855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1794713Seric 
18059060Seric 		if (e->e_id == NULL)
18159060Seric 			setproctitle("%s: %s", CurHostName, inp);
18259060Seric 		else
18359060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
18459060Seric 
1854549Seric 		/* break off command */
18658050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1874549Seric 			continue;
18857232Seric 		cmd = cmdbuf;
18958050Seric 		while (*p != '\0' &&
19058050Seric 		       !(isascii(*p) && isspace(*p)) &&
19158050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19224981Seric 			*cmd++ = *p++;
19324981Seric 		*cmd = '\0';
1944549Seric 
19525691Seric 		/* throw away leading whitespace */
19658050Seric 		while (isascii(*p) && isspace(*p))
19725691Seric 			p++;
19825691Seric 
1994549Seric 		/* decode command */
2004549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2014549Seric 		{
20233725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2034549Seric 				break;
2044549Seric 		}
2054549Seric 
20651954Seric 		/* reset errors */
20751954Seric 		errno = 0;
20851954Seric 
2094549Seric 		/* process command */
2104549Seric 		switch (c->cmdcode)
2114549Seric 		{
2124976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
21358323Seric 		  case CMDEHLO:		/* extended hello */
21458323Seric 			if (c->cmdcode == CMDEHLO)
21558323Seric 			{
21658323Seric 				protocol = "ESMTP";
21758323Seric 				SmtpPhase = "EHLO";
21858323Seric 			}
21958323Seric 			else
22058323Seric 			{
22158323Seric 				protocol = "SMTP";
22258323Seric 				SmtpPhase = "HELO";
22358323Seric 			}
22459016Seric 			sendinghost = newstr(p);
22558308Seric 			if (strcasecmp(p, RealHostName) != 0)
22611146Seric 			{
22758789Seric 				auth_warning(e, "Host %s claimed to be %s",
22858789Seric 					RealHostName, p);
22911146Seric 			}
23058957Seric 			p = macvalue('_', e);
23158957Seric 			if (p == NULL)
23259016Seric 				p = RealHostName;
23358323Seric 
23458323Seric 			/* send ext. message -- old systems must ignore */
23558323Seric 			message("250-%s Hello %s, pleased to meet you",
23658957Seric 				MyHostName, p);
23758323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
23858323Seric 				message("250-EXPN");
23959271Seric 			if (MaxMessageSize > 0)
24059271Seric 				message("250-SIZE %ld", MaxMessageSize);
24159271Seric 			else
24259271Seric 				message("250-SIZE");
24358323Seric 			message("250 HELP");
24458082Seric 			gothello = TRUE;
2454976Seric 			break;
2464976Seric 
2474549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24824943Seric 			SmtpPhase = "MAIL";
24924943Seric 
2509314Seric 			/* check for validity of this command */
25158789Seric 			if (!gothello)
25258082Seric 			{
25358957Seric 				/* set sending host to our known value */
25459016Seric 				if (sendinghost == NULL)
25559016Seric 					sendinghost = RealHostName;
25658957Seric 
25758789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25858821Seric 				{
25958789Seric 					message("503 Polite people say HELO first");
26058821Seric 					break;
26158821Seric 				}
26258789Seric 				else
26358821Seric 				{
26458789Seric 					auth_warning(e,
26558789Seric 						"Host %s didn't use HELO protocol",
26658789Seric 						RealHostName);
26758821Seric 				}
26858082Seric 			}
26958109Seric 			if (gotmail)
2704558Seric 			{
27158151Seric 				message("503 Sender already specified");
2724558Seric 				break;
2734558Seric 			}
2749339Seric 			if (InChild)
2759339Seric 			{
27636230Skarels 				errno = 0;
27758151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
27858069Seric 				finis();
2799339Seric 			}
2809339Seric 
2819339Seric 			/* fork a subprocess to process this command */
28255012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2839339Seric 				break;
28458323Seric 			if (protocol == NULL)
28558323Seric 				protocol = "SMTP";
28658323Seric 			define('r', protocol, e);
28759016Seric 			define('s', sendinghost, e);
28855012Seric 			initsys(e);
28957389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2909339Seric 
2919339Seric 			/* child -- go do the processing */
2924549Seric 			p = skipword(p, "from");
2934549Seric 			if (p == NULL)
2944549Seric 				break;
29557977Seric 			if (setjmp(TopFrame) > 0)
29658147Seric 			{
29758147Seric 				/* this failed -- undo work */
29858147Seric 				if (InChild)
29959058Seric 				{
30059058Seric 					QuickAbort = FALSE;
30159058Seric 					SuprErrs = TRUE;
30258147Seric 					finis();
30359058Seric 				}
30457977Seric 				break;
30558147Seric 			}
30657977Seric 			QuickAbort = TRUE;
30758333Seric 
30858333Seric 			/* must parse sender first */
30958333Seric 			delimptr = NULL;
31058704Seric 			setsender(p, e, &delimptr, FALSE);
31158333Seric 			p = delimptr;
31258333Seric 			if (p != NULL && *p != '\0')
31358333Seric 				*p++ = '\0';
31458333Seric 
31558333Seric 			/* now parse ESMTP arguments */
31658333Seric 			msize = 0;
31758333Seric 			for (; p != NULL && *p != '\0'; p++)
31858333Seric 			{
31958333Seric 				char *kp;
32058333Seric 				char *vp;
32158333Seric 
32258333Seric 				/* locate the beginning of the keyword */
32358333Seric 				while (isascii(*p) && isspace(*p))
32458333Seric 					p++;
32558333Seric 				if (*p == '\0')
32658333Seric 					break;
32758333Seric 				kp = p;
32858333Seric 
32958333Seric 				/* skip to the value portion */
33058333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
33158333Seric 					p++;
33258333Seric 				if (*p == '=')
33358333Seric 				{
33458333Seric 					*p++ = '\0';
33558333Seric 					vp = p;
33658333Seric 
33758333Seric 					/* skip to the end of the value */
33858333Seric 					while (*p != '\0' && *p != ' ' &&
33958333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
34058333Seric 					       *p != '=')
34158333Seric 						p++;
34258333Seric 				}
34358333Seric 
34458333Seric 				if (*p != '\0')
34558333Seric 					*p++ = '\0';
34658333Seric 
34758333Seric 				if (tTd(19, 1))
34858333Seric 					printf("MAIL: got arg %s=%s\n", kp,
34958333Seric 						vp == NULL ? "<null>" : vp);
35058333Seric 
35158333Seric 				if (strcasecmp(kp, "size") == 0)
35258333Seric 				{
35359093Seric 					if (vp == NULL)
35458333Seric 					{
35558333Seric 						usrerr("501 SIZE requires a value");
35658333Seric 						/* NOTREACHED */
35758333Seric 					}
35858333Seric 					msize = atol(vp);
35958333Seric 				}
36059093Seric 				else if (strcasecmp(kp, "body") == 0)
36159093Seric 				{
36259093Seric 					if (vp == NULL)
36359093Seric 					{
36459093Seric 						usrerr("501 BODY requires a value");
36559093Seric 						/* NOTREACHED */
36659093Seric 					}
36759093Seric # ifdef MIME
36859093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
36959093Seric 					{
37059093Seric 						e->e_bodytype = "8BITMIME";
37159709Seric 						SevenBit = FALSE;
37259093Seric 					}
37359093Seric 					else if (strcasecmp(vp, "7bit") == 0)
37459093Seric 					{
37559093Seric 						e->e_bodytype = "7BIT";
37659709Seric 						SevenBit = TRUE;
37759093Seric 					}
37859093Seric 					else
37959093Seric 					{
38059093Seric 						usrerr("501 Unknown BODY type %s",
38159093Seric 							vp);
38259093Seric 					}
38359093Seric # endif
38459093Seric 				}
38558333Seric 				else
38658333Seric 				{
38758333Seric 					usrerr("501 %s parameter unrecognized", kp);
38858333Seric 					/* NOTREACHED */
38958333Seric 				}
39058333Seric 			}
39159284Seric 
39259284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
39359284Seric 			{
39459284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
39559284Seric 					MaxMessageSize);
39659284Seric 				/* NOTREACHED */
39759284Seric 			}
39858333Seric 
39958333Seric 			if (!enoughspace(msize))
40058333Seric 			{
40158333Seric 				message("452 Insufficient disk space; try again later");
40258333Seric 				break;
40358333Seric 			}
40458151Seric 			message("250 Sender ok");
40558147Seric 			gotmail = TRUE;
4064549Seric 			break;
4074549Seric 
4084976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
40958850Seric 			if (!gotmail)
41058850Seric 			{
41158850Seric 				usrerr("503 Need MAIL before RCPT");
41258850Seric 				break;
41358850Seric 			}
41424943Seric 			SmtpPhase = "RCPT";
41512612Seric 			if (setjmp(TopFrame) > 0)
41614785Seric 			{
41755012Seric 				e->e_flags &= ~EF_FATALERRS;
41812612Seric 				break;
41914785Seric 			}
42012612Seric 			QuickAbort = TRUE;
42151951Seric 			LogUsrErrs = TRUE;
42258093Seric 
42359699Seric 			if (e->e_sendmode != SM_DELIVER)
42459699Seric 				e->e_flags |= EF_VRFYONLY;
42558919Seric 
4264549Seric 			p = skipword(p, "to");
4274549Seric 			if (p == NULL)
4284549Seric 				break;
42958333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
43012612Seric 			if (a == NULL)
43112612Seric 				break;
43216886Seric 			a->q_flags |= QPRIMARY;
43355012Seric 			a = recipient(a, &e->e_sendqueue, e);
43412612Seric 			if (Errors != 0)
43512612Seric 				break;
43612612Seric 
43712612Seric 			/* no errors during parsing, but might be a duplicate */
43855012Seric 			e->e_to = p;
43912612Seric 			if (!bitset(QBADADDR, a->q_flags))
44058151Seric 				message("250 Recipient ok");
44112612Seric 			else
4424549Seric 			{
44312612Seric 				/* punt -- should keep message in ADDRESS.... */
44458151Seric 				message("550 Addressee unknown");
4454549Seric 			}
44655012Seric 			e->e_to = NULL;
4474549Seric 			break;
4484549Seric 
4494549Seric 		  case CMDDATA:		/* data -- text of mail */
45024943Seric 			SmtpPhase = "DATA";
45158109Seric 			if (!gotmail)
4524549Seric 			{
45358151Seric 				message("503 Need MAIL command");
4544976Seric 				break;
4554549Seric 			}
45655012Seric 			else if (e->e_nrcpts <= 0)
4574549Seric 			{
45858151Seric 				message("503 Need RCPT (recipient)");
4594976Seric 				break;
4604549Seric 			}
4614976Seric 
46258929Seric 			/* check to see if we need to re-expand aliases */
46358929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
46458929Seric 			{
46558929Seric 				if (bitset(QVERIFIED, a->q_flags))
46658929Seric 					break;
46758929Seric 			}
46858929Seric 
4694976Seric 			/* collect the text of the message */
47024943Seric 			SmtpPhase = "collect";
47158929Seric 			collect(TRUE, a != NULL, e);
47259730Seric 			e->e_flags &= ~EF_FATALERRS;
4734976Seric 			if (Errors != 0)
4744976Seric 				break;
4754976Seric 
4768238Seric 			/*
4778238Seric 			**  Arrange to send to everyone.
4788238Seric 			**	If sending to multiple people, mail back
4798238Seric 			**		errors rather than reporting directly.
4808238Seric 			**	In any case, don't mail back errors for
4818238Seric 			**		anything that has happened up to
4828238Seric 			**		now (the other end will do this).
48310197Seric 			**	Truncate our transcript -- the mail has gotten
48410197Seric 			**		to us successfully, and if we have
48510197Seric 			**		to mail this back, it will be easier
48610197Seric 			**		on the reader.
4878238Seric 			**	Then send to everyone.
4888238Seric 			**	Finally give a reply code.  If an error has
4898238Seric 			**		already been given, don't mail a
4908238Seric 			**		message back.
4919339Seric 			**	We goose error returns by clearing error bit.
4928238Seric 			*/
4938238Seric 
49424943Seric 			SmtpPhase = "delivery";
49555012Seric 			if (e->e_nrcpts != 1)
4969378Seric 			{
4979378Seric 				HoldErrs = TRUE;
49858734Seric 				e->e_errormode = EM_MAIL;
4999378Seric 			}
50055012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
50158714Seric 			id = e->e_id;
5024976Seric 
5034976Seric 			/* send to all recipients */
50458919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
50555012Seric 			e->e_to = NULL;
5064976Seric 
50723516Seric 			/* save statistics */
50855012Seric 			markstats(e, (ADDRESS *) NULL);
50923516Seric 
5108238Seric 			/* issue success if appropriate and reset */
5118238Seric 			if (Errors == 0 || HoldErrs)
51258855Seric 				message("250 %s Message accepted for delivery", id);
51359730Seric 			if (bitset(EF_FATALERRS, e->e_flags))
51459730Seric 			{
51559730Seric 				/* avoid sending back an extra message */
51659730Seric 				e->e_flags &= ~EF_FATALERRS;
51759730Seric 			}
5188238Seric 			else
51958919Seric 			{
52059730Seric 				/* if we just queued, poke it */
52159730Seric 				if (a != NULL && e->e_sendmode != SM_QUEUE)
52259730Seric 				{
52359730Seric 					unlockqueue(e);
52459730Seric 					dowork(id, TRUE, TRUE, e);
52559730Seric 					e->e_id = NULL;
52659730Seric 				}
52758919Seric 			}
52858883Seric 
5299339Seric 			/* if in a child, pop back to our parent */
5309339Seric 			if (InChild)
5319339Seric 				finis();
53224943Seric 
53324943Seric 			/* clean up a bit */
53458109Seric 			gotmail = FALSE;
53555012Seric 			dropenvelope(e);
53658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
53755012Seric 			e->e_flags = BlankEnvelope.e_flags;
5384549Seric 			break;
5394549Seric 
5404549Seric 		  case CMDRSET:		/* rset -- reset state */
54158151Seric 			message("250 Reset state");
5429339Seric 			if (InChild)
5439339Seric 				finis();
54458109Seric 
54558109Seric 			/* clean up a bit */
54658109Seric 			gotmail = FALSE;
54758109Seric 			dropenvelope(e);
54858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5499339Seric 			break;
5504549Seric 
5514549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
55258092Seric 		  case CMDEXPN:		/* expn -- expand address */
55358092Seric 			vrfy = c->cmdcode == CMDVRFY;
55458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
55558092Seric 						PrivacyFlags))
55658082Seric 			{
55758412Seric 				if (vrfy)
55858412Seric 					message("252 Who's to say?");
55958412Seric 				else
56058412Seric 					message("502 That's none of your business");
56158082Seric 				break;
56258082Seric 			}
56358082Seric 			else if (!gothello &&
56458092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
56558092Seric 						PrivacyFlags))
56658082Seric 			{
56758151Seric 				message("503 I demand that you introduce yourself first");
56858082Seric 				break;
56958082Seric 			}
57058092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5719339Seric 				break;
57255173Seric #ifdef LOG
57358020Seric 			if (LogLevel > 5)
57455173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
57555173Seric #endif
5765003Seric 			vrfyqueue = NULL;
5777762Seric 			QuickAbort = TRUE;
57858092Seric 			if (vrfy)
57958092Seric 				e->e_flags |= EF_VRFYONLY;
58058082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5817762Seric 			if (Errors != 0)
5829339Seric 			{
5839339Seric 				if (InChild)
5849339Seric 					finis();
5857762Seric 				break;
5869339Seric 			}
5875003Seric 			while (vrfyqueue != NULL)
5885003Seric 			{
5895003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5905003Seric 				char *code;
5915003Seric 
5927685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5935003Seric 					a = a->q_next;
5945003Seric 
5957685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
59658151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5975003Seric 				else if (a == NULL)
59858151Seric 					message("554 Self destructive alias loop");
5995003Seric 				vrfyqueue = a;
6005003Seric 			}
6019339Seric 			if (InChild)
6029339Seric 				finis();
6034549Seric 			break;
6044549Seric 
6054549Seric 		  case CMDHELP:		/* help -- give user info */
6064577Seric 			help(p);
6074549Seric 			break;
6084549Seric 
6094549Seric 		  case CMDNOOP:		/* noop -- do nothing */
61058151Seric 			message("200 OK");
6114549Seric 			break;
6124549Seric 
6134549Seric 		  case CMDQUIT:		/* quit -- leave mail */
61458151Seric 			message("221 %s closing connection", MyHostName);
6159339Seric 			if (InChild)
6169339Seric 				ExitStat = EX_QUIT;
6174549Seric 			finis();
6184549Seric 
6198544Seric 		  case CMDVERB:		/* set verbose mode */
6208544Seric 			Verbose = TRUE;
62158734Seric 			e->e_sendmode = SM_DELIVER;
62258151Seric 			message("200 Verbose mode");
6238544Seric 			break;
6248544Seric 
6259314Seric 		  case CMDONEX:		/* doing one transaction only */
6269378Seric 			OneXact = TRUE;
62758151Seric 			message("200 Only one transaction");
6289314Seric 			break;
6299314Seric 
63036230Skarels # ifdef SMTPDEBUG
6319339Seric 		  case CMDDBGQSHOW:	/* show queues */
6326907Seric 			printf("Send Queue=");
63355012Seric 			printaddr(e->e_sendqueue, TRUE);
6345003Seric 			break;
6357275Seric 
6367275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6377676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6387676Seric 			tTflag(p);
63958151Seric 			message("200 Debug set");
6407275Seric 			break;
6417275Seric 
64236230Skarels # else /* not SMTPDEBUG */
64324945Seric 
64436230Skarels 		  case CMDDBGQSHOW:	/* show queues */
64536230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
64636233Skarels # ifdef LOG
64758308Seric 			if (LogLevel > 0)
64836230Skarels 				syslog(LOG_NOTICE,
64958020Seric 				    "\"%s\" command from %s (%s)",
65036230Skarels 				    c->cmdname, RealHostName,
65158755Seric 				    anynet_ntoa(&RealHostAddr));
65236233Skarels # endif
65336230Skarels 			/* FALL THROUGH */
65436230Skarels # endif /* SMTPDEBUG */
65536230Skarels 
6564549Seric 		  case CMDERROR:	/* unknown command */
65758151Seric 			message("500 Command unrecognized");
6584549Seric 			break;
6594549Seric 
6604549Seric 		  default:
66136230Skarels 			errno = 0;
66258151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6634549Seric 			break;
6644549Seric 		}
6654549Seric 	}
6664549Seric }
6674549Seric /*
6684549Seric **  SKIPWORD -- skip a fixed word.
6694549Seric **
6704549Seric **	Parameters:
6714549Seric **		p -- place to start looking.
6724549Seric **		w -- word to skip.
6734549Seric **
6744549Seric **	Returns:
6754549Seric **		p following w.
6764549Seric **		NULL on error.
6774549Seric **
6784549Seric **	Side Effects:
6794549Seric **		clobbers the p data area.
6804549Seric */
6814549Seric 
6824549Seric static char *
6834549Seric skipword(p, w)
6844549Seric 	register char *p;
6854549Seric 	char *w;
6864549Seric {
6874549Seric 	register char *q;
6884549Seric 
6894549Seric 	/* find beginning of word */
69058050Seric 	while (isascii(*p) && isspace(*p))
6914549Seric 		p++;
6924549Seric 	q = p;
6934549Seric 
6944549Seric 	/* find end of word */
69558050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6964549Seric 		p++;
69758050Seric 	while (isascii(*p) && isspace(*p))
6984549Seric 		*p++ = '\0';
6994549Seric 	if (*p != ':')
7004549Seric 	{
7014549Seric 	  syntax:
70258151Seric 		message("501 Syntax error");
7034549Seric 		Errors++;
7044549Seric 		return (NULL);
7054549Seric 	}
7064549Seric 	*p++ = '\0';
70758050Seric 	while (isascii(*p) && isspace(*p))
7084549Seric 		p++;
7094549Seric 
7104549Seric 	/* see if the input word matches desired word */
71133725Sbostic 	if (strcasecmp(q, w))
7124549Seric 		goto syntax;
7134549Seric 
7144549Seric 	return (p);
7154549Seric }
7164577Seric /*
71758151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
71858151Seric **
71958151Seric **	Parameters:
72058151Seric **		a -- the address to print
72158151Seric **		last -- set if this is the last one.
72258151Seric **
72358151Seric **	Returns:
72458151Seric **		none.
72558151Seric **
72658151Seric **	Side Effects:
72758151Seric **		Prints the appropriate 250 codes.
72858151Seric */
72958151Seric 
73058151Seric printvrfyaddr(a, last)
73158151Seric 	register ADDRESS *a;
73258151Seric 	bool last;
73358151Seric {
73458151Seric 	char fmtbuf[20];
73558151Seric 
73658151Seric 	strcpy(fmtbuf, "250");
73758151Seric 	fmtbuf[3] = last ? ' ' : '-';
73858151Seric 
739*59746Seric 	if (a->q_fullname == NULL)
740*59746Seric 	{
741*59746Seric 		if (strchr(a->q_user, '@') == NULL)
742*59746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
743*59746Seric 		else
744*59746Seric 			strcpy(&fmtbuf[4], "<%s>");
745*59746Seric 		message(fmtbuf, a->q_user, MyHostName);
746*59746Seric 	}
74758151Seric 	else
74858151Seric 	{
749*59746Seric 		if (strchr(a->q_user, '@') == NULL)
750*59746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
751*59746Seric 		else
752*59746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
753*59746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
75458151Seric 	}
75558151Seric }
75658151Seric /*
7574577Seric **  HELP -- implement the HELP command.
7584577Seric **
7594577Seric **	Parameters:
7604577Seric **		topic -- the topic we want help for.
7614577Seric **
7624577Seric **	Returns:
7634577Seric **		none.
7644577Seric **
7654577Seric **	Side Effects:
7664577Seric **		outputs the help file to message output.
7674577Seric */
7684577Seric 
7694577Seric help(topic)
7704577Seric 	char *topic;
7714577Seric {
7724577Seric 	register FILE *hf;
7734577Seric 	int len;
7744577Seric 	char buf[MAXLINE];
7754577Seric 	bool noinfo;
7764577Seric 
7778269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7784577Seric 	{
7794577Seric 		/* no help */
78011931Seric 		errno = 0;
78158151Seric 		message("502 HELP not implemented");
7824577Seric 		return;
7834577Seric 	}
7844577Seric 
78549669Seric 	if (topic == NULL || *topic == '\0')
78649669Seric 		topic = "smtp";
78749669Seric 	else
78849669Seric 		makelower(topic);
78949669Seric 
7904577Seric 	len = strlen(topic);
7914577Seric 	noinfo = TRUE;
7924577Seric 
7934577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7944577Seric 	{
7954577Seric 		if (strncmp(buf, topic, len) == 0)
7964577Seric 		{
7974577Seric 			register char *p;
7984577Seric 
79956795Seric 			p = strchr(buf, '\t');
8004577Seric 			if (p == NULL)
8014577Seric 				p = buf;
8024577Seric 			else
8034577Seric 				p++;
8044577Seric 			fixcrlf(p, TRUE);
80558151Seric 			message("214-%s", p);
8064577Seric 			noinfo = FALSE;
8074577Seric 		}
8084577Seric 	}
8094577Seric 
8104577Seric 	if (noinfo)
81158151Seric 		message("504 HELP topic unknown");
8124577Seric 	else
81358151Seric 		message("214 End of HELP info");
8144628Seric 	(void) fclose(hf);
8154577Seric }
8168544Seric /*
8179339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8189339Seric **
8199339Seric **	Parameters:
8209339Seric **		label -- a string used in error messages
8219339Seric **
8229339Seric **	Returns:
8239339Seric **		zero in the child
8249339Seric **		one in the parent
8259339Seric **
8269339Seric **	Side Effects:
8279339Seric **		none.
8289339Seric */
8298544Seric 
83055012Seric runinchild(label, e)
8319339Seric 	char *label;
83255012Seric 	register ENVELOPE *e;
8339339Seric {
8349339Seric 	int childpid;
8359339Seric 
83616158Seric 	if (!OneXact)
8379339Seric 	{
83816158Seric 		childpid = dofork();
83916158Seric 		if (childpid < 0)
84016158Seric 		{
84116158Seric 			syserr("%s: cannot fork", label);
84216158Seric 			return (1);
84316158Seric 		}
84416158Seric 		if (childpid > 0)
84516158Seric 		{
84616158Seric 			auto int st;
8479339Seric 
84816158Seric 			/* parent -- wait for child to complete */
84959060Seric 			setproctitle("srvrsmtp %s child wait", CurHostName);
85016158Seric 			st = waitfor(childpid);
85116158Seric 			if (st == -1)
85216158Seric 				syserr("%s: lost child", label);
8539339Seric 
85416158Seric 			/* if we exited on a QUIT command, complete the process */
85516158Seric 			if (st == (EX_QUIT << 8))
85616158Seric 				finis();
8579339Seric 
85816158Seric 			return (1);
85916158Seric 		}
86016158Seric 		else
86116158Seric 		{
86216158Seric 			/* child */
86316158Seric 			InChild = TRUE;
86425050Seric 			QuickAbort = FALSE;
86555012Seric 			clearenvelope(e, FALSE);
86616158Seric 		}
8679339Seric 	}
86815256Seric 
86916158Seric 	/* open alias database */
87059672Seric 	initaliases(FALSE, e);
87116158Seric 
87216158Seric 	return (0);
8739339Seric }
8749339Seric 
87556795Seric # endif /* SMTP */
876