14549Seric # include "sendmail.h"
24549Seric 
35181Seric # ifndef SMTP
4*8238Seric SCCSID(@(#)srvrsmtp.c	3.32		09/21/82	(no SMTP));
55181Seric # else SMTP
64556Seric 
7*8238Seric SCCSID(@(#)srvrsmtp.c	3.32		09/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 */
417275Seric # define CMDDBGSHOWQ	12	/* _showq -- show send queue (DEBUG) */
427275Seric # define CMDDBGDEBUG	13	/* _debug -- set debug mode */
437275Seric # define CMDDBGVERBOSE	14	/* _verbose -- go into verbose mode */
447282Seric # define CMDDBGKILL	15	/* _kill -- kill sendmail */
454549Seric 
464549Seric static struct cmd	CmdTab[] =
474549Seric {
484549Seric 	"mail",		CMDMAIL,
494976Seric 	"rcpt",		CMDRCPT,
504976Seric 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
514549Seric 	"data",		CMDDATA,
524549Seric 	"rset",		CMDRSET,
534549Seric 	"vrfy",		CMDVRFY,
547762Seric 	"expn",		CMDVRFY,
554549Seric 	"help",		CMDHELP,
564549Seric 	"noop",		CMDNOOP,
574549Seric 	"quit",		CMDQUIT,
584577Seric 	"mrsq",		CMDMRSQ,
594976Seric 	"helo",		CMDHELO,
605003Seric # ifdef DEBUG
617275Seric 	"_showq",	CMDDBGSHOWQ,
627275Seric 	"_debug",	CMDDBGDEBUG,
637275Seric 	"_verbose",	CMDDBGVERBOSE,
647282Seric 	"_kill",	CMDDBGKILL,
655003Seric # endif DEBUG
664549Seric 	NULL,		CMDERROR,
674549Seric };
684549Seric 
694549Seric smtp()
704549Seric {
714549Seric 	char inp[MAXLINE];
724549Seric 	register char *p;
734549Seric 	struct cmd *c;
744549Seric 	char *cmd;
754549Seric 	extern char *skipword();
764549Seric 	extern bool sameword();
774549Seric 	bool hasmail;			/* mail command received */
784713Seric 	int rcps;			/* number of recipients */
795003Seric 	auto ADDRESS *vrfyqueue;
807124Seric 	extern char Version[];
817356Seric 	extern tick();
824549Seric 
835003Seric 	hasmail = FALSE;
844713Seric 	rcps = 0;
857363Seric 	if (OutChannel != stdout)
867363Seric 	{
877363Seric 		/* arrange for debugging output to go to remote host */
887363Seric 		(void) close(1);
897363Seric 		(void) dup(fileno(OutChannel));
907363Seric 	}
917851Seric 	message("220", "%s Sendmail %s ready at %s", HostName,
927797Seric 			Version, arpadate(NULL));
937762Seric 	(void) setjmp(TopFrame);
947762Seric 	QuickAbort = FALSE;
954549Seric 	for (;;)
964549Seric 	{
977356Seric 		/* setup for the read */
986907Seric 		CurEnv->e_to = NULL;
994577Seric 		Errors = 0;
1007275Seric 		(void) fflush(stdout);
1017356Seric 
1027356Seric 		/* read the input line */
1037685Seric 		p = sfgets(inp, sizeof inp, InChannel);
1047356Seric 
1057685Seric 		/* handle errors */
1067356Seric 		if (p == NULL)
1077356Seric 		{
1084549Seric 			/* end of file, just die */
1094558Seric 			message("421", "%s Lost input channel", HostName);
1104549Seric 			finis();
1114549Seric 		}
1124549Seric 
1134549Seric 		/* clean up end of line */
1144558Seric 		fixcrlf(inp, TRUE);
1154549Seric 
1164713Seric 		/* echo command to transcript */
1177557Seric 		fprintf(Xscript, "<<< %s\n", inp);
1184713Seric 
1194549Seric 		/* break off command */
1204549Seric 		for (p = inp; isspace(*p); p++)
1214549Seric 			continue;
1224549Seric 		cmd = p;
1234549Seric 		while (*++p != '\0' && !isspace(*p))
1244549Seric 			continue;
1254549Seric 		if (*p != '\0')
1264549Seric 			*p++ = '\0';
1274549Seric 
1284549Seric 		/* decode command */
1294549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1304549Seric 		{
1314549Seric 			if (sameword(c->cmdname, cmd))
1324549Seric 				break;
1334549Seric 		}
1344549Seric 
1354549Seric 		/* process command */
1364549Seric 		switch (c->cmdcode)
1374549Seric 		{
1384976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
1395187Seric 			define('s', newstr(p));
1404997Seric 			message("250", "%s Hello %s, pleased to meet you",
1414997Seric 				HostName, p);
1424976Seric 			break;
1434976Seric 
1444549Seric 		  case CMDMAIL:		/* mail -- designate sender */
1454558Seric 			if (hasmail)
1464558Seric 			{
1474558Seric 				message("503", "Sender already specified");
1484558Seric 				break;
1494558Seric 			}
1504549Seric 			p = skipword(p, "from");
1514549Seric 			if (p == NULL)
1524549Seric 				break;
1534549Seric 			if (index(p, ',') != NULL)
1544549Seric 			{
1554549Seric 				message("501", "Source routing not implemented");
1564549Seric 				Errors++;
1574549Seric 				break;
1584549Seric 			}
1594549Seric 			setsender(p);
1604577Seric 			if (Errors == 0)
1614549Seric 			{
1624549Seric 				message("250", "Sender ok");
1634549Seric 				hasmail = TRUE;
1644549Seric 			}
1654549Seric 			break;
1664549Seric 
1674976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
1684549Seric 			p = skipword(p, "to");
1694549Seric 			if (p == NULL)
1704549Seric 				break;
1718081Seric 			sendto(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
1724577Seric 			if (Errors == 0)
1734549Seric 			{
1746057Seric 				message("250", "%s... Recipient ok", p);
1754713Seric 				rcps++;
1764549Seric 			}
1774549Seric 			break;
1784549Seric 
1794549Seric 		  case CMDDATA:		/* data -- text of mail */
1804976Seric 			if (!hasmail)
1814549Seric 			{
1824976Seric 				message("503", "Need MAIL command");
1834976Seric 				break;
1844549Seric 			}
1854713Seric 			else if (rcps <= 0)
1864549Seric 			{
1874976Seric 				message("503", "Need RCPT (recipient)");
1884976Seric 				break;
1894549Seric 			}
1904976Seric 
1914976Seric 			/* collect the text of the message */
1924976Seric 			collect(TRUE);
1934976Seric 			if (Errors != 0)
1944976Seric 				break;
1954976Seric 
196*8238Seric 			/*
197*8238Seric 			**  Arrange to send to everyone.
198*8238Seric 			**	If sending to multiple people, mail back
199*8238Seric 			**		errors rather than reporting directly.
200*8238Seric 			**	In any case, don't mail back errors for
201*8238Seric 			**		anything that has happened up to
202*8238Seric 			**		now (the other end will do this).
203*8238Seric 			**	Then send to everyone.
204*8238Seric 			**	Finally give a reply code.  If an error has
205*8238Seric 			**		already been given, don't mail a
206*8238Seric 			**		message back.
207*8238Seric 			**	We goose error returns by clearing FatalErrors.
208*8238Seric 			*/
209*8238Seric 
2104976Seric 			if (rcps != 1)
2114976Seric 				HoldErrs = MailBack = TRUE;
212*8238Seric 			FatalErrors = FALSE;
2134976Seric 
2144976Seric 			/* send to all recipients */
2157046Seric 			sendall(CurEnv, FALSE);
2166907Seric 			CurEnv->e_to = NULL;
2174976Seric 
218*8238Seric 			/* issue success if appropriate and reset */
219*8238Seric 			if (Errors == 0 || HoldErrs)
220*8238Seric 			{
221*8238Seric 				HoldErrs = FALSE;
2224976Seric 				message("250", "Sent");
223*8238Seric 			}
224*8238Seric 			else
225*8238Seric 				FatalErrors = FALSE;
2264549Seric 			break;
2274549Seric 
2284549Seric 		  case CMDRSET:		/* rset -- reset state */
2294549Seric 			message("250", "Reset state");
2304549Seric 			finis();
2314549Seric 
2324549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
2335003Seric 			vrfyqueue = NULL;
2347762Seric 			QuickAbort = TRUE;
2358081Seric 			sendto(p, (ADDRESS *) NULL, &vrfyqueue);
2367762Seric 			if (Errors != 0)
2377762Seric 				break;
2385003Seric 			while (vrfyqueue != NULL)
2395003Seric 			{
2405003Seric 				register ADDRESS *a = vrfyqueue->q_next;
2415003Seric 				char *code;
2425003Seric 
2437685Seric 				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
2445003Seric 					a = a->q_next;
2455003Seric 
2467685Seric 				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
2475003Seric 				{
2485003Seric 					if (a != NULL)
2495003Seric 						code = "250-";
2505003Seric 					else
2515003Seric 						code = "250";
2525003Seric 					if (vrfyqueue->q_fullname == NULL)
2535003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
2545003Seric 					else
2555003Seric 						message(code, "%s <%s>",
2565003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
2575003Seric 				}
2585003Seric 				else if (a == NULL)
2595003Seric 					message("554", "Self destructive alias loop");
2605003Seric 				vrfyqueue = a;
2615003Seric 			}
2624549Seric 			break;
2634549Seric 
2644549Seric 		  case CMDHELP:		/* help -- give user info */
2654577Seric 			if (*p == '\0')
2664577Seric 				p = "SMTP";
2674577Seric 			help(p);
2684549Seric 			break;
2694549Seric 
2704549Seric 		  case CMDNOOP:		/* noop -- do nothing */
2714549Seric 			message("200", "OK");
2724549Seric 			break;
2734549Seric 
2744549Seric 		  case CMDQUIT:		/* quit -- leave mail */
2754549Seric 			message("221", "%s closing connection", HostName);
2764549Seric 			finis();
2774549Seric 
2784577Seric 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
2794577Seric 			if (*p == 'R' || *p == 'T')
2804577Seric 			{
2814577Seric 				/* recipients first or text first */
2824577Seric 				message("200", "%c ok, please continue", *p);
2834577Seric 			}
2844577Seric 			else if (*p == '?')
2854577Seric 			{
2864577Seric 				/* what do I prefer?  anything, anytime */
2874577Seric 				message("215", "R Recipients first is my choice");
2884577Seric 			}
2894577Seric 			else if (*p == '\0')
2904577Seric 			{
2914577Seric 				/* no meaningful scheme */
2924577Seric 				message("200", "okey dokie boobie");
2934577Seric 			}
2944577Seric 			else
2954577Seric 			{
2964577Seric 				/* bad argument */
2974577Seric 				message("504", "Scheme unknown");
2984577Seric 			}
2994577Seric 			break;
3004577Seric 
3015003Seric # ifdef DEBUG
3025003Seric 		  case CMDDBGSHOWQ:	/* show queues */
3036907Seric 			printf("Send Queue=");
3046907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
3055003Seric 			break;
3067275Seric 
3077275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
3087676Seric 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
3097676Seric 			tTflag(p);
3107676Seric 			message("200", "Debug set");
3117275Seric 			break;
3127275Seric 
3137275Seric 		  case CMDDBGVERBOSE:	/* set verbose mode */
3147275Seric 			Verbose = TRUE;
3157275Seric 			message("200", "Verbose mode");
3167275Seric 			break;
3177282Seric 
3187282Seric 		  case CMDDBGKILL:	/* kill the parent */
3197282Seric 			if (kill(MotherPid, SIGTERM) >= 0)
3207282Seric 				message("200", "Mother is dead");
3217282Seric 			else
3227282Seric 				message("500", "Can't kill Mom");
3237282Seric 			break;
3245003Seric # endif DEBUG
3255003Seric 
3264549Seric 		  case CMDERROR:	/* unknown command */
3274549Seric 			message("500", "Command unrecognized");
3284549Seric 			break;
3294549Seric 
3304549Seric 		  default:
3314549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
3324549Seric 			break;
3334549Seric 		}
3344549Seric 	}
3354549Seric }
3364549Seric /*
3374549Seric **  SKIPWORD -- skip a fixed word.
3384549Seric **
3394549Seric **	Parameters:
3404549Seric **		p -- place to start looking.
3414549Seric **		w -- word to skip.
3424549Seric **
3434549Seric **	Returns:
3444549Seric **		p following w.
3454549Seric **		NULL on error.
3464549Seric **
3474549Seric **	Side Effects:
3484549Seric **		clobbers the p data area.
3494549Seric */
3504549Seric 
3514549Seric static char *
3524549Seric skipword(p, w)
3534549Seric 	register char *p;
3544549Seric 	char *w;
3554549Seric {
3564549Seric 	register char *q;
3574549Seric 	extern bool sameword();
3584549Seric 
3594549Seric 	/* find beginning of word */
3604549Seric 	while (isspace(*p))
3614549Seric 		p++;
3624549Seric 	q = p;
3634549Seric 
3644549Seric 	/* find end of word */
3654549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
3664549Seric 		p++;
3674549Seric 	while (isspace(*p))
3684549Seric 		*p++ = '\0';
3694549Seric 	if (*p != ':')
3704549Seric 	{
3714549Seric 	  syntax:
3724549Seric 		message("501", "Syntax error");
3734549Seric 		Errors++;
3744549Seric 		return (NULL);
3754549Seric 	}
3764549Seric 	*p++ = '\0';
3774549Seric 	while (isspace(*p))
3784549Seric 		p++;
3794549Seric 
3804549Seric 	/* see if the input word matches desired word */
3814549Seric 	if (!sameword(q, w))
3824549Seric 		goto syntax;
3834549Seric 
3844549Seric 	return (p);
3854549Seric }
3864577Seric /*
3874577Seric **  HELP -- implement the HELP command.
3884577Seric **
3894577Seric **	Parameters:
3904577Seric **		topic -- the topic we want help for.
3914577Seric **
3924577Seric **	Returns:
3934577Seric **		none.
3944577Seric **
3954577Seric **	Side Effects:
3964577Seric **		outputs the help file to message output.
3974577Seric */
3984577Seric 
3994577Seric help(topic)
4004577Seric 	char *topic;
4014577Seric {
4024577Seric 	register FILE *hf;
4034577Seric 	int len;
4044577Seric 	char buf[MAXLINE];
4054577Seric 	bool noinfo;
4064582Seric 	extern char *HelpFile;
4074577Seric 
4084582Seric 	hf = fopen(HelpFile, "r");
4094577Seric 	if (hf == NULL)
4104577Seric 	{
4114577Seric 		/* no help */
4124577Seric 		message("502", "HELP not implemented");
4134577Seric 		return;
4144577Seric 	}
4154577Seric 
4164577Seric 	len = strlen(topic);
4174577Seric 	makelower(topic);
4184577Seric 	noinfo = TRUE;
4194577Seric 
4204577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
4214577Seric 	{
4224577Seric 		if (strncmp(buf, topic, len) == 0)
4234577Seric 		{
4244577Seric 			register char *p;
4254577Seric 
4264577Seric 			p = index(buf, '\t');
4274577Seric 			if (p == NULL)
4284577Seric 				p = buf;
4294577Seric 			else
4304577Seric 				p++;
4314577Seric 			fixcrlf(p, TRUE);
4324577Seric 			message("214-", p);
4334577Seric 			noinfo = FALSE;
4344577Seric 		}
4354577Seric 	}
4364577Seric 
4374577Seric 	if (noinfo)
4384577Seric 		message("504", "HELP topic unknown");
4394577Seric 	else
4404577Seric 		message("214", "End of HELP info");
4414628Seric 	(void) fclose(hf);
4424577Seric }
4435181Seric 
4445181Seric # endif SMTP
445