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