1 # include "sendmail.h"
2 
3 static char	SccsId[] =	"@(#)srvrsmtp.c	3.7	10/31/81";
4 
5 /*
6 **  SMTP -- run the SMTP protocol.
7 **
8 **	Parameters:
9 **		none.
10 **
11 **	Returns:
12 **		never.
13 **
14 **	Side Effects:
15 **		Reads commands from the input channel and processes
16 **			them.
17 */
18 
19 struct cmd
20 {
21 	char	*cmdname;	/* command name */
22 	int	cmdcode;	/* internal code, see below */
23 };
24 
25 /* values for cmdcode */
26 # define CMDERROR	0	/* bad command */
27 # define CMDMAIL	1	/* mail -- designate sender */
28 # define CMDMRCP	2	/* mrcp -- designate recipient */
29 # define CMDDATA	3	/* data -- send message text */
30 # define CMDDOIT	4	/* doit -- actually do delivery */
31 # define CMDRSET	5	/* rset -- reset state */
32 # define CMDVRFY	6	/* vrfy -- verify address */
33 # define CMDHELP	7	/* help -- give usage info */
34 # define CMDNOOP	8	/* noop -- do nothing */
35 # define CMDQUIT	9	/* quit -- close connection and die */
36 # define CMDMRSQ	10	/* mrsq -- for old mtp compat only */
37 
38 static struct cmd	CmdTab[] =
39 {
40 	"mail",		CMDMAIL,
41 	"mrcp",		CMDMRCP,
42 	"data",		CMDDATA,
43 	"doit",		CMDDOIT,
44 	"rset",		CMDRSET,
45 	"vrfy",		CMDVRFY,
46 	"help",		CMDHELP,
47 	"noop",		CMDNOOP,
48 	"quit",		CMDQUIT,
49 	"mrsq",		CMDMRSQ,
50 	NULL,		CMDERROR,
51 };
52 
53 smtp()
54 {
55 	char inp[MAXLINE];
56 	register char *p;
57 	struct cmd *c;
58 	char *cmd;
59 	extern char *skipword();
60 	extern bool sameword();
61 	bool hasmail;			/* mail command received */
62 	int rcps;			/* number of recipients */
63 	bool hasdata;			/* has mail data */
64 
65 	hasmail = hasdata = FALSE;
66 	rcps = 0;
67 	message("220", "%s Sendmail at your service", HostName);
68 	for (;;)
69 	{
70 		To = NULL;
71 		Errors = 0;
72 		if (fgets(inp, sizeof inp, InChannel) == NULL)
73 		{
74 			/* end of file, just die */
75 			message("421", "%s Lost input channel", HostName);
76 			finis();
77 		}
78 
79 		/* clean up end of line */
80 		fixcrlf(inp, TRUE);
81 
82 		/* echo command to transcript */
83 		fprintf(Xscript, "*** %s\n", inp);
84 
85 		/* break off command */
86 		for (p = inp; isspace(*p); p++)
87 			continue;
88 		cmd = p;
89 		while (*++p != '\0' && !isspace(*p))
90 			continue;
91 		if (*p != '\0')
92 			*p++ = '\0';
93 
94 		/* decode command */
95 		for (c = CmdTab; c->cmdname != NULL; c++)
96 		{
97 			if (sameword(c->cmdname, cmd))
98 				break;
99 		}
100 
101 		/* process command */
102 		switch (c->cmdcode)
103 		{
104 		  case CMDMAIL:		/* mail -- designate sender */
105 			if (hasmail)
106 			{
107 				message("503", "Sender already specified");
108 				break;
109 			}
110 			p = skipword(p, "from");
111 			if (p == NULL)
112 				break;
113 			if (index(p, ',') != NULL)
114 			{
115 				message("501", "Source routing not implemented");
116 				Errors++;
117 				break;
118 			}
119 			setsender(p);
120 			if (Errors == 0)
121 			{
122 				message("250", "Sender ok");
123 				hasmail = TRUE;
124 			}
125 			break;
126 
127 		  case CMDMRCP:		/* mrcp -- designate recipient */
128 			p = skipword(p, "to");
129 			if (p == NULL)
130 				break;
131 			if (index(p, ',') != NULL)
132 			{
133 				message("501", "Source routing not implemented");
134 				Errors++;
135 				break;
136 			}
137 			sendto(p, 1, (ADDRESS *) NULL);
138 			if (Errors == 0)
139 			{
140 				message("250", "Recipient ok");
141 				rcps++;
142 			}
143 			break;
144 
145 		  case CMDDATA:		/* data -- text of mail */
146 			collect(TRUE);
147 			if (Errors == 0)
148 			{
149 				message("250", "Message stored");
150 				hasdata = TRUE;
151 			}
152 			break;
153 
154 		  case CMDDOIT:		/* doit -- actually send everything */
155 			if (!hasmail)
156 				message("503", "Need MAIL command");
157 			else if (rcps <= 0)
158 				message("503", "Need MRCP (recipient)");
159 			else if (!hasdata)
160 				message("503", "No message, use DATA");
161 			else
162 			{
163 				if (rcps != 1)
164 					HoldErrs = MailBack = TRUE;
165 				sendall(FALSE);
166 				HoldErrs = FALSE;
167 				To = NULL;
168 				if (Errors == 0 || rcps != 1)
169 					message("250", "Sent");
170 			}
171 			break;
172 
173 		  case CMDRSET:		/* rset -- reset state */
174 			message("250", "Reset state");
175 			finis();
176 
177 		  case CMDVRFY:		/* vrfy -- verify address */
178 			sendto(p, 1, (ADDRESS *) NULL);
179 			if (Errors == 0)
180 				message("250", "user ok");
181 			break;
182 
183 		  case CMDHELP:		/* help -- give user info */
184 			if (*p == '\0')
185 				p = "SMTP";
186 			help(p);
187 			break;
188 
189 		  case CMDNOOP:		/* noop -- do nothing */
190 			message("200", "OK");
191 			break;
192 
193 		  case CMDQUIT:		/* quit -- leave mail */
194 			message("221", "%s closing connection", HostName);
195 			finis();
196 
197 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
198 			if (*p == 'R' || *p == 'T')
199 			{
200 				/* recipients first or text first */
201 				message("200", "%c ok, please continue", *p);
202 			}
203 			else if (*p == '?')
204 			{
205 				/* what do I prefer?  anything, anytime */
206 				message("215", "R Recipients first is my choice");
207 			}
208 			else if (*p == '\0')
209 			{
210 				/* no meaningful scheme */
211 				message("200", "okey dokie boobie");
212 			}
213 			else
214 			{
215 				/* bad argument */
216 				message("504", "Scheme unknown");
217 			}
218 			break;
219 
220 		  case CMDERROR:	/* unknown command */
221 			message("500", "Command unrecognized");
222 			break;
223 
224 		  default:
225 			syserr("smtp: unknown code %d", c->cmdcode);
226 			break;
227 		}
228 	}
229 }
230 /*
231 **  SKIPWORD -- skip a fixed word.
232 **
233 **	Parameters:
234 **		p -- place to start looking.
235 **		w -- word to skip.
236 **
237 **	Returns:
238 **		p following w.
239 **		NULL on error.
240 **
241 **	Side Effects:
242 **		clobbers the p data area.
243 */
244 
245 static char *
246 skipword(p, w)
247 	register char *p;
248 	char *w;
249 {
250 	register char *q;
251 	extern bool sameword();
252 
253 	/* find beginning of word */
254 	while (isspace(*p))
255 		p++;
256 	q = p;
257 
258 	/* find end of word */
259 	while (*p != '\0' && *p != ':' && !isspace(*p))
260 		p++;
261 	while (isspace(*p))
262 		*p++ = '\0';
263 	if (*p != ':')
264 	{
265 	  syntax:
266 		message("501", "Syntax error");
267 		Errors++;
268 		return (NULL);
269 	}
270 	*p++ = '\0';
271 	while (isspace(*p))
272 		p++;
273 
274 	/* see if the input word matches desired word */
275 	if (!sameword(q, w))
276 		goto syntax;
277 
278 	return (p);
279 }
280 /*
281 **  HELP -- implement the HELP command.
282 **
283 **	Parameters:
284 **		topic -- the topic we want help for.
285 **
286 **	Returns:
287 **		none.
288 **
289 **	Side Effects:
290 **		outputs the help file to message output.
291 */
292 
293 help(topic)
294 	char *topic;
295 {
296 	register FILE *hf;
297 	int len;
298 	char buf[MAXLINE];
299 	bool noinfo;
300 	extern char *HelpFile;
301 
302 	hf = fopen(HelpFile, "r");
303 	if (hf == NULL)
304 	{
305 		/* no help */
306 		message("502", "HELP not implemented");
307 		return;
308 	}
309 
310 	len = strlen(topic);
311 	makelower(topic);
312 	noinfo = TRUE;
313 
314 	while (fgets(buf, sizeof buf, hf) != NULL)
315 	{
316 		if (strncmp(buf, topic, len) == 0)
317 		{
318 			register char *p;
319 
320 			p = index(buf, '\t');
321 			if (p == NULL)
322 				p = buf;
323 			else
324 				p++;
325 			fixcrlf(p, TRUE);
326 			message("214-", p);
327 			noinfo = FALSE;
328 		}
329 	}
330 
331 	if (noinfo)
332 		message("504", "HELP topic unknown");
333 	else
334 		message("214", "End of HELP info");
335 	(void) fclose(hf);
336 }
337