14549Seric # include "sendmail.h"
24549Seric 
35181Seric # ifndef SMTP
4*8544Seric SCCSID(@(#)srvrsmtp.c	3.34		10/16/82	(no SMTP));
55181Seric # else SMTP
64556Seric 
7*8544Seric SCCSID(@(#)srvrsmtp.c	3.34		10/16/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 */
41*8544Seric # define CMDDBGSHOWQ	12	/* showq -- show send queue (DEBUG) */
42*8544Seric # define CMDDBGDEBUG	13	/* debug -- set debug mode */
43*8544Seric # define CMDVERB	14	/* verb -- go into verbose mode */
44*8544Seric # define CMDDBGKILL	15	/* kill -- kill sendmail */
45*8544Seric # 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,
61*8544Seric 	"verb",		CMDVERB,
625003Seric # ifdef DEBUG
63*8544Seric 	"showq",	CMDDBGSHOWQ,
64*8544Seric 	"debug",	CMDDBGDEBUG,
65*8544Seric 	"kill",		CMDDBGKILL,
66*8544Seric 	"wiz",		CMDDBGWIZ,
675003Seric # endif DEBUG
684549Seric 	NULL,		CMDERROR,
694549Seric };
704549Seric 
71*8544Seric # ifdef DEBUG
72*8544Seric bool	IsWiz = FALSE;			/* set if we are a wizard */
73*8544Seric char	*WizWord = NULL;		/* the wizard word to compare against */
74*8544Seric # endif DEBUG
75*8544Seric 
764549Seric smtp()
774549Seric {
784549Seric 	register char *p;
79*8544Seric 	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;
86*8544Seric 	char inp[MAXLINE];
877124Seric 	extern char Version[];
887356Seric 	extern tick();
89*8544Seric 	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 */
2237046Seric 			sendall(CurEnv, FALSE);
2246907Seric 			CurEnv->e_to = NULL;
2254976Seric 
2268238Seric 			/* issue success if appropriate and reset */
2278238Seric 			if (Errors == 0 || HoldErrs)
2288238Seric 			{
2298238Seric 				HoldErrs = FALSE;
2304976Seric 				message("250", "Sent");
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 
309*8544Seric 		  case CMDVERB:		/* set verbose mode */
310*8544Seric 			Verbose = TRUE;
311*8544Seric 			message("200", "Verbose mode");
312*8544Seric 			break;
313*8544Seric 
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 */
327*8544Seric 			if (!iswiz())
328*8544Seric 				break;
3297282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
3307282Seric 				message("200", "Mother is dead");
3317282Seric 			else
3327282Seric 				message("500", "Can't kill Mom");
3337282Seric 			break;
334*8544Seric 
335*8544Seric 		  case CMDDBGWIZ:	/* become a wizard */
336*8544Seric 			if (WizWord != NULL)
337*8544Seric 			{
338*8544Seric 				char seed[3];
339*8544Seric 				extern char *crypt();
340*8544Seric 
341*8544Seric 				strncpy(seed, WizWord, 2);
342*8544Seric 				if (strcmp(WizWord, crypt(p, seed)) != 0)
343*8544Seric 				{
344*8544Seric 					message("500", "You are no wizard!");
345*8544Seric 					break;
346*8544Seric 				}
347*8544Seric 			}
348*8544Seric 			IsWiz = TRUE;
349*8544Seric 			message("200", "Please pass, oh mighty wizard");
350*8544Seric 			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 }
468*8544Seric /*
469*8544Seric **  ISWIZ -- tell us if we are a wizard
470*8544Seric **
471*8544Seric **	If not, print a nasty message.
472*8544Seric **
473*8544Seric **	Parameters:
474*8544Seric **		none.
475*8544Seric **
476*8544Seric **	Returns:
477*8544Seric **		TRUE if we are a wizard.
478*8544Seric **		FALSE if we are not a wizard.
479*8544Seric **
480*8544Seric **	Side Effects:
481*8544Seric **		Prints a 500 exit stat if we are not a wizard.
482*8544Seric */
4835181Seric 
484*8544Seric bool
485*8544Seric iswiz()
486*8544Seric {
487*8544Seric 	if (!IsWiz)
488*8544Seric 		message("500", "Mere mortals musn't mutter that mantra");
489*8544Seric 	return (IsWiz);
490*8544Seric }
491*8544Seric 
4925181Seric # endif SMTP
493