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*60210Seric static char sccsid[] = "@(#)srvrsmtp.c	6.54 (Berkeley) 05/21/93 (with SMTP)";
1433731Sbostic #else
15*60210Seric static char sccsid[] = "@(#)srvrsmtp.c	6.54 (Berkeley) 05/21/93 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
2011728Seric # include <signal.h>
214549Seric 
2233731Sbostic # ifdef SMTP
234556Seric 
244549Seric /*
254549Seric **  SMTP -- run the SMTP protocol.
264549Seric **
274549Seric **	Parameters:
284549Seric **		none.
294549Seric **
304549Seric **	Returns:
314549Seric **		never.
324549Seric **
334549Seric **	Side Effects:
344549Seric **		Reads commands from the input channel and processes
354549Seric **			them.
364549Seric */
374549Seric 
384549Seric struct cmd
394549Seric {
404549Seric 	char	*cmdname;	/* command name */
414549Seric 	int	cmdcode;	/* internal code, see below */
424549Seric };
434549Seric 
444549Seric /* values for cmdcode */
454549Seric # define CMDERROR	0	/* bad command */
464549Seric # define CMDMAIL	1	/* mail -- designate sender */
474976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
484549Seric # define CMDDATA	3	/* data -- send message text */
499339Seric # define CMDRSET	4	/* rset -- reset state */
509339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5158092Seric # define CMDEXPN	6	/* expn -- expand address */
529339Seric # define CMDNOOP	7	/* noop -- do nothing */
539339Seric # define CMDQUIT	8	/* quit -- close connection and die */
549339Seric # define CMDHELO	9	/* helo -- be polite */
5558092Seric # define CMDHELP	10	/* help -- give usage info */
5658323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5758092Seric /* non-standard commands */
5858092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5958092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
6036230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6158092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6258092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
634549Seric 
644549Seric static struct cmd	CmdTab[] =
654549Seric {
664549Seric 	"mail",		CMDMAIL,
674976Seric 	"rcpt",		CMDRCPT,
684549Seric 	"data",		CMDDATA,
694549Seric 	"rset",		CMDRSET,
704549Seric 	"vrfy",		CMDVRFY,
7158092Seric 	"expn",		CMDEXPN,
724549Seric 	"help",		CMDHELP,
734549Seric 	"noop",		CMDNOOP,
744549Seric 	"quit",		CMDQUIT,
754976Seric 	"helo",		CMDHELO,
7658323Seric 	"ehlo",		CMDEHLO,
778544Seric 	"verb",		CMDVERB,
789314Seric 	"onex",		CMDONEX,
7936230Skarels 	/*
8036230Skarels 	 * remaining commands are here only
8136230Skarels 	 * to trap and log attempts to use them
8236230Skarels 	 */
839339Seric 	"showq",	CMDDBGQSHOW,
848544Seric 	"debug",	CMDDBGDEBUG,
854549Seric 	NULL,		CMDERROR,
864549Seric };
874549Seric 
889339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
899378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9011146Seric 
919339Seric #define EX_QUIT		22		/* special code for QUIT command */
928544Seric 
9355012Seric smtp(e)
9455012Seric 	register ENVELOPE *e;
954549Seric {
964549Seric 	register char *p;
978544Seric 	register struct cmd *c;
984549Seric 	char *cmd;
9946928Sbostic 	static char *skipword();
1005003Seric 	auto ADDRESS *vrfyqueue;
10112612Seric 	ADDRESS *a;
10258109Seric 	bool gotmail;			/* mail command received */
10358092Seric 	bool gothello;			/* helo command received */
10458092Seric 	bool vrfy;			/* set if this is a vrfy command */
10558323Seric 	char *protocol;			/* sending protocol */
10659016Seric 	char *sendinghost;		/* sending hostname */
10758333Seric 	long msize;			/* approximate maximum message size */
10858333Seric 	auto char *delimptr;
10958714Seric 	char *id;
11059747Seric 	int nrcpts;			/* number of RCPT commands */
1118544Seric 	char inp[MAXLINE];
11257232Seric 	char cmdbuf[MAXLINE];
1137124Seric 	extern char Version[];
11411151Seric 	extern char *macvalue();
11512612Seric 	extern ADDRESS *recipient();
11624943Seric 	extern ENVELOPE BlankEnvelope;
11724943Seric 	extern ENVELOPE *newenvelope();
11858755Seric 	extern char *anynet_ntoa();
1194549Seric 
12059066Seric 	if (fileno(OutChannel) != fileno(stdout))
1217363Seric 	{
1227363Seric 		/* arrange for debugging output to go to remote host */
12359066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1247363Seric 	}
12555012Seric 	settime(e);
12657642Seric 	CurHostName = RealHostName;
12759060Seric 	setproctitle("srvrsmtp %s startup", CurHostName);
12858050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
129*60210Seric 	message("220-%s", inp);
130*60210Seric 	message("220 ESMTP spoken here");
13158330Seric 	protocol = NULL;
13259016Seric 	sendinghost = macvalue('s', e);
13358082Seric 	gothello = FALSE;
13458330Seric 	gotmail = FALSE;
1354549Seric 	for (;;)
1364549Seric 	{
13712612Seric 		/* arrange for backout */
13812612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13959058Seric 		{
14059058Seric 			QuickAbort = FALSE;
14159058Seric 			SuprErrs = TRUE;
14212612Seric 			finis();
14359058Seric 		}
14412612Seric 		QuickAbort = FALSE;
14512612Seric 		HoldErrs = FALSE;
14651951Seric 		LogUsrErrs = FALSE;
14758092Seric 		e->e_flags &= ~EF_VRFYONLY;
14812612Seric 
1497356Seric 		/* setup for the read */
15055012Seric 		e->e_to = NULL;
1514577Seric 		Errors = 0;
1527275Seric 		(void) fflush(stdout);
1537356Seric 
1547356Seric 		/* read the input line */
15559060Seric 		SmtpPhase = "srvrsmtp cmd read";
15659060Seric 		setproctitle("srvrsmtp %s cmd read", CurHostName);
15758109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1587356Seric 
1597685Seric 		/* handle errors */
1607356Seric 		if (p == NULL)
1617356Seric 		{
1624549Seric 			/* end of file, just die */
16358151Seric 			message("421 %s Lost input channel from %s",
16425050Seric 				MyHostName, CurHostName);
16555464Seric #ifdef LOG
16658020Seric 			if (LogLevel > 1)
16755464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16855464Seric 					CurHostName);
16955464Seric #endif
17058069Seric 			if (InChild)
17158069Seric 				ExitStat = EX_QUIT;
1724549Seric 			finis();
1734549Seric 		}
1744549Seric 
1754549Seric 		/* clean up end of line */
1764558Seric 		fixcrlf(inp, TRUE);
1774549Seric 
1784713Seric 		/* echo command to transcript */
17955012Seric 		if (e->e_xfp != NULL)
18055012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1814713Seric 
18259060Seric 		if (e->e_id == NULL)
18359060Seric 			setproctitle("%s: %s", CurHostName, inp);
18459060Seric 		else
18559060Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
18659060Seric 
1874549Seric 		/* break off command */
18858050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1894549Seric 			continue;
19057232Seric 		cmd = cmdbuf;
19158050Seric 		while (*p != '\0' &&
19258050Seric 		       !(isascii(*p) && isspace(*p)) &&
19358050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
19424981Seric 			*cmd++ = *p++;
19524981Seric 		*cmd = '\0';
1964549Seric 
19725691Seric 		/* throw away leading whitespace */
19858050Seric 		while (isascii(*p) && isspace(*p))
19925691Seric 			p++;
20025691Seric 
2014549Seric 		/* decode command */
2024549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2034549Seric 		{
20433725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2054549Seric 				break;
2064549Seric 		}
2074549Seric 
20851954Seric 		/* reset errors */
20951954Seric 		errno = 0;
21051954Seric 
2114549Seric 		/* process command */
2124549Seric 		switch (c->cmdcode)
2134549Seric 		{
2144976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
21558323Seric 		  case CMDEHLO:		/* extended hello */
21658323Seric 			if (c->cmdcode == CMDEHLO)
21758323Seric 			{
21858323Seric 				protocol = "ESMTP";
21958323Seric 				SmtpPhase = "EHLO";
22058323Seric 			}
22158323Seric 			else
22258323Seric 			{
22358323Seric 				protocol = "SMTP";
22458323Seric 				SmtpPhase = "HELO";
22558323Seric 			}
22659016Seric 			sendinghost = newstr(p);
22758308Seric 			if (strcasecmp(p, RealHostName) != 0)
22811146Seric 			{
22958789Seric 				auth_warning(e, "Host %s claimed to be %s",
23058789Seric 					RealHostName, p);
23111146Seric 			}
23258957Seric 			p = macvalue('_', e);
23358957Seric 			if (p == NULL)
23459016Seric 				p = RealHostName;
23558323Seric 
23658323Seric 			/* send ext. message -- old systems must ignore */
237*60210Seric 			message("250%c%s Hello %s, pleased to meet you",
238*60210Seric 				c->cmdcode == CMDEHLO ? '-' : ' ',
23958957Seric 				MyHostName, p);
240*60210Seric 			gothello = TRUE;
241*60210Seric 			if (c->cmdcode != CMDEHLO)
242*60210Seric 				break;
24358323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24458323Seric 				message("250-EXPN");
24559271Seric 			if (MaxMessageSize > 0)
24659271Seric 				message("250-SIZE %ld", MaxMessageSize);
24759271Seric 			else
24859271Seric 				message("250-SIZE");
24958323Seric 			message("250 HELP");
2504976Seric 			break;
2514976Seric 
2524549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25324943Seric 			SmtpPhase = "MAIL";
25424943Seric 
2559314Seric 			/* check for validity of this command */
25658789Seric 			if (!gothello)
25758082Seric 			{
25858957Seric 				/* set sending host to our known value */
25959016Seric 				if (sendinghost == NULL)
26059016Seric 					sendinghost = RealHostName;
26158957Seric 
26258789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
26358821Seric 				{
26458789Seric 					message("503 Polite people say HELO first");
26558821Seric 					break;
26658821Seric 				}
26758789Seric 				else
26858821Seric 				{
26958789Seric 					auth_warning(e,
27058789Seric 						"Host %s didn't use HELO protocol",
27158789Seric 						RealHostName);
27258821Seric 				}
27358082Seric 			}
27458109Seric 			if (gotmail)
2754558Seric 			{
27658151Seric 				message("503 Sender already specified");
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;
28958323Seric 			if (protocol == NULL)
29058323Seric 				protocol = "SMTP";
29158323Seric 			define('r', protocol, e);
29259016Seric 			define('s', sendinghost, e);
29355012Seric 			initsys(e);
29459747Seric 			nrcpts = 0;
29557389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2969339Seric 
2979339Seric 			/* child -- go do the processing */
2984549Seric 			p = skipword(p, "from");
2994549Seric 			if (p == NULL)
3004549Seric 				break;
30157977Seric 			if (setjmp(TopFrame) > 0)
30258147Seric 			{
30358147Seric 				/* this failed -- undo work */
30458147Seric 				if (InChild)
30559058Seric 				{
30659058Seric 					QuickAbort = FALSE;
30759058Seric 					SuprErrs = TRUE;
30858147Seric 					finis();
30959058Seric 				}
31057977Seric 				break;
31158147Seric 			}
31257977Seric 			QuickAbort = TRUE;
31358333Seric 
31458333Seric 			/* must parse sender first */
31558333Seric 			delimptr = NULL;
31658704Seric 			setsender(p, e, &delimptr, FALSE);
31758333Seric 			p = delimptr;
31858333Seric 			if (p != NULL && *p != '\0')
31958333Seric 				*p++ = '\0';
32058333Seric 
32158333Seric 			/* now parse ESMTP arguments */
32258333Seric 			msize = 0;
32358333Seric 			for (; p != NULL && *p != '\0'; p++)
32458333Seric 			{
32558333Seric 				char *kp;
32658333Seric 				char *vp;
32758333Seric 
32858333Seric 				/* locate the beginning of the keyword */
32958333Seric 				while (isascii(*p) && isspace(*p))
33058333Seric 					p++;
33158333Seric 				if (*p == '\0')
33258333Seric 					break;
33358333Seric 				kp = p;
33458333Seric 
33558333Seric 				/* skip to the value portion */
33658333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
33758333Seric 					p++;
33858333Seric 				if (*p == '=')
33958333Seric 				{
34058333Seric 					*p++ = '\0';
34158333Seric 					vp = p;
34258333Seric 
34358333Seric 					/* skip to the end of the value */
34458333Seric 					while (*p != '\0' && *p != ' ' &&
34558333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
34658333Seric 					       *p != '=')
34758333Seric 						p++;
34858333Seric 				}
34958333Seric 
35058333Seric 				if (*p != '\0')
35158333Seric 					*p++ = '\0';
35258333Seric 
35358333Seric 				if (tTd(19, 1))
35458333Seric 					printf("MAIL: got arg %s=%s\n", kp,
35558333Seric 						vp == NULL ? "<null>" : vp);
35658333Seric 
35758333Seric 				if (strcasecmp(kp, "size") == 0)
35858333Seric 				{
35959093Seric 					if (vp == NULL)
36058333Seric 					{
36158333Seric 						usrerr("501 SIZE requires a value");
36258333Seric 						/* NOTREACHED */
36358333Seric 					}
36458333Seric 					msize = atol(vp);
36558333Seric 				}
36659093Seric 				else if (strcasecmp(kp, "body") == 0)
36759093Seric 				{
36859093Seric 					if (vp == NULL)
36959093Seric 					{
37059093Seric 						usrerr("501 BODY requires a value");
37159093Seric 						/* NOTREACHED */
37259093Seric 					}
37359093Seric # ifdef MIME
37459093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
37559093Seric 					{
37659093Seric 						e->e_bodytype = "8BITMIME";
37759709Seric 						SevenBit = FALSE;
37859093Seric 					}
37959093Seric 					else if (strcasecmp(vp, "7bit") == 0)
38059093Seric 					{
38159093Seric 						e->e_bodytype = "7BIT";
38259709Seric 						SevenBit = TRUE;
38359093Seric 					}
38459093Seric 					else
38559093Seric 					{
38659093Seric 						usrerr("501 Unknown BODY type %s",
38759093Seric 							vp);
38859093Seric 					}
38959093Seric # endif
39059093Seric 				}
39158333Seric 				else
39258333Seric 				{
39358333Seric 					usrerr("501 %s parameter unrecognized", kp);
39458333Seric 					/* NOTREACHED */
39558333Seric 				}
39658333Seric 			}
39759284Seric 
39859284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
39959284Seric 			{
40059284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
40159284Seric 					MaxMessageSize);
40259284Seric 				/* NOTREACHED */
40359284Seric 			}
40458333Seric 
40558333Seric 			if (!enoughspace(msize))
40658333Seric 			{
40758333Seric 				message("452 Insufficient disk space; try again later");
40858333Seric 				break;
40958333Seric 			}
41058151Seric 			message("250 Sender ok");
41158147Seric 			gotmail = TRUE;
4124549Seric 			break;
4134549Seric 
4144976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
41558850Seric 			if (!gotmail)
41658850Seric 			{
41758850Seric 				usrerr("503 Need MAIL before RCPT");
41858850Seric 				break;
41958850Seric 			}
42024943Seric 			SmtpPhase = "RCPT";
42112612Seric 			if (setjmp(TopFrame) > 0)
42214785Seric 			{
42355012Seric 				e->e_flags &= ~EF_FATALERRS;
42412612Seric 				break;
42514785Seric 			}
42612612Seric 			QuickAbort = TRUE;
42751951Seric 			LogUsrErrs = TRUE;
42858093Seric 
42959699Seric 			if (e->e_sendmode != SM_DELIVER)
43059699Seric 				e->e_flags |= EF_VRFYONLY;
43158919Seric 
4324549Seric 			p = skipword(p, "to");
4334549Seric 			if (p == NULL)
4344549Seric 				break;
43558333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
43612612Seric 			if (a == NULL)
43712612Seric 				break;
43816886Seric 			a->q_flags |= QPRIMARY;
43955012Seric 			a = recipient(a, &e->e_sendqueue, e);
44012612Seric 			if (Errors != 0)
44112612Seric 				break;
44212612Seric 
44312612Seric 			/* no errors during parsing, but might be a duplicate */
44455012Seric 			e->e_to = p;
44512612Seric 			if (!bitset(QBADADDR, a->q_flags))
44659747Seric 			{
44758151Seric 				message("250 Recipient ok");
44859747Seric 				nrcpts++;
44959747Seric 			}
45012612Seric 			else
4514549Seric 			{
45212612Seric 				/* punt -- should keep message in ADDRESS.... */
45358151Seric 				message("550 Addressee unknown");
4544549Seric 			}
45555012Seric 			e->e_to = NULL;
4564549Seric 			break;
4574549Seric 
4584549Seric 		  case CMDDATA:		/* data -- text of mail */
45924943Seric 			SmtpPhase = "DATA";
46058109Seric 			if (!gotmail)
4614549Seric 			{
46258151Seric 				message("503 Need MAIL command");
4634976Seric 				break;
4644549Seric 			}
46555012Seric 			else if (e->e_nrcpts <= 0)
4664549Seric 			{
46758151Seric 				message("503 Need RCPT (recipient)");
4684976Seric 				break;
4694549Seric 			}
4704976Seric 
47158929Seric 			/* check to see if we need to re-expand aliases */
47258929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
47358929Seric 			{
47458929Seric 				if (bitset(QVERIFIED, a->q_flags))
47558929Seric 					break;
47658929Seric 			}
47758929Seric 
4784976Seric 			/* collect the text of the message */
47924943Seric 			SmtpPhase = "collect";
48058929Seric 			collect(TRUE, a != NULL, e);
48159730Seric 			e->e_flags &= ~EF_FATALERRS;
4824976Seric 			if (Errors != 0)
48359747Seric 				goto abortmessage;
4844976Seric 
4858238Seric 			/*
4868238Seric 			**  Arrange to send to everyone.
4878238Seric 			**	If sending to multiple people, mail back
4888238Seric 			**		errors rather than reporting directly.
4898238Seric 			**	In any case, don't mail back errors for
4908238Seric 			**		anything that has happened up to
4918238Seric 			**		now (the other end will do this).
49210197Seric 			**	Truncate our transcript -- the mail has gotten
49310197Seric 			**		to us successfully, and if we have
49410197Seric 			**		to mail this back, it will be easier
49510197Seric 			**		on the reader.
4968238Seric 			**	Then send to everyone.
4978238Seric 			**	Finally give a reply code.  If an error has
4988238Seric 			**		already been given, don't mail a
4998238Seric 			**		message back.
5009339Seric 			**	We goose error returns by clearing error bit.
5018238Seric 			*/
5028238Seric 
50324943Seric 			SmtpPhase = "delivery";
50459747Seric 			if (nrcpts != 1 && a == NULL)
5059378Seric 			{
5069378Seric 				HoldErrs = TRUE;
50758734Seric 				e->e_errormode = EM_MAIL;
5089378Seric 			}
50955012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
51058714Seric 			id = e->e_id;
5114976Seric 
5124976Seric 			/* send to all recipients */
51358919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
51455012Seric 			e->e_to = NULL;
5154976Seric 
51623516Seric 			/* save statistics */
51755012Seric 			markstats(e, (ADDRESS *) NULL);
51823516Seric 
5198238Seric 			/* issue success if appropriate and reset */
5208238Seric 			if (Errors == 0 || HoldErrs)
52158855Seric 				message("250 %s Message accepted for delivery", id);
52259747Seric 
52359747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
52459730Seric 			{
52559730Seric 				/* avoid sending back an extra message */
52659730Seric 				e->e_flags &= ~EF_FATALERRS;
52759747Seric 				e->e_flags |= EF_CLRQUEUE;
52859730Seric 			}
5298238Seric 			else
53058919Seric 			{
53159747Seric 				/* from now on, we have to operate silently */
53259747Seric 				HoldErrs = TRUE;
53359747Seric 				e->e_errormode = EM_MAIL;
53459747Seric 
53559730Seric 				/* if we just queued, poke it */
53659730Seric 				if (a != NULL && e->e_sendmode != SM_QUEUE)
53759730Seric 				{
53859730Seric 					unlockqueue(e);
53959730Seric 					dowork(id, TRUE, TRUE, e);
54059730Seric 					e->e_id = NULL;
54159730Seric 				}
54258919Seric 			}
54358883Seric 
54459747Seric   abortmessage:
5459339Seric 			/* if in a child, pop back to our parent */
5469339Seric 			if (InChild)
5479339Seric 				finis();
54824943Seric 
54924943Seric 			/* clean up a bit */
55058109Seric 			gotmail = FALSE;
55155012Seric 			dropenvelope(e);
55258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
55355012Seric 			e->e_flags = BlankEnvelope.e_flags;
5544549Seric 			break;
5554549Seric 
5564549Seric 		  case CMDRSET:		/* rset -- reset state */
55758151Seric 			message("250 Reset state");
5589339Seric 			if (InChild)
5599339Seric 				finis();
56058109Seric 
56158109Seric 			/* clean up a bit */
56258109Seric 			gotmail = FALSE;
56358109Seric 			dropenvelope(e);
56458179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5659339Seric 			break;
5664549Seric 
5674549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
56858092Seric 		  case CMDEXPN:		/* expn -- expand address */
56958092Seric 			vrfy = c->cmdcode == CMDVRFY;
57058092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
57158092Seric 						PrivacyFlags))
57258082Seric 			{
57358412Seric 				if (vrfy)
57458412Seric 					message("252 Who's to say?");
57558412Seric 				else
57658412Seric 					message("502 That's none of your business");
57758082Seric 				break;
57858082Seric 			}
57958082Seric 			else if (!gothello &&
58058092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
58158092Seric 						PrivacyFlags))
58258082Seric 			{
58358151Seric 				message("503 I demand that you introduce yourself first");
58458082Seric 				break;
58558082Seric 			}
58658092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5879339Seric 				break;
58855173Seric #ifdef LOG
58958020Seric 			if (LogLevel > 5)
59055173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
59155173Seric #endif
5925003Seric 			vrfyqueue = NULL;
5937762Seric 			QuickAbort = TRUE;
59458092Seric 			if (vrfy)
59558092Seric 				e->e_flags |= EF_VRFYONLY;
59658082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5977762Seric 			if (Errors != 0)
5989339Seric 			{
5999339Seric 				if (InChild)
6009339Seric 					finis();
6017762Seric 				break;
6029339Seric 			}
6035003Seric 			while (vrfyqueue != NULL)
6045003Seric 			{
6055003Seric 				register ADDRESS *a = vrfyqueue->q_next;
6065003Seric 				char *code;
6075003Seric 
6087685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
6095003Seric 					a = a->q_next;
6105003Seric 
6117685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
61258151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
6135003Seric 				else if (a == NULL)
61458151Seric 					message("554 Self destructive alias loop");
6155003Seric 				vrfyqueue = a;
6165003Seric 			}
6179339Seric 			if (InChild)
6189339Seric 				finis();
6194549Seric 			break;
6204549Seric 
6214549Seric 		  case CMDHELP:		/* help -- give user info */
6224577Seric 			help(p);
6234549Seric 			break;
6244549Seric 
6254549Seric 		  case CMDNOOP:		/* noop -- do nothing */
62658151Seric 			message("200 OK");
6274549Seric 			break;
6284549Seric 
6294549Seric 		  case CMDQUIT:		/* quit -- leave mail */
63058151Seric 			message("221 %s closing connection", MyHostName);
6319339Seric 			if (InChild)
6329339Seric 				ExitStat = EX_QUIT;
6334549Seric 			finis();
6344549Seric 
6358544Seric 		  case CMDVERB:		/* set verbose mode */
63659957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
63759957Seric 			{
63859957Seric 				/* this would give out the same info */
63959957Seric 				message("502 Verbose unavailable");
64059957Seric 				break;
64159957Seric 			}
6428544Seric 			Verbose = TRUE;
64358734Seric 			e->e_sendmode = SM_DELIVER;
64459957Seric 			message("250 Verbose mode");
6458544Seric 			break;
6468544Seric 
6479314Seric 		  case CMDONEX:		/* doing one transaction only */
6489378Seric 			OneXact = TRUE;
64959957Seric 			message("250 Only one transaction");
6509314Seric 			break;
6519314Seric 
65236230Skarels # ifdef SMTPDEBUG
6539339Seric 		  case CMDDBGQSHOW:	/* show queues */
6546907Seric 			printf("Send Queue=");
65555012Seric 			printaddr(e->e_sendqueue, TRUE);
6565003Seric 			break;
6577275Seric 
6587275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
6597676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6607676Seric 			tTflag(p);
66158151Seric 			message("200 Debug set");
6627275Seric 			break;
6637275Seric 
66436230Skarels # else /* not SMTPDEBUG */
66524945Seric 
66636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
66736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
66836233Skarels # ifdef LOG
66958308Seric 			if (LogLevel > 0)
67036230Skarels 				syslog(LOG_NOTICE,
67158020Seric 				    "\"%s\" command from %s (%s)",
67236230Skarels 				    c->cmdname, RealHostName,
67358755Seric 				    anynet_ntoa(&RealHostAddr));
67436233Skarels # endif
67536230Skarels 			/* FALL THROUGH */
67636230Skarels # endif /* SMTPDEBUG */
67736230Skarels 
6784549Seric 		  case CMDERROR:	/* unknown command */
67958151Seric 			message("500 Command unrecognized");
6804549Seric 			break;
6814549Seric 
6824549Seric 		  default:
68336230Skarels 			errno = 0;
68458151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6854549Seric 			break;
6864549Seric 		}
6874549Seric 	}
6884549Seric }
6894549Seric /*
6904549Seric **  SKIPWORD -- skip a fixed word.
6914549Seric **
6924549Seric **	Parameters:
6934549Seric **		p -- place to start looking.
6944549Seric **		w -- word to skip.
6954549Seric **
6964549Seric **	Returns:
6974549Seric **		p following w.
6984549Seric **		NULL on error.
6994549Seric **
7004549Seric **	Side Effects:
7014549Seric **		clobbers the p data area.
7024549Seric */
7034549Seric 
7044549Seric static char *
7054549Seric skipword(p, w)
7064549Seric 	register char *p;
7074549Seric 	char *w;
7084549Seric {
7094549Seric 	register char *q;
7104549Seric 
7114549Seric 	/* find beginning of word */
71258050Seric 	while (isascii(*p) && isspace(*p))
7134549Seric 		p++;
7144549Seric 	q = p;
7154549Seric 
7164549Seric 	/* find end of word */
71758050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
7184549Seric 		p++;
71958050Seric 	while (isascii(*p) && isspace(*p))
7204549Seric 		*p++ = '\0';
7214549Seric 	if (*p != ':')
7224549Seric 	{
7234549Seric 	  syntax:
72458151Seric 		message("501 Syntax error");
7254549Seric 		Errors++;
7264549Seric 		return (NULL);
7274549Seric 	}
7284549Seric 	*p++ = '\0';
72958050Seric 	while (isascii(*p) && isspace(*p))
7304549Seric 		p++;
7314549Seric 
7324549Seric 	/* see if the input word matches desired word */
73333725Sbostic 	if (strcasecmp(q, w))
7344549Seric 		goto syntax;
7354549Seric 
7364549Seric 	return (p);
7374549Seric }
7384577Seric /*
73958151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
74058151Seric **
74158151Seric **	Parameters:
74258151Seric **		a -- the address to print
74358151Seric **		last -- set if this is the last one.
74458151Seric **
74558151Seric **	Returns:
74658151Seric **		none.
74758151Seric **
74858151Seric **	Side Effects:
74958151Seric **		Prints the appropriate 250 codes.
75058151Seric */
75158151Seric 
75258151Seric printvrfyaddr(a, last)
75358151Seric 	register ADDRESS *a;
75458151Seric 	bool last;
75558151Seric {
75658151Seric 	char fmtbuf[20];
75758151Seric 
75858151Seric 	strcpy(fmtbuf, "250");
75958151Seric 	fmtbuf[3] = last ? ' ' : '-';
76058151Seric 
76159746Seric 	if (a->q_fullname == NULL)
76259746Seric 	{
76359746Seric 		if (strchr(a->q_user, '@') == NULL)
76459746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
76559746Seric 		else
76659746Seric 			strcpy(&fmtbuf[4], "<%s>");
76759746Seric 		message(fmtbuf, a->q_user, MyHostName);
76859746Seric 	}
76958151Seric 	else
77058151Seric 	{
77159746Seric 		if (strchr(a->q_user, '@') == NULL)
77259746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
77359746Seric 		else
77459746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
77559746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
77658151Seric 	}
77758151Seric }
77858151Seric /*
7794577Seric **  HELP -- implement the HELP command.
7804577Seric **
7814577Seric **	Parameters:
7824577Seric **		topic -- the topic we want help for.
7834577Seric **
7844577Seric **	Returns:
7854577Seric **		none.
7864577Seric **
7874577Seric **	Side Effects:
7884577Seric **		outputs the help file to message output.
7894577Seric */
7904577Seric 
7914577Seric help(topic)
7924577Seric 	char *topic;
7934577Seric {
7944577Seric 	register FILE *hf;
7954577Seric 	int len;
7964577Seric 	char buf[MAXLINE];
7974577Seric 	bool noinfo;
7984577Seric 
7998269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
8004577Seric 	{
8014577Seric 		/* no help */
80211931Seric 		errno = 0;
80358151Seric 		message("502 HELP not implemented");
8044577Seric 		return;
8054577Seric 	}
8064577Seric 
80749669Seric 	if (topic == NULL || *topic == '\0')
80849669Seric 		topic = "smtp";
80949669Seric 	else
81049669Seric 		makelower(topic);
81149669Seric 
8124577Seric 	len = strlen(topic);
8134577Seric 	noinfo = TRUE;
8144577Seric 
8154577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
8164577Seric 	{
8174577Seric 		if (strncmp(buf, topic, len) == 0)
8184577Seric 		{
8194577Seric 			register char *p;
8204577Seric 
82156795Seric 			p = strchr(buf, '\t');
8224577Seric 			if (p == NULL)
8234577Seric 				p = buf;
8244577Seric 			else
8254577Seric 				p++;
8264577Seric 			fixcrlf(p, TRUE);
82758151Seric 			message("214-%s", p);
8284577Seric 			noinfo = FALSE;
8294577Seric 		}
8304577Seric 	}
8314577Seric 
8324577Seric 	if (noinfo)
83358151Seric 		message("504 HELP topic unknown");
8344577Seric 	else
83558151Seric 		message("214 End of HELP info");
8364628Seric 	(void) fclose(hf);
8374577Seric }
8388544Seric /*
8399339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
8409339Seric **
8419339Seric **	Parameters:
8429339Seric **		label -- a string used in error messages
8439339Seric **
8449339Seric **	Returns:
8459339Seric **		zero in the child
8469339Seric **		one in the parent
8479339Seric **
8489339Seric **	Side Effects:
8499339Seric **		none.
8509339Seric */
8518544Seric 
85255012Seric runinchild(label, e)
8539339Seric 	char *label;
85455012Seric 	register ENVELOPE *e;
8559339Seric {
8569339Seric 	int childpid;
8579339Seric 
85816158Seric 	if (!OneXact)
8599339Seric 	{
86016158Seric 		childpid = dofork();
86116158Seric 		if (childpid < 0)
86216158Seric 		{
86316158Seric 			syserr("%s: cannot fork", label);
86416158Seric 			return (1);
86516158Seric 		}
86616158Seric 		if (childpid > 0)
86716158Seric 		{
86816158Seric 			auto int st;
8699339Seric 
87016158Seric 			/* parent -- wait for child to complete */
87159060Seric 			setproctitle("srvrsmtp %s child wait", CurHostName);
87216158Seric 			st = waitfor(childpid);
87316158Seric 			if (st == -1)
87416158Seric 				syserr("%s: lost child", label);
8759339Seric 
87616158Seric 			/* if we exited on a QUIT command, complete the process */
87716158Seric 			if (st == (EX_QUIT << 8))
87816158Seric 				finis();
8799339Seric 
88016158Seric 			return (1);
88116158Seric 		}
88216158Seric 		else
88316158Seric 		{
88416158Seric 			/* child */
88516158Seric 			InChild = TRUE;
88625050Seric 			QuickAbort = FALSE;
88755012Seric 			clearenvelope(e, FALSE);
88816158Seric 		}
8899339Seric 	}
89015256Seric 
89116158Seric 	/* open alias database */
89259672Seric 	initaliases(FALSE, e);
89316158Seric 
89416158Seric 	return (0);
8959339Seric }
8969339Seric 
89756795Seric # endif /* SMTP */
898