14549Seric # include "sendmail.h"
24549Seric 
35181Seric # ifndef SMTP
4*7275Seric SCCSID(@(#)srvrsmtp.c	3.18		06/25/82	(no SMTP));
55181Seric # else SMTP
64556Seric 
7*7275Seric SCCSID(@(#)srvrsmtp.c	3.18		06/25/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*7275Seric # define CMDDBGSHOWQ	12	/* _showq -- show send queue (DEBUG) */
42*7275Seric # define CMDDBGDEBUG	13	/* _debug -- set debug mode */
43*7275Seric # define CMDDBGVERBOSE	14	/* _verbose -- go into verbose mode */
444549Seric 
454549Seric static struct cmd	CmdTab[] =
464549Seric {
474549Seric 	"mail",		CMDMAIL,
484976Seric 	"rcpt",		CMDRCPT,
494976Seric 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
504549Seric 	"data",		CMDDATA,
514549Seric 	"rset",		CMDRSET,
524549Seric 	"vrfy",		CMDVRFY,
534549Seric 	"help",		CMDHELP,
544549Seric 	"noop",		CMDNOOP,
554549Seric 	"quit",		CMDQUIT,
564577Seric 	"mrsq",		CMDMRSQ,
574976Seric 	"helo",		CMDHELO,
585003Seric # ifdef DEBUG
59*7275Seric 	"_showq",	CMDDBGSHOWQ,
60*7275Seric 	"_debug",	CMDDBGDEBUG,
61*7275Seric 	"_verbose",	CMDDBGVERBOSE,
625003Seric # endif DEBUG
634549Seric 	NULL,		CMDERROR,
644549Seric };
654549Seric 
664549Seric smtp()
674549Seric {
684549Seric 	char inp[MAXLINE];
694549Seric 	register char *p;
704549Seric 	struct cmd *c;
714549Seric 	char *cmd;
724549Seric 	extern char *skipword();
734549Seric 	extern bool sameword();
744549Seric 	bool hasmail;			/* mail command received */
754713Seric 	int rcps;			/* number of recipients */
765003Seric 	auto ADDRESS *vrfyqueue;
777124Seric 	extern char Version[];
784549Seric 
795003Seric 	hasmail = FALSE;
804713Seric 	rcps = 0;
81*7275Seric 	close(1);
82*7275Seric 	dup(fileno(OutChannel));
837124Seric 	message("220", "%s Sendmail version %s at your service", HostName, Version);
844549Seric 	for (;;)
854549Seric 	{
866907Seric 		CurEnv->e_to = NULL;
874577Seric 		Errors = 0;
88*7275Seric 		(void) fflush(stdout);
894549Seric 		if (fgets(inp, sizeof inp, InChannel) == NULL)
904549Seric 		{
914549Seric 			/* end of file, just die */
924558Seric 			message("421", "%s Lost input channel", HostName);
934549Seric 			finis();
944549Seric 		}
954549Seric 
964549Seric 		/* clean up end of line */
974558Seric 		fixcrlf(inp, TRUE);
984549Seric 
994713Seric 		/* echo command to transcript */
1004713Seric 		fprintf(Xscript, "*** %s\n", inp);
1014713Seric 
1024549Seric 		/* break off command */
1034549Seric 		for (p = inp; isspace(*p); p++)
1044549Seric 			continue;
1054549Seric 		cmd = p;
1064549Seric 		while (*++p != '\0' && !isspace(*p))
1074549Seric 			continue;
1084549Seric 		if (*p != '\0')
1094549Seric 			*p++ = '\0';
1104549Seric 
1114549Seric 		/* decode command */
1124549Seric 		for (c = CmdTab; c->cmdname != NULL; c++)
1134549Seric 		{
1144549Seric 			if (sameword(c->cmdname, cmd))
1154549Seric 				break;
1164549Seric 		}
1174549Seric 
1184549Seric 		/* process command */
1194549Seric 		switch (c->cmdcode)
1204549Seric 		{
1214976Seric 		  case CMDHELO:		/* hello -- introduce yourself */
1225187Seric 			define('s', newstr(p));
1234997Seric 			message("250", "%s Hello %s, pleased to meet you",
1244997Seric 				HostName, p);
1254976Seric 			break;
1264976Seric 
1274549Seric 		  case CMDMAIL:		/* mail -- designate sender */
1284558Seric 			if (hasmail)
1294558Seric 			{
1304558Seric 				message("503", "Sender already specified");
1314558Seric 				break;
1324558Seric 			}
1334549Seric 			p = skipword(p, "from");
1344549Seric 			if (p == NULL)
1354549Seric 				break;
1364549Seric 			if (index(p, ',') != NULL)
1374549Seric 			{
1384549Seric 				message("501", "Source routing not implemented");
1394549Seric 				Errors++;
1404549Seric 				break;
1414549Seric 			}
1424549Seric 			setsender(p);
1434577Seric 			if (Errors == 0)
1444549Seric 			{
1454549Seric 				message("250", "Sender ok");
1464549Seric 				hasmail = TRUE;
1474549Seric 			}
1484549Seric 			break;
1494549Seric 
1504976Seric 		  case CMDRCPT:		/* rcpt -- designate recipient */
1514549Seric 			p = skipword(p, "to");
1524549Seric 			if (p == NULL)
1534549Seric 				break;
1544549Seric 			if (index(p, ',') != NULL)
1554549Seric 			{
1564549Seric 				message("501", "Source routing not implemented");
1574549Seric 				Errors++;
1584549Seric 				break;
1594549Seric 			}
1606907Seric 			sendto(p, 1, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
1614577Seric 			if (Errors == 0)
1624549Seric 			{
1636057Seric 				message("250", "%s... Recipient ok", p);
1644713Seric 				rcps++;
1654549Seric 			}
1664549Seric 			break;
1674549Seric 
1684549Seric 		  case CMDDATA:		/* data -- text of mail */
1694976Seric 			if (!hasmail)
1704549Seric 			{
1714976Seric 				message("503", "Need MAIL command");
1724976Seric 				break;
1734549Seric 			}
1744713Seric 			else if (rcps <= 0)
1754549Seric 			{
1764976Seric 				message("503", "Need RCPT (recipient)");
1774976Seric 				break;
1784549Seric 			}
1794976Seric 
1804976Seric 			/* collect the text of the message */
1814976Seric 			collect(TRUE);
1824976Seric 			if (Errors != 0)
1834976Seric 				break;
1844976Seric 
1854976Seric 			/* if sending to multiple people, mail back errors */
1864976Seric 			if (rcps != 1)
1874976Seric 				HoldErrs = MailBack = TRUE;
1884976Seric 
1894976Seric 			/* send to all recipients */
1907046Seric 			sendall(CurEnv, FALSE);
1914976Seric 
1924976Seric 			/* reset strange modes */
1934976Seric 			HoldErrs = FALSE;
1946907Seric 			CurEnv->e_to = NULL;
1954976Seric 
1964976Seric 			/* issue success if appropriate */
1974976Seric 			if (Errors == 0 || rcps != 1)
1984976Seric 				message("250", "Sent");
1994549Seric 			break;
2004549Seric 
2014549Seric 		  case CMDRSET:		/* rset -- reset state */
2024549Seric 			message("250", "Reset state");
2034549Seric 			finis();
2044549Seric 
2054549Seric 		  case CMDVRFY:		/* vrfy -- verify address */
2065003Seric 			vrfyqueue = NULL;
2075003Seric 			sendto(p, 1, (ADDRESS *) NULL, &vrfyqueue);
2085003Seric 			while (vrfyqueue != NULL)
2095003Seric 			{
2105003Seric 				register ADDRESS *a = vrfyqueue->q_next;
2115003Seric 				char *code;
2125003Seric 
2135003Seric 				while (a != NULL && bitset(QDONTSEND, a->q_flags))
2145003Seric 					a = a->q_next;
2155003Seric 
2165003Seric 				if (!bitset(QDONTSEND, vrfyqueue->q_flags))
2175003Seric 				{
2185003Seric 					if (a != NULL)
2195003Seric 						code = "250-";
2205003Seric 					else
2215003Seric 						code = "250";
2225003Seric 					if (vrfyqueue->q_fullname == NULL)
2235003Seric 						message(code, "<%s>", vrfyqueue->q_paddr);
2245003Seric 					else
2255003Seric 						message(code, "%s <%s>",
2265003Seric 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
2275003Seric 				}
2285003Seric 				else if (a == NULL)
2295003Seric 					message("554", "Self destructive alias loop");
2305003Seric 				vrfyqueue = a;
2315003Seric 			}
2324549Seric 			break;
2334549Seric 
2344549Seric 		  case CMDHELP:		/* help -- give user info */
2354577Seric 			if (*p == '\0')
2364577Seric 				p = "SMTP";
2374577Seric 			help(p);
2384549Seric 			break;
2394549Seric 
2404549Seric 		  case CMDNOOP:		/* noop -- do nothing */
2414549Seric 			message("200", "OK");
2424549Seric 			break;
2434549Seric 
2444549Seric 		  case CMDQUIT:		/* quit -- leave mail */
2454549Seric 			message("221", "%s closing connection", HostName);
2464549Seric 			finis();
2474549Seric 
2484577Seric 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
2494577Seric 			if (*p == 'R' || *p == 'T')
2504577Seric 			{
2514577Seric 				/* recipients first or text first */
2524577Seric 				message("200", "%c ok, please continue", *p);
2534577Seric 			}
2544577Seric 			else if (*p == '?')
2554577Seric 			{
2564577Seric 				/* what do I prefer?  anything, anytime */
2574577Seric 				message("215", "R Recipients first is my choice");
2584577Seric 			}
2594577Seric 			else if (*p == '\0')
2604577Seric 			{
2614577Seric 				/* no meaningful scheme */
2624577Seric 				message("200", "okey dokie boobie");
2634577Seric 			}
2644577Seric 			else
2654577Seric 			{
2664577Seric 				/* bad argument */
2674577Seric 				message("504", "Scheme unknown");
2684577Seric 			}
2694577Seric 			break;
2704577Seric 
2715003Seric # ifdef DEBUG
2725003Seric 		  case CMDDBGSHOWQ:	/* show queues */
2736907Seric 			printf("Send Queue=");
2746907Seric 			printaddr(CurEnv->e_sendqueue, TRUE);
2755003Seric 			break;
276*7275Seric 
277*7275Seric 		  case CMDDBGDEBUG:	/* set debug mode */
278*7275Seric 			Debug = atoi(p);
279*7275Seric 			message("200", "Debug = %d", Debug);
280*7275Seric 			break;
281*7275Seric 
282*7275Seric 		  case CMDDBGVERBOSE:	/* set verbose mode */
283*7275Seric 			Verbose = TRUE;
284*7275Seric 			message("200", "Verbose mode");
285*7275Seric 			break;
2865003Seric # endif DEBUG
2875003Seric 
2884549Seric 		  case CMDERROR:	/* unknown command */
2894549Seric 			message("500", "Command unrecognized");
2904549Seric 			break;
2914549Seric 
2924549Seric 		  default:
2934549Seric 			syserr("smtp: unknown code %d", c->cmdcode);
2944549Seric 			break;
2954549Seric 		}
2964549Seric 	}
2974549Seric }
2984549Seric /*
2994549Seric **  SKIPWORD -- skip a fixed word.
3004549Seric **
3014549Seric **	Parameters:
3024549Seric **		p -- place to start looking.
3034549Seric **		w -- word to skip.
3044549Seric **
3054549Seric **	Returns:
3064549Seric **		p following w.
3074549Seric **		NULL on error.
3084549Seric **
3094549Seric **	Side Effects:
3104549Seric **		clobbers the p data area.
3114549Seric */
3124549Seric 
3134549Seric static char *
3144549Seric skipword(p, w)
3154549Seric 	register char *p;
3164549Seric 	char *w;
3174549Seric {
3184549Seric 	register char *q;
3194549Seric 	extern bool sameword();
3204549Seric 
3214549Seric 	/* find beginning of word */
3224549Seric 	while (isspace(*p))
3234549Seric 		p++;
3244549Seric 	q = p;
3254549Seric 
3264549Seric 	/* find end of word */
3274549Seric 	while (*p != '\0' && *p != ':' && !isspace(*p))
3284549Seric 		p++;
3294549Seric 	while (isspace(*p))
3304549Seric 		*p++ = '\0';
3314549Seric 	if (*p != ':')
3324549Seric 	{
3334549Seric 	  syntax:
3344549Seric 		message("501", "Syntax error");
3354549Seric 		Errors++;
3364549Seric 		return (NULL);
3374549Seric 	}
3384549Seric 	*p++ = '\0';
3394549Seric 	while (isspace(*p))
3404549Seric 		p++;
3414549Seric 
3424549Seric 	/* see if the input word matches desired word */
3434549Seric 	if (!sameword(q, w))
3444549Seric 		goto syntax;
3454549Seric 
3464549Seric 	return (p);
3474549Seric }
3484577Seric /*
3494577Seric **  HELP -- implement the HELP command.
3504577Seric **
3514577Seric **	Parameters:
3524577Seric **		topic -- the topic we want help for.
3534577Seric **
3544577Seric **	Returns:
3554577Seric **		none.
3564577Seric **
3574577Seric **	Side Effects:
3584577Seric **		outputs the help file to message output.
3594577Seric */
3604577Seric 
3614577Seric help(topic)
3624577Seric 	char *topic;
3634577Seric {
3644577Seric 	register FILE *hf;
3654577Seric 	int len;
3664577Seric 	char buf[MAXLINE];
3674577Seric 	bool noinfo;
3684582Seric 	extern char *HelpFile;
3694577Seric 
3704582Seric 	hf = fopen(HelpFile, "r");
3714577Seric 	if (hf == NULL)
3724577Seric 	{
3734577Seric 		/* no help */
3744577Seric 		message("502", "HELP not implemented");
3754577Seric 		return;
3764577Seric 	}
3774577Seric 
3784577Seric 	len = strlen(topic);
3794577Seric 	makelower(topic);
3804577Seric 	noinfo = TRUE;
3814577Seric 
3824577Seric 	while (fgets(buf, sizeof buf, hf) != NULL)
3834577Seric 	{
3844577Seric 		if (strncmp(buf, topic, len) == 0)
3854577Seric 		{
3864577Seric 			register char *p;
3874577Seric 
3884577Seric 			p = index(buf, '\t');
3894577Seric 			if (p == NULL)
3904577Seric 				p = buf;
3914577Seric 			else
3924577Seric 				p++;
3934577Seric 			fixcrlf(p, TRUE);
3944577Seric 			message("214-", p);
3954577Seric 			noinfo = FALSE;
3964577Seric 		}
3974577Seric 	}
3984577Seric 
3994577Seric 	if (noinfo)
4004577Seric 		message("504", "HELP topic unknown");
4014577Seric 	else
4024577Seric 		message("214", "End of HELP info");
4034628Seric 	(void) fclose(hf);
4044577Seric }
4055181Seric 
4065181Seric # endif SMTP
407