14549Seric # include "sendmail.h"
24549Seric 
3*5003Seric static char	SccsId[] =	"@(#)srvrsmtp.c	3.10	11/21/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 */
284976Seric # define CMDRCPT	2	/* rcpt -- designate recipient */
294549Seric # define CMDDATA	3	/* data -- send message text */
304549Seric # define CMDRSET	5	/* rset -- reset state */
314549Seric # define CMDVRFY	6	/* vrfy -- verify address */
324549Seric # define CMDHELP	7	/* help -- give usage info */
334549Seric # define CMDNOOP	8	/* noop -- do nothing */
344549Seric # define CMDQUIT	9	/* quit -- close connection and die */
354577Seric # define CMDMRSQ	10	/* mrsq -- for old mtp compat only */
364976Seric # define CMDHELO	11	/* helo -- be polite */
37*5003Seric # define CMDDBGSHOWQ	12	/* showq -- show send queue (DEBUG) */
384549Seric 
394549Seric static struct cmd	CmdTab[] =
404549Seric {
414549Seric 	"mail",		CMDMAIL,
424976Seric 	"rcpt",		CMDRCPT,
434976Seric 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
444549Seric 	"data",		CMDDATA,
454549Seric 	"rset",		CMDRSET,
464549Seric 	"vrfy",		CMDVRFY,
474549Seric 	"help",		CMDHELP,
484549Seric 	"noop",		CMDNOOP,
494549Seric 	"quit",		CMDQUIT,
504577Seric 	"mrsq",		CMDMRSQ,
514976Seric 	"helo",		CMDHELO,
52*5003Seric # ifdef DEBUG
53*5003Seric 	"showq",	CMDDBGSHOWQ,
54*5003Seric # endif DEBUG
554549Seric 	NULL,		CMDERROR,
564549Seric };
574549Seric 
584549Seric smtp()
594549Seric {
604549Seric 	char inp[MAXLINE];
614549Seric 	register char *p;
624549Seric 	struct cmd *c;
634549Seric 	char *cmd;
644549Seric 	extern char *skipword();
654549Seric 	extern bool sameword();
664549Seric 	bool hasmail;			/* mail command received */
674713Seric 	int rcps;			/* number of recipients */
68*5003Seric 	auto ADDRESS *vrfyqueue;
69*5003Seric 	ADDRESS *prev;
704549Seric 
71*5003Seric 	hasmail = FALSE;
724713Seric 	rcps = 0;
734549Seric 	message("220", "%s Sendmail at your service", HostName);
744549Seric 	for (;;)
754549Seric 	{
764549Seric 		To = NULL;
774577Seric 		Errors = 0;
784549Seric 		if (fgets(inp, sizeof inp, InChannel) == NULL)
794549Seric 		{
804549Seric 			/* end of file, just die */
814558Seric 			message("421", "%s Lost input channel", HostName);
824549Seric 			finis();
834549Seric 		}
844549Seric 
854549Seric 		/* clean up end of line */
864558Seric 		fixcrlf(inp, TRUE);
874549Seric 
884713Seric 		/* echo command to transcript */
894713Seric 		fprintf(Xscript, "*** %s\n", inp);
904713Seric 
914549Seric 		/* break off command */
924549Seric 		for (p = inp; isspace(*p); p++)
934549Seric 			continue;
944549Seric 		cmd = p;
954549Seric 		while (*++p != '\0' && !isspace(*p))
964549Seric 			continue;
974549Seric 		if (*p != '\0')
984549Seric 			*p++ = '\0';
994549Seric 
1004549Seric 		/* decode command */
1014549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1024549Seric 		{
1034549Seric 			if (sameword(c->cmdname, cmd))
1044549Seric 				break;
1054549Seric 		}
1064549Seric 
1074549Seric 		/* process command */
1084549Seric 		switch (c->cmdcode)
1094549Seric 		{
1104976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
1114997Seric 			message("250", "%s Hello %s, pleased to meet you",
1124997Seric 				HostName, p);
1134976Seric 			break;
1144976Seric 
1154549Seric 		  case CMDMAIL:		/* mail -- designate sender */
1164558Seric 			if (hasmail)
1174558Seric 			{
1184558Seric 				message("503", "Sender already specified");
1194558Seric 				break;
1204558Seric 			}
1214549Seric 			p = skipword(p, "from");
1224549Seric 			if (p == NULL)
1234549Seric 				break;
1244549Seric 			if (index(p, ',') != NULL)
1254549Seric 			{
1264549Seric 				message("501", "Source routing not implemented");
1274549Seric 				Errors++;
1284549Seric 				break;
1294549Seric 			}
1304549Seric 			setsender(p);
1314577Seric 			if (Errors == 0)
1324549Seric 			{
1334549Seric 				message("250", "Sender ok");
1344549Seric 				hasmail = TRUE;
1354549Seric 			}
1364549Seric 			break;
1374549Seric 
1384976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
1394549Seric 			p = skipword(p, "to");
1404549Seric 			if (p == NULL)
1414549Seric 				break;
1424549Seric 			if (index(p, ',') != NULL)
1434549Seric 			{
1444549Seric 				message("501", "Source routing not implemented");
1454549Seric 				Errors++;
1464549Seric 				break;
1474549Seric 			}
148*5003Seric 			sendto(p, 1, (ADDRESS *) NULL, &SendQueue);
1494577Seric 			if (Errors == 0)
1504549Seric 			{
1514549Seric 				message("250", "Recipient ok");
1524713Seric 				rcps++;
1534549Seric 			}
1544549Seric 			break;
1554549Seric 
1564549Seric 		  case CMDDATA:		/* data -- text of mail */
1574976Seric 			if (!hasmail)
1584549Seric 			{
1594976Seric 				message("503", "Need MAIL command");
1604976Seric 				break;
1614549Seric 			}
1624713Seric 			else if (rcps <= 0)
1634549Seric 			{
1644976Seric 				message("503", "Need RCPT (recipient)");
1654976Seric 				break;
1664549Seric 			}
1674976Seric 
1684976Seric 			/* collect the text of the message */
1694976Seric 			collect(TRUE);
1704976Seric 			if (Errors != 0)
1714976Seric 				break;
1724976Seric 
1734976Seric 			/* if sending to multiple people, mail back errors */
1744976Seric 			if (rcps != 1)
1754976Seric 				HoldErrs = MailBack = TRUE;
1764976Seric 
1774976Seric 			/* send to all recipients */
1784976Seric 			sendall(FALSE);
1794976Seric 
1804976Seric 			/* reset strange modes */
1814976Seric 			HoldErrs = FALSE;
1824976Seric 			To = NULL;
1834976Seric 
1844976Seric 			/* issue success if appropriate */
1854976Seric 			if (Errors == 0 || rcps != 1)
1864976Seric 				message("250", "Sent");
1874549Seric 			break;
1884549Seric 
1894549Seric 		  case CMDRSET:		/* rset -- reset state */
1904549Seric 			message("250", "Reset state");
1914549Seric 			finis();
1924549Seric 
1934549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
194*5003Seric 			vrfyqueue = NULL;
195*5003Seric 			sendto(p, 1, (ADDRESS *) NULL, &vrfyqueue);
196*5003Seric 			while (vrfyqueue != NULL)
197*5003Seric 			{
198*5003Seric 				register ADDRESS *a = vrfyqueue->q_next;
199*5003Seric 				char *code;
200*5003Seric 
201*5003Seric 				while (a != NULL && bitset(QDONTSEND, a->q_flags))
202*5003Seric 					a = a->q_next;
203*5003Seric 
204*5003Seric 				if (!bitset(QDONTSEND, vrfyqueue->q_flags))
205*5003Seric 				{
206*5003Seric 					if (a != NULL)
207*5003Seric 						code = "250-";
208*5003Seric 					else
209*5003Seric 						code = "250";
210*5003Seric 					if (vrfyqueue->q_fullname == NULL)
211*5003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
212*5003Seric 					else
213*5003Seric 						message(code, "%s <%s>",
214*5003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
215*5003Seric 				}
216*5003Seric 				else if (a == NULL)
217*5003Seric 					message("554", "Self destructive alias loop");
218*5003Seric 				vrfyqueue = a;
219*5003Seric 			}
2204549Seric 			break;
2214549Seric 
2224549Seric 		  case CMDHELP:		/* help -- give user info */
2234577Seric 			if (*p == '\0')
2244577Seric 				p = "SMTP";
2254577Seric 			help(p);
2264549Seric 			break;
2274549Seric 
2284549Seric 		  case CMDNOOP:		/* noop -- do nothing */
2294549Seric 			message("200", "OK");
2304549Seric 			break;
2314549Seric 
2324549Seric 		  case CMDQUIT:		/* quit -- leave mail */
2334549Seric 			message("221", "%s closing connection", HostName);
2344549Seric 			finis();
2354549Seric 
2364577Seric 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
2374577Seric 			if (*p == 'R' || *p == 'T')
2384577Seric 			{
2394577Seric 				/* recipients first or text first */
2404577Seric 				message("200", "%c ok, please continue", *p);
2414577Seric 			}
2424577Seric 			else if (*p == '?')
2434577Seric 			{
2444577Seric 				/* what do I prefer?  anything, anytime */
2454577Seric 				message("215", "R Recipients first is my choice");
2464577Seric 			}
2474577Seric 			else if (*p == '\0')
2484577Seric 			{
2494577Seric 				/* no meaningful scheme */
2504577Seric 				message("200", "okey dokie boobie");
2514577Seric 			}
2524577Seric 			else
2534577Seric 			{
2544577Seric 				/* bad argument */
2554577Seric 				message("504", "Scheme unknown");
2564577Seric 			}
2574577Seric 			break;
2584577Seric 
259*5003Seric # ifdef DEBUG
260*5003Seric 		  case CMDDBGSHOWQ:	/* show queues */
261*5003Seric 			printf("SendQueue=");
262*5003Seric 			printaddr(SendQueue, TRUE);
263*5003Seric 			break;
264*5003Seric # endif DEBUG
265*5003Seric 
2664549Seric 		  case CMDERROR:	/* unknown command */
2674549Seric 			message("500", "Command unrecognized");
2684549Seric 			break;
2694549Seric 
2704549Seric 		  default:
2714549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
2724549Seric 			break;
2734549Seric 		}
2744549Seric 	}
2754549Seric }
2764549Seric /*
2774549Seric **  SKIPWORD -- skip a fixed word.
2784549Seric **
2794549Seric **	Parameters:
2804549Seric **		p -- place to start looking.
2814549Seric **		w -- word to skip.
2824549Seric **
2834549Seric **	Returns:
2844549Seric **		p following w.
2854549Seric **		NULL on error.
2864549Seric **
2874549Seric **	Side Effects:
2884549Seric **		clobbers the p data area.
2894549Seric */
2904549Seric 
2914549Seric static char *
2924549Seric skipword(p, w)
2934549Seric 	register char *p;
2944549Seric 	char *w;
2954549Seric {
2964549Seric 	register char *q;
2974549Seric 	extern bool sameword();
2984549Seric 
2994549Seric 	/* find beginning of word */
3004549Seric 	while (isspace(*p))
3014549Seric 		p++;
3024549Seric 	q = p;
3034549Seric 
3044549Seric 	/* find end of word */
3054549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
3064549Seric 		p++;
3074549Seric 	while (isspace(*p))
3084549Seric 		*p++ = '\0';
3094549Seric 	if (*p != ':')
3104549Seric 	{
3114549Seric 	  syntax:
3124549Seric 		message("501", "Syntax error");
3134549Seric 		Errors++;
3144549Seric 		return (NULL);
3154549Seric 	}
3164549Seric 	*p++ = '\0';
3174549Seric 	while (isspace(*p))
3184549Seric 		p++;
3194549Seric 
3204549Seric 	/* see if the input word matches desired word */
3214549Seric 	if (!sameword(q, w))
3224549Seric 		goto syntax;
3234549Seric 
3244549Seric 	return (p);
3254549Seric }
3264577Seric /*
3274577Seric **  HELP -- implement the HELP command.
3284577Seric **
3294577Seric **	Parameters:
3304577Seric **		topic -- the topic we want help for.
3314577Seric **
3324577Seric **	Returns:
3334577Seric **		none.
3344577Seric **
3354577Seric **	Side Effects:
3364577Seric **		outputs the help file to message output.
3374577Seric */
3384577Seric 
3394577Seric help(topic)
3404577Seric 	char *topic;
3414577Seric {
3424577Seric 	register FILE *hf;
3434577Seric 	int len;
3444577Seric 	char buf[MAXLINE];
3454577Seric 	bool noinfo;
3464582Seric 	extern char *HelpFile;
3474577Seric 
3484582Seric 	hf = fopen(HelpFile, "r");
3494577Seric 	if (hf == NULL)
3504577Seric 	{
3514577Seric 		/* no help */
3524577Seric 		message("502", "HELP not implemented");
3534577Seric 		return;
3544577Seric 	}
3554577Seric 
3564577Seric 	len = strlen(topic);
3574577Seric 	makelower(topic);
3584577Seric 	noinfo = TRUE;
3594577Seric 
3604577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
3614577Seric 	{
3624577Seric 		if (strncmp(buf, topic, len) == 0)
3634577Seric 		{
3644577Seric 			register char *p;
3654577Seric 
3664577Seric 			p = index(buf, '\t');
3674577Seric 			if (p == NULL)
3684577Seric 				p = buf;
3694577Seric 			else
3704577Seric 				p++;
3714577Seric 			fixcrlf(p, TRUE);
3724577Seric 			message("214-", p);
3734577Seric 			noinfo = FALSE;
3744577Seric 		}
3754577Seric 	}
3764577Seric 
3774577Seric 	if (noinfo)
3784577Seric 		message("504", "HELP topic unknown");
3794577Seric 	else
3804577Seric 		message("214", "End of HELP info");
3814628Seric 	(void) fclose(hf);
3824577Seric }
383