1 # include "sendmail.h"
2 
3 # ifndef SMTP
4 SCCSID(@(#)srvrsmtp.c	3.20.1.1		07/04/82	(no SMTP));
5 # else SMTP
6 
7 SCCSID(@(#)srvrsmtp.c	3.20.1.1		07/04/82);
8 
9 /*
10 **  SMTP -- run the SMTP protocol.
11 **
12 **	Parameters:
13 **		none.
14 **
15 **	Returns:
16 **		never.
17 **
18 **	Side Effects:
19 **		Reads commands from the input channel and processes
20 **			them.
21 */
22 
23 struct cmd
24 {
25 	char	*cmdname;	/* command name */
26 	int	cmdcode;	/* internal code, see below */
27 };
28 
29 /* values for cmdcode */
30 # define CMDERROR	0	/* bad command */
31 # define CMDMAIL	1	/* mail -- designate sender */
32 # define CMDRCPT	2	/* rcpt -- designate recipient */
33 # define CMDDATA	3	/* data -- send message text */
34 # define CMDHOPS	4	/* hops -- specify hop count */
35 # define CMDRSET	5	/* rset -- reset state */
36 # define CMDVRFY	6	/* vrfy -- verify address */
37 # define CMDHELP	7	/* help -- give usage info */
38 # define CMDNOOP	8	/* noop -- do nothing */
39 # define CMDQUIT	9	/* quit -- close connection and die */
40 # define CMDMRSQ	10	/* mrsq -- for old mtp compat only */
41 # define CMDHELO	11	/* helo -- be polite */
42 # define CMDDBGSHOWQ	12	/* _showq -- show send queue (DEBUG) */
43 # define CMDDBGDEBUG	13	/* _debug -- set debug mode */
44 # define CMDDBGVERBOSE	14	/* _verbose -- go into verbose mode */
45 # define CMDDBGKILL	15	/* _kill -- kill sendmail */
46 
47 static struct cmd	CmdTab[] =
48 {
49 	"mail",		CMDMAIL,
50 	"rcpt",		CMDRCPT,
51 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
52 	"data",		CMDDATA,
53 	"rset",		CMDRSET,
54 	"vrfy",		CMDVRFY,
55 	"help",		CMDHELP,
56 	"noop",		CMDNOOP,
57 	"quit",		CMDQUIT,
58 	"mrsq",		CMDMRSQ,
59 	"helo",		CMDHELO,
60 	"hops",		CMDHOPS,
61 # ifdef DEBUG
62 	"_showq",	CMDDBGSHOWQ,
63 	"_debug",	CMDDBGDEBUG,
64 	"_verbose",	CMDDBGVERBOSE,
65 	"_kill",	CMDDBGKILL,
66 # endif DEBUG
67 	NULL,		CMDERROR,
68 };
69 
70 smtp()
71 {
72 	char inp[MAXLINE];
73 	register char *p;
74 	struct cmd *c;
75 	char *cmd;
76 	extern char *skipword();
77 	extern bool sameword();
78 	bool hasmail;			/* mail command received */
79 	int rcps;			/* number of recipients */
80 	auto ADDRESS *vrfyqueue;
81 	extern char Version[];
82 
83 	hasmail = FALSE;
84 	rcps = 0;
85 	(void) close(1);
86 	(void) dup(fileno(OutChannel));
87 	message("220", "%s Sendmail version %s at your service", HostName, Version);
88 	for (;;)
89 	{
90 		CurEnv->e_to = NULL;
91 		Errors = 0;
92 		(void) fflush(stdout);
93 		if (fgets(inp, sizeof inp, InChannel) == NULL)
94 		{
95 			/* end of file, just die */
96 			message("421", "%s Lost input channel", HostName);
97 			finis();
98 		}
99 
100 		/* clean up end of line */
101 		fixcrlf(inp, TRUE);
102 
103 		/* echo command to transcript */
104 		fprintf(Xscript, "*** %s\n", inp);
105 
106 		/* break off command */
107 		for (p = inp; isspace(*p); p++)
108 			continue;
109 		cmd = p;
110 		while (*++p != '\0' && !isspace(*p))
111 			continue;
112 		if (*p != '\0')
113 			*p++ = '\0';
114 
115 		/* decode command */
116 		for (c = CmdTab; c->cmdname != NULL; c++)
117 		{
118 			if (sameword(c->cmdname, cmd))
119 				break;
120 		}
121 
122 		/* process command */
123 		switch (c->cmdcode)
124 		{
125 		  case CMDHELO:		/* hello -- introduce yourself */
126 			define('s', newstr(p));
127 			message("250", "%s Hello %s, pleased to meet you",
128 				HostName, p);
129 			break;
130 
131 		  case CMDMAIL:		/* mail -- designate sender */
132 			if (hasmail)
133 			{
134 				message("503", "Sender already specified");
135 				break;
136 			}
137 			p = skipword(p, "from");
138 			if (p == NULL)
139 				break;
140 			if (index(p, ',') != NULL)
141 			{
142 				message("501", "Source routing not implemented");
143 				Errors++;
144 				break;
145 			}
146 			setsender(p);
147 			if (Errors == 0)
148 			{
149 				message("250", "Sender ok");
150 				hasmail = TRUE;
151 			}
152 			break;
153 
154 		  case CMDRCPT:		/* rcpt -- designate recipient */
155 			p = skipword(p, "to");
156 			if (p == NULL)
157 				break;
158 			if (index(p, ',') != NULL)
159 			{
160 				message("501", "Source routing not implemented");
161 				Errors++;
162 				break;
163 			}
164 			sendto(p, 1, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
165 			if (Errors == 0)
166 			{
167 				message("250", "%s... Recipient ok", p);
168 				rcps++;
169 			}
170 			break;
171 
172 		  case CMDDATA:		/* data -- text of mail */
173 			if (!hasmail)
174 			{
175 				message("503", "Need MAIL command");
176 				break;
177 			}
178 			else if (rcps <= 0)
179 			{
180 				message("503", "Need RCPT (recipient)");
181 				break;
182 			}
183 
184 			/* collect the text of the message */
185 			collect(TRUE);
186 			if (Errors != 0)
187 				break;
188 
189 			/* if sending to multiple people, mail back errors */
190 			if (rcps != 1)
191 				HoldErrs = MailBack = TRUE;
192 
193 			/* send to all recipients */
194 			sendall(CurEnv, FALSE);
195 
196 			/* reset strange modes */
197 			HoldErrs = FALSE;
198 			CurEnv->e_to = NULL;
199 
200 			/* issue success if appropriate */
201 			if (Errors == 0 || rcps != 1)
202 				message("250", "Sent");
203 			break;
204 
205 		  case CMDRSET:		/* rset -- reset state */
206 			message("250", "Reset state");
207 			finis();
208 
209 		  case CMDVRFY:		/* vrfy -- verify address */
210 			vrfyqueue = NULL;
211 			sendto(p, 1, (ADDRESS *) NULL, &vrfyqueue);
212 			while (vrfyqueue != NULL)
213 			{
214 				register ADDRESS *a = vrfyqueue->q_next;
215 				char *code;
216 
217 				while (a != NULL && bitset(QDONTSEND, a->q_flags))
218 					a = a->q_next;
219 
220 				if (!bitset(QDONTSEND, vrfyqueue->q_flags))
221 				{
222 					if (a != NULL)
223 						code = "250-";
224 					else
225 						code = "250";
226 					if (vrfyqueue->q_fullname == NULL)
227 						message(code, "<%s>", vrfyqueue->q_paddr);
228 					else
229 						message(code, "%s <%s>",
230 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
231 				}
232 				else if (a == NULL)
233 					message("554", "Self destructive alias loop");
234 				vrfyqueue = a;
235 			}
236 			break;
237 
238 		  case CMDHELP:		/* help -- give user info */
239 			if (*p == '\0')
240 				p = "SMTP";
241 			help(p);
242 			break;
243 
244 		  case CMDNOOP:		/* noop -- do nothing */
245 			message("200", "OK");
246 			break;
247 
248 		  case CMDQUIT:		/* quit -- leave mail */
249 			message("221", "%s closing connection", HostName);
250 			finis();
251 
252 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
253 			if (*p == 'R' || *p == 'T')
254 			{
255 				/* recipients first or text first */
256 				message("200", "%c ok, please continue", *p);
257 			}
258 			else if (*p == '?')
259 			{
260 				/* what do I prefer?  anything, anytime */
261 				message("215", "R Recipients first is my choice");
262 			}
263 			else if (*p == '\0')
264 			{
265 				/* no meaningful scheme */
266 				message("200", "okey dokie boobie");
267 			}
268 			else
269 			{
270 				/* bad argument */
271 				message("504", "Scheme unknown");
272 			}
273 			break;
274 
275 		  case CMDHOPS:		/* specify hop count */
276 			HopCount = atoi(p);
277 			if (++HopCount > MAXHOP)
278 				message("501", "Hop count exceeded");
279 			else
280 				message("200", "Hop count ok");
281 			break;
282 
283 # ifdef DEBUG
284 		  case CMDDBGSHOWQ:	/* show queues */
285 			printf("Send Queue=");
286 			printaddr(CurEnv->e_sendqueue, TRUE);
287 			break;
288 
289 		  case CMDDBGDEBUG:	/* set debug mode */
290 			Debug = atoi(p);
291 			message("200", "Debug = %d", Debug);
292 			break;
293 
294 		  case CMDDBGVERBOSE:	/* set verbose mode */
295 			Verbose = TRUE;
296 			message("200", "Verbose mode");
297 			break;
298 
299 		  case CMDDBGKILL:	/* kill the parent */
300 			if (kill(MotherPid, SIGTERM) >= 0)
301 				message("200", "Mother is dead");
302 			else
303 				message("500", "Can't kill Mom");
304 			break;
305 # endif DEBUG
306 
307 		  case CMDERROR:	/* unknown command */
308 			message("500", "Command unrecognized");
309 			break;
310 
311 		  default:
312 			syserr("smtp: unknown code %d", c->cmdcode);
313 			break;
314 		}
315 	}
316 }
317 /*
318 **  SKIPWORD -- skip a fixed word.
319 **
320 **	Parameters:
321 **		p -- place to start looking.
322 **		w -- word to skip.
323 **
324 **	Returns:
325 **		p following w.
326 **		NULL on error.
327 **
328 **	Side Effects:
329 **		clobbers the p data area.
330 */
331 
332 static char *
333 skipword(p, w)
334 	register char *p;
335 	char *w;
336 {
337 	register char *q;
338 	extern bool sameword();
339 
340 	/* find beginning of word */
341 	while (isspace(*p))
342 		p++;
343 	q = p;
344 
345 	/* find end of word */
346 	while (*p != '\0' && *p != ':' && !isspace(*p))
347 		p++;
348 	while (isspace(*p))
349 		*p++ = '\0';
350 	if (*p != ':')
351 	{
352 	  syntax:
353 		message("501", "Syntax error");
354 		Errors++;
355 		return (NULL);
356 	}
357 	*p++ = '\0';
358 	while (isspace(*p))
359 		p++;
360 
361 	/* see if the input word matches desired word */
362 	if (!sameword(q, w))
363 		goto syntax;
364 
365 	return (p);
366 }
367 /*
368 **  HELP -- implement the HELP command.
369 **
370 **	Parameters:
371 **		topic -- the topic we want help for.
372 **
373 **	Returns:
374 **		none.
375 **
376 **	Side Effects:
377 **		outputs the help file to message output.
378 */
379 
380 help(topic)
381 	char *topic;
382 {
383 	register FILE *hf;
384 	int len;
385 	char buf[MAXLINE];
386 	bool noinfo;
387 	extern char *HelpFile;
388 
389 	hf = fopen(HelpFile, "r");
390 	if (hf == NULL)
391 	{
392 		/* no help */
393 		message("502", "HELP not implemented");
394 		return;
395 	}
396 
397 	len = strlen(topic);
398 	makelower(topic);
399 	noinfo = TRUE;
400 
401 	while (fgets(buf, sizeof buf, hf) != NULL)
402 	{
403 		if (strncmp(buf, topic, len) == 0)
404 		{
405 			register char *p;
406 
407 			p = index(buf, '\t');
408 			if (p == NULL)
409 				p = buf;
410 			else
411 				p++;
412 			fixcrlf(p, TRUE);
413 			message("214-", p);
414 			noinfo = FALSE;
415 		}
416 	}
417 
418 	if (noinfo)
419 		message("504", "HELP topic unknown");
420 	else
421 		message("214", "End of HELP info");
422 	(void) fclose(hf);
423 }
424 
425 # endif SMTP
426