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