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*58755Seric static char sccsid[] = "@(#)srvrsmtp.c	6.28 (Berkeley) 03/19/93 (with SMTP)";
1433731Sbostic #else
15*58755Seric static char sccsid[] = "@(#)srvrsmtp.c	6.28 (Berkeley) 03/19/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();
118*58755Seric 	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, " (");
230*58755Seric 			(void) strcat(hostbuf, anynet_ntoa(&RealHostAddr));
23158308Seric 			if (strcasecmp(p, RealHostName) != 0)
23211146Seric 			{
23358512Seric 				(void) strcat(hostbuf, "; ");
23458512Seric 				(void) strcat(hostbuf, RealHostName);
23511146Seric 			}
23658512Seric 			(void) strcat(hostbuf, ")");
23758512Seric 			sendinghost = newstr(hostbuf);
23858323Seric 
23958323Seric 			/* send ext. message -- old systems must ignore */
24058323Seric 			message("250-%s Hello %s, pleased to meet you",
24136230Skarels 				MyHostName, sendinghost);
24258323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
24358323Seric 				message("250-EXPN");
24458338Seric 			message("250-SIZE");
24558323Seric 			message("250 HELP");
24658082Seric 			gothello = TRUE;
2474976Seric 			break;
2484976Seric 
2494549Seric 		  case CMDMAIL:		/* mail -- designate sender */
25024943Seric 			SmtpPhase = "MAIL";
25124943Seric 
25211151Seric 			/* force a sending host even if no HELO given */
25358064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
25430448Seric 				sendinghost = RealHostName;
25511151Seric 
2569314Seric 			/* check for validity of this command */
25758082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25858082Seric 			{
25958151Seric 				message("503 Polite people say HELO first");
26058082Seric 				break;
26158082Seric 			}
26258109Seric 			if (gotmail)
2634558Seric 			{
26458151Seric 				message("503 Sender already specified");
2654558Seric 				break;
2664558Seric 			}
2679339Seric 			if (InChild)
2689339Seric 			{
26936230Skarels 				errno = 0;
27058151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
27158069Seric 				finis();
2729339Seric 			}
2739339Seric 
2749339Seric 			/* fork a subprocess to process this command */
27555012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2769339Seric 				break;
27758064Seric 			if (sendinghost != NULL)
27858064Seric 				define('s', sendinghost, e);
27958323Seric 			if (protocol == NULL)
28058323Seric 				protocol = "SMTP";
28158323Seric 			define('r', protocol, e);
28255012Seric 			initsys(e);
28357389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2849339Seric 
2859339Seric 			/* child -- go do the processing */
2864549Seric 			p = skipword(p, "from");
2874549Seric 			if (p == NULL)
2884549Seric 				break;
28957977Seric 			if (setjmp(TopFrame) > 0)
29058147Seric 			{
29158147Seric 				/* this failed -- undo work */
29258147Seric 				if (InChild)
29358147Seric 					finis();
29457977Seric 				break;
29558147Seric 			}
29657977Seric 			QuickAbort = TRUE;
29758333Seric 
29858333Seric 			/* must parse sender first */
29958333Seric 			delimptr = NULL;
30058704Seric 			setsender(p, e, &delimptr, FALSE);
30158333Seric 			p = delimptr;
30258333Seric 			if (p != NULL && *p != '\0')
30358333Seric 				*p++ = '\0';
30458333Seric 
30558333Seric 			/* now parse ESMTP arguments */
30658333Seric 			msize = 0;
30758333Seric 			for (; p != NULL && *p != '\0'; p++)
30858333Seric 			{
30958333Seric 				char *kp;
31058333Seric 				char *vp;
31158333Seric 
31258333Seric 				/* locate the beginning of the keyword */
31358333Seric 				while (isascii(*p) && isspace(*p))
31458333Seric 					p++;
31558333Seric 				if (*p == '\0')
31658333Seric 					break;
31758333Seric 				kp = p;
31858333Seric 
31958333Seric 				/* skip to the value portion */
32058333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
32158333Seric 					p++;
32258333Seric 				if (*p == '=')
32358333Seric 				{
32458333Seric 					*p++ = '\0';
32558333Seric 					vp = p;
32658333Seric 
32758333Seric 					/* skip to the end of the value */
32858333Seric 					while (*p != '\0' && *p != ' ' &&
32958333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
33058333Seric 					       *p != '=')
33158333Seric 						p++;
33258333Seric 				}
33358333Seric 
33458333Seric 				if (*p != '\0')
33558333Seric 					*p++ = '\0';
33658333Seric 
33758333Seric 				if (tTd(19, 1))
33858333Seric 					printf("MAIL: got arg %s=%s\n", kp,
33958333Seric 						vp == NULL ? "<null>" : vp);
34058333Seric 
34158333Seric 				if (strcasecmp(kp, "size") == 0)
34258333Seric 				{
34358333Seric 					if (kp == NULL)
34458333Seric 					{
34558333Seric 						usrerr("501 SIZE requires a value");
34658333Seric 						/* NOTREACHED */
34758333Seric 					}
34858333Seric 					msize = atol(vp);
34958333Seric 				}
35058333Seric 				else
35158333Seric 				{
35258333Seric 					usrerr("501 %s parameter unrecognized", kp);
35358333Seric 					/* NOTREACHED */
35458333Seric 				}
35558333Seric 			}
35658333Seric 
35758333Seric 			if (!enoughspace(msize))
35858333Seric 			{
35958333Seric 				message("452 Insufficient disk space; try again later");
36058333Seric 				break;
36158333Seric 			}
36258151Seric 			message("250 Sender ok");
36358147Seric 			gotmail = TRUE;
3644549Seric 			break;
3654549Seric 
3664976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
36724943Seric 			SmtpPhase = "RCPT";
36857389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
36912612Seric 			if (setjmp(TopFrame) > 0)
37014785Seric 			{
37155012Seric 				e->e_flags &= ~EF_FATALERRS;
37212612Seric 				break;
37314785Seric 			}
37412612Seric 			QuickAbort = TRUE;
37551951Seric 			LogUsrErrs = TRUE;
37658093Seric 
37758093Seric 			/* optimization -- if queueing, don't expand aliases */
37858734Seric 			if (e->e_sendmode == SM_QUEUE)
37958093Seric 				e->e_flags |= EF_VRFYONLY;
38058093Seric 
3814549Seric 			p = skipword(p, "to");
3824549Seric 			if (p == NULL)
3834549Seric 				break;
38458333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
38512612Seric 			if (a == NULL)
38612612Seric 				break;
38716886Seric 			a->q_flags |= QPRIMARY;
38855012Seric 			a = recipient(a, &e->e_sendqueue, e);
38912612Seric 			if (Errors != 0)
39012612Seric 				break;
39112612Seric 
39212612Seric 			/* no errors during parsing, but might be a duplicate */
39355012Seric 			e->e_to = p;
39412612Seric 			if (!bitset(QBADADDR, a->q_flags))
39558151Seric 				message("250 Recipient ok");
39612612Seric 			else
3974549Seric 			{
39812612Seric 				/* punt -- should keep message in ADDRESS.... */
39958151Seric 				message("550 Addressee unknown");
4004549Seric 			}
40155012Seric 			e->e_to = NULL;
4024549Seric 			break;
4034549Seric 
4044549Seric 		  case CMDDATA:		/* data -- text of mail */
40524943Seric 			SmtpPhase = "DATA";
40658109Seric 			if (!gotmail)
4074549Seric 			{
40858151Seric 				message("503 Need MAIL command");
4094976Seric 				break;
4104549Seric 			}
41155012Seric 			else if (e->e_nrcpts <= 0)
4124549Seric 			{
41358151Seric 				message("503 Need RCPT (recipient)");
4144976Seric 				break;
4154549Seric 			}
4164976Seric 
4174976Seric 			/* collect the text of the message */
41824943Seric 			SmtpPhase = "collect";
41957389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
42055012Seric 			collect(TRUE, e);
4214976Seric 			if (Errors != 0)
4224976Seric 				break;
4234976Seric 
4248238Seric 			/*
4258238Seric 			**  Arrange to send to everyone.
4268238Seric 			**	If sending to multiple people, mail back
4278238Seric 			**		errors rather than reporting directly.
4288238Seric 			**	In any case, don't mail back errors for
4298238Seric 			**		anything that has happened up to
4308238Seric 			**		now (the other end will do this).
43110197Seric 			**	Truncate our transcript -- the mail has gotten
43210197Seric 			**		to us successfully, and if we have
43310197Seric 			**		to mail this back, it will be easier
43410197Seric 			**		on the reader.
4358238Seric 			**	Then send to everyone.
4368238Seric 			**	Finally give a reply code.  If an error has
4378238Seric 			**		already been given, don't mail a
4388238Seric 			**		message back.
4399339Seric 			**	We goose error returns by clearing error bit.
4408238Seric 			*/
4418238Seric 
44224943Seric 			SmtpPhase = "delivery";
44355012Seric 			if (e->e_nrcpts != 1)
4449378Seric 			{
4459378Seric 				HoldErrs = TRUE;
44658734Seric 				e->e_errormode = EM_MAIL;
4479378Seric 			}
44855012Seric 			e->e_flags &= ~EF_FATALERRS;
44955012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
45058714Seric 			id = e->e_id;
4514976Seric 
4524976Seric 			/* send to all recipients */
45355012Seric 			sendall(e, SM_DEFAULT);
45455012Seric 			e->e_to = NULL;
4554976Seric 
45623516Seric 			/* save statistics */
45755012Seric 			markstats(e, (ADDRESS *) NULL);
45823516Seric 
4598238Seric 			/* issue success if appropriate and reset */
4608238Seric 			if (Errors == 0 || HoldErrs)
46158714Seric 				message("250 %s OK", id);
4628238Seric 			else
46355012Seric 				e->e_flags &= ~EF_FATALERRS;
4649339Seric 
4659339Seric 			/* if in a child, pop back to our parent */
4669339Seric 			if (InChild)
4679339Seric 				finis();
46824943Seric 
46924943Seric 			/* clean up a bit */
47058109Seric 			gotmail = FALSE;
47155012Seric 			dropenvelope(e);
47258179Seric 			CurEnv = e = newenvelope(e, CurEnv);
47355012Seric 			e->e_flags = BlankEnvelope.e_flags;
4744549Seric 			break;
4754549Seric 
4764549Seric 		  case CMDRSET:		/* rset -- reset state */
47758151Seric 			message("250 Reset state");
4789339Seric 			if (InChild)
4799339Seric 				finis();
48058109Seric 
48158109Seric 			/* clean up a bit */
48258109Seric 			gotmail = FALSE;
48358109Seric 			dropenvelope(e);
48458179Seric 			CurEnv = e = newenvelope(e, CurEnv);
4859339Seric 			break;
4864549Seric 
4874549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
48858092Seric 		  case CMDEXPN:		/* expn -- expand address */
48958092Seric 			vrfy = c->cmdcode == CMDVRFY;
49058092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
49158092Seric 						PrivacyFlags))
49258082Seric 			{
49358412Seric 				if (vrfy)
49458412Seric 					message("252 Who's to say?");
49558412Seric 				else
49658412Seric 					message("502 That's none of your business");
49758082Seric 				break;
49858082Seric 			}
49958082Seric 			else if (!gothello &&
50058092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
50158092Seric 						PrivacyFlags))
50258082Seric 			{
50358151Seric 				message("503 I demand that you introduce yourself first");
50458082Seric 				break;
50558082Seric 			}
50658092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5079339Seric 				break;
50825050Seric 			setproctitle("%s: %s", CurHostName, inp);
50955173Seric #ifdef LOG
51058020Seric 			if (LogLevel > 5)
51155173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
51255173Seric #endif
5135003Seric 			vrfyqueue = NULL;
5147762Seric 			QuickAbort = TRUE;
51558092Seric 			if (vrfy)
51658092Seric 				e->e_flags |= EF_VRFYONLY;
51758082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5187762Seric 			if (Errors != 0)
5199339Seric 			{
5209339Seric 				if (InChild)
5219339Seric 					finis();
5227762Seric 				break;
5239339Seric 			}
5245003Seric 			while (vrfyqueue != NULL)
5255003Seric 			{
5265003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5275003Seric 				char *code;
5285003Seric 
5297685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5305003Seric 					a = a->q_next;
5315003Seric 
5327685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
53358151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5345003Seric 				else if (a == NULL)
53558151Seric 					message("554 Self destructive alias loop");
5365003Seric 				vrfyqueue = a;
5375003Seric 			}
5389339Seric 			if (InChild)
5399339Seric 				finis();
5404549Seric 			break;
5414549Seric 
5424549Seric 		  case CMDHELP:		/* help -- give user info */
5434577Seric 			help(p);
5444549Seric 			break;
5454549Seric 
5464549Seric 		  case CMDNOOP:		/* noop -- do nothing */
54758151Seric 			message("200 OK");
5484549Seric 			break;
5494549Seric 
5504549Seric 		  case CMDQUIT:		/* quit -- leave mail */
55158151Seric 			message("221 %s closing connection", MyHostName);
5529339Seric 			if (InChild)
5539339Seric 				ExitStat = EX_QUIT;
5544549Seric 			finis();
5554549Seric 
5568544Seric 		  case CMDVERB:		/* set verbose mode */
5578544Seric 			Verbose = TRUE;
55858734Seric 			e->e_sendmode = SM_DELIVER;
55958151Seric 			message("200 Verbose mode");
5608544Seric 			break;
5618544Seric 
5629314Seric 		  case CMDONEX:		/* doing one transaction only */
5639378Seric 			OneXact = TRUE;
56458151Seric 			message("200 Only one transaction");
5659314Seric 			break;
5669314Seric 
56736230Skarels # ifdef SMTPDEBUG
5689339Seric 		  case CMDDBGQSHOW:	/* show queues */
5696907Seric 			printf("Send Queue=");
57055012Seric 			printaddr(e->e_sendqueue, TRUE);
5715003Seric 			break;
5727275Seric 
5737275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5747676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5757676Seric 			tTflag(p);
57658151Seric 			message("200 Debug set");
5777275Seric 			break;
5787275Seric 
57936230Skarels # else /* not SMTPDEBUG */
58024945Seric 
58136230Skarels 		  case CMDDBGQSHOW:	/* show queues */
58236230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
58336233Skarels # ifdef LOG
58458308Seric 			if (LogLevel > 0)
58536230Skarels 				syslog(LOG_NOTICE,
58658020Seric 				    "\"%s\" command from %s (%s)",
58736230Skarels 				    c->cmdname, RealHostName,
588*58755Seric 				    anynet_ntoa(&RealHostAddr));
58936233Skarels # endif
59036230Skarels 			/* FALL THROUGH */
59136230Skarels # endif /* SMTPDEBUG */
59236230Skarels 
5934549Seric 		  case CMDERROR:	/* unknown command */
59458151Seric 			message("500 Command unrecognized");
5954549Seric 			break;
5964549Seric 
5974549Seric 		  default:
59836230Skarels 			errno = 0;
59958151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6004549Seric 			break;
6014549Seric 		}
6024549Seric 	}
6034549Seric }
6044549Seric /*
6054549Seric **  SKIPWORD -- skip a fixed word.
6064549Seric **
6074549Seric **	Parameters:
6084549Seric **		p -- place to start looking.
6094549Seric **		w -- word to skip.
6104549Seric **
6114549Seric **	Returns:
6124549Seric **		p following w.
6134549Seric **		NULL on error.
6144549Seric **
6154549Seric **	Side Effects:
6164549Seric **		clobbers the p data area.
6174549Seric */
6184549Seric 
6194549Seric static char *
6204549Seric skipword(p, w)
6214549Seric 	register char *p;
6224549Seric 	char *w;
6234549Seric {
6244549Seric 	register char *q;
6254549Seric 
6264549Seric 	/* find beginning of word */
62758050Seric 	while (isascii(*p) && isspace(*p))
6284549Seric 		p++;
6294549Seric 	q = p;
6304549Seric 
6314549Seric 	/* find end of word */
63258050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6334549Seric 		p++;
63458050Seric 	while (isascii(*p) && isspace(*p))
6354549Seric 		*p++ = '\0';
6364549Seric 	if (*p != ':')
6374549Seric 	{
6384549Seric 	  syntax:
63958151Seric 		message("501 Syntax error");
6404549Seric 		Errors++;
6414549Seric 		return (NULL);
6424549Seric 	}
6434549Seric 	*p++ = '\0';
64458050Seric 	while (isascii(*p) && isspace(*p))
6454549Seric 		p++;
6464549Seric 
6474549Seric 	/* see if the input word matches desired word */
64833725Sbostic 	if (strcasecmp(q, w))
6494549Seric 		goto syntax;
6504549Seric 
6514549Seric 	return (p);
6524549Seric }
6534577Seric /*
65458151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
65558151Seric **
65658151Seric **	Parameters:
65758151Seric **		a -- the address to print
65858151Seric **		last -- set if this is the last one.
65958151Seric **
66058151Seric **	Returns:
66158151Seric **		none.
66258151Seric **
66358151Seric **	Side Effects:
66458151Seric **		Prints the appropriate 250 codes.
66558151Seric */
66658151Seric 
66758151Seric printvrfyaddr(a, last)
66858151Seric 	register ADDRESS *a;
66958151Seric 	bool last;
67058151Seric {
67158151Seric 	char fmtbuf[20];
67258151Seric 
67358151Seric 	strcpy(fmtbuf, "250");
67458151Seric 	fmtbuf[3] = last ? ' ' : '-';
67558151Seric 
67658151Seric 	if (strchr(a->q_paddr, '<') != NULL)
67758151Seric 		strcpy(&fmtbuf[4], "%s");
67858151Seric 	else if (a->q_fullname == NULL)
67958151Seric 		strcpy(&fmtbuf[4], "<%s>");
68058151Seric 	else
68158151Seric 	{
68258151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
68358151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
68458151Seric 		return;
68558151Seric 	}
68658151Seric 	message(fmtbuf, a->q_paddr);
68758151Seric }
68858151Seric /*
6894577Seric **  HELP -- implement the HELP command.
6904577Seric **
6914577Seric **	Parameters:
6924577Seric **		topic -- the topic we want help for.
6934577Seric **
6944577Seric **	Returns:
6954577Seric **		none.
6964577Seric **
6974577Seric **	Side Effects:
6984577Seric **		outputs the help file to message output.
6994577Seric */
7004577Seric 
7014577Seric help(topic)
7024577Seric 	char *topic;
7034577Seric {
7044577Seric 	register FILE *hf;
7054577Seric 	int len;
7064577Seric 	char buf[MAXLINE];
7074577Seric 	bool noinfo;
7084577Seric 
7098269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7104577Seric 	{
7114577Seric 		/* no help */
71211931Seric 		errno = 0;
71358151Seric 		message("502 HELP not implemented");
7144577Seric 		return;
7154577Seric 	}
7164577Seric 
71749669Seric 	if (topic == NULL || *topic == '\0')
71849669Seric 		topic = "smtp";
71949669Seric 	else
72049669Seric 		makelower(topic);
72149669Seric 
7224577Seric 	len = strlen(topic);
7234577Seric 	noinfo = TRUE;
7244577Seric 
7254577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7264577Seric 	{
7274577Seric 		if (strncmp(buf, topic, len) == 0)
7284577Seric 		{
7294577Seric 			register char *p;
7304577Seric 
73156795Seric 			p = strchr(buf, '\t');
7324577Seric 			if (p == NULL)
7334577Seric 				p = buf;
7344577Seric 			else
7354577Seric 				p++;
7364577Seric 			fixcrlf(p, TRUE);
73758151Seric 			message("214-%s", p);
7384577Seric 			noinfo = FALSE;
7394577Seric 		}
7404577Seric 	}
7414577Seric 
7424577Seric 	if (noinfo)
74358151Seric 		message("504 HELP topic unknown");
7444577Seric 	else
74558151Seric 		message("214 End of HELP info");
7464628Seric 	(void) fclose(hf);
7474577Seric }
7488544Seric /*
7499339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7509339Seric **
7519339Seric **	Parameters:
7529339Seric **		label -- a string used in error messages
7539339Seric **
7549339Seric **	Returns:
7559339Seric **		zero in the child
7569339Seric **		one in the parent
7579339Seric **
7589339Seric **	Side Effects:
7599339Seric **		none.
7609339Seric */
7618544Seric 
76255012Seric runinchild(label, e)
7639339Seric 	char *label;
76455012Seric 	register ENVELOPE *e;
7659339Seric {
7669339Seric 	int childpid;
7679339Seric 
76816158Seric 	if (!OneXact)
7699339Seric 	{
77016158Seric 		childpid = dofork();
77116158Seric 		if (childpid < 0)
77216158Seric 		{
77316158Seric 			syserr("%s: cannot fork", label);
77416158Seric 			return (1);
77516158Seric 		}
77616158Seric 		if (childpid > 0)
77716158Seric 		{
77816158Seric 			auto int st;
7799339Seric 
78016158Seric 			/* parent -- wait for child to complete */
78116158Seric 			st = waitfor(childpid);
78216158Seric 			if (st == -1)
78316158Seric 				syserr("%s: lost child", label);
7849339Seric 
78516158Seric 			/* if we exited on a QUIT command, complete the process */
78616158Seric 			if (st == (EX_QUIT << 8))
78716158Seric 				finis();
7889339Seric 
78916158Seric 			return (1);
79016158Seric 		}
79116158Seric 		else
79216158Seric 		{
79316158Seric 			/* child */
79416158Seric 			InChild = TRUE;
79525050Seric 			QuickAbort = FALSE;
79655012Seric 			clearenvelope(e, FALSE);
79716158Seric 		}
7989339Seric 	}
79915256Seric 
80016158Seric 	/* open alias database */
80155012Seric 	initaliases(AliasFile, FALSE, e);
80216158Seric 
80316158Seric 	return (0);
8049339Seric }
8059339Seric 
80656795Seric # endif /* SMTP */
807