122712Sdist /*
234921Sbostic  * Copyright (c) 1983 Eric P. Allman
362532Sbostic  * Copyright (c) 1988, 1993
462532Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642829Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822712Sdist 
933731Sbostic # include "sendmail.h"
1022712Sdist 
1133731Sbostic #ifndef lint
1233731Sbostic #ifdef SMTP
13*66772Seric static char sccsid[] = "@(#)srvrsmtp.c	8.37 (Berkeley) 04/13/94 (with SMTP)";
1433731Sbostic #else
15*66772Seric static char sccsid[] = "@(#)srvrsmtp.c	8.37 (Berkeley) 04/13/94 (without SMTP)";
1633731Sbostic #endif
1733731Sbostic #endif /* not lint */
1833731Sbostic 
199339Seric # include <errno.h>
204549Seric 
2133731Sbostic # ifdef SMTP
224556Seric 
234549Seric /*
244549Seric **  SMTP -- run the SMTP protocol.
254549Seric **
264549Seric **	Parameters:
274549Seric **		none.
284549Seric **
294549Seric **	Returns:
304549Seric **		never.
314549Seric **
324549Seric **	Side Effects:
334549Seric **		Reads commands from the input channel and processes
344549Seric **			them.
354549Seric */
364549Seric 
374549Seric struct cmd
384549Seric {
394549Seric 	char	*cmdname;	/* command name */
404549Seric 	int	cmdcode;	/* internal code, see below */
414549Seric };
424549Seric 
434549Seric /* values for cmdcode */
444549Seric # define CMDERROR	0	/* bad command */
454549Seric # define CMDMAIL	1	/* mail -- designate sender */
464976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
474549Seric # define CMDDATA	3	/* data -- send message text */
489339Seric # define CMDRSET	4	/* rset -- reset state */
499339Seric # define CMDVRFY	5	/* vrfy -- verify address */
5058092Seric # define CMDEXPN	6	/* expn -- expand address */
519339Seric # define CMDNOOP	7	/* noop -- do nothing */
529339Seric # define CMDQUIT	8	/* quit -- close connection and die */
539339Seric # define CMDHELO	9	/* helo -- be polite */
5458092Seric # define CMDHELP	10	/* help -- give usage info */
5558323Seric # define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
5658092Seric /* non-standard commands */
5758092Seric # define CMDONEX	16	/* onex -- sending one transaction only */
5858092Seric # define CMDVERB	17	/* verb -- go into verbose mode */
5964685Seric /* use this to catch and log "door handle" attempts on your system */
6064685Seric # define CMDLOGBOGUS	23	/* bogus command that should be logged */
6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */
6258092Seric # define CMDDBGQSHOW	24	/* showq -- show send queue */
6358092Seric # define CMDDBGDEBUG	25	/* debug -- set debug mode */
644549Seric 
654549Seric static struct cmd	CmdTab[] =
664549Seric {
674549Seric 	"mail",		CMDMAIL,
684976Seric 	"rcpt",		CMDRCPT,
694549Seric 	"data",		CMDDATA,
704549Seric 	"rset",		CMDRSET,
714549Seric 	"vrfy",		CMDVRFY,
7258092Seric 	"expn",		CMDEXPN,
734549Seric 	"help",		CMDHELP,
744549Seric 	"noop",		CMDNOOP,
754549Seric 	"quit",		CMDQUIT,
764976Seric 	"helo",		CMDHELO,
7758323Seric 	"ehlo",		CMDEHLO,
788544Seric 	"verb",		CMDVERB,
799314Seric 	"onex",		CMDONEX,
8036230Skarels 	/*
8136230Skarels 	 * remaining commands are here only
8236230Skarels 	 * to trap and log attempts to use them
8336230Skarels 	 */
849339Seric 	"showq",	CMDDBGQSHOW,
858544Seric 	"debug",	CMDDBGDEBUG,
8664685Seric 	"wiz",		CMDLOGBOGUS,
874549Seric 	NULL,		CMDERROR,
884549Seric };
894549Seric 
909378Seric bool	OneXact = FALSE;		/* one xaction only this run */
9165017Seric char	*CurSmtpClient;			/* who's at the other end of channel */
9211146Seric 
9363937Seric static char	*skipword();
9466325Seric extern char	RealUserName[];
9563937Seric 
9666325Seric 
9766283Seric #define MAXBADCOMMANDS	25		/* maximum number of bad commands */
9866283Seric 
9955012Seric smtp(e)
10055012Seric 	register ENVELOPE *e;
1014549Seric {
1024549Seric 	register char *p;
1038544Seric 	register struct cmd *c;
1044549Seric 	char *cmd;
1055003Seric 	auto ADDRESS *vrfyqueue;
10612612Seric 	ADDRESS *a;
10758109Seric 	bool gotmail;			/* mail command received */
10858092Seric 	bool gothello;			/* helo command received */
10958092Seric 	bool vrfy;			/* set if this is a vrfy command */
11058323Seric 	char *protocol;			/* sending protocol */
11159016Seric 	char *sendinghost;		/* sending hostname */
112*66772Seric 	unsigned long msize;		/* approximate maximum message size */
11366005Seric 	char *peerhostname;		/* name of SMTP peer or "localhost" */
11458333Seric 	auto char *delimptr;
11558714Seric 	char *id;
11659747Seric 	int nrcpts;			/* number of RCPT commands */
11763787Seric 	bool doublequeue;
11866283Seric 	int badcommands = 0;		/* count of bad commands */
1198544Seric 	char inp[MAXLINE];
12057232Seric 	char cmdbuf[MAXLINE];
1217124Seric 	extern char Version[];
12224943Seric 	extern ENVELOPE BlankEnvelope;
1234549Seric 
12459066Seric 	if (fileno(OutChannel) != fileno(stdout))
1257363Seric 	{
1267363Seric 		/* arrange for debugging output to go to remote host */
12759066Seric 		(void) dup2(fileno(OutChannel), fileno(stdout));
1287363Seric 	}
12955012Seric 	settime(e);
13066005Seric 	peerhostname = RealHostName;
13166005Seric 	if (peerhostname == NULL)
13266005Seric 		peerhostname = "localhost";
13366005Seric 	CurHostName = peerhostname;
13465017Seric 	CurSmtpClient = macvalue('_', e);
13565017Seric 	if (CurSmtpClient == NULL)
13666003Seric 		CurSmtpClient = CurHostName;
13765017Seric 
13865017Seric 	setproctitle("server %s startup", CurSmtpClient);
13958050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
14064496Seric 	if (BrokenSmtpPeers)
14164496Seric 	{
14266762Seric 		p = strchr(inp, '\n');
14366762Seric 		if (p != NULL)
14466762Seric 			*p = '\0';
14564496Seric 		message("220 %s", inp);
14664496Seric 	}
14764496Seric 	else
14864496Seric 	{
14966745Seric 		char *q = inp;
15066745Seric 
15166745Seric 		while (q != NULL)
15266745Seric 		{
15366762Seric 			p = strchr(q, '\n');
15466762Seric 			if (p != NULL)
15566762Seric 				*p++ = '\0';
15666745Seric 			message("220-%s", q);
15766745Seric 			q = p;
15866745Seric 		}
15964496Seric 		message("220 ESMTP spoken here");
16064496Seric 	}
16158330Seric 	protocol = NULL;
16259016Seric 	sendinghost = macvalue('s', e);
16358082Seric 	gothello = FALSE;
16458330Seric 	gotmail = FALSE;
1654549Seric 	for (;;)
1664549Seric 	{
16712612Seric 		/* arrange for backout */
16865751Seric 		if (setjmp(TopFrame) > 0)
16959058Seric 		{
17065751Seric 			/* if() nesting is necessary for Cray UNICOS */
17165751Seric 			if (InChild)
17265751Seric 			{
17365751Seric 				QuickAbort = FALSE;
17465751Seric 				SuprErrs = TRUE;
17565751Seric 				finis();
17665751Seric 			}
17759058Seric 		}
17812612Seric 		QuickAbort = FALSE;
17912612Seric 		HoldErrs = FALSE;
18051951Seric 		LogUsrErrs = FALSE;
18163843Seric 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
18212612Seric 
1837356Seric 		/* setup for the read */
18455012Seric 		e->e_to = NULL;
1854577Seric 		Errors = 0;
1867275Seric 		(void) fflush(stdout);
1877356Seric 
1887356Seric 		/* read the input line */
18961093Seric 		SmtpPhase = "server cmd read";
19061093Seric 		setproctitle("server %s cmd read", CurHostName);
19161093Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
19261093Seric 				SmtpPhase);
1937356Seric 
1947685Seric 		/* handle errors */
1957356Seric 		if (p == NULL)
1967356Seric 		{
1974549Seric 			/* end of file, just die */
19866017Seric 			disconnect(1, e);
19958151Seric 			message("421 %s Lost input channel from %s",
20065017Seric 				MyHostName, CurSmtpClient);
20155464Seric #ifdef LOG
20263843Seric 			if (LogLevel > (gotmail ? 1 : 19))
20355464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
20465017Seric 					CurSmtpClient);
20555464Seric #endif
20658069Seric 			if (InChild)
20758069Seric 				ExitStat = EX_QUIT;
2084549Seric 			finis();
2094549Seric 		}
2104549Seric 
2114549Seric 		/* clean up end of line */
2124558Seric 		fixcrlf(inp, TRUE);
2134549Seric 
2144713Seric 		/* echo command to transcript */
21555012Seric 		if (e->e_xfp != NULL)
21655012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
2174713Seric 
21859060Seric 		if (e->e_id == NULL)
21965058Seric 			setproctitle("%s: %.80s", CurSmtpClient, inp);
22059060Seric 		else
22165058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
22259060Seric 
2234549Seric 		/* break off command */
22458050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
2254549Seric 			continue;
22657232Seric 		cmd = cmdbuf;
22758050Seric 		while (*p != '\0' &&
22858050Seric 		       !(isascii(*p) && isspace(*p)) &&
22958050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
23024981Seric 			*cmd++ = *p++;
23124981Seric 		*cmd = '\0';
2324549Seric 
23325691Seric 		/* throw away leading whitespace */
23458050Seric 		while (isascii(*p) && isspace(*p))
23525691Seric 			p++;
23625691Seric 
2374549Seric 		/* decode command */
2384549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
2394549Seric 		{
24033725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
2414549Seric 				break;
2424549Seric 		}
2434549Seric 
24451954Seric 		/* reset errors */
24551954Seric 		errno = 0;
24651954Seric 
2474549Seric 		/* process command */
2484549Seric 		switch (c->cmdcode)
2494549Seric 		{
2504976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
25158323Seric 		  case CMDEHLO:		/* extended hello */
25258323Seric 			if (c->cmdcode == CMDEHLO)
25358323Seric 			{
25458323Seric 				protocol = "ESMTP";
25561093Seric 				SmtpPhase = "server EHLO";
25658323Seric 			}
25758323Seric 			else
25858323Seric 			{
25958323Seric 				protocol = "SMTP";
26061093Seric 				SmtpPhase = "server HELO";
26158323Seric 			}
26259016Seric 			sendinghost = newstr(p);
26360210Seric 			gothello = TRUE;
26460210Seric 			if (c->cmdcode != CMDEHLO)
26560239Seric 			{
26660239Seric 				/* print old message and be done with it */
26760239Seric 				message("250 %s Hello %s, pleased to meet you",
26865017Seric 					MyHostName, CurSmtpClient);
26960210Seric 				break;
27060239Seric 			}
27160239Seric 
27260239Seric 			/* print extended message and brag */
27360239Seric 			message("250-%s Hello %s, pleased to meet you",
27466760Seric 				MyHostName, CurSmtpClient);
27558323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
27658323Seric 				message("250-EXPN");
27764359Seric 			if (MaxMessageSize > 0)
27864359Seric 				message("250-SIZE %ld", MaxMessageSize);
27959271Seric 			else
28059271Seric 				message("250-SIZE");
28158323Seric 			message("250 HELP");
2824976Seric 			break;
2834976Seric 
2844549Seric 		  case CMDMAIL:		/* mail -- designate sender */
28561093Seric 			SmtpPhase = "server MAIL";
28624943Seric 
2879314Seric 			/* check for validity of this command */
28858789Seric 			if (!gothello)
28958082Seric 			{
29058957Seric 				/* set sending host to our known value */
29159016Seric 				if (sendinghost == NULL)
29266005Seric 					sendinghost = peerhostname;
29358957Seric 
29458789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
29558821Seric 				{
29658789Seric 					message("503 Polite people say HELO first");
29758821Seric 					break;
29858821Seric 				}
29958082Seric 			}
30058109Seric 			if (gotmail)
3014558Seric 			{
30258151Seric 				message("503 Sender already specified");
30363843Seric 				if (InChild)
30463843Seric 					finis();
3054558Seric 				break;
3064558Seric 			}
3079339Seric 			if (InChild)
3089339Seric 			{
30936230Skarels 				errno = 0;
31058151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
31158069Seric 				finis();
3129339Seric 			}
3139339Seric 
3149339Seric 			/* fork a subprocess to process this command */
31555012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
3169339Seric 				break;
31763753Seric 			if (!gothello)
31863753Seric 			{
31963753Seric 				auth_warning(e,
32066005Seric 					"Host %s didn't use HELO protocol",
32166005Seric 					peerhostname);
32263753Seric 			}
32365947Seric #ifdef PICKY_HELO_CHECK
32466005Seric 			if (strcasecmp(sendinghost, peerhostname) != 0 &&
32566005Seric 			    (strcasecmp(peerhostname, "localhost") != 0 ||
32665823Seric 			     strcasecmp(sendinghost, MyHostName) != 0))
32765823Seric 			{
32865823Seric 				auth_warning(e, "Host %s claimed to be %s",
32966005Seric 					peerhostname, sendinghost);
33065823Seric 			}
33165947Seric #endif
33265823Seric 
33358323Seric 			if (protocol == NULL)
33458323Seric 				protocol = "SMTP";
33558323Seric 			define('r', protocol, e);
33659016Seric 			define('s', sendinghost, e);
33755012Seric 			initsys(e);
33859747Seric 			nrcpts = 0;
33965089Seric 			e->e_flags |= EF_LOGSENDER;
34065058Seric 			setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp);
3419339Seric 
3429339Seric 			/* child -- go do the processing */
3434549Seric 			p = skipword(p, "from");
3444549Seric 			if (p == NULL)
3454549Seric 				break;
34657977Seric 			if (setjmp(TopFrame) > 0)
34758147Seric 			{
34858147Seric 				/* this failed -- undo work */
34958147Seric 				if (InChild)
35059058Seric 				{
35159058Seric 					QuickAbort = FALSE;
35259058Seric 					SuprErrs = TRUE;
35363787Seric 					e->e_flags &= ~EF_FATALERRS;
35458147Seric 					finis();
35559058Seric 				}
35657977Seric 				break;
35758147Seric 			}
35857977Seric 			QuickAbort = TRUE;
35958333Seric 
36058333Seric 			/* must parse sender first */
36158333Seric 			delimptr = NULL;
36258704Seric 			setsender(p, e, &delimptr, FALSE);
36358333Seric 			p = delimptr;
36458333Seric 			if (p != NULL && *p != '\0')
36558333Seric 				*p++ = '\0';
36658333Seric 
36766325Seric 			/* check for possible spoofing */
36866325Seric 			if (RealUid != 0 && OpMode == MD_SMTP &&
36966325Seric 			    (e->e_from.q_mailer != LocalMailer &&
37066325Seric 			     strcmp(e->e_from.q_user, RealUserName) != 0))
37166325Seric 			{
37266325Seric 				auth_warning(e, "%s owned process doing -bs",
37366325Seric 					RealUserName);
37466325Seric 			}
37566325Seric 
37658333Seric 			/* now parse ESMTP arguments */
37758333Seric 			msize = 0;
37866764Seric 			while (p != NULL && *p != '\0')
37958333Seric 			{
38058333Seric 				char *kp;
38166304Seric 				char *vp = NULL;
38258333Seric 
38358333Seric 				/* locate the beginning of the keyword */
38458333Seric 				while (isascii(*p) && isspace(*p))
38558333Seric 					p++;
38658333Seric 				if (*p == '\0')
38758333Seric 					break;
38858333Seric 				kp = p;
38958333Seric 
39058333Seric 				/* skip to the value portion */
39158333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
39258333Seric 					p++;
39358333Seric 				if (*p == '=')
39458333Seric 				{
39558333Seric 					*p++ = '\0';
39658333Seric 					vp = p;
39758333Seric 
39858333Seric 					/* skip to the end of the value */
39958333Seric 					while (*p != '\0' && *p != ' ' &&
40058333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
40158333Seric 					       *p != '=')
40258333Seric 						p++;
40358333Seric 				}
40458333Seric 
40558333Seric 				if (*p != '\0')
40658333Seric 					*p++ = '\0';
40758333Seric 
40858333Seric 				if (tTd(19, 1))
40966764Seric 					printf("MAIL: got arg %s=\"%s\"\n", kp,
41058333Seric 						vp == NULL ? "<null>" : vp);
41158333Seric 
41258333Seric 				if (strcasecmp(kp, "size") == 0)
41358333Seric 				{
41459093Seric 					if (vp == NULL)
41558333Seric 					{
41658333Seric 						usrerr("501 SIZE requires a value");
41758333Seric 						/* NOTREACHED */
41858333Seric 					}
419*66772Seric # ifdef __STDC__
420*66772Seric 					msize = strtoul(vp, (char **) NULL, 10);
421*66772Seric # else
422*66772Seric 					msize = strtol(vp, (char **) NULL, 10);
423*66772Seric # endif
42458333Seric 				}
42559093Seric 				else if (strcasecmp(kp, "body") == 0)
42659093Seric 				{
42759093Seric 					if (vp == NULL)
42859093Seric 					{
42959093Seric 						usrerr("501 BODY requires a value");
43059093Seric 						/* NOTREACHED */
43159093Seric 					}
43259093Seric # ifdef MIME
43359093Seric 					if (strcasecmp(vp, "8bitmime") == 0)
43459093Seric 					{
43559093Seric 						e->e_bodytype = "8BITMIME";
43659709Seric 						SevenBit = FALSE;
43759093Seric 					}
43859093Seric 					else if (strcasecmp(vp, "7bit") == 0)
43959093Seric 					{
44059093Seric 						e->e_bodytype = "7BIT";
44159709Seric 						SevenBit = TRUE;
44259093Seric 					}
44359093Seric 					else
44459093Seric 					{
44559093Seric 						usrerr("501 Unknown BODY type %s",
44659093Seric 							vp);
44759093Seric 					}
44859093Seric # endif
44959093Seric 				}
45058333Seric 				else
45158333Seric 				{
45258333Seric 					usrerr("501 %s parameter unrecognized", kp);
45358333Seric 					/* NOTREACHED */
45458333Seric 				}
45558333Seric 			}
45659284Seric 
45759284Seric 			if (MaxMessageSize > 0 && msize > MaxMessageSize)
45859284Seric 			{
45959284Seric 				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
46059284Seric 					MaxMessageSize);
46159284Seric 				/* NOTREACHED */
46259284Seric 			}
46358333Seric 
46458333Seric 			if (!enoughspace(msize))
46558333Seric 			{
46658333Seric 				message("452 Insufficient disk space; try again later");
46758333Seric 				break;
46858333Seric 			}
46958151Seric 			message("250 Sender ok");
47058147Seric 			gotmail = TRUE;
4714549Seric 			break;
4724549Seric 
4734976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
47458850Seric 			if (!gotmail)
47558850Seric 			{
47658850Seric 				usrerr("503 Need MAIL before RCPT");
47758850Seric 				break;
47858850Seric 			}
47961093Seric 			SmtpPhase = "server RCPT";
48012612Seric 			if (setjmp(TopFrame) > 0)
48114785Seric 			{
48255012Seric 				e->e_flags &= ~EF_FATALERRS;
48312612Seric 				break;
48414785Seric 			}
48512612Seric 			QuickAbort = TRUE;
48651951Seric 			LogUsrErrs = TRUE;
48758093Seric 
48859699Seric 			if (e->e_sendmode != SM_DELIVER)
48959699Seric 				e->e_flags |= EF_VRFYONLY;
49058919Seric 
4914549Seric 			p = skipword(p, "to");
4924549Seric 			if (p == NULL)
4934549Seric 				break;
49464284Seric 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e);
49512612Seric 			if (a == NULL)
49612612Seric 				break;
49716886Seric 			a->q_flags |= QPRIMARY;
49855012Seric 			a = recipient(a, &e->e_sendqueue, e);
49912612Seric 			if (Errors != 0)
50012612Seric 				break;
50112612Seric 
50212612Seric 			/* no errors during parsing, but might be a duplicate */
50355012Seric 			e->e_to = p;
50412612Seric 			if (!bitset(QBADADDR, a->q_flags))
50559747Seric 			{
50664718Seric 				message("250 Recipient ok%s",
50764718Seric 					bitset(QQUEUEUP, a->q_flags) ?
50864718Seric 						" (will queue)" : "");
50959747Seric 				nrcpts++;
51059747Seric 			}
51112612Seric 			else
5124549Seric 			{
51312612Seric 				/* punt -- should keep message in ADDRESS.... */
51458151Seric 				message("550 Addressee unknown");
5154549Seric 			}
51655012Seric 			e->e_to = NULL;
5174549Seric 			break;
5184549Seric 
5194549Seric 		  case CMDDATA:		/* data -- text of mail */
52061093Seric 			SmtpPhase = "server DATA";
52158109Seric 			if (!gotmail)
5224549Seric 			{
52358151Seric 				message("503 Need MAIL command");
5244976Seric 				break;
5254549Seric 			}
52664718Seric 			else if (nrcpts <= 0)
5274549Seric 			{
52858151Seric 				message("503 Need RCPT (recipient)");
5294976Seric 				break;
5304549Seric 			}
5314976Seric 
53258929Seric 			/* check to see if we need to re-expand aliases */
53363787Seric 			/* also reset QBADADDR on already-diagnosted addrs */
53463787Seric 			doublequeue = FALSE;
53558929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
53658929Seric 			{
53758929Seric 				if (bitset(QVERIFIED, a->q_flags))
53863787Seric 				{
53963787Seric 					/* need to re-expand aliases */
54063787Seric 					doublequeue = TRUE;
54163787Seric 				}
54263787Seric 				if (bitset(QBADADDR, a->q_flags))
54363787Seric 				{
54463787Seric 					/* make this "go away" */
54563787Seric 					a->q_flags |= QDONTSEND;
54663787Seric 					a->q_flags &= ~QBADADDR;
54763787Seric 				}
54858929Seric 			}
54958929Seric 
5504976Seric 			/* collect the text of the message */
55124943Seric 			SmtpPhase = "collect";
55264766Seric 			collect(TRUE, doublequeue, e);
55364766Seric 			if (Errors != 0)
55464766Seric 				goto abortmessage;
55563965Seric 			HoldErrs = TRUE;
5564976Seric 
5578238Seric 			/*
5588238Seric 			**  Arrange to send to everyone.
5598238Seric 			**	If sending to multiple people, mail back
5608238Seric 			**		errors rather than reporting directly.
5618238Seric 			**	In any case, don't mail back errors for
5628238Seric 			**		anything that has happened up to
5638238Seric 			**		now (the other end will do this).
56410197Seric 			**	Truncate our transcript -- the mail has gotten
56510197Seric 			**		to us successfully, and if we have
56610197Seric 			**		to mail this back, it will be easier
56710197Seric 			**		on the reader.
5688238Seric 			**	Then send to everyone.
5698238Seric 			**	Finally give a reply code.  If an error has
5708238Seric 			**		already been given, don't mail a
5718238Seric 			**		message back.
5729339Seric 			**	We goose error returns by clearing error bit.
5738238Seric 			*/
5748238Seric 
57524943Seric 			SmtpPhase = "delivery";
57663787Seric 			if (nrcpts != 1 && !doublequeue)
5779378Seric 			{
5789378Seric 				HoldErrs = TRUE;
57958734Seric 				e->e_errormode = EM_MAIL;
5809378Seric 			}
58155012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
58258714Seric 			id = e->e_id;
5834976Seric 
5844976Seric 			/* send to all recipients */
58563787Seric 			sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT);
58655012Seric 			e->e_to = NULL;
5874976Seric 
5888238Seric 			/* issue success if appropriate and reset */
5898238Seric 			if (Errors == 0 || HoldErrs)
59058855Seric 				message("250 %s Message accepted for delivery", id);
59159747Seric 
59259747Seric 			if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs)
59359730Seric 			{
59459730Seric 				/* avoid sending back an extra message */
59559730Seric 				e->e_flags &= ~EF_FATALERRS;
59659747Seric 				e->e_flags |= EF_CLRQUEUE;
59759730Seric 			}
5988238Seric 			else
59958919Seric 			{
60059747Seric 				/* from now on, we have to operate silently */
60159747Seric 				HoldErrs = TRUE;
60259747Seric 				e->e_errormode = EM_MAIL;
60359747Seric 
60459730Seric 				/* if we just queued, poke it */
60563787Seric 				if (doublequeue && e->e_sendmode != SM_QUEUE)
60659730Seric 				{
60764296Seric 					extern pid_t dowork();
60864296Seric 
60959730Seric 					unlockqueue(e);
61064296Seric 					(void) dowork(id, TRUE, TRUE, e);
61159730Seric 				}
61258919Seric 			}
61358883Seric 
61459747Seric   abortmessage:
6159339Seric 			/* if in a child, pop back to our parent */
6169339Seric 			if (InChild)
6179339Seric 				finis();
61824943Seric 
61924943Seric 			/* clean up a bit */
62058109Seric 			gotmail = FALSE;
62155012Seric 			dropenvelope(e);
62258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
62355012Seric 			e->e_flags = BlankEnvelope.e_flags;
6244549Seric 			break;
6254549Seric 
6264549Seric 		  case CMDRSET:		/* rset -- reset state */
62758151Seric 			message("250 Reset state");
62864359Seric 			e->e_flags |= EF_CLRQUEUE;
6299339Seric 			if (InChild)
6309339Seric 				finis();
63158109Seric 
63258109Seric 			/* clean up a bit */
63358109Seric 			gotmail = FALSE;
63458109Seric 			dropenvelope(e);
63558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
6369339Seric 			break;
6374549Seric 
6384549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
63958092Seric 		  case CMDEXPN:		/* expn -- expand address */
64058092Seric 			vrfy = c->cmdcode == CMDVRFY;
64158092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
64258092Seric 						PrivacyFlags))
64358082Seric 			{
64458412Seric 				if (vrfy)
64558412Seric 					message("252 Who's to say?");
64658412Seric 				else
64765192Seric 					message("502 Sorry, we do not allow this operation");
64865017Seric #ifdef LOG
64965017Seric 				if (LogLevel > 5)
65065017Seric 					syslog(LOG_INFO, "%s: %s [rejected]",
65165017Seric 						CurSmtpClient, inp);
65265017Seric #endif
65358082Seric 				break;
65458082Seric 			}
65558082Seric 			else if (!gothello &&
65658092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
65758092Seric 						PrivacyFlags))
65858082Seric 			{
65958151Seric 				message("503 I demand that you introduce yourself first");
66058082Seric 				break;
66158082Seric 			}
66258092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
6639339Seric 				break;
66455173Seric #ifdef LOG
66558020Seric 			if (LogLevel > 5)
66665017Seric 				syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp);
66755173Seric #endif
6685003Seric 			vrfyqueue = NULL;
6697762Seric 			QuickAbort = TRUE;
67058092Seric 			if (vrfy)
67158092Seric 				e->e_flags |= EF_VRFYONLY;
67262373Seric 			while (*p != '\0' && isascii(*p) && isspace(*p))
67362373Seric 				*p++;
67462373Seric 			if (*p == '\0')
67562373Seric 			{
67662373Seric 				message("501 Argument required");
67762373Seric 				Errors++;
67862373Seric 			}
67962373Seric 			else
68062373Seric 			{
68164284Seric 				(void) sendtolist(p, NULLADDR, &vrfyqueue, e);
68262373Seric 			}
6837762Seric 			if (Errors != 0)
6849339Seric 			{
6859339Seric 				if (InChild)
6869339Seric 					finis();
6877762Seric 				break;
6889339Seric 			}
68962373Seric 			if (vrfyqueue == NULL)
69062373Seric 			{
69162373Seric 				message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
69262373Seric 			}
6935003Seric 			while (vrfyqueue != NULL)
6945003Seric 			{
69563971Seric 				a = vrfyqueue;
69663971Seric 				while ((a = a->q_next) != NULL &&
69763971Seric 				       bitset(QDONTSEND|QBADADDR, a->q_flags))
69863971Seric 					continue;
6997685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
70058151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
70163847Seric 				vrfyqueue = vrfyqueue->q_next;
7025003Seric 			}
7039339Seric 			if (InChild)
7049339Seric 				finis();
7054549Seric 			break;
7064549Seric 
7074549Seric 		  case CMDHELP:		/* help -- give user info */
7084577Seric 			help(p);
7094549Seric 			break;
7104549Seric 
7114549Seric 		  case CMDNOOP:		/* noop -- do nothing */
71264122Seric 			message("250 OK");
7134549Seric 			break;
7144549Seric 
7154549Seric 		  case CMDQUIT:		/* quit -- leave mail */
71658151Seric 			message("221 %s closing connection", MyHostName);
71761051Seric 
71866283Seric doquit:
71961051Seric 			/* avoid future 050 messages */
72066017Seric 			disconnect(1, e);
72161051Seric 
7229339Seric 			if (InChild)
7239339Seric 				ExitStat = EX_QUIT;
7244549Seric 			finis();
7254549Seric 
7268544Seric 		  case CMDVERB:		/* set verbose mode */
72759957Seric 			if (bitset(PRIV_NOEXPN, PrivacyFlags))
72859957Seric 			{
72959957Seric 				/* this would give out the same info */
73059957Seric 				message("502 Verbose unavailable");
73159957Seric 				break;
73259957Seric 			}
7338544Seric 			Verbose = TRUE;
73458734Seric 			e->e_sendmode = SM_DELIVER;
73559957Seric 			message("250 Verbose mode");
7368544Seric 			break;
7378544Seric 
7389314Seric 		  case CMDONEX:		/* doing one transaction only */
7399378Seric 			OneXact = TRUE;
74059957Seric 			message("250 Only one transaction");
7419314Seric 			break;
7429314Seric 
74336230Skarels # ifdef SMTPDEBUG
7449339Seric 		  case CMDDBGQSHOW:	/* show queues */
7456907Seric 			printf("Send Queue=");
74655012Seric 			printaddr(e->e_sendqueue, TRUE);
7475003Seric 			break;
7487275Seric 
7497275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
7507676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
7517676Seric 			tTflag(p);
75258151Seric 			message("200 Debug set");
7537275Seric 			break;
7547275Seric 
75536230Skarels # else /* not SMTPDEBUG */
75636230Skarels 		  case CMDDBGQSHOW:	/* show queues */
75736230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
75864685Seric # endif /* SMTPDEBUG */
75964685Seric 		  case CMDLOGBOGUS:	/* bogus command */
76036233Skarels # ifdef LOG
76158308Seric 			if (LogLevel > 0)
76264685Seric 				syslog(LOG_CRIT,
76358020Seric 				    "\"%s\" command from %s (%s)",
76466005Seric 				    c->cmdname, peerhostname,
76558755Seric 				    anynet_ntoa(&RealHostAddr));
76636233Skarels # endif
76736230Skarels 			/* FALL THROUGH */
76836230Skarels 
7694549Seric 		  case CMDERROR:	/* unknown command */
77066283Seric 			if (++badcommands > MAXBADCOMMANDS)
77166283Seric 			{
77266283Seric 				message("421 %s Too many bad commands; closing connection",
77366283Seric 					MyHostName);
77466283Seric 				goto doquit;
77566283Seric 			}
77666283Seric 
77758151Seric 			message("500 Command unrecognized");
7784549Seric 			break;
7794549Seric 
7804549Seric 		  default:
78136230Skarels 			errno = 0;
78258151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
7834549Seric 			break;
7844549Seric 		}
7854549Seric 	}
7864549Seric }
7874549Seric /*
7884549Seric **  SKIPWORD -- skip a fixed word.
7894549Seric **
7904549Seric **	Parameters:
7914549Seric **		p -- place to start looking.
7924549Seric **		w -- word to skip.
7934549Seric **
7944549Seric **	Returns:
7954549Seric **		p following w.
7964549Seric **		NULL on error.
7974549Seric **
7984549Seric **	Side Effects:
7994549Seric **		clobbers the p data area.
8004549Seric */
8014549Seric 
8024549Seric static char *
8034549Seric skipword(p, w)
8044549Seric 	register char *p;
8054549Seric 	char *w;
8064549Seric {
8074549Seric 	register char *q;
80866005Seric 	char *firstp = p;
8094549Seric 
8104549Seric 	/* find beginning of word */
81158050Seric 	while (isascii(*p) && isspace(*p))
8124549Seric 		p++;
8134549Seric 	q = p;
8144549Seric 
8154549Seric 	/* find end of word */
81658050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
8174549Seric 		p++;
81858050Seric 	while (isascii(*p) && isspace(*p))
8194549Seric 		*p++ = '\0';
8204549Seric 	if (*p != ':')
8214549Seric 	{
8224549Seric 	  syntax:
82366005Seric 		message("501 Syntax error in parameters scanning \"%s\"",
82466005Seric 			firstp);
8254549Seric 		Errors++;
8264549Seric 		return (NULL);
8274549Seric 	}
8284549Seric 	*p++ = '\0';
82958050Seric 	while (isascii(*p) && isspace(*p))
8304549Seric 		p++;
8314549Seric 
83262373Seric 	if (*p == '\0')
83362373Seric 		goto syntax;
83462373Seric 
8354549Seric 	/* see if the input word matches desired word */
83633725Sbostic 	if (strcasecmp(q, w))
8374549Seric 		goto syntax;
8384549Seric 
8394549Seric 	return (p);
8404549Seric }
8414577Seric /*
84258151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
84358151Seric **
84458151Seric **	Parameters:
84558151Seric **		a -- the address to print
84658151Seric **		last -- set if this is the last one.
84758151Seric **
84858151Seric **	Returns:
84958151Seric **		none.
85058151Seric **
85158151Seric **	Side Effects:
85258151Seric **		Prints the appropriate 250 codes.
85358151Seric */
85458151Seric 
85558151Seric printvrfyaddr(a, last)
85658151Seric 	register ADDRESS *a;
85758151Seric 	bool last;
85858151Seric {
85958151Seric 	char fmtbuf[20];
86058151Seric 
86158151Seric 	strcpy(fmtbuf, "250");
86258151Seric 	fmtbuf[3] = last ? ' ' : '-';
86358151Seric 
86459746Seric 	if (a->q_fullname == NULL)
86559746Seric 	{
86659746Seric 		if (strchr(a->q_user, '@') == NULL)
86759746Seric 			strcpy(&fmtbuf[4], "<%s@%s>");
86859746Seric 		else
86959746Seric 			strcpy(&fmtbuf[4], "<%s>");
87059746Seric 		message(fmtbuf, a->q_user, MyHostName);
87159746Seric 	}
87258151Seric 	else
87358151Seric 	{
87459746Seric 		if (strchr(a->q_user, '@') == NULL)
87559746Seric 			strcpy(&fmtbuf[4], "%s <%s@%s>");
87659746Seric 		else
87759746Seric 			strcpy(&fmtbuf[4], "%s <%s>");
87859746Seric 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
87958151Seric 	}
88058151Seric }
88158151Seric /*
8824577Seric **  HELP -- implement the HELP command.
8834577Seric **
8844577Seric **	Parameters:
8854577Seric **		topic -- the topic we want help for.
8864577Seric **
8874577Seric **	Returns:
8884577Seric **		none.
8894577Seric **
8904577Seric **	Side Effects:
8914577Seric **		outputs the help file to message output.
8924577Seric */
8934577Seric 
8944577Seric help(topic)
8954577Seric 	char *topic;
8964577Seric {
8974577Seric 	register FILE *hf;
8984577Seric 	int len;
8994577Seric 	char buf[MAXLINE];
9004577Seric 	bool noinfo;
9014577Seric 
9028269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
9034577Seric 	{
9044577Seric 		/* no help */
90511931Seric 		errno = 0;
90658151Seric 		message("502 HELP not implemented");
9074577Seric 		return;
9084577Seric 	}
9094577Seric 
91049669Seric 	if (topic == NULL || *topic == '\0')
91149669Seric 		topic = "smtp";
91249669Seric 	else
91349669Seric 		makelower(topic);
91449669Seric 
9154577Seric 	len = strlen(topic);
9164577Seric 	noinfo = TRUE;
9174577Seric 
9184577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
9194577Seric 	{
9204577Seric 		if (strncmp(buf, topic, len) == 0)
9214577Seric 		{
9224577Seric 			register char *p;
9234577Seric 
92456795Seric 			p = strchr(buf, '\t');
9254577Seric 			if (p == NULL)
9264577Seric 				p = buf;
9274577Seric 			else
9284577Seric 				p++;
9294577Seric 			fixcrlf(p, TRUE);
93058151Seric 			message("214-%s", p);
9314577Seric 			noinfo = FALSE;
9324577Seric 		}
9334577Seric 	}
9344577Seric 
9354577Seric 	if (noinfo)
93658151Seric 		message("504 HELP topic unknown");
9374577Seric 	else
93858151Seric 		message("214 End of HELP info");
9394628Seric 	(void) fclose(hf);
9404577Seric }
9418544Seric /*
9429339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
9439339Seric **
9449339Seric **	Parameters:
9459339Seric **		label -- a string used in error messages
9469339Seric **
9479339Seric **	Returns:
9489339Seric **		zero in the child
9499339Seric **		one in the parent
9509339Seric **
9519339Seric **	Side Effects:
9529339Seric **		none.
9539339Seric */
9548544Seric 
95555012Seric runinchild(label, e)
9569339Seric 	char *label;
95755012Seric 	register ENVELOPE *e;
9589339Seric {
9599339Seric 	int childpid;
9609339Seric 
96116158Seric 	if (!OneXact)
9629339Seric 	{
96316158Seric 		childpid = dofork();
96416158Seric 		if (childpid < 0)
96516158Seric 		{
96616158Seric 			syserr("%s: cannot fork", label);
96716158Seric 			return (1);
96816158Seric 		}
96916158Seric 		if (childpid > 0)
97016158Seric 		{
97116158Seric 			auto int st;
9729339Seric 
97316158Seric 			/* parent -- wait for child to complete */
97461093Seric 			setproctitle("server %s child wait", CurHostName);
97516158Seric 			st = waitfor(childpid);
97616158Seric 			if (st == -1)
97716158Seric 				syserr("%s: lost child", label);
97864948Seric 			else if (!WIFEXITED(st))
97964948Seric 				syserr("%s: died on signal %d",
98064948Seric 					label, st & 0177);
9819339Seric 
98216158Seric 			/* if we exited on a QUIT command, complete the process */
98366017Seric 			if (WEXITSTATUS(st) == EX_QUIT)
98466017Seric 			{
98566017Seric 				disconnect(1, e);
98616158Seric 				finis();
98766017Seric 			}
9889339Seric 
98916158Seric 			return (1);
99016158Seric 		}
99116158Seric 		else
99216158Seric 		{
99316158Seric 			/* child */
99416158Seric 			InChild = TRUE;
99525050Seric 			QuickAbort = FALSE;
99655012Seric 			clearenvelope(e, FALSE);
99716158Seric 		}
9989339Seric 	}
99915256Seric 
100016158Seric 	/* open alias database */
100160537Seric 	initmaps(FALSE, e);
100216158Seric 
100316158Seric 	return (0);
10049339Seric }
10059339Seric 
100656795Seric # endif /* SMTP */
1007