19339Seric # include <errno.h>
24549Seric # include "sendmail.h"
34549Seric 
45181Seric # ifndef SMTP
5*9349Seric SCCSID(@(#)srvrsmtp.c	3.38		11/24/82	(no SMTP));
65181Seric # else SMTP
74556Seric 
8*9349Seric SCCSID(@(#)srvrsmtp.c	3.38		11/24/82);
95181Seric 
104549Seric /*
114549Seric **  SMTP -- run the SMTP protocol.
124549Seric **
134549Seric **	Parameters:
144549Seric **		none.
154549Seric **
164549Seric **	Returns:
174549Seric **		never.
184549Seric **
194549Seric **	Side Effects:
204549Seric **		Reads commands from the input channel and processes
214549Seric **			them.
224549Seric */
234549Seric 
244549Seric struct cmd
254549Seric {
264549Seric 	char	*cmdname;	/* command name */
274549Seric 	int	cmdcode;	/* internal code, see below */
284549Seric };
294549Seric 
304549Seric /* values for cmdcode */
314549Seric # define CMDERROR	0	/* bad command */
324549Seric # define CMDMAIL	1	/* mail -- designate sender */
334976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
344549Seric # define CMDDATA	3	/* data -- send message text */
359339Seric # define CMDRSET	4	/* rset -- reset state */
369339Seric # define CMDVRFY	5	/* vrfy -- verify address */
379339Seric # define CMDHELP	6	/* help -- give usage info */
389339Seric # define CMDNOOP	7	/* noop -- do nothing */
399339Seric # define CMDQUIT	8	/* quit -- close connection and die */
409339Seric # define CMDHELO	9	/* helo -- be polite */
419339Seric # define CMDDBGQSHOW	10	/* showq -- show send queue (DEBUG) */
429339Seric # define CMDDBGDEBUG	11	/* debug -- set debug mode */
439339Seric # define CMDVERB	12	/* verb -- go into verbose mode */
449339Seric # define CMDDBGKILL	13	/* kill -- kill sendmail */
459339Seric # define CMDDBGWIZ	14	/* wiz -- become a wizard */
469339Seric # define CMDONEX	15	/* onex -- sending one transaction only */
479339Seric # define CMDDBGSHELL	16	/* shell -- give us a shell */
484549Seric 
494549Seric static struct cmd	CmdTab[] =
504549Seric {
514549Seric 	"mail",		CMDMAIL,
524976Seric 	"rcpt",		CMDRCPT,
534549Seric 	"data",		CMDDATA,
544549Seric 	"rset",		CMDRSET,
554549Seric 	"vrfy",		CMDVRFY,
567762Seric 	"expn",		CMDVRFY,
574549Seric 	"help",		CMDHELP,
584549Seric 	"noop",		CMDNOOP,
594549Seric 	"quit",		CMDQUIT,
604976Seric 	"helo",		CMDHELO,
618544Seric 	"verb",		CMDVERB,
629314Seric 	"onex",		CMDONEX,
635003Seric # ifdef DEBUG
649339Seric 	"showq",	CMDDBGQSHOW,
658544Seric 	"debug",	CMDDBGDEBUG,
668544Seric 	"kill",		CMDDBGKILL,
678544Seric 	"wiz",		CMDDBGWIZ,
689339Seric 	"shell",	CMDDBGSHELL,
695003Seric # endif DEBUG
704549Seric 	NULL,		CMDERROR,
714549Seric };
724549Seric 
738544Seric # ifdef DEBUG
748544Seric bool	IsWiz = FALSE;			/* set if we are a wizard */
758544Seric char	*WizWord = NULL;		/* the wizard word to compare against */
768544Seric # endif DEBUG
779339Seric bool	InChild = FALSE;		/* true if running in a subprocess */
789339Seric #define EX_QUIT		22		/* special code for QUIT command */
798544Seric 
804549Seric smtp()
814549Seric {
824549Seric 	register char *p;
838544Seric 	register struct cmd *c;
844549Seric 	char *cmd;
854549Seric 	extern char *skipword();
864549Seric 	extern bool sameword();
874549Seric 	bool hasmail;			/* mail command received */
884713Seric 	int rcps;			/* number of recipients */
895003Seric 	auto ADDRESS *vrfyqueue;
909314Seric 	bool onexact = FALSE;		/* one transaction this connection */
918544Seric 	char inp[MAXLINE];
927124Seric 	extern char Version[];
937356Seric 	extern tick();
948544Seric 	extern bool iswiz();
95*9349Seric 	extern char *arpadate();
964549Seric 
975003Seric 	hasmail = FALSE;
984713Seric 	rcps = 0;
997363Seric 	if (OutChannel != stdout)
1007363Seric 	{
1017363Seric 		/* arrange for debugging output to go to remote host */
1027363Seric 		(void) close(1);
1037363Seric 		(void) dup(fileno(OutChannel));
1047363Seric 	}
1057851Seric 	message("220", "%s Sendmail %s ready at %s", HostName,
106*9349Seric 			Version, arpadate((char *) NULL));
1077762Seric 	(void) setjmp(TopFrame);
1087762Seric 	QuickAbort = FALSE;
1094549Seric 	for (;;)
1104549Seric 	{
1117356Seric 		/* setup for the read */
1126907Seric 		CurEnv->e_to = NULL;
1134577Seric 		Errors = 0;
1147275Seric 		(void) fflush(stdout);
1157356Seric 
1167356Seric 		/* read the input line */
1177685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1187356Seric 
1197685Seric 		/* handle errors */
1207356Seric 		if (p == NULL)
1217356Seric 		{
1224549Seric 			/* end of file, just die */
1234558Seric 			message("421", "%s Lost input channel", HostName);
1244549Seric 			finis();
1254549Seric 		}
1264549Seric 
1274549Seric 		/* clean up end of line */
1284558Seric 		fixcrlf(inp, TRUE);
1294549Seric 
1304713Seric 		/* echo command to transcript */
1319339Seric 		if (Xscript != NULL)
1329339Seric 			fprintf(Xscript, "<<< %s\n", inp);
1334713Seric 
1344549Seric 		/* break off command */
1354549Seric 		for (p = inp; isspace(*p); p++)
1364549Seric 			continue;
1374549Seric 		cmd = p;
1384549Seric 		while (*++p != '\0' && !isspace(*p))
1394549Seric 			continue;
1404549Seric 		if (*p != '\0')
1414549Seric 			*p++ = '\0';
1424549Seric 
1434549Seric 		/* decode command */
1444549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1454549Seric 		{
1464549Seric 			if (sameword(c->cmdname, cmd))
1474549Seric 				break;
1484549Seric 		}
1494549Seric 
1504549Seric 		/* process command */
1514549Seric 		switch (c->cmdcode)
1524549Seric 		{
1534976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
1545187Seric 			define('s', newstr(p));
1554997Seric 			message("250", "%s Hello %s, pleased to meet you",
1564997Seric 				HostName, p);
1574976Seric 			break;
1584976Seric 
1594549Seric 		  case CMDMAIL:		/* mail -- designate sender */
1609314Seric 			/* check for validity of this command */
1614558Seric 			if (hasmail)
1624558Seric 			{
1634558Seric 				message("503", "Sender already specified");
1644558Seric 				break;
1654558Seric 			}
1669339Seric 			if (InChild)
1679339Seric 			{
1689339Seric 				syserr("Nested MAIL command");
1699339Seric 				exit(0);
1709339Seric 			}
1719339Seric 
1729339Seric 			/* fork a subprocess to process this command */
1739339Seric 			if (runinchild("SMTP-MAIL") > 0)
1749339Seric 				break;
1759339Seric 			initsys();
1769339Seric 
1779339Seric 			/* child -- go do the processing */
1784549Seric 			p = skipword(p, "from");
1794549Seric 			if (p == NULL)
1804549Seric 				break;
1814549Seric 			setsender(p);
1824577Seric 			if (Errors == 0)
1834549Seric 			{
1844549Seric 				message("250", "Sender ok");
1854549Seric 				hasmail = TRUE;
1864549Seric 			}
1879339Seric 			else if (InChild)
1889339Seric 				finis();
1894549Seric 			break;
1904549Seric 
1914976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
1924549Seric 			p = skipword(p, "to");
1934549Seric 			if (p == NULL)
1944549Seric 				break;
1958081Seric 			sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
1964577Seric 			if (Errors == 0)
1974549Seric 			{
1986057Seric 				message("250", "%s... Recipient ok", p);
1994713Seric 				rcps++;
2004549Seric 			}
2014549Seric 			break;
2024549Seric 
2034549Seric 		  case CMDDATA:		/* data -- text of mail */
2044976Seric 			if (!hasmail)
2054549Seric 			{
2064976Seric 				message("503", "Need MAIL command");
2074976Seric 				break;
2084549Seric 			}
2094713Seric 			else if (rcps <= 0)
2104549Seric 			{
2114976Seric 				message("503", "Need RCPT (recipient)");
2124976Seric 				break;
2134549Seric 			}
2144976Seric 
2154976Seric 			/* collect the text of the message */
2164976Seric 			collect(TRUE);
2174976Seric 			if (Errors != 0)
2184976Seric 				break;
2194976Seric 
2208238Seric 			/*
2218238Seric 			**  Arrange to send to everyone.
2228238Seric 			**	If sending to multiple people, mail back
2238238Seric 			**		errors rather than reporting directly.
2248238Seric 			**	In any case, don't mail back errors for
2258238Seric 			**		anything that has happened up to
2268238Seric 			**		now (the other end will do this).
2278238Seric 			**	Then send to everyone.
2288238Seric 			**	Finally give a reply code.  If an error has
2298238Seric 			**		already been given, don't mail a
2308238Seric 			**		message back.
2319339Seric 			**	We goose error returns by clearing error bit.
2328238Seric 			*/
2338238Seric 
2344976Seric 			if (rcps != 1)
2354976Seric 				HoldErrs = MailBack = TRUE;
2369339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
2374976Seric 
2384976Seric 			/* send to all recipients */
2399283Seric 			sendall(CurEnv, SendMode);
2406907Seric 			CurEnv->e_to = NULL;
2414976Seric 
2428238Seric 			/* issue success if appropriate and reset */
2438238Seric 			if (Errors == 0 || HoldErrs)
2448238Seric 			{
2458238Seric 				HoldErrs = FALSE;
2469283Seric 				message("250", "Ok");
2478238Seric 			}
2488238Seric 			else
2499339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
2509339Seric 
2519339Seric 			/* if in a child, pop back to our parent */
2529339Seric 			if (InChild)
2539339Seric 				finis();
2544549Seric 			break;
2554549Seric 
2564549Seric 		  case CMDRSET:		/* rset -- reset state */
2574549Seric 			message("250", "Reset state");
2589339Seric 			if (InChild)
2599339Seric 				finis();
2609339Seric 			break;
2614549Seric 
2624549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
2639339Seric 			if (runinchild("SMTP-VRFY") > 0)
2649339Seric 				break;
2655003Seric 			vrfyqueue = NULL;
2667762Seric 			QuickAbort = TRUE;
2678081Seric 			sendto(p, (ADDRESS *) NULL, &vrfyqueue);
2687762Seric 			if (Errors != 0)
2699339Seric 			{
2709339Seric 				if (InChild)
2719339Seric 					finis();
2727762Seric 				break;
2739339Seric 			}
2745003Seric 			while (vrfyqueue != NULL)
2755003Seric 			{
2765003Seric 				register ADDRESS *a = vrfyqueue->q_next;
2775003Seric 				char *code;
2785003Seric 
2797685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
2805003Seric 					a = a->q_next;
2815003Seric 
2827685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
2835003Seric 				{
2845003Seric 					if (a != NULL)
2855003Seric 						code = "250-";
2865003Seric 					else
2875003Seric 						code = "250";
2885003Seric 					if (vrfyqueue->q_fullname == NULL)
2895003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
2905003Seric 					else
2915003Seric 						message(code, "%s <%s>",
2925003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
2935003Seric 				}
2945003Seric 				else if (a == NULL)
2955003Seric 					message("554", "Self destructive alias loop");
2965003Seric 				vrfyqueue = a;
2975003Seric 			}
2989339Seric 			if (InChild)
2999339Seric 				finis();
3004549Seric 			break;
3014549Seric 
3024549Seric 		  case CMDHELP:		/* help -- give user info */
3034577Seric 			if (*p == '\0')
3044577Seric 				p = "SMTP";
3054577Seric 			help(p);
3064549Seric 			break;
3074549Seric 
3084549Seric 		  case CMDNOOP:		/* noop -- do nothing */
3094549Seric 			message("200", "OK");
3104549Seric 			break;
3114549Seric 
3124549Seric 		  case CMDQUIT:		/* quit -- leave mail */
3134549Seric 			message("221", "%s closing connection", HostName);
3149339Seric 			if (InChild)
3159339Seric 				ExitStat = EX_QUIT;
3164549Seric 			finis();
3174549Seric 
3188544Seric 		  case CMDVERB:		/* set verbose mode */
3198544Seric 			Verbose = TRUE;
3208544Seric 			message("200", "Verbose mode");
3218544Seric 			break;
3228544Seric 
3239314Seric 		  case CMDONEX:		/* doing one transaction only */
3249314Seric 			onexact = TRUE;
3259314Seric 			message("200", "Only one transaction");
3269314Seric 			break;
3279314Seric 
3285003Seric # ifdef DEBUG
3299339Seric 		  case CMDDBGQSHOW:	/* show queues */
3306907Seric 			printf("Send Queue=");
3316907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
3325003Seric 			break;
3337275Seric 
3347275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
3357676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
3367676Seric 			tTflag(p);
3377676Seric 			message("200", "Debug set");
3387275Seric 			break;
3397275Seric 
3407282Seric 		  case CMDDBGKILL:	/* kill the parent */
3418544Seric 			if (!iswiz())
3428544Seric 				break;
3437282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
3447282Seric 				message("200", "Mother is dead");
3457282Seric 			else
3467282Seric 				message("500", "Can't kill Mom");
3477282Seric 			break;
3488544Seric 
3499339Seric 		  case CMDDBGSHELL:	/* give us an interactive shell */
3509339Seric 			if (!iswiz())
3519339Seric 				break;
3529339Seric 			execl("/bin/csh", "sendmail", 0);
3539339Seric 			execl("/bin/sh", "sendmail", 0);
3549339Seric 			message("500", "Can't");
3559339Seric 			break;
3569339Seric 
3578544Seric 		  case CMDDBGWIZ:	/* become a wizard */
3588544Seric 			if (WizWord != NULL)
3598544Seric 			{
3608544Seric 				char seed[3];
3618544Seric 				extern char *crypt();
3628544Seric 
3638544Seric 				strncpy(seed, WizWord, 2);
3648544Seric 				if (strcmp(WizWord, crypt(p, seed)) != 0)
3658544Seric 				{
3668544Seric 					message("500", "You are no wizard!");
3678544Seric 					break;
3688544Seric 				}
3698544Seric 			}
3708544Seric 			IsWiz = TRUE;
3718544Seric 			message("200", "Please pass, oh mighty wizard");
3728544Seric 			break;
3735003Seric # endif DEBUG
3745003Seric 
3754549Seric 		  case CMDERROR:	/* unknown command */
3764549Seric 			message("500", "Command unrecognized");
3774549Seric 			break;
3784549Seric 
3794549Seric 		  default:
3804549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
3814549Seric 			break;
3824549Seric 		}
3834549Seric 	}
3844549Seric }
3854549Seric /*
3864549Seric **  SKIPWORD -- skip a fixed word.
3874549Seric **
3884549Seric **	Parameters:
3894549Seric **		p -- place to start looking.
3904549Seric **		w -- word to skip.
3914549Seric **
3924549Seric **	Returns:
3934549Seric **		p following w.
3944549Seric **		NULL on error.
3954549Seric **
3964549Seric **	Side Effects:
3974549Seric **		clobbers the p data area.
3984549Seric */
3994549Seric 
4004549Seric static char *
4014549Seric skipword(p, w)
4024549Seric 	register char *p;
4034549Seric 	char *w;
4044549Seric {
4054549Seric 	register char *q;
4064549Seric 	extern bool sameword();
4074549Seric 
4084549Seric 	/* find beginning of word */
4094549Seric 	while (isspace(*p))
4104549Seric 		p++;
4114549Seric 	q = p;
4124549Seric 
4134549Seric 	/* find end of word */
4144549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
4154549Seric 		p++;
4164549Seric 	while (isspace(*p))
4174549Seric 		*p++ = '\0';
4184549Seric 	if (*p != ':')
4194549Seric 	{
4204549Seric 	  syntax:
4214549Seric 		message("501", "Syntax error");
4224549Seric 		Errors++;
4234549Seric 		return (NULL);
4244549Seric 	}
4254549Seric 	*p++ = '\0';
4264549Seric 	while (isspace(*p))
4274549Seric 		p++;
4284549Seric 
4294549Seric 	/* see if the input word matches desired word */
4304549Seric 	if (!sameword(q, w))
4314549Seric 		goto syntax;
4324549Seric 
4334549Seric 	return (p);
4344549Seric }
4354577Seric /*
4364577Seric **  HELP -- implement the HELP command.
4374577Seric **
4384577Seric **	Parameters:
4394577Seric **		topic -- the topic we want help for.
4404577Seric **
4414577Seric **	Returns:
4424577Seric **		none.
4434577Seric **
4444577Seric **	Side Effects:
4454577Seric **		outputs the help file to message output.
4464577Seric */
4474577Seric 
4484577Seric help(topic)
4494577Seric 	char *topic;
4504577Seric {
4514577Seric 	register FILE *hf;
4524577Seric 	int len;
4534577Seric 	char buf[MAXLINE];
4544577Seric 	bool noinfo;
4554577Seric 
4568269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
4574577Seric 	{
4584577Seric 		/* no help */
4594577Seric 		message("502", "HELP not implemented");
4604577Seric 		return;
4614577Seric 	}
4624577Seric 
4634577Seric 	len = strlen(topic);
4644577Seric 	makelower(topic);
4654577Seric 	noinfo = TRUE;
4664577Seric 
4674577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
4684577Seric 	{
4694577Seric 		if (strncmp(buf, topic, len) == 0)
4704577Seric 		{
4714577Seric 			register char *p;
4724577Seric 
4734577Seric 			p = index(buf, '\t');
4744577Seric 			if (p == NULL)
4754577Seric 				p = buf;
4764577Seric 			else
4774577Seric 				p++;
4784577Seric 			fixcrlf(p, TRUE);
4794577Seric 			message("214-", p);
4804577Seric 			noinfo = FALSE;
4814577Seric 		}
4824577Seric 	}
4834577Seric 
4844577Seric 	if (noinfo)
4854577Seric 		message("504", "HELP topic unknown");
4864577Seric 	else
4874577Seric 		message("214", "End of HELP info");
4884628Seric 	(void) fclose(hf);
4894577Seric }
4908544Seric /*
4918544Seric **  ISWIZ -- tell us if we are a wizard
4928544Seric **
4938544Seric **	If not, print a nasty message.
4948544Seric **
4958544Seric **	Parameters:
4968544Seric **		none.
4978544Seric **
4988544Seric **	Returns:
4998544Seric **		TRUE if we are a wizard.
5008544Seric **		FALSE if we are not a wizard.
5018544Seric **
5028544Seric **	Side Effects:
5038544Seric **		Prints a 500 exit stat if we are not a wizard.
5048544Seric */
5055181Seric 
5068544Seric bool
5078544Seric iswiz()
5088544Seric {
5098544Seric 	if (!IsWiz)
5108544Seric 		message("500", "Mere mortals musn't mutter that mantra");
5118544Seric 	return (IsWiz);
5128544Seric }
5139339Seric /*
5149339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
5159339Seric **
5169339Seric **	Parameters:
5179339Seric **		label -- a string used in error messages
5189339Seric **
5199339Seric **	Returns:
5209339Seric **		zero in the child
5219339Seric **		one in the parent
5229339Seric **
5239339Seric **	Side Effects:
5249339Seric **		none.
5259339Seric */
5268544Seric 
5279339Seric runinchild(label)
5289339Seric 	char *label;
5299339Seric {
5309339Seric 	int childpid;
5319339Seric 
5329339Seric 	childpid = dofork();
5339339Seric 	if (childpid < 0)
5349339Seric 	{
5359339Seric 		syserr("%s: cannot fork", label);
5369339Seric 		return (1);
5379339Seric 	}
5389339Seric 	if (childpid > 0)
5399339Seric 	{
5409339Seric 		/* parent -- wait for child to complete */
5419339Seric 		auto int st;
5429339Seric 		int i;
5439339Seric 
5449339Seric 		while ((i = wait(&st)) != childpid)
5459339Seric 		{
5469339Seric 			if (i < 0 && errno != EINTR)
5479339Seric 				break;
5489339Seric 		}
5499339Seric 		if (i < 0)
5509339Seric 			syserr("%s: lost child", label);
5519339Seric 
5529339Seric 		/* if we exited on a QUIT command, complete the process */
5539339Seric 		if (st == (EX_QUIT << 8))
5549339Seric 			finis();
5559339Seric 
5569339Seric 		return (1);
5579339Seric 	}
5589339Seric 	else
5599339Seric 	{
5609339Seric 		/* child */
5619339Seric 		InChild = TRUE;
5629339Seric 		return (0);
5639339Seric 	}
5649339Seric }
5659339Seric 
5665181Seric # endif SMTP
567