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*58338Seric static char sccsid[] = "@(#)srvrsmtp.c	6.22 (Berkeley) 03/01/93 (with SMTP)";
1433731Sbostic #else
15*58338Seric static char sccsid[] = "@(#)srvrsmtp.c	6.22 (Berkeley) 03/01/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];
1117124Seric 	extern char Version[];
11211151Seric 	extern char *macvalue();
11312612Seric 	extern ADDRESS *recipient();
11424943Seric 	extern ENVELOPE BlankEnvelope;
11524943Seric 	extern ENVELOPE *newenvelope();
1164549Seric 
1177363Seric 	if (OutChannel != stdout)
1187363Seric 	{
1197363Seric 		/* arrange for debugging output to go to remote host */
1207363Seric 		(void) close(1);
1217363Seric 		(void) dup(fileno(OutChannel));
1227363Seric 	}
12355012Seric 	settime(e);
12457642Seric 	CurHostName = RealHostName;
12557642Seric 	setproctitle("srvrsmtp %s", CurHostName);
12658050Seric 	expand("\201e", inp, &inp[sizeof inp], e);
12758151Seric 	message("220 %s", inp);
12824943Seric 	SmtpPhase = "startup";
12930448Seric 	sendinghost = NULL;
13058330Seric 	protocol = NULL;
13158082Seric 	gothello = FALSE;
13258330Seric 	gotmail = FALSE;
1334549Seric 	for (;;)
1344549Seric 	{
13512612Seric 		/* arrange for backout */
13612612Seric 		if (setjmp(TopFrame) > 0 && InChild)
13712612Seric 			finis();
13812612Seric 		QuickAbort = FALSE;
13912612Seric 		HoldErrs = FALSE;
14051951Seric 		LogUsrErrs = FALSE;
14158092Seric 		e->e_flags &= ~EF_VRFYONLY;
14212612Seric 
1437356Seric 		/* setup for the read */
14455012Seric 		e->e_to = NULL;
1454577Seric 		Errors = 0;
1467275Seric 		(void) fflush(stdout);
1477356Seric 
1487356Seric 		/* read the input line */
14958109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1507356Seric 
1517685Seric 		/* handle errors */
1527356Seric 		if (p == NULL)
1537356Seric 		{
1544549Seric 			/* end of file, just die */
15558151Seric 			message("421 %s Lost input channel from %s",
15625050Seric 				MyHostName, CurHostName);
15755464Seric #ifdef LOG
15858020Seric 			if (LogLevel > 1)
15955464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16055464Seric 					CurHostName);
16155464Seric #endif
16258069Seric 			if (InChild)
16358069Seric 				ExitStat = EX_QUIT;
1644549Seric 			finis();
1654549Seric 		}
1664549Seric 
1674549Seric 		/* clean up end of line */
1684558Seric 		fixcrlf(inp, TRUE);
1694549Seric 
1704713Seric 		/* echo command to transcript */
17155012Seric 		if (e->e_xfp != NULL)
17255012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1734713Seric 
1744549Seric 		/* break off command */
17558050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1764549Seric 			continue;
17757232Seric 		cmd = cmdbuf;
17858050Seric 		while (*p != '\0' &&
17958050Seric 		       !(isascii(*p) && isspace(*p)) &&
18058050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
18124981Seric 			*cmd++ = *p++;
18224981Seric 		*cmd = '\0';
1834549Seric 
18425691Seric 		/* throw away leading whitespace */
18558050Seric 		while (isascii(*p) && isspace(*p))
18625691Seric 			p++;
18725691Seric 
1884549Seric 		/* decode command */
1894549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1904549Seric 		{
19133725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1924549Seric 				break;
1934549Seric 		}
1944549Seric 
19551954Seric 		/* reset errors */
19651954Seric 		errno = 0;
19751954Seric 
1984549Seric 		/* process command */
1994549Seric 		switch (c->cmdcode)
2004549Seric 		{
2014976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20258323Seric 		  case CMDEHLO:		/* extended hello */
20358323Seric 			if (c->cmdcode == CMDEHLO)
20458323Seric 			{
20558323Seric 				protocol = "ESMTP";
20658323Seric 				SmtpPhase = "EHLO";
20758323Seric 			}
20858323Seric 			else
20958323Seric 			{
21058323Seric 				protocol = "SMTP";
21158323Seric 				SmtpPhase = "HELO";
21258323Seric 			}
21325050Seric 			setproctitle("%s: %s", CurHostName, inp);
21458109Seric 			if (strcasecmp(p, MyHostName) == 0)
21514877Seric 			{
21636230Skarels 				/*
21758109Seric 				**  Didn't know about alias or MX,
21858109Seric 				**  or connected to an echo server
21958109Seric 				*/
22058109Seric 
22158151Seric 				message("553 %s config error: mail loops back to myself",
22247570Seric 					MyHostName);
22314877Seric 				break;
22414877Seric 			}
22558308Seric 			if (strcasecmp(p, RealHostName) != 0)
22611146Seric 			{
22724981Seric 				char hostbuf[MAXNAME];
22811146Seric 
22924981Seric 				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
23030448Seric 				sendinghost = newstr(hostbuf);
23111146Seric 			}
23211146Seric 			else
23330448Seric 				sendinghost = newstr(p);
23458323Seric 
23558323Seric 			/* send ext. message -- old systems must ignore */
23658323Seric 			message("250-%s Hello %s, pleased to meet you",
23736230Skarels 				MyHostName, sendinghost);
23858323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
23958323Seric 				message("250-EXPN");
240*58338Seric 			message("250-SIZE");
24158323Seric 			message("250 HELP");
24258082Seric 			gothello = TRUE;
2434976Seric 			break;
2444976Seric 
2454549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24624943Seric 			SmtpPhase = "MAIL";
24724943Seric 
24811151Seric 			/* force a sending host even if no HELO given */
24958064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
25030448Seric 				sendinghost = RealHostName;
25111151Seric 
2529314Seric 			/* check for validity of this command */
25358082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25458082Seric 			{
25558151Seric 				message("503 Polite people say HELO first");
25658082Seric 				break;
25758082Seric 			}
25858109Seric 			if (gotmail)
2594558Seric 			{
26058151Seric 				message("503 Sender already specified");
2614558Seric 				break;
2624558Seric 			}
2639339Seric 			if (InChild)
2649339Seric 			{
26536230Skarels 				errno = 0;
26658151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
26758069Seric 				finis();
2689339Seric 			}
2699339Seric 
2709339Seric 			/* fork a subprocess to process this command */
27155012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2729339Seric 				break;
27358064Seric 			if (sendinghost != NULL)
27458064Seric 				define('s', sendinghost, e);
27558323Seric 			if (protocol == NULL)
27658323Seric 				protocol = "SMTP";
27758323Seric 			define('r', protocol, e);
27855012Seric 			initsys(e);
27957389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2809339Seric 
2819339Seric 			/* child -- go do the processing */
2824549Seric 			p = skipword(p, "from");
2834549Seric 			if (p == NULL)
2844549Seric 				break;
28557977Seric 			if (setjmp(TopFrame) > 0)
28658147Seric 			{
28758147Seric 				/* this failed -- undo work */
28858147Seric 				if (InChild)
28958147Seric 					finis();
29057977Seric 				break;
29158147Seric 			}
29257977Seric 			QuickAbort = TRUE;
29358333Seric 
29458333Seric 			/* must parse sender first */
29558333Seric 			delimptr = NULL;
29658333Seric 			setsender(p, e, &delimptr);
29758333Seric 			p = delimptr;
29858333Seric 			if (p != NULL && *p != '\0')
29958333Seric 				*p++ = '\0';
30058333Seric 
30158333Seric 			/* now parse ESMTP arguments */
30258333Seric 			msize = 0;
30358333Seric 			for (; p != NULL && *p != '\0'; p++)
30458333Seric 			{
30558333Seric 				char *kp;
30658333Seric 				char *vp;
30758333Seric 
30858333Seric 				/* locate the beginning of the keyword */
30958333Seric 				while (isascii(*p) && isspace(*p))
31058333Seric 					p++;
31158333Seric 				if (*p == '\0')
31258333Seric 					break;
31358333Seric 				kp = p;
31458333Seric 
31558333Seric 				/* skip to the value portion */
31658333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
31758333Seric 					p++;
31858333Seric 				if (*p == '=')
31958333Seric 				{
32058333Seric 					*p++ = '\0';
32158333Seric 					vp = p;
32258333Seric 
32358333Seric 					/* skip to the end of the value */
32458333Seric 					while (*p != '\0' && *p != ' ' &&
32558333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
32658333Seric 					       *p != '=')
32758333Seric 						p++;
32858333Seric 				}
32958333Seric 
33058333Seric 				if (*p != '\0')
33158333Seric 					*p++ = '\0';
33258333Seric 
33358333Seric 				if (tTd(19, 1))
33458333Seric 					printf("MAIL: got arg %s=%s\n", kp,
33558333Seric 						vp == NULL ? "<null>" : vp);
33658333Seric 
33758333Seric 				if (strcasecmp(kp, "size") == 0)
33858333Seric 				{
33958333Seric 					if (kp == NULL)
34058333Seric 					{
34158333Seric 						usrerr("501 SIZE requires a value");
34258333Seric 						/* NOTREACHED */
34358333Seric 					}
34458333Seric 					msize = atol(vp);
34558333Seric 				}
34658333Seric 				else
34758333Seric 				{
34858333Seric 					usrerr("501 %s parameter unrecognized", kp);
34958333Seric 					/* NOTREACHED */
35058333Seric 				}
35158333Seric 			}
35258333Seric 
35358333Seric 			if (!enoughspace(msize))
35458333Seric 			{
35558333Seric 				message("452 Insufficient disk space; try again later");
35658333Seric 				break;
35758333Seric 			}
35858151Seric 			message("250 Sender ok");
35958147Seric 			gotmail = TRUE;
3604549Seric 			break;
3614549Seric 
3624976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
36324943Seric 			SmtpPhase = "RCPT";
36457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
36512612Seric 			if (setjmp(TopFrame) > 0)
36614785Seric 			{
36755012Seric 				e->e_flags &= ~EF_FATALERRS;
36812612Seric 				break;
36914785Seric 			}
37012612Seric 			QuickAbort = TRUE;
37151951Seric 			LogUsrErrs = TRUE;
37258093Seric 
37358093Seric 			/* optimization -- if queueing, don't expand aliases */
37458093Seric 			if (SendMode == SM_QUEUE)
37558093Seric 				e->e_flags |= EF_VRFYONLY;
37658093Seric 
3774549Seric 			p = skipword(p, "to");
3784549Seric 			if (p == NULL)
3794549Seric 				break;
38058333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
38112612Seric 			if (a == NULL)
38212612Seric 				break;
38316886Seric 			a->q_flags |= QPRIMARY;
38455012Seric 			a = recipient(a, &e->e_sendqueue, e);
38512612Seric 			if (Errors != 0)
38612612Seric 				break;
38712612Seric 
38812612Seric 			/* no errors during parsing, but might be a duplicate */
38955012Seric 			e->e_to = p;
39012612Seric 			if (!bitset(QBADADDR, a->q_flags))
39158151Seric 				message("250 Recipient ok");
39212612Seric 			else
3934549Seric 			{
39412612Seric 				/* punt -- should keep message in ADDRESS.... */
39558151Seric 				message("550 Addressee unknown");
3964549Seric 			}
39755012Seric 			e->e_to = NULL;
3984549Seric 			break;
3994549Seric 
4004549Seric 		  case CMDDATA:		/* data -- text of mail */
40124943Seric 			SmtpPhase = "DATA";
40258109Seric 			if (!gotmail)
4034549Seric 			{
40458151Seric 				message("503 Need MAIL command");
4054976Seric 				break;
4064549Seric 			}
40755012Seric 			else if (e->e_nrcpts <= 0)
4084549Seric 			{
40958151Seric 				message("503 Need RCPT (recipient)");
4104976Seric 				break;
4114549Seric 			}
4124976Seric 
4134976Seric 			/* collect the text of the message */
41424943Seric 			SmtpPhase = "collect";
41557389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
41655012Seric 			collect(TRUE, e);
4174976Seric 			if (Errors != 0)
4184976Seric 				break;
4194976Seric 
4208238Seric 			/*
4218238Seric 			**  Arrange to send to everyone.
4228238Seric 			**	If sending to multiple people, mail back
4238238Seric 			**		errors rather than reporting directly.
4248238Seric 			**	In any case, don't mail back errors for
4258238Seric 			**		anything that has happened up to
4268238Seric 			**		now (the other end will do this).
42710197Seric 			**	Truncate our transcript -- the mail has gotten
42810197Seric 			**		to us successfully, and if we have
42910197Seric 			**		to mail this back, it will be easier
43010197Seric 			**		on the reader.
4318238Seric 			**	Then send to everyone.
4328238Seric 			**	Finally give a reply code.  If an error has
4338238Seric 			**		already been given, don't mail a
4348238Seric 			**		message back.
4359339Seric 			**	We goose error returns by clearing error bit.
4368238Seric 			*/
4378238Seric 
43824943Seric 			SmtpPhase = "delivery";
43955012Seric 			if (e->e_nrcpts != 1)
4409378Seric 			{
4419378Seric 				HoldErrs = TRUE;
44216886Seric 				ErrorMode = EM_MAIL;
4439378Seric 			}
44455012Seric 			e->e_flags &= ~EF_FATALERRS;
44555012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
4464976Seric 
4474976Seric 			/* send to all recipients */
44855012Seric 			sendall(e, SM_DEFAULT);
44955012Seric 			e->e_to = NULL;
4504976Seric 
45123516Seric 			/* save statistics */
45255012Seric 			markstats(e, (ADDRESS *) NULL);
45323516Seric 
4548238Seric 			/* issue success if appropriate and reset */
4558238Seric 			if (Errors == 0 || HoldErrs)
45658151Seric 				message("250 Ok");
4578238Seric 			else
45855012Seric 				e->e_flags &= ~EF_FATALERRS;
4599339Seric 
4609339Seric 			/* if in a child, pop back to our parent */
4619339Seric 			if (InChild)
4629339Seric 				finis();
46324943Seric 
46424943Seric 			/* clean up a bit */
46558109Seric 			gotmail = FALSE;
46655012Seric 			dropenvelope(e);
46758179Seric 			CurEnv = e = newenvelope(e, CurEnv);
46855012Seric 			e->e_flags = BlankEnvelope.e_flags;
4694549Seric 			break;
4704549Seric 
4714549Seric 		  case CMDRSET:		/* rset -- reset state */
47258151Seric 			message("250 Reset state");
4739339Seric 			if (InChild)
4749339Seric 				finis();
47558109Seric 
47658109Seric 			/* clean up a bit */
47758109Seric 			gotmail = FALSE;
47858109Seric 			dropenvelope(e);
47958179Seric 			CurEnv = e = newenvelope(e, CurEnv);
4809339Seric 			break;
4814549Seric 
4824549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
48358092Seric 		  case CMDEXPN:		/* expn -- expand address */
48458092Seric 			vrfy = c->cmdcode == CMDVRFY;
48558092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
48658092Seric 						PrivacyFlags))
48758082Seric 			{
48858151Seric 				message("502 That's none of your business");
48958082Seric 				break;
49058082Seric 			}
49158082Seric 			else if (!gothello &&
49258092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
49358092Seric 						PrivacyFlags))
49458082Seric 			{
49558151Seric 				message("503 I demand that you introduce yourself first");
49658082Seric 				break;
49758082Seric 			}
49858092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
4999339Seric 				break;
50025050Seric 			setproctitle("%s: %s", CurHostName, inp);
50155173Seric #ifdef LOG
50258020Seric 			if (LogLevel > 5)
50355173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
50455173Seric #endif
5055003Seric 			vrfyqueue = NULL;
5067762Seric 			QuickAbort = TRUE;
50758092Seric 			if (vrfy)
50858092Seric 				e->e_flags |= EF_VRFYONLY;
50958082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5107762Seric 			if (Errors != 0)
5119339Seric 			{
5129339Seric 				if (InChild)
5139339Seric 					finis();
5147762Seric 				break;
5159339Seric 			}
5165003Seric 			while (vrfyqueue != NULL)
5175003Seric 			{
5185003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5195003Seric 				char *code;
5205003Seric 
5217685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5225003Seric 					a = a->q_next;
5235003Seric 
5247685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
52558151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5265003Seric 				else if (a == NULL)
52758151Seric 					message("554 Self destructive alias loop");
5285003Seric 				vrfyqueue = a;
5295003Seric 			}
5309339Seric 			if (InChild)
5319339Seric 				finis();
5324549Seric 			break;
5334549Seric 
5344549Seric 		  case CMDHELP:		/* help -- give user info */
5354577Seric 			help(p);
5364549Seric 			break;
5374549Seric 
5384549Seric 		  case CMDNOOP:		/* noop -- do nothing */
53958151Seric 			message("200 OK");
5404549Seric 			break;
5414549Seric 
5424549Seric 		  case CMDQUIT:		/* quit -- leave mail */
54358151Seric 			message("221 %s closing connection", MyHostName);
5449339Seric 			if (InChild)
5459339Seric 				ExitStat = EX_QUIT;
5464549Seric 			finis();
5474549Seric 
5488544Seric 		  case CMDVERB:		/* set verbose mode */
5498544Seric 			Verbose = TRUE;
55025025Seric 			SendMode = SM_DELIVER;
55158151Seric 			message("200 Verbose mode");
5528544Seric 			break;
5538544Seric 
5549314Seric 		  case CMDONEX:		/* doing one transaction only */
5559378Seric 			OneXact = TRUE;
55658151Seric 			message("200 Only one transaction");
5579314Seric 			break;
5589314Seric 
55936230Skarels # ifdef SMTPDEBUG
5609339Seric 		  case CMDDBGQSHOW:	/* show queues */
5616907Seric 			printf("Send Queue=");
56255012Seric 			printaddr(e->e_sendqueue, TRUE);
5635003Seric 			break;
5647275Seric 
5657275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5667676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5677676Seric 			tTflag(p);
56858151Seric 			message("200 Debug set");
5697275Seric 			break;
5707275Seric 
57136230Skarels # else /* not SMTPDEBUG */
57224945Seric 
57336230Skarels 		  case CMDDBGQSHOW:	/* show queues */
57436230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
57536233Skarels # ifdef LOG
57658308Seric 			if (LogLevel > 0)
57736230Skarels 				syslog(LOG_NOTICE,
57858020Seric 				    "\"%s\" command from %s (%s)",
57936230Skarels 				    c->cmdname, RealHostName,
58036230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
58136233Skarels # endif
58236230Skarels 			/* FALL THROUGH */
58336230Skarels # endif /* SMTPDEBUG */
58436230Skarels 
5854549Seric 		  case CMDERROR:	/* unknown command */
58658151Seric 			message("500 Command unrecognized");
5874549Seric 			break;
5884549Seric 
5894549Seric 		  default:
59036230Skarels 			errno = 0;
59158151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
5924549Seric 			break;
5934549Seric 		}
5944549Seric 	}
5954549Seric }
5964549Seric /*
5974549Seric **  SKIPWORD -- skip a fixed word.
5984549Seric **
5994549Seric **	Parameters:
6004549Seric **		p -- place to start looking.
6014549Seric **		w -- word to skip.
6024549Seric **
6034549Seric **	Returns:
6044549Seric **		p following w.
6054549Seric **		NULL on error.
6064549Seric **
6074549Seric **	Side Effects:
6084549Seric **		clobbers the p data area.
6094549Seric */
6104549Seric 
6114549Seric static char *
6124549Seric skipword(p, w)
6134549Seric 	register char *p;
6144549Seric 	char *w;
6154549Seric {
6164549Seric 	register char *q;
6174549Seric 
6184549Seric 	/* find beginning of word */
61958050Seric 	while (isascii(*p) && isspace(*p))
6204549Seric 		p++;
6214549Seric 	q = p;
6224549Seric 
6234549Seric 	/* find end of word */
62458050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6254549Seric 		p++;
62658050Seric 	while (isascii(*p) && isspace(*p))
6274549Seric 		*p++ = '\0';
6284549Seric 	if (*p != ':')
6294549Seric 	{
6304549Seric 	  syntax:
63158151Seric 		message("501 Syntax error");
6324549Seric 		Errors++;
6334549Seric 		return (NULL);
6344549Seric 	}
6354549Seric 	*p++ = '\0';
63658050Seric 	while (isascii(*p) && isspace(*p))
6374549Seric 		p++;
6384549Seric 
6394549Seric 	/* see if the input word matches desired word */
64033725Sbostic 	if (strcasecmp(q, w))
6414549Seric 		goto syntax;
6424549Seric 
6434549Seric 	return (p);
6444549Seric }
6454577Seric /*
64658151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
64758151Seric **
64858151Seric **	Parameters:
64958151Seric **		a -- the address to print
65058151Seric **		last -- set if this is the last one.
65158151Seric **
65258151Seric **	Returns:
65358151Seric **		none.
65458151Seric **
65558151Seric **	Side Effects:
65658151Seric **		Prints the appropriate 250 codes.
65758151Seric */
65858151Seric 
65958151Seric printvrfyaddr(a, last)
66058151Seric 	register ADDRESS *a;
66158151Seric 	bool last;
66258151Seric {
66358151Seric 	char fmtbuf[20];
66458151Seric 
66558151Seric 	strcpy(fmtbuf, "250");
66658151Seric 	fmtbuf[3] = last ? ' ' : '-';
66758151Seric 
66858151Seric 	if (strchr(a->q_paddr, '<') != NULL)
66958151Seric 		strcpy(&fmtbuf[4], "%s");
67058151Seric 	else if (a->q_fullname == NULL)
67158151Seric 		strcpy(&fmtbuf[4], "<%s>");
67258151Seric 	else
67358151Seric 	{
67458151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
67558151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
67658151Seric 		return;
67758151Seric 	}
67858151Seric 	message(fmtbuf, a->q_paddr);
67958151Seric }
68058151Seric /*
6814577Seric **  HELP -- implement the HELP command.
6824577Seric **
6834577Seric **	Parameters:
6844577Seric **		topic -- the topic we want help for.
6854577Seric **
6864577Seric **	Returns:
6874577Seric **		none.
6884577Seric **
6894577Seric **	Side Effects:
6904577Seric **		outputs the help file to message output.
6914577Seric */
6924577Seric 
6934577Seric help(topic)
6944577Seric 	char *topic;
6954577Seric {
6964577Seric 	register FILE *hf;
6974577Seric 	int len;
6984577Seric 	char buf[MAXLINE];
6994577Seric 	bool noinfo;
7004577Seric 
7018269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7024577Seric 	{
7034577Seric 		/* no help */
70411931Seric 		errno = 0;
70558151Seric 		message("502 HELP not implemented");
7064577Seric 		return;
7074577Seric 	}
7084577Seric 
70949669Seric 	if (topic == NULL || *topic == '\0')
71049669Seric 		topic = "smtp";
71149669Seric 	else
71249669Seric 		makelower(topic);
71349669Seric 
7144577Seric 	len = strlen(topic);
7154577Seric 	noinfo = TRUE;
7164577Seric 
7174577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7184577Seric 	{
7194577Seric 		if (strncmp(buf, topic, len) == 0)
7204577Seric 		{
7214577Seric 			register char *p;
7224577Seric 
72356795Seric 			p = strchr(buf, '\t');
7244577Seric 			if (p == NULL)
7254577Seric 				p = buf;
7264577Seric 			else
7274577Seric 				p++;
7284577Seric 			fixcrlf(p, TRUE);
72958151Seric 			message("214-%s", p);
7304577Seric 			noinfo = FALSE;
7314577Seric 		}
7324577Seric 	}
7334577Seric 
7344577Seric 	if (noinfo)
73558151Seric 		message("504 HELP topic unknown");
7364577Seric 	else
73758151Seric 		message("214 End of HELP info");
7384628Seric 	(void) fclose(hf);
7394577Seric }
7408544Seric /*
7419339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7429339Seric **
7439339Seric **	Parameters:
7449339Seric **		label -- a string used in error messages
7459339Seric **
7469339Seric **	Returns:
7479339Seric **		zero in the child
7489339Seric **		one in the parent
7499339Seric **
7509339Seric **	Side Effects:
7519339Seric **		none.
7529339Seric */
7538544Seric 
75455012Seric runinchild(label, e)
7559339Seric 	char *label;
75655012Seric 	register ENVELOPE *e;
7579339Seric {
7589339Seric 	int childpid;
7599339Seric 
76016158Seric 	if (!OneXact)
7619339Seric 	{
76216158Seric 		childpid = dofork();
76316158Seric 		if (childpid < 0)
76416158Seric 		{
76516158Seric 			syserr("%s: cannot fork", label);
76616158Seric 			return (1);
76716158Seric 		}
76816158Seric 		if (childpid > 0)
76916158Seric 		{
77016158Seric 			auto int st;
7719339Seric 
77216158Seric 			/* parent -- wait for child to complete */
77316158Seric 			st = waitfor(childpid);
77416158Seric 			if (st == -1)
77516158Seric 				syserr("%s: lost child", label);
7769339Seric 
77716158Seric 			/* if we exited on a QUIT command, complete the process */
77816158Seric 			if (st == (EX_QUIT << 8))
77916158Seric 				finis();
7809339Seric 
78116158Seric 			return (1);
78216158Seric 		}
78316158Seric 		else
78416158Seric 		{
78516158Seric 			/* child */
78616158Seric 			InChild = TRUE;
78725050Seric 			QuickAbort = FALSE;
78855012Seric 			clearenvelope(e, FALSE);
78916158Seric 		}
7909339Seric 	}
79115256Seric 
79216158Seric 	/* open alias database */
79355012Seric 	initaliases(AliasFile, FALSE, e);
79416158Seric 
79516158Seric 	return (0);
7969339Seric }
7979339Seric 
79856795Seric # endif /* SMTP */
799