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*58704Seric static char sccsid[] = "@(#)srvrsmtp.c	6.25 (Berkeley) 03/18/93 (with SMTP)";
1433731Sbostic #else
15*58704Seric static char sccsid[] = "@(#)srvrsmtp.c	6.25 (Berkeley) 03/18/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;
1098544Seric 	char inp[MAXLINE];
11057232Seric 	char cmdbuf[MAXLINE];
11158512Seric 	char hostbuf[MAXNAME];
1127124Seric 	extern char Version[];
11311151Seric 	extern char *macvalue();
11412612Seric 	extern ADDRESS *recipient();
11524943Seric 	extern ENVELOPE BlankEnvelope;
11624943Seric 	extern ENVELOPE *newenvelope();
11758512Seric 	extern char *inet_ntoa();
1184549Seric 
1197363Seric 	if (OutChannel != stdout)
1207363Seric 	{
1217363Seric 		/* arrange for debugging output to go to remote host */
1227363Seric 		(void) close(1);
1237363Seric 		(void) dup(fileno(OutChannel));
1247363Seric 	}
12555012Seric 	settime(e);
12657642Seric 	CurHostName = RealHostName;
12757642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12858050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12958151Seric 	message("220 %s", inp);
13024943Seric 	SmtpPhase = "startup";
13130448Seric 	sendinghost = NULL;
13258330Seric 	protocol = NULL;
13358082Seric 	gothello = FALSE;
13458330Seric 	gotmail = FALSE;
1354549Seric 	for (;;)
1364549Seric 	{
13712612Seric 		/* arrange for backout */
13812612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13912612Seric 			finis();
14012612Seric 		QuickAbort = FALSE;
14112612Seric 		HoldErrs = FALSE;
14251951Seric 		LogUsrErrs = FALSE;
14358092Seric 		e->e_flags &= ~EF_VRFYONLY;
14412612Seric 
1457356Seric 		/* setup for the read */
14655012Seric 		e->e_to = NULL;
1474577Seric 		Errors = 0;
1487275Seric 		(void) fflush(stdout);
1497356Seric 
1507356Seric 		/* read the input line */
15158109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1527356Seric 
1537685Seric 		/* handle errors */
1547356Seric 		if (p == NULL)
1557356Seric 		{
1564549Seric 			/* end of file, just die */
15758151Seric 			message("421 %s Lost input channel from %s",
15825050Seric 				MyHostName, CurHostName);
15955464Seric #ifdef LOG
16058020Seric 			if (LogLevel > 1)
16155464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16255464Seric 					CurHostName);
16355464Seric #endif
16458069Seric 			if (InChild)
16558069Seric 				ExitStat = EX_QUIT;
1664549Seric 			finis();
1674549Seric 		}
1684549Seric 
1694549Seric 		/* clean up end of line */
1704558Seric 		fixcrlf(inp, TRUE);
1714549Seric 
1724713Seric 		/* echo command to transcript */
17355012Seric 		if (e->e_xfp != NULL)
17455012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1754713Seric 
1764549Seric 		/* break off command */
17758050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1784549Seric 			continue;
17957232Seric 		cmd = cmdbuf;
18058050Seric 		while (*p != '\0' &&
18158050Seric 		       !(isascii(*p) && isspace(*p)) &&
18258050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
18324981Seric 			*cmd++ = *p++;
18424981Seric 		*cmd = '\0';
1854549Seric 
18625691Seric 		/* throw away leading whitespace */
18758050Seric 		while (isascii(*p) && isspace(*p))
18825691Seric 			p++;
18925691Seric 
1904549Seric 		/* decode command */
1914549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1924549Seric 		{
19333725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1944549Seric 				break;
1954549Seric 		}
1964549Seric 
19751954Seric 		/* reset errors */
19851954Seric 		errno = 0;
19951954Seric 
2004549Seric 		/* process command */
2014549Seric 		switch (c->cmdcode)
2024549Seric 		{
2034976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20458323Seric 		  case CMDEHLO:		/* extended hello */
20558323Seric 			if (c->cmdcode == CMDEHLO)
20658323Seric 			{
20758323Seric 				protocol = "ESMTP";
20858323Seric 				SmtpPhase = "EHLO";
20958323Seric 			}
21058323Seric 			else
21158323Seric 			{
21258323Seric 				protocol = "SMTP";
21358323Seric 				SmtpPhase = "HELO";
21458323Seric 			}
21525050Seric 			setproctitle("%s: %s", CurHostName, inp);
21658109Seric 			if (strcasecmp(p, MyHostName) == 0)
21714877Seric 			{
21836230Skarels 				/*
21958109Seric 				**  Didn't know about alias or MX,
22058109Seric 				**  or connected to an echo server
22158109Seric 				*/
22258109Seric 
22358151Seric 				message("553 %s config error: mail loops back to myself",
22447570Seric 					MyHostName);
22514877Seric 				break;
22614877Seric 			}
22758512Seric 			(void) strcpy(hostbuf, p);
22858512Seric 			(void) strcat(hostbuf, " (");
22958512Seric 			(void) strcat(hostbuf, inet_ntoa(RealHostAddr.sin_addr));
23058308Seric 			if (strcasecmp(p, RealHostName) != 0)
23111146Seric 			{
23258512Seric 				(void) strcat(hostbuf, "; ");
23358512Seric 				(void) strcat(hostbuf, RealHostName);
23411146Seric 			}
23558512Seric 			(void) strcat(hostbuf, ")");
23658512Seric 			sendinghost = newstr(hostbuf);
23758323Seric 
23858323Seric 			/* send ext. message -- old systems must ignore */
23958323Seric 			message("250-%s Hello %s, pleased to meet you",
24036230Skarels 				MyHostName, sendinghost);
24158323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24258323Seric 				message("250-EXPN");
24358338Seric 			message("250-SIZE");
24458323Seric 			message("250 HELP");
24558082Seric 			gothello = TRUE;
2464976Seric 			break;
2474976Seric 
2484549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24924943Seric 			SmtpPhase = "MAIL";
25024943Seric 
25111151Seric 			/* force a sending host even if no HELO given */
25258064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
25330448Seric 				sendinghost = RealHostName;
25411151Seric 
2559314Seric 			/* check for validity of this command */
25658082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25758082Seric 			{
25858151Seric 				message("503 Polite people say HELO first");
25958082Seric 				break;
26058082Seric 			}
26158109Seric 			if (gotmail)
2624558Seric 			{
26358151Seric 				message("503 Sender already specified");
2644558Seric 				break;
2654558Seric 			}
2669339Seric 			if (InChild)
2679339Seric 			{
26836230Skarels 				errno = 0;
26958151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
27058069Seric 				finis();
2719339Seric 			}
2729339Seric 
2739339Seric 			/* fork a subprocess to process this command */
27455012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2759339Seric 				break;
27658064Seric 			if (sendinghost != NULL)
27758064Seric 				define('s', sendinghost, e);
27858323Seric 			if (protocol == NULL)
27958323Seric 				protocol = "SMTP";
28058323Seric 			define('r', protocol, e);
28155012Seric 			initsys(e);
28257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2839339Seric 
2849339Seric 			/* child -- go do the processing */
2854549Seric 			p = skipword(p, "from");
2864549Seric 			if (p == NULL)
2874549Seric 				break;
28857977Seric 			if (setjmp(TopFrame) > 0)
28958147Seric 			{
29058147Seric 				/* this failed -- undo work */
29158147Seric 				if (InChild)
29258147Seric 					finis();
29357977Seric 				break;
29458147Seric 			}
29557977Seric 			QuickAbort = TRUE;
29658333Seric 
29758333Seric 			/* must parse sender first */
29858333Seric 			delimptr = NULL;
299*58704Seric 			setsender(p, e, &delimptr, FALSE);
30058333Seric 			p = delimptr;
30158333Seric 			if (p != NULL && *p != '\0')
30258333Seric 				*p++ = '\0';
30358333Seric 
30458333Seric 			/* now parse ESMTP arguments */
30558333Seric 			msize = 0;
30658333Seric 			for (; p != NULL && *p != '\0'; p++)
30758333Seric 			{
30858333Seric 				char *kp;
30958333Seric 				char *vp;
31058333Seric 
31158333Seric 				/* locate the beginning of the keyword */
31258333Seric 				while (isascii(*p) && isspace(*p))
31358333Seric 					p++;
31458333Seric 				if (*p == '\0')
31558333Seric 					break;
31658333Seric 				kp = p;
31758333Seric 
31858333Seric 				/* skip to the value portion */
31958333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
32058333Seric 					p++;
32158333Seric 				if (*p == '=')
32258333Seric 				{
32358333Seric 					*p++ = '\0';
32458333Seric 					vp = p;
32558333Seric 
32658333Seric 					/* skip to the end of the value */
32758333Seric 					while (*p != '\0' && *p != ' ' &&
32858333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
32958333Seric 					       *p != '=')
33058333Seric 						p++;
33158333Seric 				}
33258333Seric 
33358333Seric 				if (*p != '\0')
33458333Seric 					*p++ = '\0';
33558333Seric 
33658333Seric 				if (tTd(19, 1))
33758333Seric 					printf("MAIL: got arg %s=%s\n", kp,
33858333Seric 						vp == NULL ? "<null>" : vp);
33958333Seric 
34058333Seric 				if (strcasecmp(kp, "size") == 0)
34158333Seric 				{
34258333Seric 					if (kp == NULL)
34358333Seric 					{
34458333Seric 						usrerr("501 SIZE requires a value");
34558333Seric 						/* NOTREACHED */
34658333Seric 					}
34758333Seric 					msize = atol(vp);
34858333Seric 				}
34958333Seric 				else
35058333Seric 				{
35158333Seric 					usrerr("501 %s parameter unrecognized", kp);
35258333Seric 					/* NOTREACHED */
35358333Seric 				}
35458333Seric 			}
35558333Seric 
35658333Seric 			if (!enoughspace(msize))
35758333Seric 			{
35858333Seric 				message("452 Insufficient disk space; try again later");
35958333Seric 				break;
36058333Seric 			}
36158151Seric 			message("250 Sender ok");
36258147Seric 			gotmail = TRUE;
3634549Seric 			break;
3644549Seric 
3654976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
36624943Seric 			SmtpPhase = "RCPT";
36757389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
36812612Seric 			if (setjmp(TopFrame) > 0)
36914785Seric 			{
37055012Seric 				e->e_flags &= ~EF_FATALERRS;
37112612Seric 				break;
37214785Seric 			}
37312612Seric 			QuickAbort = TRUE;
37451951Seric 			LogUsrErrs = TRUE;
37558093Seric 
37658093Seric 			/* optimization -- if queueing, don't expand aliases */
37758093Seric 			if (SendMode == SM_QUEUE)
37858093Seric 				e->e_flags |= EF_VRFYONLY;
37958093Seric 
3804549Seric 			p = skipword(p, "to");
3814549Seric 			if (p == NULL)
3824549Seric 				break;
38358333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
38412612Seric 			if (a == NULL)
38512612Seric 				break;
38616886Seric 			a->q_flags |= QPRIMARY;
38755012Seric 			a = recipient(a, &e->e_sendqueue, e);
38812612Seric 			if (Errors != 0)
38912612Seric 				break;
39012612Seric 
39112612Seric 			/* no errors during parsing, but might be a duplicate */
39255012Seric 			e->e_to = p;
39312612Seric 			if (!bitset(QBADADDR, a->q_flags))
39458151Seric 				message("250 Recipient ok");
39512612Seric 			else
3964549Seric 			{
39712612Seric 				/* punt -- should keep message in ADDRESS.... */
39858151Seric 				message("550 Addressee unknown");
3994549Seric 			}
40055012Seric 			e->e_to = NULL;
4014549Seric 			break;
4024549Seric 
4034549Seric 		  case CMDDATA:		/* data -- text of mail */
40424943Seric 			SmtpPhase = "DATA";
40558109Seric 			if (!gotmail)
4064549Seric 			{
40758151Seric 				message("503 Need MAIL command");
4084976Seric 				break;
4094549Seric 			}
41055012Seric 			else if (e->e_nrcpts <= 0)
4114549Seric 			{
41258151Seric 				message("503 Need RCPT (recipient)");
4134976Seric 				break;
4144549Seric 			}
4154976Seric 
4164976Seric 			/* collect the text of the message */
41724943Seric 			SmtpPhase = "collect";
41857389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
41955012Seric 			collect(TRUE, e);
4204976Seric 			if (Errors != 0)
4214976Seric 				break;
4224976Seric 
4238238Seric 			/*
4248238Seric 			**  Arrange to send to everyone.
4258238Seric 			**	If sending to multiple people, mail back
4268238Seric 			**		errors rather than reporting directly.
4278238Seric 			**	In any case, don't mail back errors for
4288238Seric 			**		anything that has happened up to
4298238Seric 			**		now (the other end will do this).
43010197Seric 			**	Truncate our transcript -- the mail has gotten
43110197Seric 			**		to us successfully, and if we have
43210197Seric 			**		to mail this back, it will be easier
43310197Seric 			**		on the reader.
4348238Seric 			**	Then send to everyone.
4358238Seric 			**	Finally give a reply code.  If an error has
4368238Seric 			**		already been given, don't mail a
4378238Seric 			**		message back.
4389339Seric 			**	We goose error returns by clearing error bit.
4398238Seric 			*/
4408238Seric 
44124943Seric 			SmtpPhase = "delivery";
44255012Seric 			if (e->e_nrcpts != 1)
4439378Seric 			{
4449378Seric 				HoldErrs = TRUE;
44516886Seric 				ErrorMode = EM_MAIL;
4469378Seric 			}
44755012Seric 			e->e_flags &= ~EF_FATALERRS;
44855012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
4494976Seric 
4504976Seric 			/* send to all recipients */
45155012Seric 			sendall(e, SM_DEFAULT);
45255012Seric 			e->e_to = NULL;
4534976Seric 
45423516Seric 			/* save statistics */
45555012Seric 			markstats(e, (ADDRESS *) NULL);
45623516Seric 
4578238Seric 			/* issue success if appropriate and reset */
4588238Seric 			if (Errors == 0 || HoldErrs)
45958151Seric 				message("250 Ok");
4608238Seric 			else
46155012Seric 				e->e_flags &= ~EF_FATALERRS;
4629339Seric 
4639339Seric 			/* if in a child, pop back to our parent */
4649339Seric 			if (InChild)
4659339Seric 				finis();
46624943Seric 
46724943Seric 			/* clean up a bit */
46858109Seric 			gotmail = FALSE;
46955012Seric 			dropenvelope(e);
47058179Seric 			CurEnv = e = newenvelope(e, CurEnv);
47155012Seric 			e->e_flags = BlankEnvelope.e_flags;
4724549Seric 			break;
4734549Seric 
4744549Seric 		  case CMDRSET:		/* rset -- reset state */
47558151Seric 			message("250 Reset state");
4769339Seric 			if (InChild)
4779339Seric 				finis();
47858109Seric 
47958109Seric 			/* clean up a bit */
48058109Seric 			gotmail = FALSE;
48158109Seric 			dropenvelope(e);
48258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
4839339Seric 			break;
4844549Seric 
4854549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
48658092Seric 		  case CMDEXPN:		/* expn -- expand address */
48758092Seric 			vrfy = c->cmdcode == CMDVRFY;
48858092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
48958092Seric 						PrivacyFlags))
49058082Seric 			{
49158412Seric 				if (vrfy)
49258412Seric 					message("252 Who's to say?");
49358412Seric 				else
49458412Seric 					message("502 That's none of your business");
49558082Seric 				break;
49658082Seric 			}
49758082Seric 			else if (!gothello &&
49858092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
49958092Seric 						PrivacyFlags))
50058082Seric 			{
50158151Seric 				message("503 I demand that you introduce yourself first");
50258082Seric 				break;
50358082Seric 			}
50458092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5059339Seric 				break;
50625050Seric 			setproctitle("%s: %s", CurHostName, inp);
50755173Seric #ifdef LOG
50858020Seric 			if (LogLevel > 5)
50955173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
51055173Seric #endif
5115003Seric 			vrfyqueue = NULL;
5127762Seric 			QuickAbort = TRUE;
51358092Seric 			if (vrfy)
51458092Seric 				e->e_flags |= EF_VRFYONLY;
51558082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5167762Seric 			if (Errors != 0)
5179339Seric 			{
5189339Seric 				if (InChild)
5199339Seric 					finis();
5207762Seric 				break;
5219339Seric 			}
5225003Seric 			while (vrfyqueue != NULL)
5235003Seric 			{
5245003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5255003Seric 				char *code;
5265003Seric 
5277685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5285003Seric 					a = a->q_next;
5295003Seric 
5307685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
53158151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5325003Seric 				else if (a == NULL)
53358151Seric 					message("554 Self destructive alias loop");
5345003Seric 				vrfyqueue = a;
5355003Seric 			}
5369339Seric 			if (InChild)
5379339Seric 				finis();
5384549Seric 			break;
5394549Seric 
5404549Seric 		  case CMDHELP:		/* help -- give user info */
5414577Seric 			help(p);
5424549Seric 			break;
5434549Seric 
5444549Seric 		  case CMDNOOP:		/* noop -- do nothing */
54558151Seric 			message("200 OK");
5464549Seric 			break;
5474549Seric 
5484549Seric 		  case CMDQUIT:		/* quit -- leave mail */
54958151Seric 			message("221 %s closing connection", MyHostName);
5509339Seric 			if (InChild)
5519339Seric 				ExitStat = EX_QUIT;
5524549Seric 			finis();
5534549Seric 
5548544Seric 		  case CMDVERB:		/* set verbose mode */
5558544Seric 			Verbose = TRUE;
55625025Seric 			SendMode = SM_DELIVER;
55758151Seric 			message("200 Verbose mode");
5588544Seric 			break;
5598544Seric 
5609314Seric 		  case CMDONEX:		/* doing one transaction only */
5619378Seric 			OneXact = TRUE;
56258151Seric 			message("200 Only one transaction");
5639314Seric 			break;
5649314Seric 
56536230Skarels # ifdef SMTPDEBUG
5669339Seric 		  case CMDDBGQSHOW:	/* show queues */
5676907Seric 			printf("Send Queue=");
56855012Seric 			printaddr(e->e_sendqueue, TRUE);
5695003Seric 			break;
5707275Seric 
5717275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5727676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5737676Seric 			tTflag(p);
57458151Seric 			message("200 Debug set");
5757275Seric 			break;
5767275Seric 
57736230Skarels # else /* not SMTPDEBUG */
57824945Seric 
57936230Skarels 		  case CMDDBGQSHOW:	/* show queues */
58036230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
58136233Skarels # ifdef LOG
58258308Seric 			if (LogLevel > 0)
58336230Skarels 				syslog(LOG_NOTICE,
58458020Seric 				    "\"%s\" command from %s (%s)",
58536230Skarels 				    c->cmdname, RealHostName,
58636230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
58736233Skarels # endif
58836230Skarels 			/* FALL THROUGH */
58936230Skarels # endif /* SMTPDEBUG */
59036230Skarels 
5914549Seric 		  case CMDERROR:	/* unknown command */
59258151Seric 			message("500 Command unrecognized");
5934549Seric 			break;
5944549Seric 
5954549Seric 		  default:
59636230Skarels 			errno = 0;
59758151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
5984549Seric 			break;
5994549Seric 		}
6004549Seric 	}
6014549Seric }
6024549Seric /*
6034549Seric **  SKIPWORD -- skip a fixed word.
6044549Seric **
6054549Seric **	Parameters:
6064549Seric **		p -- place to start looking.
6074549Seric **		w -- word to skip.
6084549Seric **
6094549Seric **	Returns:
6104549Seric **		p following w.
6114549Seric **		NULL on error.
6124549Seric **
6134549Seric **	Side Effects:
6144549Seric **		clobbers the p data area.
6154549Seric */
6164549Seric 
6174549Seric static char *
6184549Seric skipword(p, w)
6194549Seric 	register char *p;
6204549Seric 	char *w;
6214549Seric {
6224549Seric 	register char *q;
6234549Seric 
6244549Seric 	/* find beginning of word */
62558050Seric 	while (isascii(*p) && isspace(*p))
6264549Seric 		p++;
6274549Seric 	q = p;
6284549Seric 
6294549Seric 	/* find end of word */
63058050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6314549Seric 		p++;
63258050Seric 	while (isascii(*p) && isspace(*p))
6334549Seric 		*p++ = '\0';
6344549Seric 	if (*p != ':')
6354549Seric 	{
6364549Seric 	  syntax:
63758151Seric 		message("501 Syntax error");
6384549Seric 		Errors++;
6394549Seric 		return (NULL);
6404549Seric 	}
6414549Seric 	*p++ = '\0';
64258050Seric 	while (isascii(*p) && isspace(*p))
6434549Seric 		p++;
6444549Seric 
6454549Seric 	/* see if the input word matches desired word */
64633725Sbostic 	if (strcasecmp(q, w))
6474549Seric 		goto syntax;
6484549Seric 
6494549Seric 	return (p);
6504549Seric }
6514577Seric /*
65258151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
65358151Seric **
65458151Seric **	Parameters:
65558151Seric **		a -- the address to print
65658151Seric **		last -- set if this is the last one.
65758151Seric **
65858151Seric **	Returns:
65958151Seric **		none.
66058151Seric **
66158151Seric **	Side Effects:
66258151Seric **		Prints the appropriate 250 codes.
66358151Seric */
66458151Seric 
66558151Seric printvrfyaddr(a, last)
66658151Seric 	register ADDRESS *a;
66758151Seric 	bool last;
66858151Seric {
66958151Seric 	char fmtbuf[20];
67058151Seric 
67158151Seric 	strcpy(fmtbuf, "250");
67258151Seric 	fmtbuf[3] = last ? ' ' : '-';
67358151Seric 
67458151Seric 	if (strchr(a->q_paddr, '<') != NULL)
67558151Seric 		strcpy(&fmtbuf[4], "%s");
67658151Seric 	else if (a->q_fullname == NULL)
67758151Seric 		strcpy(&fmtbuf[4], "<%s>");
67858151Seric 	else
67958151Seric 	{
68058151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
68158151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
68258151Seric 		return;
68358151Seric 	}
68458151Seric 	message(fmtbuf, a->q_paddr);
68558151Seric }
68658151Seric /*
6874577Seric **  HELP -- implement the HELP command.
6884577Seric **
6894577Seric **	Parameters:
6904577Seric **		topic -- the topic we want help for.
6914577Seric **
6924577Seric **	Returns:
6934577Seric **		none.
6944577Seric **
6954577Seric **	Side Effects:
6964577Seric **		outputs the help file to message output.
6974577Seric */
6984577Seric 
6994577Seric help(topic)
7004577Seric 	char *topic;
7014577Seric {
7024577Seric 	register FILE *hf;
7034577Seric 	int len;
7044577Seric 	char buf[MAXLINE];
7054577Seric 	bool noinfo;
7064577Seric 
7078269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7084577Seric 	{
7094577Seric 		/* no help */
71011931Seric 		errno = 0;
71158151Seric 		message("502 HELP not implemented");
7124577Seric 		return;
7134577Seric 	}
7144577Seric 
71549669Seric 	if (topic == NULL || *topic == '\0')
71649669Seric 		topic = "smtp";
71749669Seric 	else
71849669Seric 		makelower(topic);
71949669Seric 
7204577Seric 	len = strlen(topic);
7214577Seric 	noinfo = TRUE;
7224577Seric 
7234577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7244577Seric 	{
7254577Seric 		if (strncmp(buf, topic, len) == 0)
7264577Seric 		{
7274577Seric 			register char *p;
7284577Seric 
72956795Seric 			p = strchr(buf, '\t');
7304577Seric 			if (p == NULL)
7314577Seric 				p = buf;
7324577Seric 			else
7334577Seric 				p++;
7344577Seric 			fixcrlf(p, TRUE);
73558151Seric 			message("214-%s", p);
7364577Seric 			noinfo = FALSE;
7374577Seric 		}
7384577Seric 	}
7394577Seric 
7404577Seric 	if (noinfo)
74158151Seric 		message("504 HELP topic unknown");
7424577Seric 	else
74358151Seric 		message("214 End of HELP info");
7444628Seric 	(void) fclose(hf);
7454577Seric }
7468544Seric /*
7479339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7489339Seric **
7499339Seric **	Parameters:
7509339Seric **		label -- a string used in error messages
7519339Seric **
7529339Seric **	Returns:
7539339Seric **		zero in the child
7549339Seric **		one in the parent
7559339Seric **
7569339Seric **	Side Effects:
7579339Seric **		none.
7589339Seric */
7598544Seric 
76055012Seric runinchild(label, e)
7619339Seric 	char *label;
76255012Seric 	register ENVELOPE *e;
7639339Seric {
7649339Seric 	int childpid;
7659339Seric 
76616158Seric 	if (!OneXact)
7679339Seric 	{
76816158Seric 		childpid = dofork();
76916158Seric 		if (childpid < 0)
77016158Seric 		{
77116158Seric 			syserr("%s: cannot fork", label);
77216158Seric 			return (1);
77316158Seric 		}
77416158Seric 		if (childpid > 0)
77516158Seric 		{
77616158Seric 			auto int st;
7779339Seric 
77816158Seric 			/* parent -- wait for child to complete */
77916158Seric 			st = waitfor(childpid);
78016158Seric 			if (st == -1)
78116158Seric 				syserr("%s: lost child", label);
7829339Seric 
78316158Seric 			/* if we exited on a QUIT command, complete the process */
78416158Seric 			if (st == (EX_QUIT << 8))
78516158Seric 				finis();
7869339Seric 
78716158Seric 			return (1);
78816158Seric 		}
78916158Seric 		else
79016158Seric 		{
79116158Seric 			/* child */
79216158Seric 			InChild = TRUE;
79325050Seric 			QuickAbort = FALSE;
79455012Seric 			clearenvelope(e, FALSE);
79516158Seric 		}
7969339Seric 	}
79715256Seric 
79816158Seric 	/* open alias database */
79955012Seric 	initaliases(AliasFile, FALSE, e);
80016158Seric 
80116158Seric 	return (0);
8029339Seric }
8039339Seric 
80456795Seric # endif /* SMTP */
805