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*60537Seric static char sccsid[] = "@(#)srvrsmtp.c	6.57 (Berkeley) 05/28/93 (with SMTP)";
1433731Sbostic #else
15*60537Seric static char sccsid[] = "@(#)srvrsmtp.c	6.57 (Berkeley) 05/28/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[];
11424943Seric 	extern ENVELOPE BlankEnvelope;
1154549Seric 
11659066Seric 	if (fileno(OutChannel) != fileno(stdout))
1177363Seric 	{
1187363Seric 		/* arrange for debugging output to go to remote host */
11959066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1207363Seric 	}
12155012Seric 	settime(e);
12257642Seric 	CurHostName = RealHostName;
12359060Seric 	setproctitle("srvrsmtp %s startup", CurHostName);
12458050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12560210Seric 	message("220-%s", inp);
12660210Seric 	message("220 ESMTP spoken here");
12758330Seric 	protocol = NULL;
12859016Seric 	sendinghost = macvalue('s', e);
12958082Seric 	gothello = FALSE;
13058330Seric 	gotmail = FALSE;
1314549Seric 	for (;;)
1324549Seric 	{
13312612Seric 		/* arrange for backout */
13412612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13559058Seric 		{
13659058Seric 			QuickAbort = FALSE;
13759058Seric 			SuprErrs = TRUE;
13812612Seric 			finis();
13959058Seric 		}
14012612Seric 		QuickAbort = FALSE;
14112612Seric 		HoldErrs = FALSE;
14251951Seric 		LogUsrErrs = FALSE;
14358092Seric 		e->e_flags &= ~EF_VRFYONLY;
14412612Seric 
1457356Seric 		/* setup for the read */
14655012Seric 		e->e_to = NULL;
1474577Seric 		Errors = 0;
1487275Seric 		(void) fflush(stdout);
1497356Seric 
1507356Seric 		/* read the input line */
15159060Seric 		SmtpPhase = "srvrsmtp cmd read";
15259060Seric 		setproctitle("srvrsmtp %s cmd read", CurHostName);
15358109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1547356Seric 
1557685Seric 		/* handle errors */
1567356Seric 		if (p == NULL)
1577356Seric 		{
1584549Seric 			/* end of file, just die */
15958151Seric 			message("421 %s Lost input channel from %s",
16025050Seric 				MyHostName, CurHostName);
16155464Seric #ifdef LOG
16258020Seric 			if (LogLevel > 1)
16355464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16455464Seric 					CurHostName);
16555464Seric #endif
16658069Seric 			if (InChild)
16758069Seric 				ExitStat = EX_QUIT;
1684549Seric 			finis();
1694549Seric 		}
1704549Seric 
1714549Seric 		/* clean up end of line */
1724558Seric 		fixcrlf(inp, TRUE);
1734549Seric 
1744713Seric 		/* echo command to transcript */
17555012Seric 		if (e->e_xfp != NULL)
17655012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1774713Seric 
17859060Seric 		if (e->e_id == NULL)
17959060Seric 			setproctitle("%s: %s", CurHostName, inp);
18059060Seric 		else
18159060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
18259060Seric 
1834549Seric 		/* break off command */
18458050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1854549Seric 			continue;
18657232Seric 		cmd = cmdbuf;
18758050Seric 		while (*p != '\0' &&
18858050Seric 		       !(isascii(*p) && isspace(*p)) &&
18958050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19024981Seric 			*cmd++ = *p++;
19124981Seric 		*cmd = '\0';
1924549Seric 
19325691Seric 		/* throw away leading whitespace */
19458050Seric 		while (isascii(*p) && isspace(*p))
19525691Seric 			p++;
19625691Seric 
1974549Seric 		/* decode command */
1984549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1994549Seric 		{
20033725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2014549Seric 				break;
2024549Seric 		}
2034549Seric 
20451954Seric 		/* reset errors */
20551954Seric 		errno = 0;
20651954Seric 
2074549Seric 		/* process command */
2084549Seric 		switch (c->cmdcode)
2094549Seric 		{
2104976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
21158323Seric 		  case CMDEHLO:		/* extended hello */
21258323Seric 			if (c->cmdcode == CMDEHLO)
21358323Seric 			{
21458323Seric 				protocol = "ESMTP";
21558323Seric 				SmtpPhase = "EHLO";
21658323Seric 			}
21758323Seric 			else
21858323Seric 			{
21958323Seric 				protocol = "SMTP";
22058323Seric 				SmtpPhase = "HELO";
22158323Seric 			}
22259016Seric 			sendinghost = newstr(p);
22358308Seric 			if (strcasecmp(p, RealHostName) != 0)
22411146Seric 			{
22558789Seric 				auth_warning(e, "Host %s claimed to be %s",
22658789Seric 					RealHostName, p);
22711146Seric 			}
22858957Seric 			p = macvalue('_', e);
22958957Seric 			if (p == NULL)
23059016Seric 				p = RealHostName;
23158323Seric 
23260210Seric 			gothello = TRUE;
23360210Seric 			if (c->cmdcode != CMDEHLO)
23460239Seric 			{
23560239Seric 				/* print old message and be done with it */
23660239Seric 				message("250 %s Hello %s, pleased to meet you",
23760239Seric 					MyHostName, p);
23860210Seric 				break;
23960239Seric 			}
24060239Seric 
24160239Seric 			/* print extended message and brag */
24260239Seric 			message("250-%s Hello %s, pleased to meet you",
24360239Seric 				MyHostName, p);
24458323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24558323Seric 				message("250-EXPN");
24659271Seric 			if (MaxMessageSize > 0)
24759271Seric 				message("250-SIZE %ld", MaxMessageSize);
24859271Seric 			else
24959271Seric 				message("250-SIZE");
25058323Seric 			message("250 HELP");
2514976Seric 			break;
2524976Seric 
2534549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25424943Seric 			SmtpPhase = "MAIL";
25524943Seric 
2569314Seric 			/* check for validity of this command */
25758789Seric 			if (!gothello)
25858082Seric 			{
25958957Seric 				/* set sending host to our known value */
26059016Seric 				if (sendinghost == NULL)
26159016Seric 					sendinghost = RealHostName;
26258957Seric 
26358789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
26458821Seric 				{
26558789Seric 					message("503 Polite people say HELO first");
26658821Seric 					break;
26758821Seric 				}
26858789Seric 				else
26958821Seric 				{
27058789Seric 					auth_warning(e,
27158789Seric 						"Host %s didn't use HELO protocol",
27258789Seric 						RealHostName);
27358821Seric 				}
27458082Seric 			}
27558109Seric 			if (gotmail)
2764558Seric 			{
27758151Seric 				message("503 Sender already specified");
2784558Seric 				break;
2794558Seric 			}
2809339Seric 			if (InChild)
2819339Seric 			{
28236230Skarels 				errno = 0;
28358151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
28458069Seric 				finis();
2859339Seric 			}
2869339Seric 
2879339Seric 			/* fork a subprocess to process this command */
28855012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2899339Seric 				break;
29058323Seric 			if (protocol == NULL)
29158323Seric 				protocol = "SMTP";
29258323Seric 			define('r', protocol, e);
29359016Seric 			define('s', sendinghost, e);
29455012Seric 			initsys(e);
29559747Seric 			nrcpts = 0;
29657389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2979339Seric 
2989339Seric 			/* child -- go do the processing */
2994549Seric 			p = skipword(p, "from");
3004549Seric 			if (p == NULL)
3014549Seric 				break;
30257977Seric 			if (setjmp(TopFrame) > 0)
30358147Seric 			{
30458147Seric 				/* this failed -- undo work */
30558147Seric 				if (InChild)
30659058Seric 				{
30759058Seric 					QuickAbort = FALSE;
30859058Seric 					SuprErrs = TRUE;
30958147Seric 					finis();
31059058Seric 				}
31157977Seric 				break;
31258147Seric 			}
31357977Seric 			QuickAbort = TRUE;
31458333Seric 
31558333Seric 			/* must parse sender first */
31658333Seric 			delimptr = NULL;
31758704Seric 			setsender(p, e, &delimptr, FALSE);
31858333Seric 			p = delimptr;
31958333Seric 			if (p != NULL && *p != '\0')
32058333Seric 				*p++ = '\0';
32158333Seric 
32258333Seric 			/* now parse ESMTP arguments */
32358333Seric 			msize = 0;
32458333Seric 			for (; p != NULL && *p != '\0'; p++)
32558333Seric 			{
32658333Seric 				char *kp;
32758333Seric 				char *vp;
32858333Seric 
32958333Seric 				/* locate the beginning of the keyword */
33058333Seric 				while (isascii(*p) && isspace(*p))
33158333Seric 					p++;
33258333Seric 				if (*p == '\0')
33358333Seric 					break;
33458333Seric 				kp = p;
33558333Seric 
33658333Seric 				/* skip to the value portion */
33758333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
33858333Seric 					p++;
33958333Seric 				if (*p == '=')
34058333Seric 				{
34158333Seric 					*p++ = '\0';
34258333Seric 					vp = p;
34358333Seric 
34458333Seric 					/* skip to the end of the value */
34558333Seric 					while (*p != '\0' && *p != ' ' &&
34658333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
34758333Seric 					       *p != '=')
34858333Seric 						p++;
34958333Seric 				}
35058333Seric 
35158333Seric 				if (*p != '\0')
35258333Seric 					*p++ = '\0';
35358333Seric 
35458333Seric 				if (tTd(19, 1))
35558333Seric 					printf("MAIL: got arg %s=%s\n", kp,
35658333Seric 						vp == NULL ? "<null>" : vp);
35758333Seric 
35858333Seric 				if (strcasecmp(kp, "size") == 0)
35958333Seric 				{
36059093Seric 					if (vp == NULL)
36158333Seric 					{
36258333Seric 						usrerr("501 SIZE requires a value");
36358333Seric 						/* NOTREACHED */
36458333Seric 					}
36558333Seric 					msize = atol(vp);
36658333Seric 				}
36759093Seric 				else if (strcasecmp(kp, "body") == 0)
36859093Seric 				{
36959093Seric 					if (vp == NULL)
37059093Seric 					{
37159093Seric 						usrerr("501 BODY requires a value");
37259093Seric 						/* NOTREACHED */
37359093Seric 					}
37459093Seric # ifdef MIME
37559093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
37659093Seric 					{
37759093Seric 						e->e_bodytype = "8BITMIME";
37859709Seric 						SevenBit = FALSE;
37959093Seric 					}
38059093Seric 					else if (strcasecmp(vp, "7bit") == 0)
38159093Seric 					{
38259093Seric 						e->e_bodytype = "7BIT";
38359709Seric 						SevenBit = TRUE;
38459093Seric 					}
38559093Seric 					else
38659093Seric 					{
38759093Seric 						usrerr("501 Unknown BODY type %s",
38859093Seric 							vp);
38959093Seric 					}
39059093Seric # endif
39159093Seric 				}
39258333Seric 				else
39358333Seric 				{
39458333Seric 					usrerr("501 %s parameter unrecognized", kp);
39558333Seric 					/* NOTREACHED */
39658333Seric 				}
39758333Seric 			}
39859284Seric 
39959284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
40059284Seric 			{
40159284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
40259284Seric 					MaxMessageSize);
40359284Seric 				/* NOTREACHED */
40459284Seric 			}
40558333Seric 
40658333Seric 			if (!enoughspace(msize))
40758333Seric 			{
40858333Seric 				message("452 Insufficient disk space; try again later");
40958333Seric 				break;
41058333Seric 			}
41158151Seric 			message("250 Sender ok");
41258147Seric 			gotmail = TRUE;
4134549Seric 			break;
4144549Seric 
4154976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
41658850Seric 			if (!gotmail)
41758850Seric 			{
41858850Seric 				usrerr("503 Need MAIL before RCPT");
41958850Seric 				break;
42058850Seric 			}
42124943Seric 			SmtpPhase = "RCPT";
42212612Seric 			if (setjmp(TopFrame) > 0)
42314785Seric 			{
42455012Seric 				e->e_flags &= ~EF_FATALERRS;
42512612Seric 				break;
42614785Seric 			}
42712612Seric 			QuickAbort = TRUE;
42851951Seric 			LogUsrErrs = TRUE;
42958093Seric 
43059699Seric 			if (e->e_sendmode != SM_DELIVER)
43159699Seric 				e->e_flags |= EF_VRFYONLY;
43258919Seric 
4334549Seric 			p = skipword(p, "to");
4344549Seric 			if (p == NULL)
4354549Seric 				break;
43658333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
43712612Seric 			if (a == NULL)
43812612Seric 				break;
43916886Seric 			a->q_flags |= QPRIMARY;
44055012Seric 			a = recipient(a, &e->e_sendqueue, e);
44112612Seric 			if (Errors != 0)
44212612Seric 				break;
44312612Seric 
44412612Seric 			/* no errors during parsing, but might be a duplicate */
44555012Seric 			e->e_to = p;
44612612Seric 			if (!bitset(QBADADDR, a->q_flags))
44759747Seric 			{
44858151Seric 				message("250 Recipient ok");
44959747Seric 				nrcpts++;
45059747Seric 			}
45112612Seric 			else
4524549Seric 			{
45312612Seric 				/* punt -- should keep message in ADDRESS.... */
45458151Seric 				message("550 Addressee unknown");
4554549Seric 			}
45655012Seric 			e->e_to = NULL;
4574549Seric 			break;
4584549Seric 
4594549Seric 		  case CMDDATA:		/* data -- text of mail */
46024943Seric 			SmtpPhase = "DATA";
46158109Seric 			if (!gotmail)
4624549Seric 			{
46358151Seric 				message("503 Need MAIL command");
4644976Seric 				break;
4654549Seric 			}
46655012Seric 			else if (e->e_nrcpts <= 0)
4674549Seric 			{
46858151Seric 				message("503 Need RCPT (recipient)");
4694976Seric 				break;
4704549Seric 			}
4714976Seric 
47258929Seric 			/* check to see if we need to re-expand aliases */
47358929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
47458929Seric 			{
47558929Seric 				if (bitset(QVERIFIED, a->q_flags))
47658929Seric 					break;
47758929Seric 			}
47858929Seric 
4794976Seric 			/* collect the text of the message */
48024943Seric 			SmtpPhase = "collect";
48158929Seric 			collect(TRUE, a != NULL, e);
48259730Seric 			e->e_flags &= ~EF_FATALERRS;
4834976Seric 			if (Errors != 0)
48459747Seric 				goto abortmessage;
4854976Seric 
4868238Seric 			/*
4878238Seric 			**  Arrange to send to everyone.
4888238Seric 			**	If sending to multiple people, mail back
4898238Seric 			**		errors rather than reporting directly.
4908238Seric 			**	In any case, don't mail back errors for
4918238Seric 			**		anything that has happened up to
4928238Seric 			**		now (the other end will do this).
49310197Seric 			**	Truncate our transcript -- the mail has gotten
49410197Seric 			**		to us successfully, and if we have
49510197Seric 			**		to mail this back, it will be easier
49610197Seric 			**		on the reader.
4978238Seric 			**	Then send to everyone.
4988238Seric 			**	Finally give a reply code.  If an error has
4998238Seric 			**		already been given, don't mail a
5008238Seric 			**		message back.
5019339Seric 			**	We goose error returns by clearing error bit.
5028238Seric 			*/
5038238Seric 
50424943Seric 			SmtpPhase = "delivery";
50559747Seric 			if (nrcpts != 1 && a == NULL)
5069378Seric 			{
5079378Seric 				HoldErrs = TRUE;
50858734Seric 				e->e_errormode = EM_MAIL;
5099378Seric 			}
51055012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
51158714Seric 			id = e->e_id;
5124976Seric 
5134976Seric 			/* send to all recipients */
51458919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
51555012Seric 			e->e_to = NULL;
5164976Seric 
51723516Seric 			/* save statistics */
51855012Seric 			markstats(e, (ADDRESS *) NULL);
51923516Seric 
5208238Seric 			/* issue success if appropriate and reset */
5218238Seric 			if (Errors == 0 || HoldErrs)
52258855Seric 				message("250 %s Message accepted for delivery", id);
52359747Seric 
52459747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
52559730Seric 			{
52659730Seric 				/* avoid sending back an extra message */
52759730Seric 				e->e_flags &= ~EF_FATALERRS;
52859747Seric 				e->e_flags |= EF_CLRQUEUE;
52959730Seric 			}
5308238Seric 			else
53158919Seric 			{
53259747Seric 				/* from now on, we have to operate silently */
53359747Seric 				HoldErrs = TRUE;
53459747Seric 				e->e_errormode = EM_MAIL;
53559747Seric 
53659730Seric 				/* if we just queued, poke it */
53759730Seric 				if (a != NULL && e->e_sendmode != SM_QUEUE)
53859730Seric 				{
53959730Seric 					unlockqueue(e);
54059730Seric 					dowork(id, TRUE, TRUE, e);
54159730Seric 					e->e_id = NULL;
54259730Seric 				}
54358919Seric 			}
54458883Seric 
54559747Seric   abortmessage:
5469339Seric 			/* if in a child, pop back to our parent */
5479339Seric 			if (InChild)
5489339Seric 				finis();
54924943Seric 
55024943Seric 			/* clean up a bit */
55158109Seric 			gotmail = FALSE;
55255012Seric 			dropenvelope(e);
55358179Seric 			CurEnv = e = newenvelope(e, CurEnv);
55455012Seric 			e->e_flags = BlankEnvelope.e_flags;
5554549Seric 			break;
5564549Seric 
5574549Seric 		  case CMDRSET:		/* rset -- reset state */
55858151Seric 			message("250 Reset state");
5599339Seric 			if (InChild)
5609339Seric 				finis();
56158109Seric 
56258109Seric 			/* clean up a bit */
56358109Seric 			gotmail = FALSE;
56458109Seric 			dropenvelope(e);
56558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5669339Seric 			break;
5674549Seric 
5684549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
56958092Seric 		  case CMDEXPN:		/* expn -- expand address */
57058092Seric 			vrfy = c->cmdcode == CMDVRFY;
57158092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
57258092Seric 						PrivacyFlags))
57358082Seric 			{
57458412Seric 				if (vrfy)
57558412Seric 					message("252 Who's to say?");
57658412Seric 				else
57758412Seric 					message("502 That's none of your business");
57858082Seric 				break;
57958082Seric 			}
58058082Seric 			else if (!gothello &&
58158092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
58258092Seric 						PrivacyFlags))
58358082Seric 			{
58458151Seric 				message("503 I demand that you introduce yourself first");
58558082Seric 				break;
58658082Seric 			}
58758092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5889339Seric 				break;
58955173Seric #ifdef LOG
59058020Seric 			if (LogLevel > 5)
59155173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
59255173Seric #endif
5935003Seric 			vrfyqueue = NULL;
5947762Seric 			QuickAbort = TRUE;
59558092Seric 			if (vrfy)
59658092Seric 				e->e_flags |= EF_VRFYONLY;
59758082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5987762Seric 			if (Errors != 0)
5999339Seric 			{
6009339Seric 				if (InChild)
6019339Seric 					finis();
6027762Seric 				break;
6039339Seric 			}
6045003Seric 			while (vrfyqueue != NULL)
6055003Seric 			{
6065003Seric 				register ADDRESS *a = vrfyqueue->q_next;
6075003Seric 				char *code;
6085003Seric 
6097685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
6105003Seric 					a = a->q_next;
6115003Seric 
6127685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
61358151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
6145003Seric 				else if (a == NULL)
61558151Seric 					message("554 Self destructive alias loop");
6165003Seric 				vrfyqueue = a;
6175003Seric 			}
6189339Seric 			if (InChild)
6199339Seric 				finis();
6204549Seric 			break;
6214549Seric 
6224549Seric 		  case CMDHELP:		/* help -- give user info */
6234577Seric 			help(p);
6244549Seric 			break;
6254549Seric 
6264549Seric 		  case CMDNOOP:		/* noop -- do nothing */
62758151Seric 			message("200 OK");
6284549Seric 			break;
6294549Seric 
6304549Seric 		  case CMDQUIT:		/* quit -- leave mail */
63158151Seric 			message("221 %s closing connection", MyHostName);
6329339Seric 			if (InChild)
6339339Seric 				ExitStat = EX_QUIT;
6344549Seric 			finis();
6354549Seric 
6368544Seric 		  case CMDVERB:		/* set verbose mode */
63759957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
63859957Seric 			{
63959957Seric 				/* this would give out the same info */
64059957Seric 				message("502 Verbose unavailable");
64159957Seric 				break;
64259957Seric 			}
6438544Seric 			Verbose = TRUE;
64458734Seric 			e->e_sendmode = SM_DELIVER;
64559957Seric 			message("250 Verbose mode");
6468544Seric 			break;
6478544Seric 
6489314Seric 		  case CMDONEX:		/* doing one transaction only */
6499378Seric 			OneXact = TRUE;
65059957Seric 			message("250 Only one transaction");
6519314Seric 			break;
6529314Seric 
65336230Skarels # ifdef SMTPDEBUG
6549339Seric 		  case CMDDBGQSHOW:	/* show queues */
6556907Seric 			printf("Send Queue=");
65655012Seric 			printaddr(e->e_sendqueue, TRUE);
6575003Seric 			break;
6587275Seric 
6597275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6607676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6617676Seric 			tTflag(p);
66258151Seric 			message("200 Debug set");
6637275Seric 			break;
6647275Seric 
66536230Skarels # else /* not SMTPDEBUG */
66624945Seric 
66736230Skarels 		  case CMDDBGQSHOW:	/* show queues */
66836230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
66936233Skarels # ifdef LOG
67058308Seric 			if (LogLevel > 0)
67136230Skarels 				syslog(LOG_NOTICE,
67258020Seric 				    "\"%s\" command from %s (%s)",
67336230Skarels 				    c->cmdname, RealHostName,
67458755Seric 				    anynet_ntoa(&RealHostAddr));
67536233Skarels # endif
67636230Skarels 			/* FALL THROUGH */
67736230Skarels # endif /* SMTPDEBUG */
67836230Skarels 
6794549Seric 		  case CMDERROR:	/* unknown command */
68058151Seric 			message("500 Command unrecognized");
6814549Seric 			break;
6824549Seric 
6834549Seric 		  default:
68436230Skarels 			errno = 0;
68558151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6864549Seric 			break;
6874549Seric 		}
6884549Seric 	}
6894549Seric }
6904549Seric /*
6914549Seric **  SKIPWORD -- skip a fixed word.
6924549Seric **
6934549Seric **	Parameters:
6944549Seric **		p -- place to start looking.
6954549Seric **		w -- word to skip.
6964549Seric **
6974549Seric **	Returns:
6984549Seric **		p following w.
6994549Seric **		NULL on error.
7004549Seric **
7014549Seric **	Side Effects:
7024549Seric **		clobbers the p data area.
7034549Seric */
7044549Seric 
7054549Seric static char *
7064549Seric skipword(p, w)
7074549Seric 	register char *p;
7084549Seric 	char *w;
7094549Seric {
7104549Seric 	register char *q;
7114549Seric 
7124549Seric 	/* find beginning of word */
71358050Seric 	while (isascii(*p) && isspace(*p))
7144549Seric 		p++;
7154549Seric 	q = p;
7164549Seric 
7174549Seric 	/* find end of word */
71858050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7194549Seric 		p++;
72058050Seric 	while (isascii(*p) && isspace(*p))
7214549Seric 		*p++ = '\0';
7224549Seric 	if (*p != ':')
7234549Seric 	{
7244549Seric 	  syntax:
72558151Seric 		message("501 Syntax error");
7264549Seric 		Errors++;
7274549Seric 		return (NULL);
7284549Seric 	}
7294549Seric 	*p++ = '\0';
73058050Seric 	while (isascii(*p) && isspace(*p))
7314549Seric 		p++;
7324549Seric 
7334549Seric 	/* see if the input word matches desired word */
73433725Sbostic 	if (strcasecmp(q, w))
7354549Seric 		goto syntax;
7364549Seric 
7374549Seric 	return (p);
7384549Seric }
7394577Seric /*
74058151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
74158151Seric **
74258151Seric **	Parameters:
74358151Seric **		a -- the address to print
74458151Seric **		last -- set if this is the last one.
74558151Seric **
74658151Seric **	Returns:
74758151Seric **		none.
74858151Seric **
74958151Seric **	Side Effects:
75058151Seric **		Prints the appropriate 250 codes.
75158151Seric */
75258151Seric 
75358151Seric printvrfyaddr(a, last)
75458151Seric 	register ADDRESS *a;
75558151Seric 	bool last;
75658151Seric {
75758151Seric 	char fmtbuf[20];
75858151Seric 
75958151Seric 	strcpy(fmtbuf, "250");
76058151Seric 	fmtbuf[3] = last ? ' ' : '-';
76158151Seric 
76259746Seric 	if (a->q_fullname == NULL)
76359746Seric 	{
76459746Seric 		if (strchr(a->q_user, '@') == NULL)
76559746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
76659746Seric 		else
76759746Seric 			strcpy(&fmtbuf[4], "<%s>");
76859746Seric 		message(fmtbuf, a->q_user, MyHostName);
76959746Seric 	}
77058151Seric 	else
77158151Seric 	{
77259746Seric 		if (strchr(a->q_user, '@') == NULL)
77359746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
77459746Seric 		else
77559746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
77659746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
77758151Seric 	}
77858151Seric }
77958151Seric /*
7804577Seric **  HELP -- implement the HELP command.
7814577Seric **
7824577Seric **	Parameters:
7834577Seric **		topic -- the topic we want help for.
7844577Seric **
7854577Seric **	Returns:
7864577Seric **		none.
7874577Seric **
7884577Seric **	Side Effects:
7894577Seric **		outputs the help file to message output.
7904577Seric */
7914577Seric 
7924577Seric help(topic)
7934577Seric 	char *topic;
7944577Seric {
7954577Seric 	register FILE *hf;
7964577Seric 	int len;
7974577Seric 	char buf[MAXLINE];
7984577Seric 	bool noinfo;
7994577Seric 
8008269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8014577Seric 	{
8024577Seric 		/* no help */
80311931Seric 		errno = 0;
80458151Seric 		message("502 HELP not implemented");
8054577Seric 		return;
8064577Seric 	}
8074577Seric 
80849669Seric 	if (topic == NULL || *topic == '\0')
80949669Seric 		topic = "smtp";
81049669Seric 	else
81149669Seric 		makelower(topic);
81249669Seric 
8134577Seric 	len = strlen(topic);
8144577Seric 	noinfo = TRUE;
8154577Seric 
8164577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8174577Seric 	{
8184577Seric 		if (strncmp(buf, topic, len) == 0)
8194577Seric 		{
8204577Seric 			register char *p;
8214577Seric 
82256795Seric 			p = strchr(buf, '\t');
8234577Seric 			if (p == NULL)
8244577Seric 				p = buf;
8254577Seric 			else
8264577Seric 				p++;
8274577Seric 			fixcrlf(p, TRUE);
82858151Seric 			message("214-%s", p);
8294577Seric 			noinfo = FALSE;
8304577Seric 		}
8314577Seric 	}
8324577Seric 
8334577Seric 	if (noinfo)
83458151Seric 		message("504 HELP topic unknown");
8354577Seric 	else
83658151Seric 		message("214 End of HELP info");
8374628Seric 	(void) fclose(hf);
8384577Seric }
8398544Seric /*
8409339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8419339Seric **
8429339Seric **	Parameters:
8439339Seric **		label -- a string used in error messages
8449339Seric **
8459339Seric **	Returns:
8469339Seric **		zero in the child
8479339Seric **		one in the parent
8489339Seric **
8499339Seric **	Side Effects:
8509339Seric **		none.
8519339Seric */
8528544Seric 
85355012Seric runinchild(label, e)
8549339Seric 	char *label;
85555012Seric 	register ENVELOPE *e;
8569339Seric {
8579339Seric 	int childpid;
8589339Seric 
85916158Seric 	if (!OneXact)
8609339Seric 	{
86116158Seric 		childpid = dofork();
86216158Seric 		if (childpid < 0)
86316158Seric 		{
86416158Seric 			syserr("%s: cannot fork", label);
86516158Seric 			return (1);
86616158Seric 		}
86716158Seric 		if (childpid > 0)
86816158Seric 		{
86916158Seric 			auto int st;
8709339Seric 
87116158Seric 			/* parent -- wait for child to complete */
87259060Seric 			setproctitle("srvrsmtp %s child wait", CurHostName);
87316158Seric 			st = waitfor(childpid);
87416158Seric 			if (st == -1)
87516158Seric 				syserr("%s: lost child", label);
8769339Seric 
87716158Seric 			/* if we exited on a QUIT command, complete the process */
87816158Seric 			if (st == (EX_QUIT << 8))
87916158Seric 				finis();
8809339Seric 
88116158Seric 			return (1);
88216158Seric 		}
88316158Seric 		else
88416158Seric 		{
88516158Seric 			/* child */
88616158Seric 			InChild = TRUE;
88725050Seric 			QuickAbort = FALSE;
88855012Seric 			clearenvelope(e, FALSE);
88916158Seric 		}
8909339Seric 	}
89115256Seric 
89216158Seric 	/* open alias database */
893*60537Seric 	initmaps(FALSE, e);
89416158Seric 
89516158Seric 	return (0);
8969339Seric }
8979339Seric 
89856795Seric # endif /* SMTP */
899