14549Seric # include "sendmail.h"
24549Seric 
3*4577Seric static char	SccsId[] =	"@(#)srvrsmtp.c	3.4	10/22/81";
44556Seric 
54549Seric /*
64549Seric **  SMTP -- run the SMTP protocol.
74549Seric **
84549Seric **	Parameters:
94549Seric **		none.
104549Seric **
114549Seric **	Returns:
124549Seric **		never.
134549Seric **
144549Seric **	Side Effects:
154549Seric **		Reads commands from the input channel and processes
164549Seric **			them.
174549Seric */
184549Seric 
194549Seric struct cmd
204549Seric {
214549Seric 	char	*cmdname;	/* command name */
224549Seric 	int	cmdcode;	/* internal code, see below */
234549Seric };
244549Seric 
254549Seric /* values for cmdcode */
264549Seric # define CMDERROR	0	/* bad command */
274549Seric # define CMDMAIL	1	/* mail -- designate sender */
284549Seric # define CMDMRCP	2	/* mrcp -- designate recipient */
294549Seric # define CMDDATA	3	/* data -- send message text */
304549Seric # define CMDDOIT	4	/* doit -- actually do delivery */
314549Seric # define CMDRSET	5	/* rset -- reset state */
324549Seric # define CMDVRFY	6	/* vrfy -- verify address */
334549Seric # define CMDHELP	7	/* help -- give usage info */
344549Seric # define CMDNOOP	8	/* noop -- do nothing */
354549Seric # define CMDQUIT	9	/* quit -- close connection and die */
36*4577Seric # define CMDMRSQ	10	/* mrsq -- for old mtp compat only */
374549Seric 
384549Seric static struct cmd	CmdTab[] =
394549Seric {
404549Seric 	"mail",		CMDMAIL,
414549Seric 	"mrcp",		CMDMRCP,
424549Seric 	"data",		CMDDATA,
434549Seric 	"doit",		CMDDOIT,
444549Seric 	"rset",		CMDRSET,
454549Seric 	"vrfy",		CMDVRFY,
464549Seric 	"help",		CMDHELP,
474549Seric 	"noop",		CMDNOOP,
484549Seric 	"quit",		CMDQUIT,
49*4577Seric 	"mrsq",		CMDMRSQ,
504549Seric 	NULL,		CMDERROR,
514549Seric };
524549Seric 
534549Seric smtp()
544549Seric {
554549Seric 	char inp[MAXLINE];
564549Seric 	register char *p;
574549Seric 	struct cmd *c;
584549Seric 	char *cmd;
594549Seric 	extern char *skipword();
604549Seric 	extern bool sameword();
614549Seric 	bool hasmail;			/* mail command received */
624549Seric 	bool hasmrcp;			/* has a recipient */
634549Seric 	bool hasdata;			/* has mail data */
644549Seric 
654549Seric 	hasmail = hasmrcp = hasdata = FALSE;
664549Seric 	message("220", "%s Sendmail at your service", HostName);
674549Seric 	for (;;)
684549Seric 	{
694549Seric 		To = NULL;
70*4577Seric 		Errors = 0;
714549Seric 		if (fgets(inp, sizeof inp, InChannel) == NULL)
724549Seric 		{
734549Seric 			/* end of file, just die */
744558Seric 			message("421", "%s Lost input channel", HostName);
754549Seric 			finis();
764549Seric 		}
774549Seric 
784549Seric 		/* clean up end of line */
794558Seric 		fixcrlf(inp, TRUE);
804549Seric 
814549Seric 		/* break off command */
824549Seric 		for (p = inp; isspace(*p); p++)
834549Seric 			continue;
844549Seric 		cmd = p;
854549Seric 		while (*++p != '\0' && !isspace(*p))
864549Seric 			continue;
874549Seric 		if (*p != '\0')
884549Seric 			*p++ = '\0';
894549Seric 
904549Seric 		/* decode command */
914549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
924549Seric 		{
934549Seric 			if (sameword(c->cmdname, cmd))
944549Seric 				break;
954549Seric 		}
964549Seric 
974549Seric 		/* process command */
984549Seric 		switch (c->cmdcode)
994549Seric 		{
1004549Seric 		  case CMDMAIL:		/* mail -- designate sender */
1014558Seric 			if (hasmail)
1024558Seric 			{
1034558Seric 				message("503", "Sender already specified");
1044558Seric 				break;
1054558Seric 			}
1064549Seric 			p = skipword(p, "from");
1074549Seric 			if (p == NULL)
1084549Seric 				break;
1094549Seric 			if (index(p, ',') != NULL)
1104549Seric 			{
1114549Seric 				message("501", "Source routing not implemented");
1124549Seric 				Errors++;
1134549Seric 				break;
1144549Seric 			}
1154549Seric 			setsender(p);
116*4577Seric 			if (Errors == 0)
1174549Seric 			{
1184549Seric 				message("250", "Sender ok");
1194549Seric 				hasmail = TRUE;
1204549Seric 			}
1214549Seric 			break;
1224549Seric 
1234549Seric 		  case CMDMRCP:		/* mrcp -- designate recipient */
1244549Seric 			p = skipword(p, "to");
1254549Seric 			if (p == NULL)
1264549Seric 				break;
1274549Seric 			if (index(p, ',') != NULL)
1284549Seric 			{
1294549Seric 				message("501", "Source routing not implemented");
1304549Seric 				Errors++;
1314549Seric 				break;
1324549Seric 			}
1334549Seric 			sendto(p, 1, NULL);
134*4577Seric 			if (Errors == 0)
1354549Seric 			{
1364549Seric 				message("250", "Recipient ok");
1374549Seric 				hasmrcp = TRUE;
1384549Seric 			}
1394549Seric 			break;
1404549Seric 
1414549Seric 		  case CMDDATA:		/* data -- text of mail */
1424549Seric 			message("354", "Enter mail, end with dot");
1434549Seric 			collect();
144*4577Seric 			if (Errors == 0)
1454549Seric 			{
1464549Seric 				message("250", "Message stored");
1474549Seric 				hasdata = TRUE;
1484549Seric 			}
1494549Seric 			break;
1504549Seric 
1514549Seric 		  case CMDDOIT:		/* doit -- actually send everything */
1524549Seric 			if (!hasmail)
1534549Seric 				message("503", "Need MAIL command");
1544549Seric 			else if (!hasmrcp)
1554549Seric 				message("503", "Need MRCP (recipient)");
1564549Seric 			else if (!hasdata)
1574549Seric 				message("503", "No message, use DATA");
1584549Seric 			else
1594549Seric 			{
1604549Seric 				sendall(FALSE);
161*4577Seric 				if (Errors == 0)
1624549Seric 					message("250", "Sent");
1634549Seric 			}
1644549Seric 			break;
1654549Seric 
1664549Seric 		  case CMDRSET:		/* rset -- reset state */
1674549Seric 			message("250", "Reset state");
1684549Seric 			finis();
1694549Seric 
1704549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
1714549Seric 			sendto(p, 1, NULL);
172*4577Seric 			if (Errors == 0)
1734549Seric 				message("250", "user ok");
1744549Seric 			break;
1754549Seric 
1764549Seric 		  case CMDHELP:		/* help -- give user info */
177*4577Seric 			if (*p == '\0')
178*4577Seric 				p = "SMTP";
179*4577Seric 			help(p);
1804549Seric 			break;
1814549Seric 
1824549Seric 		  case CMDNOOP:		/* noop -- do nothing */
1834549Seric 			message("200", "OK");
1844549Seric 			break;
1854549Seric 
1864549Seric 		  case CMDQUIT:		/* quit -- leave mail */
1874549Seric 			message("221", "%s closing connection", HostName);
1884549Seric 			finis();
1894549Seric 
190*4577Seric 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
191*4577Seric 			if (*p == 'R' || *p == 'T')
192*4577Seric 			{
193*4577Seric 				/* recipients first or text first */
194*4577Seric 				message("200", "%c ok, please continue", *p);
195*4577Seric 			}
196*4577Seric 			else if (*p == '?')
197*4577Seric 			{
198*4577Seric 				/* what do I prefer?  anything, anytime */
199*4577Seric 				message("215", "R Recipients first is my choice");
200*4577Seric 			}
201*4577Seric 			else if (*p == '\0')
202*4577Seric 			{
203*4577Seric 				/* no meaningful scheme */
204*4577Seric 				message("200", "okey dokie boobie");
205*4577Seric 			}
206*4577Seric 			else
207*4577Seric 			{
208*4577Seric 				/* bad argument */
209*4577Seric 				message("504", "Scheme unknown");
210*4577Seric 			}
211*4577Seric 			break;
212*4577Seric 
2134549Seric 		  case CMDERROR:	/* unknown command */
2144549Seric 			message("500", "Command unrecognized");
2154549Seric 			break;
2164549Seric 
2174549Seric 		  default:
2184549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
2194549Seric 			break;
2204549Seric 		}
2214549Seric 	}
2224549Seric }
2234549Seric /*
2244549Seric **  SKIPWORD -- skip a fixed word.
2254549Seric **
2264549Seric **	Parameters:
2274549Seric **		p -- place to start looking.
2284549Seric **		w -- word to skip.
2294549Seric **
2304549Seric **	Returns:
2314549Seric **		p following w.
2324549Seric **		NULL on error.
2334549Seric **
2344549Seric **	Side Effects:
2354549Seric **		clobbers the p data area.
2364549Seric */
2374549Seric 
2384549Seric static char *
2394549Seric skipword(p, w)
2404549Seric 	register char *p;
2414549Seric 	char *w;
2424549Seric {
2434549Seric 	register char *q;
2444549Seric 	extern bool sameword();
2454549Seric 
2464549Seric 	/* find beginning of word */
2474549Seric 	while (isspace(*p))
2484549Seric 		p++;
2494549Seric 	q = p;
2504549Seric 
2514549Seric 	/* find end of word */
2524549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
2534549Seric 		p++;
2544549Seric 	while (isspace(*p))
2554549Seric 		*p++ = '\0';
2564549Seric 	if (*p != ':')
2574549Seric 	{
2584549Seric 	  syntax:
2594549Seric 		message("501", "Syntax error");
2604549Seric 		Errors++;
2614549Seric 		return (NULL);
2624549Seric 	}
2634549Seric 	*p++ = '\0';
2644549Seric 	while (isspace(*p))
2654549Seric 		p++;
2664549Seric 
2674549Seric 	/* see if the input word matches desired word */
2684549Seric 	if (!sameword(q, w))
2694549Seric 		goto syntax;
2704549Seric 
2714549Seric 	return (p);
2724549Seric }
273*4577Seric /*
274*4577Seric **  HELP -- implement the HELP command.
275*4577Seric **
276*4577Seric **	Parameters:
277*4577Seric **		topic -- the topic we want help for.
278*4577Seric **
279*4577Seric **	Returns:
280*4577Seric **		none.
281*4577Seric **
282*4577Seric **	Side Effects:
283*4577Seric **		outputs the help file to message output.
284*4577Seric */
285*4577Seric 
286*4577Seric help(topic)
287*4577Seric 	char *topic;
288*4577Seric {
289*4577Seric 	register FILE *hf;
290*4577Seric 	int len;
291*4577Seric 	char buf[MAXLINE];
292*4577Seric 	bool noinfo;
293*4577Seric 
294*4577Seric 	hf = fopen("/usr/lib/sendmail.hf", "r");
295*4577Seric 	if (hf == NULL)
296*4577Seric 	{
297*4577Seric 		/* no help */
298*4577Seric 		message("502", "HELP not implemented");
299*4577Seric 		return;
300*4577Seric 	}
301*4577Seric 
302*4577Seric 	len = strlen(topic);
303*4577Seric 	makelower(topic);
304*4577Seric 	noinfo = TRUE;
305*4577Seric 
306*4577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
307*4577Seric 	{
308*4577Seric 		if (strncmp(buf, topic, len) == 0)
309*4577Seric 		{
310*4577Seric 			register char *p;
311*4577Seric 
312*4577Seric 			p = index(buf, '\t');
313*4577Seric 			if (p == NULL)
314*4577Seric 				p = buf;
315*4577Seric 			else
316*4577Seric 				p++;
317*4577Seric 			fixcrlf(p, TRUE);
318*4577Seric 			message("214-", p);
319*4577Seric 			noinfo = FALSE;
320*4577Seric 		}
321*4577Seric 	}
322*4577Seric 
323*4577Seric 	if (noinfo)
324*4577Seric 		message("504", "HELP topic unknown");
325*4577Seric 	else
326*4577Seric 		message("214", "End of HELP info");
327*4577Seric 	fclose(hf);
328*4577Seric }
329