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*58333Seric static char sccsid[] = "@(#)srvrsmtp.c	6.21 (Berkeley) 03/01/93 (with SMTP)";
1433731Sbostic #else
15*58333Seric static char sccsid[] = "@(#)srvrsmtp.c	6.21 (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 */
107*58333Seric 	long msize;			/* approximate maximum message size */
108*58333Seric 	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");
24058323Seric 			message("250 HELP");
24158082Seric 			gothello = TRUE;
2424976Seric 			break;
2434976Seric 
2444549Seric 		  case CMDMAIL:		/* mail -- designate sender */
24524943Seric 			SmtpPhase = "MAIL";
24624943Seric 
24711151Seric 			/* force a sending host even if no HELO given */
24858064Seric 			if (sendinghost == NULL && macvalue('s', e) == NULL)
24930448Seric 				sendinghost = RealHostName;
25011151Seric 
2519314Seric 			/* check for validity of this command */
25258082Seric 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
25358082Seric 			{
25458151Seric 				message("503 Polite people say HELO first");
25558082Seric 				break;
25658082Seric 			}
25758109Seric 			if (gotmail)
2584558Seric 			{
25958151Seric 				message("503 Sender already specified");
2604558Seric 				break;
2614558Seric 			}
2629339Seric 			if (InChild)
2639339Seric 			{
26436230Skarels 				errno = 0;
26558151Seric 				syserr("503 Nested MAIL command: MAIL %s", p);
26658069Seric 				finis();
2679339Seric 			}
2689339Seric 
2699339Seric 			/* fork a subprocess to process this command */
27055012Seric 			if (runinchild("SMTP-MAIL", e) > 0)
2719339Seric 				break;
27258064Seric 			if (sendinghost != NULL)
27358064Seric 				define('s', sendinghost, e);
27458323Seric 			if (protocol == NULL)
27558323Seric 				protocol = "SMTP";
27658323Seric 			define('r', protocol, e);
27755012Seric 			initsys(e);
27857389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
2799339Seric 
2809339Seric 			/* child -- go do the processing */
2814549Seric 			p = skipword(p, "from");
2824549Seric 			if (p == NULL)
2834549Seric 				break;
28457977Seric 			if (setjmp(TopFrame) > 0)
28558147Seric 			{
28658147Seric 				/* this failed -- undo work */
28758147Seric 				if (InChild)
28858147Seric 					finis();
28957977Seric 				break;
29058147Seric 			}
29157977Seric 			QuickAbort = TRUE;
292*58333Seric 
293*58333Seric 			/* must parse sender first */
294*58333Seric 			delimptr = NULL;
295*58333Seric 			setsender(p, e, &delimptr);
296*58333Seric 			p = delimptr;
297*58333Seric 			if (p != NULL && *p != '\0')
298*58333Seric 				*p++ = '\0';
299*58333Seric 
300*58333Seric 			/* now parse ESMTP arguments */
301*58333Seric 			msize = 0;
302*58333Seric 			for (; p != NULL && *p != '\0'; p++)
303*58333Seric 			{
304*58333Seric 				char *kp;
305*58333Seric 				char *vp;
306*58333Seric 
307*58333Seric 				/* locate the beginning of the keyword */
308*58333Seric 				while (isascii(*p) && isspace(*p))
309*58333Seric 					p++;
310*58333Seric 				if (*p == '\0')
311*58333Seric 					break;
312*58333Seric 				kp = p;
313*58333Seric 
314*58333Seric 				/* skip to the value portion */
315*58333Seric 				while (isascii(*p) && isalnum(*p) || *p == '-')
316*58333Seric 					p++;
317*58333Seric 				if (*p == '=')
318*58333Seric 				{
319*58333Seric 					*p++ = '\0';
320*58333Seric 					vp = p;
321*58333Seric 
322*58333Seric 					/* skip to the end of the value */
323*58333Seric 					while (*p != '\0' && *p != ' ' &&
324*58333Seric 					       !(isascii(*p) && iscntrl(*p)) &&
325*58333Seric 					       *p != '=')
326*58333Seric 						p++;
327*58333Seric 				}
328*58333Seric 
329*58333Seric 				if (*p != '\0')
330*58333Seric 					*p++ = '\0';
331*58333Seric 
332*58333Seric 				if (tTd(19, 1))
333*58333Seric 					printf("MAIL: got arg %s=%s\n", kp,
334*58333Seric 						vp == NULL ? "<null>" : vp);
335*58333Seric 
336*58333Seric 				if (strcasecmp(kp, "size") == 0)
337*58333Seric 				{
338*58333Seric 					if (kp == NULL)
339*58333Seric 					{
340*58333Seric 						usrerr("501 SIZE requires a value");
341*58333Seric 						/* NOTREACHED */
342*58333Seric 					}
343*58333Seric 					msize = atol(vp);
344*58333Seric 				}
345*58333Seric 				else
346*58333Seric 				{
347*58333Seric 					usrerr("501 %s parameter unrecognized", kp);
348*58333Seric 					/* NOTREACHED */
349*58333Seric 				}
350*58333Seric 			}
351*58333Seric 
352*58333Seric 			if (!enoughspace(msize))
353*58333Seric 			{
354*58333Seric 				message("452 Insufficient disk space; try again later");
355*58333Seric 				break;
356*58333Seric 			}
35758151Seric 			message("250 Sender ok");
35858147Seric 			gotmail = TRUE;
3594549Seric 			break;
3604549Seric 
3614976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
36224943Seric 			SmtpPhase = "RCPT";
36357389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
36412612Seric 			if (setjmp(TopFrame) > 0)
36514785Seric 			{
36655012Seric 				e->e_flags &= ~EF_FATALERRS;
36712612Seric 				break;
36814785Seric 			}
36912612Seric 			QuickAbort = TRUE;
37051951Seric 			LogUsrErrs = TRUE;
37158093Seric 
37258093Seric 			/* optimization -- if queueing, don't expand aliases */
37358093Seric 			if (SendMode == SM_QUEUE)
37458093Seric 				e->e_flags |= EF_VRFYONLY;
37558093Seric 
3764549Seric 			p = skipword(p, "to");
3774549Seric 			if (p == NULL)
3784549Seric 				break;
379*58333Seric 			a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e);
38012612Seric 			if (a == NULL)
38112612Seric 				break;
38216886Seric 			a->q_flags |= QPRIMARY;
38355012Seric 			a = recipient(a, &e->e_sendqueue, e);
38412612Seric 			if (Errors != 0)
38512612Seric 				break;
38612612Seric 
38712612Seric 			/* no errors during parsing, but might be a duplicate */
38855012Seric 			e->e_to = p;
38912612Seric 			if (!bitset(QBADADDR, a->q_flags))
39058151Seric 				message("250 Recipient ok");
39112612Seric 			else
3924549Seric 			{
39312612Seric 				/* punt -- should keep message in ADDRESS.... */
39458151Seric 				message("550 Addressee unknown");
3954549Seric 			}
39655012Seric 			e->e_to = NULL;
3974549Seric 			break;
3984549Seric 
3994549Seric 		  case CMDDATA:		/* data -- text of mail */
40024943Seric 			SmtpPhase = "DATA";
40158109Seric 			if (!gotmail)
4024549Seric 			{
40358151Seric 				message("503 Need MAIL command");
4044976Seric 				break;
4054549Seric 			}
40655012Seric 			else if (e->e_nrcpts <= 0)
4074549Seric 			{
40858151Seric 				message("503 Need RCPT (recipient)");
4094976Seric 				break;
4104549Seric 			}
4114976Seric 
4124976Seric 			/* collect the text of the message */
41324943Seric 			SmtpPhase = "collect";
41457389Seric 			setproctitle("%s %s: %s", e->e_id, CurHostName, inp);
41555012Seric 			collect(TRUE, e);
4164976Seric 			if (Errors != 0)
4174976Seric 				break;
4184976Seric 
4198238Seric 			/*
4208238Seric 			**  Arrange to send to everyone.
4218238Seric 			**	If sending to multiple people, mail back
4228238Seric 			**		errors rather than reporting directly.
4238238Seric 			**	In any case, don't mail back errors for
4248238Seric 			**		anything that has happened up to
4258238Seric 			**		now (the other end will do this).
42610197Seric 			**	Truncate our transcript -- the mail has gotten
42710197Seric 			**		to us successfully, and if we have
42810197Seric 			**		to mail this back, it will be easier
42910197Seric 			**		on the reader.
4308238Seric 			**	Then send to everyone.
4318238Seric 			**	Finally give a reply code.  If an error has
4328238Seric 			**		already been given, don't mail a
4338238Seric 			**		message back.
4349339Seric 			**	We goose error returns by clearing error bit.
4358238Seric 			*/
4368238Seric 
43724943Seric 			SmtpPhase = "delivery";
43855012Seric 			if (e->e_nrcpts != 1)
4399378Seric 			{
4409378Seric 				HoldErrs = TRUE;
44116886Seric 				ErrorMode = EM_MAIL;
4429378Seric 			}
44355012Seric 			e->e_flags &= ~EF_FATALERRS;
44455012Seric 			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
4454976Seric 
4464976Seric 			/* send to all recipients */
44755012Seric 			sendall(e, SM_DEFAULT);
44855012Seric 			e->e_to = NULL;
4494976Seric 
45023516Seric 			/* save statistics */
45155012Seric 			markstats(e, (ADDRESS *) NULL);
45223516Seric 
4538238Seric 			/* issue success if appropriate and reset */
4548238Seric 			if (Errors == 0 || HoldErrs)
45558151Seric 				message("250 Ok");
4568238Seric 			else
45755012Seric 				e->e_flags &= ~EF_FATALERRS;
4589339Seric 
4599339Seric 			/* if in a child, pop back to our parent */
4609339Seric 			if (InChild)
4619339Seric 				finis();
46224943Seric 
46324943Seric 			/* clean up a bit */
46458109Seric 			gotmail = FALSE;
46555012Seric 			dropenvelope(e);
46658179Seric 			CurEnv = e = newenvelope(e, CurEnv);
46755012Seric 			e->e_flags = BlankEnvelope.e_flags;
4684549Seric 			break;
4694549Seric 
4704549Seric 		  case CMDRSET:		/* rset -- reset state */
47158151Seric 			message("250 Reset state");
4729339Seric 			if (InChild)
4739339Seric 				finis();
47458109Seric 
47558109Seric 			/* clean up a bit */
47658109Seric 			gotmail = FALSE;
47758109Seric 			dropenvelope(e);
47858179Seric 			CurEnv = e = newenvelope(e, CurEnv);
4799339Seric 			break;
4804549Seric 
4814549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
48258092Seric 		  case CMDEXPN:		/* expn -- expand address */
48358092Seric 			vrfy = c->cmdcode == CMDVRFY;
48458092Seric 			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
48558092Seric 						PrivacyFlags))
48658082Seric 			{
48758151Seric 				message("502 That's none of your business");
48858082Seric 				break;
48958082Seric 			}
49058082Seric 			else if (!gothello &&
49158092Seric 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
49258092Seric 						PrivacyFlags))
49358082Seric 			{
49458151Seric 				message("503 I demand that you introduce yourself first");
49558082Seric 				break;
49658082Seric 			}
49758092Seric 			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
4989339Seric 				break;
49925050Seric 			setproctitle("%s: %s", CurHostName, inp);
50055173Seric #ifdef LOG
50158020Seric 			if (LogLevel > 5)
50255173Seric 				syslog(LOG_INFO, "%s: %s", CurHostName, inp);
50355173Seric #endif
5045003Seric 			vrfyqueue = NULL;
5057762Seric 			QuickAbort = TRUE;
50658092Seric 			if (vrfy)
50758092Seric 				e->e_flags |= EF_VRFYONLY;
50858082Seric 			(void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e);
5097762Seric 			if (Errors != 0)
5109339Seric 			{
5119339Seric 				if (InChild)
5129339Seric 					finis();
5137762Seric 				break;
5149339Seric 			}
5155003Seric 			while (vrfyqueue != NULL)
5165003Seric 			{
5175003Seric 				register ADDRESS *a = vrfyqueue->q_next;
5185003Seric 				char *code;
5195003Seric 
5207685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
5215003Seric 					a = a->q_next;
5225003Seric 
5237685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
52458151Seric 					printvrfyaddr(vrfyqueue, a == NULL);
5255003Seric 				else if (a == NULL)
52658151Seric 					message("554 Self destructive alias loop");
5275003Seric 				vrfyqueue = a;
5285003Seric 			}
5299339Seric 			if (InChild)
5309339Seric 				finis();
5314549Seric 			break;
5324549Seric 
5334549Seric 		  case CMDHELP:		/* help -- give user info */
5344577Seric 			help(p);
5354549Seric 			break;
5364549Seric 
5374549Seric 		  case CMDNOOP:		/* noop -- do nothing */
53858151Seric 			message("200 OK");
5394549Seric 			break;
5404549Seric 
5414549Seric 		  case CMDQUIT:		/* quit -- leave mail */
54258151Seric 			message("221 %s closing connection", MyHostName);
5439339Seric 			if (InChild)
5449339Seric 				ExitStat = EX_QUIT;
5454549Seric 			finis();
5464549Seric 
5478544Seric 		  case CMDVERB:		/* set verbose mode */
5488544Seric 			Verbose = TRUE;
54925025Seric 			SendMode = SM_DELIVER;
55058151Seric 			message("200 Verbose mode");
5518544Seric 			break;
5528544Seric 
5539314Seric 		  case CMDONEX:		/* doing one transaction only */
5549378Seric 			OneXact = TRUE;
55558151Seric 			message("200 Only one transaction");
5569314Seric 			break;
5579314Seric 
55836230Skarels # ifdef SMTPDEBUG
5599339Seric 		  case CMDDBGQSHOW:	/* show queues */
5606907Seric 			printf("Send Queue=");
56155012Seric 			printaddr(e->e_sendqueue, TRUE);
5625003Seric 			break;
5637275Seric 
5647275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
5657676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
5667676Seric 			tTflag(p);
56758151Seric 			message("200 Debug set");
5687275Seric 			break;
5697275Seric 
57036230Skarels # else /* not SMTPDEBUG */
57124945Seric 
57236230Skarels 		  case CMDDBGQSHOW:	/* show queues */
57336230Skarels 		  case CMDDBGDEBUG:	/* set debug mode */
57436233Skarels # ifdef LOG
57558308Seric 			if (LogLevel > 0)
57636230Skarels 				syslog(LOG_NOTICE,
57758020Seric 				    "\"%s\" command from %s (%s)",
57836230Skarels 				    c->cmdname, RealHostName,
57936230Skarels 				    inet_ntoa(RealHostAddr.sin_addr));
58036233Skarels # endif
58136230Skarels 			/* FALL THROUGH */
58236230Skarels # endif /* SMTPDEBUG */
58336230Skarels 
5844549Seric 		  case CMDERROR:	/* unknown command */
58558151Seric 			message("500 Command unrecognized");
5864549Seric 			break;
5874549Seric 
5884549Seric 		  default:
58936230Skarels 			errno = 0;
59058151Seric 			syserr("500 smtp: unknown code %d", c->cmdcode);
5914549Seric 			break;
5924549Seric 		}
5934549Seric 	}
5944549Seric }
5954549Seric /*
5964549Seric **  SKIPWORD -- skip a fixed word.
5974549Seric **
5984549Seric **	Parameters:
5994549Seric **		p -- place to start looking.
6004549Seric **		w -- word to skip.
6014549Seric **
6024549Seric **	Returns:
6034549Seric **		p following w.
6044549Seric **		NULL on error.
6054549Seric **
6064549Seric **	Side Effects:
6074549Seric **		clobbers the p data area.
6084549Seric */
6094549Seric 
6104549Seric static char *
6114549Seric skipword(p, w)
6124549Seric 	register char *p;
6134549Seric 	char *w;
6144549Seric {
6154549Seric 	register char *q;
6164549Seric 
6174549Seric 	/* find beginning of word */
61858050Seric 	while (isascii(*p) && isspace(*p))
6194549Seric 		p++;
6204549Seric 	q = p;
6214549Seric 
6224549Seric 	/* find end of word */
62358050Seric 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
6244549Seric 		p++;
62558050Seric 	while (isascii(*p) && isspace(*p))
6264549Seric 		*p++ = '\0';
6274549Seric 	if (*p != ':')
6284549Seric 	{
6294549Seric 	  syntax:
63058151Seric 		message("501 Syntax error");
6314549Seric 		Errors++;
6324549Seric 		return (NULL);
6334549Seric 	}
6344549Seric 	*p++ = '\0';
63558050Seric 	while (isascii(*p) && isspace(*p))
6364549Seric 		p++;
6374549Seric 
6384549Seric 	/* see if the input word matches desired word */
63933725Sbostic 	if (strcasecmp(q, w))
6404549Seric 		goto syntax;
6414549Seric 
6424549Seric 	return (p);
6434549Seric }
6444577Seric /*
64558151Seric **  PRINTVRFYADDR -- print an entry in the verify queue
64658151Seric **
64758151Seric **	Parameters:
64858151Seric **		a -- the address to print
64958151Seric **		last -- set if this is the last one.
65058151Seric **
65158151Seric **	Returns:
65258151Seric **		none.
65358151Seric **
65458151Seric **	Side Effects:
65558151Seric **		Prints the appropriate 250 codes.
65658151Seric */
65758151Seric 
65858151Seric printvrfyaddr(a, last)
65958151Seric 	register ADDRESS *a;
66058151Seric 	bool last;
66158151Seric {
66258151Seric 	char fmtbuf[20];
66358151Seric 
66458151Seric 	strcpy(fmtbuf, "250");
66558151Seric 	fmtbuf[3] = last ? ' ' : '-';
66658151Seric 
66758151Seric 	if (strchr(a->q_paddr, '<') != NULL)
66858151Seric 		strcpy(&fmtbuf[4], "%s");
66958151Seric 	else if (a->q_fullname == NULL)
67058151Seric 		strcpy(&fmtbuf[4], "<%s>");
67158151Seric 	else
67258151Seric 	{
67358151Seric 		strcpy(&fmtbuf[4], "%s <%s>");
67458151Seric 		message(fmtbuf, a->q_fullname, a->q_paddr);
67558151Seric 		return;
67658151Seric 	}
67758151Seric 	message(fmtbuf, a->q_paddr);
67858151Seric }
67958151Seric /*
6804577Seric **  HELP -- implement the HELP command.
6814577Seric **
6824577Seric **	Parameters:
6834577Seric **		topic -- the topic we want help for.
6844577Seric **
6854577Seric **	Returns:
6864577Seric **		none.
6874577Seric **
6884577Seric **	Side Effects:
6894577Seric **		outputs the help file to message output.
6904577Seric */
6914577Seric 
6924577Seric help(topic)
6934577Seric 	char *topic;
6944577Seric {
6954577Seric 	register FILE *hf;
6964577Seric 	int len;
6974577Seric 	char buf[MAXLINE];
6984577Seric 	bool noinfo;
6994577Seric 
7008269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
7014577Seric 	{
7024577Seric 		/* no help */
70311931Seric 		errno = 0;
70458151Seric 		message("502 HELP not implemented");
7054577Seric 		return;
7064577Seric 	}
7074577Seric 
70849669Seric 	if (topic == NULL || *topic == '\0')
70949669Seric 		topic = "smtp";
71049669Seric 	else
71149669Seric 		makelower(topic);
71249669Seric 
7134577Seric 	len = strlen(topic);
7144577Seric 	noinfo = TRUE;
7154577Seric 
7164577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
7174577Seric 	{
7184577Seric 		if (strncmp(buf, topic, len) == 0)
7194577Seric 		{
7204577Seric 			register char *p;
7214577Seric 
72256795Seric 			p = strchr(buf, '\t');
7234577Seric 			if (p == NULL)
7244577Seric 				p = buf;
7254577Seric 			else
7264577Seric 				p++;
7274577Seric 			fixcrlf(p, TRUE);
72858151Seric 			message("214-%s", p);
7294577Seric 			noinfo = FALSE;
7304577Seric 		}
7314577Seric 	}
7324577Seric 
7334577Seric 	if (noinfo)
73458151Seric 		message("504 HELP topic unknown");
7354577Seric 	else
73658151Seric 		message("214 End of HELP info");
7374628Seric 	(void) fclose(hf);
7384577Seric }
7398544Seric /*
7409339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
7419339Seric **
7429339Seric **	Parameters:
7439339Seric **		label -- a string used in error messages
7449339Seric **
7459339Seric **	Returns:
7469339Seric **		zero in the child
7479339Seric **		one in the parent
7489339Seric **
7499339Seric **	Side Effects:
7509339Seric **		none.
7519339Seric */
7528544Seric 
75355012Seric runinchild(label, e)
7549339Seric 	char *label;
75555012Seric 	register ENVELOPE *e;
7569339Seric {
7579339Seric 	int childpid;
7589339Seric 
75916158Seric 	if (!OneXact)
7609339Seric 	{
76116158Seric 		childpid = dofork();
76216158Seric 		if (childpid < 0)
76316158Seric 		{
76416158Seric 			syserr("%s: cannot fork", label);
76516158Seric 			return (1);
76616158Seric 		}
76716158Seric 		if (childpid > 0)
76816158Seric 		{
76916158Seric 			auto int st;
7709339Seric 
77116158Seric 			/* parent -- wait for child to complete */
77216158Seric 			st = waitfor(childpid);
77316158Seric 			if (st == -1)
77416158Seric 				syserr("%s: lost child", label);
7759339Seric 
77616158Seric 			/* if we exited on a QUIT command, complete the process */
77716158Seric 			if (st == (EX_QUIT << 8))
77816158Seric 				finis();
7799339Seric 
78016158Seric 			return (1);
78116158Seric 		}
78216158Seric 		else
78316158Seric 		{
78416158Seric 			/* child */
78516158Seric 			InChild = TRUE;
78625050Seric 			QuickAbort = FALSE;
78755012Seric 			clearenvelope(e, FALSE);
78816158Seric 		}
7899339Seric 	}
79015256Seric 
79116158Seric 	/* open alias database */
79255012Seric 	initaliases(AliasFile, FALSE, e);
79316158Seric 
79416158Seric 	return (0);
7959339Seric }
7969339Seric 
79756795Seric # endif /* SMTP */
798