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*58789Seric static char sccsid[] = "@(#)srvrsmtp.c	6.29 (Berkeley) 03/23/93 (with SMTP)";
1433731Sbostic #else
15*58789Seric static char sccsid[] = "@(#)srvrsmtp.c	6.29 (Berkeley) 03/23/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;
10230448Seric 	char *sendinghost;
10358109Seric 	bool gotmail;			/* mail command received */
10458092Seric 	bool gothello;			/* helo command received */
10558092Seric 	bool vrfy;			/* set if this is a vrfy command */
10658323Seric 	char *protocol;			/* sending protocol */
10758333Seric 	long msize;			/* approximate maximum message size */
10858333Seric 	auto char *delimptr;
10958714Seric 	char *id;
1108544Seric 	char inp[MAXLINE];
11157232Seric 	char cmdbuf[MAXLINE];
11258512Seric 	char hostbuf[MAXNAME];
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 
1207363Seric 	if (OutChannel != stdout)
1217363Seric 	{
1227363Seric 		/* arrange for debugging output to go to remote host */
1237363Seric 		(void) close(1);
1247363Seric 		(void) dup(fileno(OutChannel));
1257363Seric 	}
12655012Seric 	settime(e);
12757642Seric 	CurHostName = RealHostName;
12857642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12958050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
13058151Seric 	message("220 %s", inp);
13124943Seric 	SmtpPhase = "startup";
13230448Seric 	sendinghost = NULL;
13358330Seric 	protocol = NULL;
13458082Seric 	gothello = FALSE;
13558330Seric 	gotmail = FALSE;
1364549Seric 	for (;;)
1374549Seric 	{
13812612Seric 		/* arrange for backout */
13912612Seric 		if (setjmp(TopFrame) > 0 && InChild)
14012612Seric 			finis();
14112612Seric 		QuickAbort = FALSE;
14212612Seric 		HoldErrs = FALSE;
14351951Seric 		LogUsrErrs = FALSE;
14458092Seric 		e->e_flags &= ~EF_VRFYONLY;
14512612Seric 
1467356Seric 		/* setup for the read */
14755012Seric 		e->e_to = NULL;
1484577Seric 		Errors = 0;
1497275Seric 		(void) fflush(stdout);
1507356Seric 
1517356Seric 		/* read the input line */
15258109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1537356Seric 
1547685Seric 		/* handle errors */
1557356Seric 		if (p == NULL)
1567356Seric 		{
1574549Seric 			/* end of file, just die */
15858151Seric 			message("421 %s Lost input channel from %s",
15925050Seric 				MyHostName, CurHostName);
16055464Seric #ifdef LOG
16158020Seric 			if (LogLevel > 1)
16255464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16355464Seric 					CurHostName);
16455464Seric #endif
16558069Seric 			if (InChild)
16658069Seric 				ExitStat = EX_QUIT;
1674549Seric 			finis();
1684549Seric 		}
1694549Seric 
1704549Seric 		/* clean up end of line */
1714558Seric 		fixcrlf(inp, TRUE);
1724549Seric 
1734713Seric 		/* echo command to transcript */
17455012Seric 		if (e->e_xfp != NULL)
17555012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1764713Seric 
1774549Seric 		/* break off command */
17858050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1794549Seric 			continue;
18057232Seric 		cmd = cmdbuf;
18158050Seric 		while (*p != '\0' &&
18258050Seric 		       !(isascii(*p) && isspace(*p)) &&
18358050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
18424981Seric 			*cmd++ = *p++;
18524981Seric 		*cmd = '\0';
1864549Seric 
18725691Seric 		/* throw away leading whitespace */
18858050Seric 		while (isascii(*p) && isspace(*p))
18925691Seric 			p++;
19025691Seric 
1914549Seric 		/* decode command */
1924549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1934549Seric 		{
19433725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1954549Seric 				break;
1964549Seric 		}
1974549Seric 
19851954Seric 		/* reset errors */
19951954Seric 		errno = 0;
20051954Seric 
2014549Seric 		/* process command */
2024549Seric 		switch (c->cmdcode)
2034549Seric 		{
2044976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20558323Seric 		  case CMDEHLO:		/* extended hello */
20658323Seric 			if (c->cmdcode == CMDEHLO)
20758323Seric 			{
20858323Seric 				protocol = "ESMTP";
20958323Seric 				SmtpPhase = "EHLO";
21058323Seric 			}
21158323Seric 			else
21258323Seric 			{
21358323Seric 				protocol = "SMTP";
21458323Seric 				SmtpPhase = "HELO";
21558323Seric 			}
21625050Seric 			setproctitle("%s: %s", CurHostName, inp);
21758109Seric 			if (strcasecmp(p, MyHostName) == 0)
21814877Seric 			{
21936230Skarels 				/*
22058109Seric 				**  Didn't know about alias or MX,
22158109Seric 				**  or connected to an echo server
22258109Seric 				*/
22358109Seric 
22458151Seric 				message("553 %s config error: mail loops back to myself",
22547570Seric 					MyHostName);
22614877Seric 				break;
22714877Seric 			}
22858512Seric 			(void) strcpy(hostbuf, p);
22958512Seric 			(void) strcat(hostbuf, " (");
23058755Seric 			(void) strcat(hostbuf, anynet_ntoa(&RealHostAddr));
23158308Seric 			if (strcasecmp(p, RealHostName) != 0)
23211146Seric 			{
233*58789Seric 				auth_warning(e, "Host %s claimed to be %s",
234*58789Seric 					RealHostName, p);
23558512Seric 				(void) strcat(hostbuf, "; ");
23658512Seric 				(void) strcat(hostbuf, RealHostName);
23711146Seric 			}
23858512Seric 			(void) strcat(hostbuf, ")");
23958512Seric 			sendinghost = newstr(hostbuf);
24058323Seric 
24158323Seric 			/* send ext. message -- old systems must ignore */
24258323Seric 			message("250-%s Hello %s, pleased to meet you",
24336230Skarels 				MyHostName, sendinghost);
24458323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24558323Seric 				message("250-EXPN");
24658338Seric 			message("250-SIZE");
24758323Seric 			message("250 HELP");
24858082Seric 			gothello = TRUE;
2494976Seric 			break;
2504976Seric 
2514549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25224943Seric 			SmtpPhase = "MAIL";
25324943Seric 
25411151Seric 			/* force a sending host even if no HELO given */
25558064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
25630448Seric 				sendinghost = RealHostName;
25711151Seric 
2589314Seric 			/* check for validity of this command */
259*58789Seric 			if (!gothello)
26058082Seric 			{
261*58789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
262*58789Seric 					message("503 Polite people say HELO first");
263*58789Seric 				else
264*58789Seric 					auth_warning(e,
265*58789Seric 						"Host %s didn't use HELO protocol",
266*58789Seric 						RealHostName);
26758082Seric 				break;
26858082Seric 			}
26958109Seric 			if (gotmail)
2704558Seric 			{
27158151Seric 				message("503 Sender already specified");
2724558Seric 				break;
2734558Seric 			}
2749339Seric 			if (InChild)
2759339Seric 			{
27636230Skarels 				errno = 0;
27758151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
27858069Seric 				finis();
2799339Seric 			}
2809339Seric 
2819339Seric 			/* fork a subprocess to process this command */
28255012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2839339Seric 				break;
28458064Seric 			if (sendinghost != NULL)
28558064Seric 				define('s', sendinghost, e);
28658323Seric 			if (protocol == NULL)
28758323Seric 				protocol = "SMTP";
28858323Seric 			define('r', protocol, e);
28955012Seric 			initsys(e);
29057389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2919339Seric 
2929339Seric 			/* child -- go do the processing */
2934549Seric 			p = skipword(p, "from");
2944549Seric 			if (p == NULL)
2954549Seric 				break;
29657977Seric 			if (setjmp(TopFrame) > 0)
29758147Seric 			{
29858147Seric 				/* this failed -- undo work */
29958147Seric 				if (InChild)
30058147Seric 					finis();
30157977Seric 				break;
30258147Seric 			}
30357977Seric 			QuickAbort = TRUE;
30458333Seric 
30558333Seric 			/* must parse sender first */
30658333Seric 			delimptr = NULL;
30758704Seric 			setsender(p, e, &delimptr, FALSE);
30858333Seric 			p = delimptr;
30958333Seric 			if (p != NULL && *p != '\0')
31058333Seric 				*p++ = '\0';
31158333Seric 
31258333Seric 			/* now parse ESMTP arguments */
31358333Seric 			msize = 0;
31458333Seric 			for (; p != NULL && *p != '\0'; p++)
31558333Seric 			{
31658333Seric 				char *kp;
31758333Seric 				char *vp;
31858333Seric 
31958333Seric 				/* locate the beginning of the keyword */
32058333Seric 				while (isascii(*p) && isspace(*p))
32158333Seric 					p++;
32258333Seric 				if (*p == '\0')
32358333Seric 					break;
32458333Seric 				kp = p;
32558333Seric 
32658333Seric 				/* skip to the value portion */
32758333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
32858333Seric 					p++;
32958333Seric 				if (*p == '=')
33058333Seric 				{
33158333Seric 					*p++ = '\0';
33258333Seric 					vp = p;
33358333Seric 
33458333Seric 					/* skip to the end of the value */
33558333Seric 					while (*p != '\0' && *p != ' ' &&
33658333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
33758333Seric 					       *p != '=')
33858333Seric 						p++;
33958333Seric 				}
34058333Seric 
34158333Seric 				if (*p != '\0')
34258333Seric 					*p++ = '\0';
34358333Seric 
34458333Seric 				if (tTd(19, 1))
34558333Seric 					printf("MAIL: got arg %s=%s\n", kp,
34658333Seric 						vp == NULL ? "<null>" : vp);
34758333Seric 
34858333Seric 				if (strcasecmp(kp, "size") == 0)
34958333Seric 				{
35058333Seric 					if (kp == NULL)
35158333Seric 					{
35258333Seric 						usrerr("501 SIZE requires a value");
35358333Seric 						/* NOTREACHED */
35458333Seric 					}
35558333Seric 					msize = atol(vp);
35658333Seric 				}
35758333Seric 				else
35858333Seric 				{
35958333Seric 					usrerr("501 %s parameter unrecognized", kp);
36058333Seric 					/* NOTREACHED */
36158333Seric 				}
36258333Seric 			}
36358333Seric 
36458333Seric 			if (!enoughspace(msize))
36558333Seric 			{
36658333Seric 				message("452 Insufficient disk space; try again later");
36758333Seric 				break;
36858333Seric 			}
36958151Seric 			message("250 Sender ok");
37058147Seric 			gotmail = TRUE;
3714549Seric 			break;
3724549Seric 
3734976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
37424943Seric 			SmtpPhase = "RCPT";
37557389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
37612612Seric 			if (setjmp(TopFrame) > 0)
37714785Seric 			{
37855012Seric 				e->e_flags &= ~EF_FATALERRS;
37912612Seric 				break;
38014785Seric 			}
38112612Seric 			QuickAbort = TRUE;
38251951Seric 			LogUsrErrs = TRUE;
38358093Seric 
38458093Seric 			/* optimization -- if queueing, don't expand aliases */
38558734Seric 			if (e->e_sendmode == SM_QUEUE)
38658093Seric 				e->e_flags |= EF_VRFYONLY;
38758093Seric 
3884549Seric 			p = skipword(p, "to");
3894549Seric 			if (p == NULL)
3904549Seric 				break;
39158333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
39212612Seric 			if (a == NULL)
39312612Seric 				break;
39416886Seric 			a->q_flags |= QPRIMARY;
39555012Seric 			a = recipient(a, &e->e_sendqueue, e);
39612612Seric 			if (Errors != 0)
39712612Seric 				break;
39812612Seric 
39912612Seric 			/* no errors during parsing, but might be a duplicate */
40055012Seric 			e->e_to = p;
40112612Seric 			if (!bitset(QBADADDR, a->q_flags))
40258151Seric 				message("250 Recipient ok");
40312612Seric 			else
4044549Seric 			{
40512612Seric 				/* punt -- should keep message in ADDRESS.... */
40658151Seric 				message("550 Addressee unknown");
4074549Seric 			}
40855012Seric 			e->e_to = NULL;
4094549Seric 			break;
4104549Seric 
4114549Seric 		  case CMDDATA:		/* data -- text of mail */
41224943Seric 			SmtpPhase = "DATA";
41358109Seric 			if (!gotmail)
4144549Seric 			{
41558151Seric 				message("503 Need MAIL command");
4164976Seric 				break;
4174549Seric 			}
41855012Seric 			else if (e->e_nrcpts <= 0)
4194549Seric 			{
42058151Seric 				message("503 Need RCPT (recipient)");
4214976Seric 				break;
4224549Seric 			}
4234976Seric 
4244976Seric 			/* collect the text of the message */
42524943Seric 			SmtpPhase = "collect";
42657389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
42755012Seric 			collect(TRUE, e);
4284976Seric 			if (Errors != 0)
4294976Seric 				break;
4304976Seric 
4318238Seric 			/*
4328238Seric 			**  Arrange to send to everyone.
4338238Seric 			**	If sending to multiple people, mail back
4348238Seric 			**		errors rather than reporting directly.
4358238Seric 			**	In any case, don't mail back errors for
4368238Seric 			**		anything that has happened up to
4378238Seric 			**		now (the other end will do this).
43810197Seric 			**	Truncate our transcript -- the mail has gotten
43910197Seric 			**		to us successfully, and if we have
44010197Seric 			**		to mail this back, it will be easier
44110197Seric 			**		on the reader.
4428238Seric 			**	Then send to everyone.
4438238Seric 			**	Finally give a reply code.  If an error has
4448238Seric 			**		already been given, don't mail a
4458238Seric 			**		message back.
4469339Seric 			**	We goose error returns by clearing error bit.
4478238Seric 			*/
4488238Seric 
44924943Seric 			SmtpPhase = "delivery";
45055012Seric 			if (e->e_nrcpts != 1)
4519378Seric 			{
4529378Seric 				HoldErrs = TRUE;
45358734Seric 				e->e_errormode = EM_MAIL;
4549378Seric 			}
45555012Seric 			e->e_flags &= ~EF_FATALERRS;
45655012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
45758714Seric 			id = e->e_id;
4584976Seric 
4594976Seric 			/* send to all recipients */
46055012Seric 			sendall(e, SM_DEFAULT);
46155012Seric 			e->e_to = NULL;
4624976Seric 
46323516Seric 			/* save statistics */
46455012Seric 			markstats(e, (ADDRESS *) NULL);
46523516Seric 
4668238Seric 			/* issue success if appropriate and reset */
4678238Seric 			if (Errors == 0 || HoldErrs)
46858714Seric 				message("250 %s OK", id);
4698238Seric 			else
47055012Seric 				e->e_flags &= ~EF_FATALERRS;
4719339Seric 
4729339Seric 			/* if in a child, pop back to our parent */
4739339Seric 			if (InChild)
4749339Seric 				finis();
47524943Seric 
47624943Seric 			/* clean up a bit */
47758109Seric 			gotmail = FALSE;
47855012Seric 			dropenvelope(e);
47958179Seric 			CurEnv = e = newenvelope(e, CurEnv);
48055012Seric 			e->e_flags = BlankEnvelope.e_flags;
4814549Seric 			break;
4824549Seric 
4834549Seric 		  case CMDRSET:		/* rset -- reset state */
48458151Seric 			message("250 Reset state");
4859339Seric 			if (InChild)
4869339Seric 				finis();
48758109Seric 
48858109Seric 			/* clean up a bit */
48958109Seric 			gotmail = FALSE;
49058109Seric 			dropenvelope(e);
49158179Seric 			CurEnv = e = newenvelope(e, CurEnv);
4929339Seric 			break;
4934549Seric 
4944549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
49558092Seric 		  case CMDEXPN:		/* expn -- expand address */
49658092Seric 			vrfy = c->cmdcode == CMDVRFY;
49758092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
49858092Seric 						PrivacyFlags))
49958082Seric 			{
50058412Seric 				if (vrfy)
50158412Seric 					message("252 Who's to say?");
50258412Seric 				else
50358412Seric 					message("502 That's none of your business");
50458082Seric 				break;
50558082Seric 			}
50658082Seric 			else if (!gothello &&
50758092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
50858092Seric 						PrivacyFlags))
50958082Seric 			{
51058151Seric 				message("503 I demand that you introduce yourself first");
51158082Seric 				break;
51258082Seric 			}
51358092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5149339Seric 				break;
51525050Seric 			setproctitle("%s: %s", CurHostName, inp);
51655173Seric #ifdef LOG
51758020Seric 			if (LogLevel > 5)
51855173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
51955173Seric #endif
5205003Seric 			vrfyqueue = NULL;
5217762Seric 			QuickAbort = TRUE;
52258092Seric 			if (vrfy)
52358092Seric 				e->e_flags |= EF_VRFYONLY;
52458082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5257762Seric 			if (Errors != 0)
5269339Seric 			{
5279339Seric 				if (InChild)
5289339Seric 					finis();
5297762Seric 				break;
5309339Seric 			}
5315003Seric 			while (vrfyqueue != NULL)
5325003Seric 			{
5335003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5345003Seric 				char *code;
5355003Seric 
5367685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5375003Seric 					a = a->q_next;
5385003Seric 
5397685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
54058151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5415003Seric 				else if (a == NULL)
54258151Seric 					message("554 Self destructive alias loop");
5435003Seric 				vrfyqueue = a;
5445003Seric 			}
5459339Seric 			if (InChild)
5469339Seric 				finis();
5474549Seric 			break;
5484549Seric 
5494549Seric 		  case CMDHELP:		/* help -- give user info */
5504577Seric 			help(p);
5514549Seric 			break;
5524549Seric 
5534549Seric 		  case CMDNOOP:		/* noop -- do nothing */
55458151Seric 			message("200 OK");
5554549Seric 			break;
5564549Seric 
5574549Seric 		  case CMDQUIT:		/* quit -- leave mail */
55858151Seric 			message("221 %s closing connection", MyHostName);
5599339Seric 			if (InChild)
5609339Seric 				ExitStat = EX_QUIT;
5614549Seric 			finis();
5624549Seric 
5638544Seric 		  case CMDVERB:		/* set verbose mode */
5648544Seric 			Verbose = TRUE;
56558734Seric 			e->e_sendmode = SM_DELIVER;
56658151Seric 			message("200 Verbose mode");
5678544Seric 			break;
5688544Seric 
5699314Seric 		  case CMDONEX:		/* doing one transaction only */
5709378Seric 			OneXact = TRUE;
57158151Seric 			message("200 Only one transaction");
5729314Seric 			break;
5739314Seric 
57436230Skarels # ifdef SMTPDEBUG
5759339Seric 		  case CMDDBGQSHOW:	/* show queues */
5766907Seric 			printf("Send Queue=");
57755012Seric 			printaddr(e->e_sendqueue, TRUE);
5785003Seric 			break;
5797275Seric 
5807275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5817676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5827676Seric 			tTflag(p);
58358151Seric 			message("200 Debug set");
5847275Seric 			break;
5857275Seric 
58636230Skarels # else /* not SMTPDEBUG */
58724945Seric 
58836230Skarels 		  case CMDDBGQSHOW:	/* show queues */
58936230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
59036233Skarels # ifdef LOG
59158308Seric 			if (LogLevel > 0)
59236230Skarels 				syslog(LOG_NOTICE,
59358020Seric 				    "\"%s\" command from %s (%s)",
59436230Skarels 				    c->cmdname, RealHostName,
59558755Seric 				    anynet_ntoa(&RealHostAddr));
59636233Skarels # endif
59736230Skarels 			/* FALL THROUGH */
59836230Skarels # endif /* SMTPDEBUG */
59936230Skarels 
6004549Seric 		  case CMDERROR:	/* unknown command */
60158151Seric 			message("500 Command unrecognized");
6024549Seric 			break;
6034549Seric 
6044549Seric 		  default:
60536230Skarels 			errno = 0;
60658151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6074549Seric 			break;
6084549Seric 		}
6094549Seric 	}
6104549Seric }
6114549Seric /*
6124549Seric **  SKIPWORD -- skip a fixed word.
6134549Seric **
6144549Seric **	Parameters:
6154549Seric **		p -- place to start looking.
6164549Seric **		w -- word to skip.
6174549Seric **
6184549Seric **	Returns:
6194549Seric **		p following w.
6204549Seric **		NULL on error.
6214549Seric **
6224549Seric **	Side Effects:
6234549Seric **		clobbers the p data area.
6244549Seric */
6254549Seric 
6264549Seric static char *
6274549Seric skipword(p, w)
6284549Seric 	register char *p;
6294549Seric 	char *w;
6304549Seric {
6314549Seric 	register char *q;
6324549Seric 
6334549Seric 	/* find beginning of word */
63458050Seric 	while (isascii(*p) && isspace(*p))
6354549Seric 		p++;
6364549Seric 	q = p;
6374549Seric 
6384549Seric 	/* find end of word */
63958050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6404549Seric 		p++;
64158050Seric 	while (isascii(*p) && isspace(*p))
6424549Seric 		*p++ = '\0';
6434549Seric 	if (*p != ':')
6444549Seric 	{
6454549Seric 	  syntax:
64658151Seric 		message("501 Syntax error");
6474549Seric 		Errors++;
6484549Seric 		return (NULL);
6494549Seric 	}
6504549Seric 	*p++ = '\0';
65158050Seric 	while (isascii(*p) && isspace(*p))
6524549Seric 		p++;
6534549Seric 
6544549Seric 	/* see if the input word matches desired word */
65533725Sbostic 	if (strcasecmp(q, w))
6564549Seric 		goto syntax;
6574549Seric 
6584549Seric 	return (p);
6594549Seric }
6604577Seric /*
66158151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
66258151Seric **
66358151Seric **	Parameters:
66458151Seric **		a -- the address to print
66558151Seric **		last -- set if this is the last one.
66658151Seric **
66758151Seric **	Returns:
66858151Seric **		none.
66958151Seric **
67058151Seric **	Side Effects:
67158151Seric **		Prints the appropriate 250 codes.
67258151Seric */
67358151Seric 
67458151Seric printvrfyaddr(a, last)
67558151Seric 	register ADDRESS *a;
67658151Seric 	bool last;
67758151Seric {
67858151Seric 	char fmtbuf[20];
67958151Seric 
68058151Seric 	strcpy(fmtbuf, "250");
68158151Seric 	fmtbuf[3] = last ? ' ' : '-';
68258151Seric 
68358151Seric 	if (strchr(a->q_paddr, '<') != NULL)
68458151Seric 		strcpy(&fmtbuf[4], "%s");
68558151Seric 	else if (a->q_fullname == NULL)
68658151Seric 		strcpy(&fmtbuf[4], "<%s>");
68758151Seric 	else
68858151Seric 	{
68958151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
69058151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
69158151Seric 		return;
69258151Seric 	}
69358151Seric 	message(fmtbuf, a->q_paddr);
69458151Seric }
69558151Seric /*
6964577Seric **  HELP -- implement the HELP command.
6974577Seric **
6984577Seric **	Parameters:
6994577Seric **		topic -- the topic we want help for.
7004577Seric **
7014577Seric **	Returns:
7024577Seric **		none.
7034577Seric **
7044577Seric **	Side Effects:
7054577Seric **		outputs the help file to message output.
7064577Seric */
7074577Seric 
7084577Seric help(topic)
7094577Seric 	char *topic;
7104577Seric {
7114577Seric 	register FILE *hf;
7124577Seric 	int len;
7134577Seric 	char buf[MAXLINE];
7144577Seric 	bool noinfo;
7154577Seric 
7168269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7174577Seric 	{
7184577Seric 		/* no help */
71911931Seric 		errno = 0;
72058151Seric 		message("502 HELP not implemented");
7214577Seric 		return;
7224577Seric 	}
7234577Seric 
72449669Seric 	if (topic == NULL || *topic == '\0')
72549669Seric 		topic = "smtp";
72649669Seric 	else
72749669Seric 		makelower(topic);
72849669Seric 
7294577Seric 	len = strlen(topic);
7304577Seric 	noinfo = TRUE;
7314577Seric 
7324577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7334577Seric 	{
7344577Seric 		if (strncmp(buf, topic, len) == 0)
7354577Seric 		{
7364577Seric 			register char *p;
7374577Seric 
73856795Seric 			p = strchr(buf, '\t');
7394577Seric 			if (p == NULL)
7404577Seric 				p = buf;
7414577Seric 			else
7424577Seric 				p++;
7434577Seric 			fixcrlf(p, TRUE);
74458151Seric 			message("214-%s", p);
7454577Seric 			noinfo = FALSE;
7464577Seric 		}
7474577Seric 	}
7484577Seric 
7494577Seric 	if (noinfo)
75058151Seric 		message("504 HELP topic unknown");
7514577Seric 	else
75258151Seric 		message("214 End of HELP info");
7534628Seric 	(void) fclose(hf);
7544577Seric }
7558544Seric /*
7569339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7579339Seric **
7589339Seric **	Parameters:
7599339Seric **		label -- a string used in error messages
7609339Seric **
7619339Seric **	Returns:
7629339Seric **		zero in the child
7639339Seric **		one in the parent
7649339Seric **
7659339Seric **	Side Effects:
7669339Seric **		none.
7679339Seric */
7688544Seric 
76955012Seric runinchild(label, e)
7709339Seric 	char *label;
77155012Seric 	register ENVELOPE *e;
7729339Seric {
7739339Seric 	int childpid;
7749339Seric 
77516158Seric 	if (!OneXact)
7769339Seric 	{
77716158Seric 		childpid = dofork();
77816158Seric 		if (childpid < 0)
77916158Seric 		{
78016158Seric 			syserr("%s: cannot fork", label);
78116158Seric 			return (1);
78216158Seric 		}
78316158Seric 		if (childpid > 0)
78416158Seric 		{
78516158Seric 			auto int st;
7869339Seric 
78716158Seric 			/* parent -- wait for child to complete */
78816158Seric 			st = waitfor(childpid);
78916158Seric 			if (st == -1)
79016158Seric 				syserr("%s: lost child", label);
7919339Seric 
79216158Seric 			/* if we exited on a QUIT command, complete the process */
79316158Seric 			if (st == (EX_QUIT << 8))
79416158Seric 				finis();
7959339Seric 
79616158Seric 			return (1);
79716158Seric 		}
79816158Seric 		else
79916158Seric 		{
80016158Seric 			/* child */
80116158Seric 			InChild = TRUE;
80225050Seric 			QuickAbort = FALSE;
80355012Seric 			clearenvelope(e, FALSE);
80416158Seric 		}
8059339Seric 	}
80615256Seric 
80716158Seric 	/* open alias database */
80855012Seric 	initaliases(AliasFile, FALSE, e);
80916158Seric 
81016158Seric 	return (0);
8119339Seric }
8129339Seric 
81356795Seric # endif /* SMTP */
814