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*59058Seric static char sccsid[] = "@(#)srvrsmtp.c	6.41 (Berkeley) 04/13/93 (with SMTP)";
1433731Sbostic #else
15*59058Seric static char sccsid[] = "@(#)srvrsmtp.c	6.41 (Berkeley) 04/13/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;
10258109Seric 	bool gotmail;			/* mail command received */
10358092Seric 	bool gothello;			/* helo command received */
10458092Seric 	bool vrfy;			/* set if this is a vrfy command */
10558323Seric 	char *protocol;			/* sending protocol */
10659016Seric 	char *sendinghost;		/* sending hostname */
10758333Seric 	long msize;			/* approximate maximum message size */
10858333Seric 	auto char *delimptr;
10958714Seric 	char *id;
1108544Seric 	char inp[MAXLINE];
11157232Seric 	char cmdbuf[MAXLINE];
1127124Seric 	extern char Version[];
11311151Seric 	extern char *macvalue();
11412612Seric 	extern ADDRESS *recipient();
11524943Seric 	extern ENVELOPE BlankEnvelope;
11624943Seric 	extern ENVELOPE *newenvelope();
11758755Seric 	extern char *anynet_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";
13158330Seric 	protocol = NULL;
13259016Seric 	sendinghost = macvalue('s', e);
13358082Seric 	gothello = FALSE;
13458330Seric 	gotmail = FALSE;
1354549Seric 	for (;;)
1364549Seric 	{
13712612Seric 		/* arrange for backout */
13812612Seric 		if (setjmp(TopFrame) > 0 && InChild)
139*59058Seric 		{
140*59058Seric 			QuickAbort = FALSE;
141*59058Seric 			SuprErrs = TRUE;
14212612Seric 			finis();
143*59058Seric 		}
14412612Seric 		QuickAbort = FALSE;
14512612Seric 		HoldErrs = FALSE;
14651951Seric 		LogUsrErrs = FALSE;
14758092Seric 		e->e_flags &= ~EF_VRFYONLY;
14812612Seric 
1497356Seric 		/* setup for the read */
15055012Seric 		e->e_to = NULL;
1514577Seric 		Errors = 0;
1527275Seric 		(void) fflush(stdout);
1537356Seric 
1547356Seric 		/* read the input line */
15558109Seric 		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand);
1567356Seric 
1577685Seric 		/* handle errors */
1587356Seric 		if (p == NULL)
1597356Seric 		{
1604549Seric 			/* end of file, just die */
16158151Seric 			message("421 %s Lost input channel from %s",
16225050Seric 				MyHostName, CurHostName);
16355464Seric #ifdef LOG
16458020Seric 			if (LogLevel > 1)
16555464Seric 				syslog(LOG_NOTICE, "lost input channel from %s",
16655464Seric 					CurHostName);
16755464Seric #endif
16858069Seric 			if (InChild)
16958069Seric 				ExitStat = EX_QUIT;
1704549Seric 			finis();
1714549Seric 		}
1724549Seric 
1734549Seric 		/* clean up end of line */
1744558Seric 		fixcrlf(inp, TRUE);
1754549Seric 
1764713Seric 		/* echo command to transcript */
17755012Seric 		if (e->e_xfp != NULL)
17855012Seric 			fprintf(e->e_xfp, "<<< %s\n", inp);
1794713Seric 
1804549Seric 		/* break off command */
18158050Seric 		for (p = inp; isascii(*p) && isspace(*p); p++)
1824549Seric 			continue;
18357232Seric 		cmd = cmdbuf;
18458050Seric 		while (*p != '\0' &&
18558050Seric 		       !(isascii(*p) && isspace(*p)) &&
18658050Seric 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
18724981Seric 			*cmd++ = *p++;
18824981Seric 		*cmd = '\0';
1894549Seric 
19025691Seric 		/* throw away leading whitespace */
19158050Seric 		while (isascii(*p) && isspace(*p))
19225691Seric 			p++;
19325691Seric 
1944549Seric 		/* decode command */
1954549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1964549Seric 		{
19733725Sbostic 			if (!strcasecmp(c->cmdname, cmdbuf))
1984549Seric 				break;
1994549Seric 		}
2004549Seric 
20151954Seric 		/* reset errors */
20251954Seric 		errno = 0;
20351954Seric 
2044549Seric 		/* process command */
2054549Seric 		switch (c->cmdcode)
2064549Seric 		{
2074976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
20858323Seric 		  case CMDEHLO:		/* extended hello */
20958323Seric 			if (c->cmdcode == CMDEHLO)
21058323Seric 			{
21158323Seric 				protocol = "ESMTP";
21258323Seric 				SmtpPhase = "EHLO";
21358323Seric 			}
21458323Seric 			else
21558323Seric 			{
21658323Seric 				protocol = "SMTP";
21758323Seric 				SmtpPhase = "HELO";
21858323Seric 			}
21925050Seric 			setproctitle("%s: %s", CurHostName, inp);
22059016Seric 			sendinghost = newstr(p);
22158308Seric 			if (strcasecmp(p, RealHostName) != 0)
22211146Seric 			{
22358789Seric 				auth_warning(e, "Host %s claimed to be %s",
22458789Seric 					RealHostName, p);
22511146Seric 			}
22658957Seric 			p = macvalue('_', e);
22758957Seric 			if (p == NULL)
22859016Seric 				p = RealHostName;
22958323Seric 
23058323Seric 			/* send ext. message -- old systems must ignore */
23158323Seric 			message("250-%s Hello %s, pleased to meet you",
23258957Seric 				MyHostName, p);
23358323Seric 			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
23458323Seric 				message("250-EXPN");
23558338Seric 			message("250-SIZE");
23658323Seric 			message("250 HELP");
23758082Seric 			gothello = TRUE;
2384976Seric 			break;
2394976Seric 
2404549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24124943Seric 			SmtpPhase = "MAIL";
24224943Seric 
2439314Seric 			/* check for validity of this command */
24458789Seric 			if (!gothello)
24558082Seric 			{
24658957Seric 				/* set sending host to our known value */
24759016Seric 				if (sendinghost == NULL)
24859016Seric 					sendinghost = RealHostName;
24958957Seric 
25058789Seric 				if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25158821Seric 				{
25258789Seric 					message("503 Polite people say HELO first");
25358821Seric 					break;
25458821Seric 				}
25558789Seric 				else
25658821Seric 				{
25758789Seric 					auth_warning(e,
25858789Seric 						"Host %s didn't use HELO protocol",
25958789Seric 						RealHostName);
26058821Seric 				}
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;
27758323Seric 			if (protocol == NULL)
27858323Seric 				protocol = "SMTP";
27958323Seric 			define('r', protocol, e);
28059016Seric 			define('s', sendinghost, 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)
292*59058Seric 				{
293*59058Seric 					QuickAbort = FALSE;
294*59058Seric 					SuprErrs = TRUE;
29558147Seric 					finis();
296*59058Seric 				}
29757977Seric 				break;
29858147Seric 			}
29957977Seric 			QuickAbort = TRUE;
30058333Seric 
30158333Seric 			/* must parse sender first */
30258333Seric 			delimptr = NULL;
30358704Seric 			setsender(p, e, &delimptr, FALSE);
30458333Seric 			p = delimptr;
30558333Seric 			if (p != NULL && *p != '\0')
30658333Seric 				*p++ = '\0';
30758333Seric 
30858333Seric 			/* now parse ESMTP arguments */
30958333Seric 			msize = 0;
31058333Seric 			for (; p != NULL && *p != '\0'; p++)
31158333Seric 			{
31258333Seric 				char *kp;
31358333Seric 				char *vp;
31458333Seric 
31558333Seric 				/* locate the beginning of the keyword */
31658333Seric 				while (isascii(*p) && isspace(*p))
31758333Seric 					p++;
31858333Seric 				if (*p == '\0')
31958333Seric 					break;
32058333Seric 				kp = p;
32158333Seric 
32258333Seric 				/* skip to the value portion */
32358333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
32458333Seric 					p++;
32558333Seric 				if (*p == '=')
32658333Seric 				{
32758333Seric 					*p++ = '\0';
32858333Seric 					vp = p;
32958333Seric 
33058333Seric 					/* skip to the end of the value */
33158333Seric 					while (*p != '\0' && *p != ' ' &&
33258333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
33358333Seric 					       *p != '=')
33458333Seric 						p++;
33558333Seric 				}
33658333Seric 
33758333Seric 				if (*p != '\0')
33858333Seric 					*p++ = '\0';
33958333Seric 
34058333Seric 				if (tTd(19, 1))
34158333Seric 					printf("MAIL: got arg %s=%s\n", kp,
34258333Seric 						vp == NULL ? "<null>" : vp);
34358333Seric 
34458333Seric 				if (strcasecmp(kp, "size") == 0)
34558333Seric 				{
34658333Seric 					if (kp == NULL)
34758333Seric 					{
34858333Seric 						usrerr("501 SIZE requires a value");
34958333Seric 						/* NOTREACHED */
35058333Seric 					}
35158333Seric 					msize = atol(vp);
35258333Seric 				}
35358333Seric 				else
35458333Seric 				{
35558333Seric 					usrerr("501 %s parameter unrecognized", kp);
35658333Seric 					/* NOTREACHED */
35758333Seric 				}
35858333Seric 			}
35958333Seric 
36058333Seric 			if (!enoughspace(msize))
36158333Seric 			{
36258333Seric 				message("452 Insufficient disk space; try again later");
36358333Seric 				break;
36458333Seric 			}
36558151Seric 			message("250 Sender ok");
36658147Seric 			gotmail = TRUE;
3674549Seric 			break;
3684549Seric 
3694976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
37058850Seric 			if (!gotmail)
37158850Seric 			{
37258850Seric 				usrerr("503 Need MAIL before RCPT");
37358850Seric 				break;
37458850Seric 			}
37524943Seric 			SmtpPhase = "RCPT";
37657389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
37712612Seric 			if (setjmp(TopFrame) > 0)
37814785Seric 			{
37955012Seric 				e->e_flags &= ~EF_FATALERRS;
38012612Seric 				break;
38114785Seric 			}
38212612Seric 			QuickAbort = TRUE;
38351951Seric 			LogUsrErrs = TRUE;
38458093Seric 
38558919Seric 			e->e_flags |= EF_VRFYONLY;
38658919Seric 
3874549Seric 			p = skipword(p, "to");
3884549Seric 			if (p == NULL)
3894549Seric 				break;
39058333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
39112612Seric 			if (a == NULL)
39212612Seric 				break;
39316886Seric 			a->q_flags |= QPRIMARY;
39455012Seric 			a = recipient(a, &e->e_sendqueue, e);
39512612Seric 			if (Errors != 0)
39612612Seric 				break;
39712612Seric 
39812612Seric 			/* no errors during parsing, but might be a duplicate */
39955012Seric 			e->e_to = p;
40012612Seric 			if (!bitset(QBADADDR, a->q_flags))
40158151Seric 				message("250 Recipient ok");
40212612Seric 			else
4034549Seric 			{
40412612Seric 				/* punt -- should keep message in ADDRESS.... */
40558151Seric 				message("550 Addressee unknown");
4064549Seric 			}
40755012Seric 			e->e_to = NULL;
4084549Seric 			break;
4094549Seric 
4104549Seric 		  case CMDDATA:		/* data -- text of mail */
41124943Seric 			SmtpPhase = "DATA";
41258109Seric 			if (!gotmail)
4134549Seric 			{
41458151Seric 				message("503 Need MAIL command");
4154976Seric 				break;
4164549Seric 			}
41755012Seric 			else if (e->e_nrcpts <= 0)
4184549Seric 			{
41958151Seric 				message("503 Need RCPT (recipient)");
4204976Seric 				break;
4214549Seric 			}
4224976Seric 
42358929Seric 			/* check to see if we need to re-expand aliases */
42458929Seric 			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
42558929Seric 			{
42658929Seric 				if (bitset(QVERIFIED, a->q_flags))
42758929Seric 					break;
42858929Seric 			}
42958929Seric 
4304976Seric 			/* collect the text of the message */
43124943Seric 			SmtpPhase = "collect";
43257389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
43358929Seric 			collect(TRUE, a != NULL, e);
4344976Seric 			if (Errors != 0)
4354976Seric 				break;
4364976Seric 
4378238Seric 			/*
4388238Seric 			**  Arrange to send to everyone.
4398238Seric 			**	If sending to multiple people, mail back
4408238Seric 			**		errors rather than reporting directly.
4418238Seric 			**	In any case, don't mail back errors for
4428238Seric 			**		anything that has happened up to
4438238Seric 			**		now (the other end will do this).
44410197Seric 			**	Truncate our transcript -- the mail has gotten
44510197Seric 			**		to us successfully, and if we have
44610197Seric 			**		to mail this back, it will be easier
44710197Seric 			**		on the reader.
4488238Seric 			**	Then send to everyone.
4498238Seric 			**	Finally give a reply code.  If an error has
4508238Seric 			**		already been given, don't mail a
4518238Seric 			**		message back.
4529339Seric 			**	We goose error returns by clearing error bit.
4538238Seric 			*/
4548238Seric 
45524943Seric 			SmtpPhase = "delivery";
45655012Seric 			if (e->e_nrcpts != 1)
4579378Seric 			{
4589378Seric 				HoldErrs = TRUE;
45958734Seric 				e->e_errormode = EM_MAIL;
4609378Seric 			}
46155012Seric 			e->e_flags &= ~EF_FATALERRS;
46255012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
46358714Seric 			id = e->e_id;
4644976Seric 
4654976Seric 			/* send to all recipients */
46658919Seric 			sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE);
46755012Seric 			e->e_to = NULL;
4684976Seric 
46923516Seric 			/* save statistics */
47055012Seric 			markstats(e, (ADDRESS *) NULL);
47123516Seric 
4728238Seric 			/* issue success if appropriate and reset */
4738238Seric 			if (Errors == 0 || HoldErrs)
47458855Seric 				message("250 %s Message accepted for delivery", id);
4758238Seric 			else
47655012Seric 				e->e_flags &= ~EF_FATALERRS;
4779339Seric 
47858919Seric 			/* if we just queued, poke it */
47958919Seric 			if (a != NULL && e->e_sendmode != SM_QUEUE)
48058919Seric 			{
48158919Seric 				unlockqueue(e);
48258924Seric 				dowork(id, TRUE, TRUE, e);
48358919Seric 				e->e_id = NULL;
48458919Seric 			}
48558883Seric 
4869339Seric 			/* if in a child, pop back to our parent */
4879339Seric 			if (InChild)
4889339Seric 				finis();
48924943Seric 
49024943Seric 			/* clean up a bit */
49158109Seric 			gotmail = FALSE;
49255012Seric 			dropenvelope(e);
49358179Seric 			CurEnv = e = newenvelope(e, CurEnv);
49455012Seric 			e->e_flags = BlankEnvelope.e_flags;
4954549Seric 			break;
4964549Seric 
4974549Seric 		  case CMDRSET:		/* rset -- reset state */
49858151Seric 			message("250 Reset state");
4999339Seric 			if (InChild)
5009339Seric 				finis();
50158109Seric 
50258109Seric 			/* clean up a bit */
50358109Seric 			gotmail = FALSE;
50458109Seric 			dropenvelope(e);
50558179Seric 			CurEnv = e = newenvelope(e, CurEnv);
5069339Seric 			break;
5074549Seric 
5084549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
50958092Seric 		  case CMDEXPN:		/* expn -- expand address */
51058092Seric 			vrfy = c->cmdcode == CMDVRFY;
51158092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
51258092Seric 						PrivacyFlags))
51358082Seric 			{
51458412Seric 				if (vrfy)
51558412Seric 					message("252 Who's to say?");
51658412Seric 				else
51758412Seric 					message("502 That's none of your business");
51858082Seric 				break;
51958082Seric 			}
52058082Seric 			else if (!gothello &&
52158092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
52258092Seric 						PrivacyFlags))
52358082Seric 			{
52458151Seric 				message("503 I demand that you introduce yourself first");
52558082Seric 				break;
52658082Seric 			}
52758092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
5289339Seric 				break;
52925050Seric 			setproctitle("%s: %s", CurHostName, inp);
53055173Seric #ifdef LOG
53158020Seric 			if (LogLevel > 5)
53255173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
53355173Seric #endif
5345003Seric 			vrfyqueue = NULL;
5357762Seric 			QuickAbort = TRUE;
53658092Seric 			if (vrfy)
53758092Seric 				e->e_flags |= EF_VRFYONLY;
53858082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5397762Seric 			if (Errors != 0)
5409339Seric 			{
5419339Seric 				if (InChild)
5429339Seric 					finis();
5437762Seric 				break;
5449339Seric 			}
5455003Seric 			while (vrfyqueue != NULL)
5465003Seric 			{
5475003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5485003Seric 				char *code;
5495003Seric 
5507685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5515003Seric 					a = a->q_next;
5525003Seric 
5537685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
55458151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5555003Seric 				else if (a == NULL)
55658151Seric 					message("554 Self destructive alias loop");
5575003Seric 				vrfyqueue = a;
5585003Seric 			}
5599339Seric 			if (InChild)
5609339Seric 				finis();
5614549Seric 			break;
5624549Seric 
5634549Seric 		  case CMDHELP:		/* help -- give user info */
5644577Seric 			help(p);
5654549Seric 			break;
5664549Seric 
5674549Seric 		  case CMDNOOP:		/* noop -- do nothing */
56858151Seric 			message("200 OK");
5694549Seric 			break;
5704549Seric 
5714549Seric 		  case CMDQUIT:		/* quit -- leave mail */
57258151Seric 			message("221 %s closing connection", MyHostName);
5739339Seric 			if (InChild)
5749339Seric 				ExitStat = EX_QUIT;
5754549Seric 			finis();
5764549Seric 
5778544Seric 		  case CMDVERB:		/* set verbose mode */
5788544Seric 			Verbose = TRUE;
57958734Seric 			e->e_sendmode = SM_DELIVER;
58058151Seric 			message("200 Verbose mode");
5818544Seric 			break;
5828544Seric 
5839314Seric 		  case CMDONEX:		/* doing one transaction only */
5849378Seric 			OneXact = TRUE;
58558151Seric 			message("200 Only one transaction");
5869314Seric 			break;
5879314Seric 
58836230Skarels # ifdef SMTPDEBUG
5899339Seric 		  case CMDDBGQSHOW:	/* show queues */
5906907Seric 			printf("Send Queue=");
59155012Seric 			printaddr(e->e_sendqueue, TRUE);
5925003Seric 			break;
5937275Seric 
5947275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5957676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5967676Seric 			tTflag(p);
59758151Seric 			message("200 Debug set");
5987275Seric 			break;
5997275Seric 
60036230Skarels # else /* not SMTPDEBUG */
60124945Seric 
60236230Skarels 		  case CMDDBGQSHOW:	/* show queues */
60336230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
60436233Skarels # ifdef LOG
60558308Seric 			if (LogLevel > 0)
60636230Skarels 				syslog(LOG_NOTICE,
60758020Seric 				    "\"%s\" command from %s (%s)",
60836230Skarels 				    c->cmdname, RealHostName,
60958755Seric 				    anynet_ntoa(&RealHostAddr));
61036233Skarels # endif
61136230Skarels 			/* FALL THROUGH */
61236230Skarels # endif /* SMTPDEBUG */
61336230Skarels 
6144549Seric 		  case CMDERROR:	/* unknown command */
61558151Seric 			message("500 Command unrecognized");
6164549Seric 			break;
6174549Seric 
6184549Seric 		  default:
61936230Skarels 			errno = 0;
62058151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
6214549Seric 			break;
6224549Seric 		}
6234549Seric 	}
6244549Seric }
6254549Seric /*
6264549Seric **  SKIPWORD -- skip a fixed word.
6274549Seric **
6284549Seric **	Parameters:
6294549Seric **		p -- place to start looking.
6304549Seric **		w -- word to skip.
6314549Seric **
6324549Seric **	Returns:
6334549Seric **		p following w.
6344549Seric **		NULL on error.
6354549Seric **
6364549Seric **	Side Effects:
6374549Seric **		clobbers the p data area.
6384549Seric */
6394549Seric 
6404549Seric static char *
6414549Seric skipword(p, w)
6424549Seric 	register char *p;
6434549Seric 	char *w;
6444549Seric {
6454549Seric 	register char *q;
6464549Seric 
6474549Seric 	/* find beginning of word */
64858050Seric 	while (isascii(*p) && isspace(*p))
6494549Seric 		p++;
6504549Seric 	q = p;
6514549Seric 
6524549Seric 	/* find end of word */
65358050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6544549Seric 		p++;
65558050Seric 	while (isascii(*p) && isspace(*p))
6564549Seric 		*p++ = '\0';
6574549Seric 	if (*p != ':')
6584549Seric 	{
6594549Seric 	  syntax:
66058151Seric 		message("501 Syntax error");
6614549Seric 		Errors++;
6624549Seric 		return (NULL);
6634549Seric 	}
6644549Seric 	*p++ = '\0';
66558050Seric 	while (isascii(*p) && isspace(*p))
6664549Seric 		p++;
6674549Seric 
6684549Seric 	/* see if the input word matches desired word */
66933725Sbostic 	if (strcasecmp(q, w))
6704549Seric 		goto syntax;
6714549Seric 
6724549Seric 	return (p);
6734549Seric }
6744577Seric /*
67558151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
67658151Seric **
67758151Seric **	Parameters:
67858151Seric **		a -- the address to print
67958151Seric **		last -- set if this is the last one.
68058151Seric **
68158151Seric **	Returns:
68258151Seric **		none.
68358151Seric **
68458151Seric **	Side Effects:
68558151Seric **		Prints the appropriate 250 codes.
68658151Seric */
68758151Seric 
68858151Seric printvrfyaddr(a, last)
68958151Seric 	register ADDRESS *a;
69058151Seric 	bool last;
69158151Seric {
69258151Seric 	char fmtbuf[20];
69358151Seric 
69458151Seric 	strcpy(fmtbuf, "250");
69558151Seric 	fmtbuf[3] = last ? ' ' : '-';
69658151Seric 
69758151Seric 	if (strchr(a->q_paddr, '<') != NULL)
69858151Seric 		strcpy(&fmtbuf[4], "%s");
69958151Seric 	else if (a->q_fullname == NULL)
70058151Seric 		strcpy(&fmtbuf[4], "<%s>");
70158151Seric 	else
70258151Seric 	{
70358151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
70458151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
70558151Seric 		return;
70658151Seric 	}
70758151Seric 	message(fmtbuf, a->q_paddr);
70858151Seric }
70958151Seric /*
7104577Seric **  HELP -- implement the HELP command.
7114577Seric **
7124577Seric **	Parameters:
7134577Seric **		topic -- the topic we want help for.
7144577Seric **
7154577Seric **	Returns:
7164577Seric **		none.
7174577Seric **
7184577Seric **	Side Effects:
7194577Seric **		outputs the help file to message output.
7204577Seric */
7214577Seric 
7224577Seric help(topic)
7234577Seric 	char *topic;
7244577Seric {
7254577Seric 	register FILE *hf;
7264577Seric 	int len;
7274577Seric 	char buf[MAXLINE];
7284577Seric 	bool noinfo;
7294577Seric 
7308269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7314577Seric 	{
7324577Seric 		/* no help */
73311931Seric 		errno = 0;
73458151Seric 		message("502 HELP not implemented");
7354577Seric 		return;
7364577Seric 	}
7374577Seric 
73849669Seric 	if (topic == NULL || *topic == '\0')
73949669Seric 		topic = "smtp";
74049669Seric 	else
74149669Seric 		makelower(topic);
74249669Seric 
7434577Seric 	len = strlen(topic);
7444577Seric 	noinfo = TRUE;
7454577Seric 
7464577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7474577Seric 	{
7484577Seric 		if (strncmp(buf, topic, len) == 0)
7494577Seric 		{
7504577Seric 			register char *p;
7514577Seric 
75256795Seric 			p = strchr(buf, '\t');
7534577Seric 			if (p == NULL)
7544577Seric 				p = buf;
7554577Seric 			else
7564577Seric 				p++;
7574577Seric 			fixcrlf(p, TRUE);
75858151Seric 			message("214-%s", p);
7594577Seric 			noinfo = FALSE;
7604577Seric 		}
7614577Seric 	}
7624577Seric 
7634577Seric 	if (noinfo)
76458151Seric 		message("504 HELP topic unknown");
7654577Seric 	else
76658151Seric 		message("214 End of HELP info");
7674628Seric 	(void) fclose(hf);
7684577Seric }
7698544Seric /*
7709339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7719339Seric **
7729339Seric **	Parameters:
7739339Seric **		label -- a string used in error messages
7749339Seric **
7759339Seric **	Returns:
7769339Seric **		zero in the child
7779339Seric **		one in the parent
7789339Seric **
7799339Seric **	Side Effects:
7809339Seric **		none.
7819339Seric */
7828544Seric 
78355012Seric runinchild(label, e)
7849339Seric 	char *label;
78555012Seric 	register ENVELOPE *e;
7869339Seric {
7879339Seric 	int childpid;
7889339Seric 
78916158Seric 	if (!OneXact)
7909339Seric 	{
79116158Seric 		childpid = dofork();
79216158Seric 		if (childpid < 0)
79316158Seric 		{
79416158Seric 			syserr("%s: cannot fork", label);
79516158Seric 			return (1);
79616158Seric 		}
79716158Seric 		if (childpid > 0)
79816158Seric 		{
79916158Seric 			auto int st;
8009339Seric 
80116158Seric 			/* parent -- wait for child to complete */
80216158Seric 			st = waitfor(childpid);
80316158Seric 			if (st == -1)
80416158Seric 				syserr("%s: lost child", label);
8059339Seric 
80616158Seric 			/* if we exited on a QUIT command, complete the process */
80716158Seric 			if (st == (EX_QUIT << 8))
80816158Seric 				finis();
8099339Seric 
81016158Seric 			return (1);
81116158Seric 		}
81216158Seric 		else
81316158Seric 		{
81416158Seric 			/* child */
81516158Seric 			InChild = TRUE;
81625050Seric 			QuickAbort = FALSE;
81755012Seric 			clearenvelope(e, FALSE);
81816158Seric 		}
8199339Seric 	}
82015256Seric 
82116158Seric 	/* open alias database */
82255012Seric 	initaliases(AliasFile, FALSE, e);
82316158Seric 
82416158Seric 	return (0);
8259339Seric }
8269339Seric 
82756795Seric # endif /* SMTP */
828