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