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