14549Seric # include "sendmail.h"
24549Seric 
35181Seric # ifndef SMTP
4*9283Seric SCCSID(@(#)srvrsmtp.c	3.35		11/17/82	(no SMTP));
55181Seric # else SMTP
64556Seric 
7*9283Seric SCCSID(@(#)srvrsmtp.c	3.35		11/17/82);
85181Seric 
94549Seric /*
104549Seric **  SMTP -- run the SMTP protocol.
114549Seric **
124549Seric **	Parameters:
134549Seric **		none.
144549Seric **
154549Seric **	Returns:
164549Seric **		never.
174549Seric **
184549Seric **	Side Effects:
194549Seric **		Reads commands from the input channel and processes
204549Seric **			them.
214549Seric */
224549Seric 
234549Seric struct cmd
244549Seric {
254549Seric 	char	*cmdname;	/* command name */
264549Seric 	int	cmdcode;	/* internal code, see below */
274549Seric };
284549Seric 
294549Seric /* values for cmdcode */
304549Seric # define CMDERROR	0	/* bad command */
314549Seric # define CMDMAIL	1	/* mail -- designate sender */
324976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
334549Seric # define CMDDATA	3	/* data -- send message text */
344549Seric # define CMDRSET	5	/* rset -- reset state */
354549Seric # define CMDVRFY	6	/* vrfy -- verify address */
364549Seric # define CMDHELP	7	/* help -- give usage info */
374549Seric # define CMDNOOP	8	/* noop -- do nothing */
384549Seric # define CMDQUIT	9	/* quit -- close connection and die */
394577Seric # define CMDMRSQ	10	/* mrsq -- for old mtp compat only */
404976Seric # define CMDHELO	11	/* helo -- be polite */
418544Seric # define CMDDBGSHOWQ	12	/* showq -- show send queue (DEBUG) */
428544Seric # define CMDDBGDEBUG	13	/* debug -- set debug mode */
438544Seric # define CMDVERB	14	/* verb -- go into verbose mode */
448544Seric # define CMDDBGKILL	15	/* kill -- kill sendmail */
458544Seric # define CMDDBGWIZ	16	/* wiz -- become a wizard */
464549Seric 
474549Seric static struct cmd	CmdTab[] =
484549Seric {
494549Seric 	"mail",		CMDMAIL,
504976Seric 	"rcpt",		CMDRCPT,
514976Seric 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
524549Seric 	"data",		CMDDATA,
534549Seric 	"rset",		CMDRSET,
544549Seric 	"vrfy",		CMDVRFY,
557762Seric 	"expn",		CMDVRFY,
564549Seric 	"help",		CMDHELP,
574549Seric 	"noop",		CMDNOOP,
584549Seric 	"quit",		CMDQUIT,
594577Seric 	"mrsq",		CMDMRSQ,
604976Seric 	"helo",		CMDHELO,
618544Seric 	"verb",		CMDVERB,
625003Seric # ifdef DEBUG
638544Seric 	"showq",	CMDDBGSHOWQ,
648544Seric 	"debug",	CMDDBGDEBUG,
658544Seric 	"kill",		CMDDBGKILL,
668544Seric 	"wiz",		CMDDBGWIZ,
675003Seric # endif DEBUG
684549Seric 	NULL,		CMDERROR,
694549Seric };
704549Seric 
718544Seric # ifdef DEBUG
728544Seric bool	IsWiz = FALSE;			/* set if we are a wizard */
738544Seric char	*WizWord = NULL;		/* the wizard word to compare against */
748544Seric # endif DEBUG
758544Seric 
764549Seric smtp()
774549Seric {
784549Seric 	register char *p;
798544Seric 	register struct cmd *c;
804549Seric 	char *cmd;
814549Seric 	extern char *skipword();
824549Seric 	extern bool sameword();
834549Seric 	bool hasmail;			/* mail command received */
844713Seric 	int rcps;			/* number of recipients */
855003Seric 	auto ADDRESS *vrfyqueue;
868544Seric 	char inp[MAXLINE];
877124Seric 	extern char Version[];
887356Seric 	extern tick();
898544Seric 	extern bool iswiz();
904549Seric 
915003Seric 	hasmail = FALSE;
924713Seric 	rcps = 0;
937363Seric 	if (OutChannel != stdout)
947363Seric 	{
957363Seric 		/* arrange for debugging output to go to remote host */
967363Seric 		(void) close(1);
977363Seric 		(void) dup(fileno(OutChannel));
987363Seric 	}
997851Seric 	message("220", "%s Sendmail %s ready at %s", HostName,
1007797Seric 			Version, arpadate(NULL));
1017762Seric 	(void) setjmp(TopFrame);
1027762Seric 	QuickAbort = FALSE;
1034549Seric 	for (;;)
1044549Seric 	{
1057356Seric 		/* setup for the read */
1066907Seric 		CurEnv->e_to = NULL;
1074577Seric 		Errors = 0;
1087275Seric 		(void) fflush(stdout);
1097356Seric 
1107356Seric 		/* read the input line */
1117685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1127356Seric 
1137685Seric 		/* handle errors */
1147356Seric 		if (p == NULL)
1157356Seric 		{
1164549Seric 			/* end of file, just die */
1174558Seric 			message("421", "%s Lost input channel", HostName);
1184549Seric 			finis();
1194549Seric 		}
1204549Seric 
1214549Seric 		/* clean up end of line */
1224558Seric 		fixcrlf(inp, TRUE);
1234549Seric 
1244713Seric 		/* echo command to transcript */
1257557Seric 		fprintf(Xscript, "<<< %s\n", inp);
1264713Seric 
1274549Seric 		/* break off command */
1284549Seric 		for (p = inp; isspace(*p); p++)
1294549Seric 			continue;
1304549Seric 		cmd = p;
1314549Seric 		while (*++p != '\0' && !isspace(*p))
1324549Seric 			continue;
1334549Seric 		if (*p != '\0')
1344549Seric 			*p++ = '\0';
1354549Seric 
1364549Seric 		/* decode command */
1374549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1384549Seric 		{
1394549Seric 			if (sameword(c->cmdname, cmd))
1404549Seric 				break;
1414549Seric 		}
1424549Seric 
1434549Seric 		/* process command */
1444549Seric 		switch (c->cmdcode)
1454549Seric 		{
1464976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
1475187Seric 			define('s', newstr(p));
1484997Seric 			message("250", "%s Hello %s, pleased to meet you",
1494997Seric 				HostName, p);
1504976Seric 			break;
1514976Seric 
1524549Seric 		  case CMDMAIL:		/* mail -- designate sender */
1534558Seric 			if (hasmail)
1544558Seric 			{
1554558Seric 				message("503", "Sender already specified");
1564558Seric 				break;
1574558Seric 			}
1584549Seric 			p = skipword(p, "from");
1594549Seric 			if (p == NULL)
1604549Seric 				break;
1614549Seric 			if (index(p, ',') != NULL)
1624549Seric 			{
1634549Seric 				message("501", "Source routing not implemented");
1644549Seric 				Errors++;
1654549Seric 				break;
1664549Seric 			}
1674549Seric 			setsender(p);
1684577Seric 			if (Errors == 0)
1694549Seric 			{
1704549Seric 				message("250", "Sender ok");
1714549Seric 				hasmail = TRUE;
1724549Seric 			}
1734549Seric 			break;
1744549Seric 
1754976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
1764549Seric 			p = skipword(p, "to");
1774549Seric 			if (p == NULL)
1784549Seric 				break;
1798081Seric 			sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
1804577Seric 			if (Errors == 0)
1814549Seric 			{
1826057Seric 				message("250", "%s... Recipient ok", p);
1834713Seric 				rcps++;
1844549Seric 			}
1854549Seric 			break;
1864549Seric 
1874549Seric 		  case CMDDATA:		/* data -- text of mail */
1884976Seric 			if (!hasmail)
1894549Seric 			{
1904976Seric 				message("503", "Need MAIL command");
1914976Seric 				break;
1924549Seric 			}
1934713Seric 			else if (rcps <= 0)
1944549Seric 			{
1954976Seric 				message("503", "Need RCPT (recipient)");
1964976Seric 				break;
1974549Seric 			}
1984976Seric 
1994976Seric 			/* collect the text of the message */
2004976Seric 			collect(TRUE);
2014976Seric 			if (Errors != 0)
2024976Seric 				break;
2034976Seric 
2048238Seric 			/*
2058238Seric 			**  Arrange to send to everyone.
2068238Seric 			**	If sending to multiple people, mail back
2078238Seric 			**		errors rather than reporting directly.
2088238Seric 			**	In any case, don't mail back errors for
2098238Seric 			**		anything that has happened up to
2108238Seric 			**		now (the other end will do this).
2118238Seric 			**	Then send to everyone.
2128238Seric 			**	Finally give a reply code.  If an error has
2138238Seric 			**		already been given, don't mail a
2148238Seric 			**		message back.
2158238Seric 			**	We goose error returns by clearing FatalErrors.
2168238Seric 			*/
2178238Seric 
2184976Seric 			if (rcps != 1)
2194976Seric 				HoldErrs = MailBack = TRUE;
2208238Seric 			FatalErrors = FALSE;
2214976Seric 
2224976Seric 			/* send to all recipients */
223*9283Seric 			sendall(CurEnv, SendMode);
2246907Seric 			CurEnv->e_to = NULL;
2254976Seric 
2268238Seric 			/* issue success if appropriate and reset */
2278238Seric 			if (Errors == 0 || HoldErrs)
2288238Seric 			{
2298238Seric 				HoldErrs = FALSE;
230*9283Seric 				message("250", "Ok");
2318238Seric 			}
2328238Seric 			else
2338238Seric 				FatalErrors = FALSE;
2344549Seric 			break;
2354549Seric 
2364549Seric 		  case CMDRSET:		/* rset -- reset state */
2374549Seric 			message("250", "Reset state");
2384549Seric 			finis();
2394549Seric 
2404549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
2415003Seric 			vrfyqueue = NULL;
2427762Seric 			QuickAbort = TRUE;
2438081Seric 			sendto(p, (ADDRESS *) NULL, &vrfyqueue);
2447762Seric 			if (Errors != 0)
2457762Seric 				break;
2465003Seric 			while (vrfyqueue != NULL)
2475003Seric 			{
2485003Seric 				register ADDRESS *a = vrfyqueue->q_next;
2495003Seric 				char *code;
2505003Seric 
2517685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
2525003Seric 					a = a->q_next;
2535003Seric 
2547685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
2555003Seric 				{
2565003Seric 					if (a != NULL)
2575003Seric 						code = "250-";
2585003Seric 					else
2595003Seric 						code = "250";
2605003Seric 					if (vrfyqueue->q_fullname == NULL)
2615003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
2625003Seric 					else
2635003Seric 						message(code, "%s <%s>",
2645003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
2655003Seric 				}
2665003Seric 				else if (a == NULL)
2675003Seric 					message("554", "Self destructive alias loop");
2685003Seric 				vrfyqueue = a;
2695003Seric 			}
2704549Seric 			break;
2714549Seric 
2724549Seric 		  case CMDHELP:		/* help -- give user info */
2734577Seric 			if (*p == '\0')
2744577Seric 				p = "SMTP";
2754577Seric 			help(p);
2764549Seric 			break;
2774549Seric 
2784549Seric 		  case CMDNOOP:		/* noop -- do nothing */
2794549Seric 			message("200", "OK");
2804549Seric 			break;
2814549Seric 
2824549Seric 		  case CMDQUIT:		/* quit -- leave mail */
2834549Seric 			message("221", "%s closing connection", HostName);
2844549Seric 			finis();
2854549Seric 
2864577Seric 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
2874577Seric 			if (*p == 'R' || *p == 'T')
2884577Seric 			{
2894577Seric 				/* recipients first or text first */
2904577Seric 				message("200", "%c ok, please continue", *p);
2914577Seric 			}
2924577Seric 			else if (*p == '?')
2934577Seric 			{
2944577Seric 				/* what do I prefer?  anything, anytime */
2954577Seric 				message("215", "R Recipients first is my choice");
2964577Seric 			}
2974577Seric 			else if (*p == '\0')
2984577Seric 			{
2994577Seric 				/* no meaningful scheme */
3004577Seric 				message("200", "okey dokie boobie");
3014577Seric 			}
3024577Seric 			else
3034577Seric 			{
3044577Seric 				/* bad argument */
3054577Seric 				message("504", "Scheme unknown");
3064577Seric 			}
3074577Seric 			break;
3084577Seric 
3098544Seric 		  case CMDVERB:		/* set verbose mode */
3108544Seric 			Verbose = TRUE;
3118544Seric 			message("200", "Verbose mode");
3128544Seric 			break;
3138544Seric 
3145003Seric # ifdef DEBUG
3155003Seric 		  case CMDDBGSHOWQ:	/* show queues */
3166907Seric 			printf("Send Queue=");
3176907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
3185003Seric 			break;
3197275Seric 
3207275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
3217676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
3227676Seric 			tTflag(p);
3237676Seric 			message("200", "Debug set");
3247275Seric 			break;
3257275Seric 
3267282Seric 		  case CMDDBGKILL:	/* kill the parent */
3278544Seric 			if (!iswiz())
3288544Seric 				break;
3297282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
3307282Seric 				message("200", "Mother is dead");
3317282Seric 			else
3327282Seric 				message("500", "Can't kill Mom");
3337282Seric 			break;
3348544Seric 
3358544Seric 		  case CMDDBGWIZ:	/* become a wizard */
3368544Seric 			if (WizWord != NULL)
3378544Seric 			{
3388544Seric 				char seed[3];
3398544Seric 				extern char *crypt();
3408544Seric 
3418544Seric 				strncpy(seed, WizWord, 2);
3428544Seric 				if (strcmp(WizWord, crypt(p, seed)) != 0)
3438544Seric 				{
3448544Seric 					message("500", "You are no wizard!");
3458544Seric 					break;
3468544Seric 				}
3478544Seric 			}
3488544Seric 			IsWiz = TRUE;
3498544Seric 			message("200", "Please pass, oh mighty wizard");
3508544Seric 			break;
3515003Seric # endif DEBUG
3525003Seric 
3534549Seric 		  case CMDERROR:	/* unknown command */
3544549Seric 			message("500", "Command unrecognized");
3554549Seric 			break;
3564549Seric 
3574549Seric 		  default:
3584549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
3594549Seric 			break;
3604549Seric 		}
3614549Seric 	}
3624549Seric }
3634549Seric /*
3644549Seric **  SKIPWORD -- skip a fixed word.
3654549Seric **
3664549Seric **	Parameters:
3674549Seric **		p -- place to start looking.
3684549Seric **		w -- word to skip.
3694549Seric **
3704549Seric **	Returns:
3714549Seric **		p following w.
3724549Seric **		NULL on error.
3734549Seric **
3744549Seric **	Side Effects:
3754549Seric **		clobbers the p data area.
3764549Seric */
3774549Seric 
3784549Seric static char *
3794549Seric skipword(p, w)
3804549Seric 	register char *p;
3814549Seric 	char *w;
3824549Seric {
3834549Seric 	register char *q;
3844549Seric 	extern bool sameword();
3854549Seric 
3864549Seric 	/* find beginning of word */
3874549Seric 	while (isspace(*p))
3884549Seric 		p++;
3894549Seric 	q = p;
3904549Seric 
3914549Seric 	/* find end of word */
3924549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
3934549Seric 		p++;
3944549Seric 	while (isspace(*p))
3954549Seric 		*p++ = '\0';
3964549Seric 	if (*p != ':')
3974549Seric 	{
3984549Seric 	  syntax:
3994549Seric 		message("501", "Syntax error");
4004549Seric 		Errors++;
4014549Seric 		return (NULL);
4024549Seric 	}
4034549Seric 	*p++ = '\0';
4044549Seric 	while (isspace(*p))
4054549Seric 		p++;
4064549Seric 
4074549Seric 	/* see if the input word matches desired word */
4084549Seric 	if (!sameword(q, w))
4094549Seric 		goto syntax;
4104549Seric 
4114549Seric 	return (p);
4124549Seric }
4134577Seric /*
4144577Seric **  HELP -- implement the HELP command.
4154577Seric **
4164577Seric **	Parameters:
4174577Seric **		topic -- the topic we want help for.
4184577Seric **
4194577Seric **	Returns:
4204577Seric **		none.
4214577Seric **
4224577Seric **	Side Effects:
4234577Seric **		outputs the help file to message output.
4244577Seric */
4254577Seric 
4264577Seric help(topic)
4274577Seric 	char *topic;
4284577Seric {
4294577Seric 	register FILE *hf;
4304577Seric 	int len;
4314577Seric 	char buf[MAXLINE];
4324577Seric 	bool noinfo;
4334577Seric 
4348269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
4354577Seric 	{
4364577Seric 		/* no help */
4374577Seric 		message("502", "HELP not implemented");
4384577Seric 		return;
4394577Seric 	}
4404577Seric 
4414577Seric 	len = strlen(topic);
4424577Seric 	makelower(topic);
4434577Seric 	noinfo = TRUE;
4444577Seric 
4454577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
4464577Seric 	{
4474577Seric 		if (strncmp(buf, topic, len) == 0)
4484577Seric 		{
4494577Seric 			register char *p;
4504577Seric 
4514577Seric 			p = index(buf, '\t');
4524577Seric 			if (p == NULL)
4534577Seric 				p = buf;
4544577Seric 			else
4554577Seric 				p++;
4564577Seric 			fixcrlf(p, TRUE);
4574577Seric 			message("214-", p);
4584577Seric 			noinfo = FALSE;
4594577Seric 		}
4604577Seric 	}
4614577Seric 
4624577Seric 	if (noinfo)
4634577Seric 		message("504", "HELP topic unknown");
4644577Seric 	else
4654577Seric 		message("214", "End of HELP info");
4664628Seric 	(void) fclose(hf);
4674577Seric }
4688544Seric /*
4698544Seric **  ISWIZ -- tell us if we are a wizard
4708544Seric **
4718544Seric **	If not, print a nasty message.
4728544Seric **
4738544Seric **	Parameters:
4748544Seric **		none.
4758544Seric **
4768544Seric **	Returns:
4778544Seric **		TRUE if we are a wizard.
4788544Seric **		FALSE if we are not a wizard.
4798544Seric **
4808544Seric **	Side Effects:
4818544Seric **		Prints a 500 exit stat if we are not a wizard.
4828544Seric */
4835181Seric 
4848544Seric bool
4858544Seric iswiz()
4868544Seric {
4878544Seric 	if (!IsWiz)
4888544Seric 		message("500", "Mere mortals musn't mutter that mantra");
4898544Seric 	return (IsWiz);
4908544Seric }
4918544Seric 
4925181Seric # endif SMTP
493