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