19339Seric # include <errno.h>
24549Seric # include "sendmail.h"
34549Seric 
45181Seric # ifndef SMTP
5*9545Seric SCCSID(@(#)srvrsmtp.c	3.41		12/05/82	(no SMTP));
65181Seric # else SMTP
74556Seric 
8*9545Seric SCCSID(@(#)srvrsmtp.c	3.41		12/05/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 */
789378Seric bool	OneXact = FALSE;		/* one xaction only this run */
799339Seric #define EX_QUIT		22		/* special code for QUIT command */
808544Seric 
814549Seric smtp()
824549Seric {
834549Seric 	register char *p;
848544Seric 	register struct cmd *c;
854549Seric 	char *cmd;
864549Seric 	extern char *skipword();
874549Seric 	extern bool sameword();
884549Seric 	bool hasmail;			/* mail command received */
894713Seric 	int rcps;			/* number of recipients */
905003Seric 	auto ADDRESS *vrfyqueue;
918544Seric 	char inp[MAXLINE];
927124Seric 	extern char Version[];
937356Seric 	extern tick();
948544Seric 	extern bool iswiz();
959349Seric 	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,
1069349Seric 			Version, arpadate((char *) NULL));
1077762Seric 	(void) setjmp(TopFrame);
1087762Seric 	QuickAbort = FALSE;
1099390Seric 	HoldErrs = FALSE;
1104549Seric 	for (;;)
1114549Seric 	{
1127356Seric 		/* setup for the read */
1136907Seric 		CurEnv->e_to = NULL;
1144577Seric 		Errors = 0;
1157275Seric 		(void) fflush(stdout);
1167356Seric 
1177356Seric 		/* read the input line */
1187685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1197356Seric 
1207685Seric 		/* handle errors */
1217356Seric 		if (p == NULL)
1227356Seric 		{
1234549Seric 			/* end of file, just die */
1244558Seric 			message("421", "%s Lost input channel", HostName);
1254549Seric 			finis();
1264549Seric 		}
1274549Seric 
1284549Seric 		/* clean up end of line */
1294558Seric 		fixcrlf(inp, TRUE);
1304549Seric 
1314713Seric 		/* echo command to transcript */
132*9545Seric 		if (CurEnv->e_xfp != NULL)
133*9545Seric 			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
1344713Seric 
1354549Seric 		/* break off command */
1364549Seric 		for (p = inp; isspace(*p); p++)
1374549Seric 			continue;
1384549Seric 		cmd = p;
1394549Seric 		while (*++p != '\0' && !isspace(*p))
1404549Seric 			continue;
1414549Seric 		if (*p != '\0')
1424549Seric 			*p++ = '\0';
1434549Seric 
1444549Seric 		/* decode command */
1454549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1464549Seric 		{
1474549Seric 			if (sameword(c->cmdname, cmd))
1484549Seric 				break;
1494549Seric 		}
1504549Seric 
1514549Seric 		/* process command */
1524549Seric 		switch (c->cmdcode)
1534549Seric 		{
1544976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
1559378Seric 			define('s', newstr(p), CurEnv);
1564997Seric 			message("250", "%s Hello %s, pleased to meet you",
1574997Seric 				HostName, p);
1584976Seric 			break;
1594976Seric 
1604549Seric 		  case CMDMAIL:		/* mail -- designate sender */
1619314Seric 			/* check for validity of this command */
1624558Seric 			if (hasmail)
1634558Seric 			{
1644558Seric 				message("503", "Sender already specified");
1654558Seric 				break;
1664558Seric 			}
1679339Seric 			if (InChild)
1689339Seric 			{
1699339Seric 				syserr("Nested MAIL command");
1709339Seric 				exit(0);
1719339Seric 			}
1729339Seric 
1739339Seric 			/* fork a subprocess to process this command */
1749339Seric 			if (runinchild("SMTP-MAIL") > 0)
1759339Seric 				break;
1769339Seric 			initsys();
1779339Seric 
1789339Seric 			/* child -- go do the processing */
1794549Seric 			p = skipword(p, "from");
1804549Seric 			if (p == NULL)
1814549Seric 				break;
1824549Seric 			setsender(p);
1834577Seric 			if (Errors == 0)
1844549Seric 			{
1854549Seric 				message("250", "Sender ok");
1864549Seric 				hasmail = TRUE;
1874549Seric 			}
1889339Seric 			else if (InChild)
1899339Seric 				finis();
1904549Seric 			break;
1914549Seric 
1924976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
1934549Seric 			p = skipword(p, "to");
1944549Seric 			if (p == NULL)
1954549Seric 				break;
1968081Seric 			sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
1979390Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
1984577Seric 			if (Errors == 0)
1994549Seric 			{
2006057Seric 				message("250", "%s... Recipient ok", p);
2014713Seric 				rcps++;
2024549Seric 			}
2034549Seric 			break;
2044549Seric 
2054549Seric 		  case CMDDATA:		/* data -- text of mail */
2064976Seric 			if (!hasmail)
2074549Seric 			{
2084976Seric 				message("503", "Need MAIL command");
2094976Seric 				break;
2104549Seric 			}
2114713Seric 			else if (rcps <= 0)
2124549Seric 			{
2134976Seric 				message("503", "Need RCPT (recipient)");
2144976Seric 				break;
2154549Seric 			}
2164976Seric 
2174976Seric 			/* collect the text of the message */
2184976Seric 			collect(TRUE);
2194976Seric 			if (Errors != 0)
2204976Seric 				break;
2214976Seric 
2228238Seric 			/*
2238238Seric 			**  Arrange to send to everyone.
2248238Seric 			**	If sending to multiple people, mail back
2258238Seric 			**		errors rather than reporting directly.
2268238Seric 			**	In any case, don't mail back errors for
2278238Seric 			**		anything that has happened up to
2288238Seric 			**		now (the other end will do this).
2298238Seric 			**	Then send to everyone.
2308238Seric 			**	Finally give a reply code.  If an error has
2318238Seric 			**		already been given, don't mail a
2328238Seric 			**		message back.
2339339Seric 			**	We goose error returns by clearing error bit.
2348238Seric 			*/
2358238Seric 
2364976Seric 			if (rcps != 1)
2379378Seric 			{
2389378Seric 				HoldErrs = TRUE;
2399378Seric 				ErrorMode == EM_MAIL;
2409378Seric 			}
2419339Seric 			CurEnv->e_flags &= ~EF_FATALERRS;
2424976Seric 
2434976Seric 			/* send to all recipients */
2449283Seric 			sendall(CurEnv, SendMode);
2456907Seric 			CurEnv->e_to = NULL;
2464976Seric 
2478238Seric 			/* issue success if appropriate and reset */
2488238Seric 			if (Errors == 0 || HoldErrs)
2499283Seric 				message("250", "Ok");
2508238Seric 			else
2519339Seric 				CurEnv->e_flags &= ~EF_FATALERRS;
2529339Seric 
2539339Seric 			/* if in a child, pop back to our parent */
2549339Seric 			if (InChild)
2559339Seric 				finis();
2564549Seric 			break;
2574549Seric 
2584549Seric 		  case CMDRSET:		/* rset -- reset state */
2594549Seric 			message("250", "Reset state");
2609339Seric 			if (InChild)
2619339Seric 				finis();
2629339Seric 			break;
2634549Seric 
2644549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
2659339Seric 			if (runinchild("SMTP-VRFY") > 0)
2669339Seric 				break;
2675003Seric 			vrfyqueue = NULL;
2687762Seric 			QuickAbort = TRUE;
2698081Seric 			sendto(p, (ADDRESS *) NULL, &vrfyqueue);
2707762Seric 			if (Errors != 0)
2719339Seric 			{
2729339Seric 				if (InChild)
2739339Seric 					finis();
2747762Seric 				break;
2759339Seric 			}
2765003Seric 			while (vrfyqueue != NULL)
2775003Seric 			{
2785003Seric 				register ADDRESS *a = vrfyqueue->q_next;
2795003Seric 				char *code;
2805003Seric 
2817685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
2825003Seric 					a = a->q_next;
2835003Seric 
2847685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
2855003Seric 				{
2865003Seric 					if (a != NULL)
2875003Seric 						code = "250-";
2885003Seric 					else
2895003Seric 						code = "250";
2905003Seric 					if (vrfyqueue->q_fullname == NULL)
2915003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
2925003Seric 					else
2935003Seric 						message(code, "%s <%s>",
2945003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
2955003Seric 				}
2965003Seric 				else if (a == NULL)
2975003Seric 					message("554", "Self destructive alias loop");
2985003Seric 				vrfyqueue = a;
2995003Seric 			}
3009339Seric 			if (InChild)
3019339Seric 				finis();
3024549Seric 			break;
3034549Seric 
3044549Seric 		  case CMDHELP:		/* help -- give user info */
3054577Seric 			if (*p == '\0')
3064577Seric 				p = "SMTP";
3074577Seric 			help(p);
3084549Seric 			break;
3094549Seric 
3104549Seric 		  case CMDNOOP:		/* noop -- do nothing */
3114549Seric 			message("200", "OK");
3124549Seric 			break;
3134549Seric 
3144549Seric 		  case CMDQUIT:		/* quit -- leave mail */
3154549Seric 			message("221", "%s closing connection", HostName);
3169339Seric 			if (InChild)
3179339Seric 				ExitStat = EX_QUIT;
3184549Seric 			finis();
3194549Seric 
3208544Seric 		  case CMDVERB:		/* set verbose mode */
3218544Seric 			Verbose = TRUE;
3228544Seric 			message("200", "Verbose mode");
3238544Seric 			break;
3248544Seric 
3259314Seric 		  case CMDONEX:		/* doing one transaction only */
3269378Seric 			OneXact = TRUE;
3279314Seric 			message("200", "Only one transaction");
3289314Seric 			break;
3299314Seric 
3305003Seric # ifdef DEBUG
3319339Seric 		  case CMDDBGQSHOW:	/* show queues */
3326907Seric 			printf("Send Queue=");
3336907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
3345003Seric 			break;
3357275Seric 
3367275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
3377676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
3387676Seric 			tTflag(p);
3397676Seric 			message("200", "Debug set");
3407275Seric 			break;
3417275Seric 
3427282Seric 		  case CMDDBGKILL:	/* kill the parent */
3438544Seric 			if (!iswiz())
3448544Seric 				break;
3457282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
3467282Seric 				message("200", "Mother is dead");
3477282Seric 			else
3487282Seric 				message("500", "Can't kill Mom");
3497282Seric 			break;
3508544Seric 
3519339Seric 		  case CMDDBGSHELL:	/* give us an interactive shell */
3529339Seric 			if (!iswiz())
3539339Seric 				break;
3549378Seric 			if (fileno(InChannel) != 0)
3559378Seric 			{
3569378Seric 				(void) close(0);
3579378Seric 				(void) dup(fileno(InChannel));
3589378Seric 				(void) fclose(InChannel);
3599378Seric 				InChannel = stdin;
3609378Seric 			}
3619378Seric 			if (fileno(OutChannel) != 1)
3629378Seric 			{
3639378Seric 				(void) close(1);
3649378Seric 				(void) dup(fileno(OutChannel));
3659378Seric 				(void) fclose(OutChannel);
3669378Seric 				OutChannel = stdout;
3679378Seric 			}
3689339Seric 			execl("/bin/csh", "sendmail", 0);
3699339Seric 			execl("/bin/sh", "sendmail", 0);
3709339Seric 			message("500", "Can't");
3719378Seric 			exit(EX_UNAVAILABLE);
3729339Seric 
3738544Seric 		  case CMDDBGWIZ:	/* become a wizard */
3748544Seric 			if (WizWord != NULL)
3758544Seric 			{
3768544Seric 				char seed[3];
3778544Seric 				extern char *crypt();
3788544Seric 
3798544Seric 				strncpy(seed, WizWord, 2);
3808544Seric 				if (strcmp(WizWord, crypt(p, seed)) != 0)
3818544Seric 				{
3828544Seric 					message("500", "You are no wizard!");
3838544Seric 					break;
3848544Seric 				}
3858544Seric 			}
3868544Seric 			IsWiz = TRUE;
3878544Seric 			message("200", "Please pass, oh mighty wizard");
3888544Seric 			break;
3895003Seric # endif DEBUG
3905003Seric 
3914549Seric 		  case CMDERROR:	/* unknown command */
3924549Seric 			message("500", "Command unrecognized");
3934549Seric 			break;
3944549Seric 
3954549Seric 		  default:
3964549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
3974549Seric 			break;
3984549Seric 		}
3994549Seric 	}
4004549Seric }
4014549Seric /*
4024549Seric **  SKIPWORD -- skip a fixed word.
4034549Seric **
4044549Seric **	Parameters:
4054549Seric **		p -- place to start looking.
4064549Seric **		w -- word to skip.
4074549Seric **
4084549Seric **	Returns:
4094549Seric **		p following w.
4104549Seric **		NULL on error.
4114549Seric **
4124549Seric **	Side Effects:
4134549Seric **		clobbers the p data area.
4144549Seric */
4154549Seric 
4164549Seric static char *
4174549Seric skipword(p, w)
4184549Seric 	register char *p;
4194549Seric 	char *w;
4204549Seric {
4214549Seric 	register char *q;
4224549Seric 	extern bool sameword();
4234549Seric 
4244549Seric 	/* find beginning of word */
4254549Seric 	while (isspace(*p))
4264549Seric 		p++;
4274549Seric 	q = p;
4284549Seric 
4294549Seric 	/* find end of word */
4304549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
4314549Seric 		p++;
4324549Seric 	while (isspace(*p))
4334549Seric 		*p++ = '\0';
4344549Seric 	if (*p != ':')
4354549Seric 	{
4364549Seric 	  syntax:
4374549Seric 		message("501", "Syntax error");
4384549Seric 		Errors++;
4394549Seric 		return (NULL);
4404549Seric 	}
4414549Seric 	*p++ = '\0';
4424549Seric 	while (isspace(*p))
4434549Seric 		p++;
4444549Seric 
4454549Seric 	/* see if the input word matches desired word */
4464549Seric 	if (!sameword(q, w))
4474549Seric 		goto syntax;
4484549Seric 
4494549Seric 	return (p);
4504549Seric }
4514577Seric /*
4524577Seric **  HELP -- implement the HELP command.
4534577Seric **
4544577Seric **	Parameters:
4554577Seric **		topic -- the topic we want help for.
4564577Seric **
4574577Seric **	Returns:
4584577Seric **		none.
4594577Seric **
4604577Seric **	Side Effects:
4614577Seric **		outputs the help file to message output.
4624577Seric */
4634577Seric 
4644577Seric help(topic)
4654577Seric 	char *topic;
4664577Seric {
4674577Seric 	register FILE *hf;
4684577Seric 	int len;
4694577Seric 	char buf[MAXLINE];
4704577Seric 	bool noinfo;
4714577Seric 
4728269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
4734577Seric 	{
4744577Seric 		/* no help */
4754577Seric 		message("502", "HELP not implemented");
4764577Seric 		return;
4774577Seric 	}
4784577Seric 
4794577Seric 	len = strlen(topic);
4804577Seric 	makelower(topic);
4814577Seric 	noinfo = TRUE;
4824577Seric 
4834577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
4844577Seric 	{
4854577Seric 		if (strncmp(buf, topic, len) == 0)
4864577Seric 		{
4874577Seric 			register char *p;
4884577Seric 
4894577Seric 			p = index(buf, '\t');
4904577Seric 			if (p == NULL)
4914577Seric 				p = buf;
4924577Seric 			else
4934577Seric 				p++;
4944577Seric 			fixcrlf(p, TRUE);
4954577Seric 			message("214-", p);
4964577Seric 			noinfo = FALSE;
4974577Seric 		}
4984577Seric 	}
4994577Seric 
5004577Seric 	if (noinfo)
5014577Seric 		message("504", "HELP topic unknown");
5024577Seric 	else
5034577Seric 		message("214", "End of HELP info");
5044628Seric 	(void) fclose(hf);
5054577Seric }
5068544Seric /*
5078544Seric **  ISWIZ -- tell us if we are a wizard
5088544Seric **
5098544Seric **	If not, print a nasty message.
5108544Seric **
5118544Seric **	Parameters:
5128544Seric **		none.
5138544Seric **
5148544Seric **	Returns:
5158544Seric **		TRUE if we are a wizard.
5168544Seric **		FALSE if we are not a wizard.
5178544Seric **
5188544Seric **	Side Effects:
5198544Seric **		Prints a 500 exit stat if we are not a wizard.
5208544Seric */
5215181Seric 
5228544Seric bool
5238544Seric iswiz()
5248544Seric {
5258544Seric 	if (!IsWiz)
5268544Seric 		message("500", "Mere mortals musn't mutter that mantra");
5278544Seric 	return (IsWiz);
5288544Seric }
5299339Seric /*
5309339Seric **  RUNINCHILD -- return twice -- once in the child, then in the parent again
5319339Seric **
5329339Seric **	Parameters:
5339339Seric **		label -- a string used in error messages
5349339Seric **
5359339Seric **	Returns:
5369339Seric **		zero in the child
5379339Seric **		one in the parent
5389339Seric **
5399339Seric **	Side Effects:
5409339Seric **		none.
5419339Seric */
5428544Seric 
5439339Seric runinchild(label)
5449339Seric 	char *label;
5459339Seric {
5469339Seric 	int childpid;
5479339Seric 
5489378Seric 	if (OneXact)
5499378Seric 		return (0);
5509378Seric 
5519339Seric 	childpid = dofork();
5529339Seric 	if (childpid < 0)
5539339Seric 	{
5549339Seric 		syserr("%s: cannot fork", label);
5559339Seric 		return (1);
5569339Seric 	}
5579339Seric 	if (childpid > 0)
5589339Seric 	{
5599339Seric 		auto int st;
5609339Seric 
5619378Seric 		/* parent -- wait for child to complete */
5629378Seric 		st = waitfor(childpid);
5639378Seric 		if (st == -1)
5649339Seric 			syserr("%s: lost child", label);
5659339Seric 
5669339Seric 		/* if we exited on a QUIT command, complete the process */
5679339Seric 		if (st == (EX_QUIT << 8))
5689339Seric 			finis();
5699339Seric 
5709339Seric 		return (1);
5719339Seric 	}
5729339Seric 	else
5739339Seric 	{
5749339Seric 		/* child */
5759339Seric 		InChild = TRUE;
576*9545Seric 		clearenvelope(CurEnv);
5779339Seric 		return (0);
5789339Seric 	}
5799339Seric }
5809339Seric 
5815181Seric # endif SMTP
582