14549Seric # include "sendmail.h"
24549Seric 
35181Seric # ifndef SMTP
4*9314Seric SCCSID(@(#)srvrsmtp.c	3.36		11/21/82	(no SMTP));
55181Seric # else SMTP
64556Seric 
7*9314Seric SCCSID(@(#)srvrsmtp.c	3.36		11/21/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 */
46*9314Seric # define CMDONEX	17	/* onex -- sending one transaction only */
474549Seric 
484549Seric static struct cmd	CmdTab[] =
494549Seric {
504549Seric 	"mail",		CMDMAIL,
514976Seric 	"rcpt",		CMDRCPT,
524976Seric 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
534549Seric 	"data",		CMDDATA,
544549Seric 	"rset",		CMDRSET,
554549Seric 	"vrfy",		CMDVRFY,
567762Seric 	"expn",		CMDVRFY,
574549Seric 	"help",		CMDHELP,
584549Seric 	"noop",		CMDNOOP,
594549Seric 	"quit",		CMDQUIT,
604577Seric 	"mrsq",		CMDMRSQ,
614976Seric 	"helo",		CMDHELO,
628544Seric 	"verb",		CMDVERB,
63*9314Seric 	"onex",		CMDONEX,
645003Seric # ifdef DEBUG
658544Seric 	"showq",	CMDDBGSHOWQ,
668544Seric 	"debug",	CMDDBGDEBUG,
678544Seric 	"kill",		CMDDBGKILL,
688544Seric 	"wiz",		CMDDBGWIZ,
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
778544Seric 
784549Seric smtp()
794549Seric {
804549Seric 	register char *p;
818544Seric 	register struct cmd *c;
824549Seric 	char *cmd;
834549Seric 	extern char *skipword();
844549Seric 	extern bool sameword();
854549Seric 	bool hasmail;			/* mail command received */
864713Seric 	int rcps;			/* number of recipients */
875003Seric 	auto ADDRESS *vrfyqueue;
88*9314Seric 	bool onexact = FALSE;		/* one transaction this connection */
89*9314Seric 	bool firsttime = TRUE;		/* this is the first transaction */
908544Seric 	char inp[MAXLINE];
917124Seric 	extern char Version[];
927356Seric 	extern tick();
938544Seric 	extern bool iswiz();
944549Seric 
955003Seric 	hasmail = FALSE;
964713Seric 	rcps = 0;
977363Seric 	if (OutChannel != stdout)
987363Seric 	{
997363Seric 		/* arrange for debugging output to go to remote host */
1007363Seric 		(void) close(1);
1017363Seric 		(void) dup(fileno(OutChannel));
1027363Seric 	}
1037851Seric 	message("220", "%s Sendmail %s ready at %s", HostName,
1047797Seric 			Version, arpadate(NULL));
1057762Seric 	(void) setjmp(TopFrame);
1067762Seric 	QuickAbort = FALSE;
1074549Seric 	for (;;)
1084549Seric 	{
1097356Seric 		/* setup for the read */
1106907Seric 		CurEnv->e_to = NULL;
1114577Seric 		Errors = 0;
1127275Seric 		(void) fflush(stdout);
1137356Seric 
1147356Seric 		/* read the input line */
1157685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1167356Seric 
1177685Seric 		/* handle errors */
1187356Seric 		if (p == NULL)
1197356Seric 		{
1204549Seric 			/* end of file, just die */
1214558Seric 			message("421", "%s Lost input channel", HostName);
1224549Seric 			finis();
1234549Seric 		}
1244549Seric 
1254549Seric 		/* clean up end of line */
1264558Seric 		fixcrlf(inp, TRUE);
1274549Seric 
1284713Seric 		/* echo command to transcript */
1297557Seric 		fprintf(Xscript, "<<< %s\n", inp);
1304713Seric 
1314549Seric 		/* break off command */
1324549Seric 		for (p = inp; isspace(*p); p++)
1334549Seric 			continue;
1344549Seric 		cmd = p;
1354549Seric 		while (*++p != '\0' && !isspace(*p))
1364549Seric 			continue;
1374549Seric 		if (*p != '\0')
1384549Seric 			*p++ = '\0';
1394549Seric 
1404549Seric 		/* decode command */
1414549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1424549Seric 		{
1434549Seric 			if (sameword(c->cmdname, cmd))
1444549Seric 				break;
1454549Seric 		}
1464549Seric 
1474549Seric 		/* process command */
1484549Seric 		switch (c->cmdcode)
1494549Seric 		{
1504976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
1515187Seric 			define('s', newstr(p));
1524997Seric 			message("250", "%s Hello %s, pleased to meet you",
1534997Seric 				HostName, p);
1544976Seric 			break;
1554976Seric 
1564549Seric 		  case CMDMAIL:		/* mail -- designate sender */
157*9314Seric 			firsttime = FALSE;
158*9314Seric 
159*9314Seric 			/* check for validity of this command */
1604558Seric 			if (hasmail)
1614558Seric 			{
1624558Seric 				message("503", "Sender already specified");
1634558Seric 				break;
1644558Seric 			}
1654549Seric 			p = skipword(p, "from");
1664549Seric 			if (p == NULL)
1674549Seric 				break;
1684549Seric 			if (index(p, ',') != NULL)
1694549Seric 			{
1704549Seric 				message("501", "Source routing not implemented");
1714549Seric 				Errors++;
1724549Seric 				break;
1734549Seric 			}
1744549Seric 			setsender(p);
1754577Seric 			if (Errors == 0)
1764549Seric 			{
1774549Seric 				message("250", "Sender ok");
1784549Seric 				hasmail = TRUE;
1794549Seric 			}
1804549Seric 			break;
1814549Seric 
1824976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
1834549Seric 			p = skipword(p, "to");
1844549Seric 			if (p == NULL)
1854549Seric 				break;
1868081Seric 			sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
1874577Seric 			if (Errors == 0)
1884549Seric 			{
1896057Seric 				message("250", "%s... Recipient ok", p);
1904713Seric 				rcps++;
1914549Seric 			}
1924549Seric 			break;
1934549Seric 
1944549Seric 		  case CMDDATA:		/* data -- text of mail */
1954976Seric 			if (!hasmail)
1964549Seric 			{
1974976Seric 				message("503", "Need MAIL command");
1984976Seric 				break;
1994549Seric 			}
2004713Seric 			else if (rcps <= 0)
2014549Seric 			{
2024976Seric 				message("503", "Need RCPT (recipient)");
2034976Seric 				break;
2044549Seric 			}
2054976Seric 
2064976Seric 			/* collect the text of the message */
2074976Seric 			collect(TRUE);
2084976Seric 			if (Errors != 0)
2094976Seric 				break;
2104976Seric 
2118238Seric 			/*
2128238Seric 			**  Arrange to send to everyone.
2138238Seric 			**	If sending to multiple people, mail back
2148238Seric 			**		errors rather than reporting directly.
2158238Seric 			**	In any case, don't mail back errors for
2168238Seric 			**		anything that has happened up to
2178238Seric 			**		now (the other end will do this).
2188238Seric 			**	Then send to everyone.
2198238Seric 			**	Finally give a reply code.  If an error has
2208238Seric 			**		already been given, don't mail a
2218238Seric 			**		message back.
2228238Seric 			**	We goose error returns by clearing FatalErrors.
2238238Seric 			*/
2248238Seric 
2254976Seric 			if (rcps != 1)
2264976Seric 				HoldErrs = MailBack = TRUE;
2278238Seric 			FatalErrors = FALSE;
2284976Seric 
2294976Seric 			/* send to all recipients */
2309283Seric 			sendall(CurEnv, SendMode);
2316907Seric 			CurEnv->e_to = NULL;
2324976Seric 
2338238Seric 			/* issue success if appropriate and reset */
2348238Seric 			if (Errors == 0 || HoldErrs)
2358238Seric 			{
2368238Seric 				HoldErrs = FALSE;
2379283Seric 				message("250", "Ok");
2388238Seric 			}
2398238Seric 			else
2408238Seric 				FatalErrors = FALSE;
2414549Seric 			break;
2424549Seric 
2434549Seric 		  case CMDRSET:		/* rset -- reset state */
2444549Seric 			message("250", "Reset state");
2454549Seric 			finis();
2464549Seric 
2474549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
2485003Seric 			vrfyqueue = NULL;
2497762Seric 			QuickAbort = TRUE;
2508081Seric 			sendto(p, (ADDRESS *) NULL, &vrfyqueue);
2517762Seric 			if (Errors != 0)
2527762Seric 				break;
2535003Seric 			while (vrfyqueue != NULL)
2545003Seric 			{
2555003Seric 				register ADDRESS *a = vrfyqueue->q_next;
2565003Seric 				char *code;
2575003Seric 
2587685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
2595003Seric 					a = a->q_next;
2605003Seric 
2617685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
2625003Seric 				{
2635003Seric 					if (a != NULL)
2645003Seric 						code = "250-";
2655003Seric 					else
2665003Seric 						code = "250";
2675003Seric 					if (vrfyqueue->q_fullname == NULL)
2685003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
2695003Seric 					else
2705003Seric 						message(code, "%s <%s>",
2715003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
2725003Seric 				}
2735003Seric 				else if (a == NULL)
2745003Seric 					message("554", "Self destructive alias loop");
2755003Seric 				vrfyqueue = a;
2765003Seric 			}
2774549Seric 			break;
2784549Seric 
2794549Seric 		  case CMDHELP:		/* help -- give user info */
2804577Seric 			if (*p == '\0')
2814577Seric 				p = "SMTP";
2824577Seric 			help(p);
2834549Seric 			break;
2844549Seric 
2854549Seric 		  case CMDNOOP:		/* noop -- do nothing */
2864549Seric 			message("200", "OK");
2874549Seric 			break;
2884549Seric 
2894549Seric 		  case CMDQUIT:		/* quit -- leave mail */
2904549Seric 			message("221", "%s closing connection", HostName);
2914549Seric 			finis();
2924549Seric 
2934577Seric 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
2944577Seric 			if (*p == 'R' || *p == 'T')
2954577Seric 			{
2964577Seric 				/* recipients first or text first */
2974577Seric 				message("200", "%c ok, please continue", *p);
2984577Seric 			}
2994577Seric 			else if (*p == '?')
3004577Seric 			{
3014577Seric 				/* what do I prefer?  anything, anytime */
3024577Seric 				message("215", "R Recipients first is my choice");
3034577Seric 			}
3044577Seric 			else if (*p == '\0')
3054577Seric 			{
3064577Seric 				/* no meaningful scheme */
3074577Seric 				message("200", "okey dokie boobie");
3084577Seric 			}
3094577Seric 			else
3104577Seric 			{
3114577Seric 				/* bad argument */
3124577Seric 				message("504", "Scheme unknown");
3134577Seric 			}
3144577Seric 			break;
3154577Seric 
3168544Seric 		  case CMDVERB:		/* set verbose mode */
3178544Seric 			Verbose = TRUE;
3188544Seric 			message("200", "Verbose mode");
3198544Seric 			break;
3208544Seric 
321*9314Seric 		  case CMDONEX:		/* doing one transaction only */
322*9314Seric 			onexact = TRUE;
323*9314Seric 			message("200", "Only one transaction");
324*9314Seric 			break;
325*9314Seric 
3265003Seric # ifdef DEBUG
3275003Seric 		  case CMDDBGSHOWQ:	/* show queues */
3286907Seric 			printf("Send Queue=");
3296907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
3305003Seric 			break;
3317275Seric 
3327275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
3337676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
3347676Seric 			tTflag(p);
3357676Seric 			message("200", "Debug set");
3367275Seric 			break;
3377275Seric 
3387282Seric 		  case CMDDBGKILL:	/* kill the parent */
3398544Seric 			if (!iswiz())
3408544Seric 				break;
3417282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
3427282Seric 				message("200", "Mother is dead");
3437282Seric 			else
3447282Seric 				message("500", "Can't kill Mom");
3457282Seric 			break;
3468544Seric 
3478544Seric 		  case CMDDBGWIZ:	/* become a wizard */
3488544Seric 			if (WizWord != NULL)
3498544Seric 			{
3508544Seric 				char seed[3];
3518544Seric 				extern char *crypt();
3528544Seric 
3538544Seric 				strncpy(seed, WizWord, 2);
3548544Seric 				if (strcmp(WizWord, crypt(p, seed)) != 0)
3558544Seric 				{
3568544Seric 					message("500", "You are no wizard!");
3578544Seric 					break;
3588544Seric 				}
3598544Seric 			}
3608544Seric 			IsWiz = TRUE;
3618544Seric 			message("200", "Please pass, oh mighty wizard");
3628544Seric 			break;
3635003Seric # endif DEBUG
3645003Seric 
3654549Seric 		  case CMDERROR:	/* unknown command */
3664549Seric 			message("500", "Command unrecognized");
3674549Seric 			break;
3684549Seric 
3694549Seric 		  default:
3704549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
3714549Seric 			break;
3724549Seric 		}
3734549Seric 	}
3744549Seric }
3754549Seric /*
3764549Seric **  SKIPWORD -- skip a fixed word.
3774549Seric **
3784549Seric **	Parameters:
3794549Seric **		p -- place to start looking.
3804549Seric **		w -- word to skip.
3814549Seric **
3824549Seric **	Returns:
3834549Seric **		p following w.
3844549Seric **		NULL on error.
3854549Seric **
3864549Seric **	Side Effects:
3874549Seric **		clobbers the p data area.
3884549Seric */
3894549Seric 
3904549Seric static char *
3914549Seric skipword(p, w)
3924549Seric 	register char *p;
3934549Seric 	char *w;
3944549Seric {
3954549Seric 	register char *q;
3964549Seric 	extern bool sameword();
3974549Seric 
3984549Seric 	/* find beginning of word */
3994549Seric 	while (isspace(*p))
4004549Seric 		p++;
4014549Seric 	q = p;
4024549Seric 
4034549Seric 	/* find end of word */
4044549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
4054549Seric 		p++;
4064549Seric 	while (isspace(*p))
4074549Seric 		*p++ = '\0';
4084549Seric 	if (*p != ':')
4094549Seric 	{
4104549Seric 	  syntax:
4114549Seric 		message("501", "Syntax error");
4124549Seric 		Errors++;
4134549Seric 		return (NULL);
4144549Seric 	}
4154549Seric 	*p++ = '\0';
4164549Seric 	while (isspace(*p))
4174549Seric 		p++;
4184549Seric 
4194549Seric 	/* see if the input word matches desired word */
4204549Seric 	if (!sameword(q, w))
4214549Seric 		goto syntax;
4224549Seric 
4234549Seric 	return (p);
4244549Seric }
4254577Seric /*
4264577Seric **  HELP -- implement the HELP command.
4274577Seric **
4284577Seric **	Parameters:
4294577Seric **		topic -- the topic we want help for.
4304577Seric **
4314577Seric **	Returns:
4324577Seric **		none.
4334577Seric **
4344577Seric **	Side Effects:
4354577Seric **		outputs the help file to message output.
4364577Seric */
4374577Seric 
4384577Seric help(topic)
4394577Seric 	char *topic;
4404577Seric {
4414577Seric 	register FILE *hf;
4424577Seric 	int len;
4434577Seric 	char buf[MAXLINE];
4444577Seric 	bool noinfo;
4454577Seric 
4468269Seric 	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
4474577Seric 	{
4484577Seric 		/* no help */
4494577Seric 		message("502", "HELP not implemented");
4504577Seric 		return;
4514577Seric 	}
4524577Seric 
4534577Seric 	len = strlen(topic);
4544577Seric 	makelower(topic);
4554577Seric 	noinfo = TRUE;
4564577Seric 
4574577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
4584577Seric 	{
4594577Seric 		if (strncmp(buf, topic, len) == 0)
4604577Seric 		{
4614577Seric 			register char *p;
4624577Seric 
4634577Seric 			p = index(buf, '\t');
4644577Seric 			if (p == NULL)
4654577Seric 				p = buf;
4664577Seric 			else
4674577Seric 				p++;
4684577Seric 			fixcrlf(p, TRUE);
4694577Seric 			message("214-", p);
4704577Seric 			noinfo = FALSE;
4714577Seric 		}
4724577Seric 	}
4734577Seric 
4744577Seric 	if (noinfo)
4754577Seric 		message("504", "HELP topic unknown");
4764577Seric 	else
4774577Seric 		message("214", "End of HELP info");
4784628Seric 	(void) fclose(hf);
4794577Seric }
4808544Seric /*
4818544Seric **  ISWIZ -- tell us if we are a wizard
4828544Seric **
4838544Seric **	If not, print a nasty message.
4848544Seric **
4858544Seric **	Parameters:
4868544Seric **		none.
4878544Seric **
4888544Seric **	Returns:
4898544Seric **		TRUE if we are a wizard.
4908544Seric **		FALSE if we are not a wizard.
4918544Seric **
4928544Seric **	Side Effects:
4938544Seric **		Prints a 500 exit stat if we are not a wizard.
4948544Seric */
4955181Seric 
4968544Seric bool
4978544Seric iswiz()
4988544Seric {
4998544Seric 	if (!IsWiz)
5008544Seric 		message("500", "Mere mortals musn't mutter that mantra");
5018544Seric 	return (IsWiz);
5028544Seric }
5038544Seric 
5045181Seric # endif SMTP
505