1 # include "sendmail.h"
2 
3 static char	SccsId[] =	"@(#)srvrsmtp.c	3.10	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 # define CMDDBGSHOWQ	12	/* showq -- show send queue (DEBUG) */
38 
39 static struct cmd	CmdTab[] =
40 {
41 	"mail",		CMDMAIL,
42 	"rcpt",		CMDRCPT,
43 	"mrcp",		CMDRCPT,	/* for old MTP compatability */
44 	"data",		CMDDATA,
45 	"rset",		CMDRSET,
46 	"vrfy",		CMDVRFY,
47 	"help",		CMDHELP,
48 	"noop",		CMDNOOP,
49 	"quit",		CMDQUIT,
50 	"mrsq",		CMDMRSQ,
51 	"helo",		CMDHELO,
52 # ifdef DEBUG
53 	"showq",	CMDDBGSHOWQ,
54 # endif DEBUG
55 	NULL,		CMDERROR,
56 };
57 
58 smtp()
59 {
60 	char inp[MAXLINE];
61 	register char *p;
62 	struct cmd *c;
63 	char *cmd;
64 	extern char *skipword();
65 	extern bool sameword();
66 	bool hasmail;			/* mail command received */
67 	int rcps;			/* number of recipients */
68 	auto ADDRESS *vrfyqueue;
69 	ADDRESS *prev;
70 
71 	hasmail = FALSE;
72 	rcps = 0;
73 	message("220", "%s Sendmail at your service", HostName);
74 	for (;;)
75 	{
76 		To = NULL;
77 		Errors = 0;
78 		if (fgets(inp, sizeof inp, InChannel) == NULL)
79 		{
80 			/* end of file, just die */
81 			message("421", "%s Lost input channel", HostName);
82 			finis();
83 		}
84 
85 		/* clean up end of line */
86 		fixcrlf(inp, TRUE);
87 
88 		/* echo command to transcript */
89 		fprintf(Xscript, "*** %s\n", inp);
90 
91 		/* break off command */
92 		for (p = inp; isspace(*p); p++)
93 			continue;
94 		cmd = p;
95 		while (*++p != '\0' && !isspace(*p))
96 			continue;
97 		if (*p != '\0')
98 			*p++ = '\0';
99 
100 		/* decode command */
101 		for (c = CmdTab; c->cmdname != NULL; c++)
102 		{
103 			if (sameword(c->cmdname, cmd))
104 				break;
105 		}
106 
107 		/* process command */
108 		switch (c->cmdcode)
109 		{
110 		  case CMDHELO:		/* hello -- introduce yourself */
111 			message("250", "%s Hello %s, pleased to meet you",
112 				HostName, p);
113 			break;
114 
115 		  case CMDMAIL:		/* mail -- designate sender */
116 			if (hasmail)
117 			{
118 				message("503", "Sender already specified");
119 				break;
120 			}
121 			p = skipword(p, "from");
122 			if (p == NULL)
123 				break;
124 			if (index(p, ',') != NULL)
125 			{
126 				message("501", "Source routing not implemented");
127 				Errors++;
128 				break;
129 			}
130 			setsender(p);
131 			if (Errors == 0)
132 			{
133 				message("250", "Sender ok");
134 				hasmail = TRUE;
135 			}
136 			break;
137 
138 		  case CMDRCPT:		/* rcpt -- designate recipient */
139 			p = skipword(p, "to");
140 			if (p == NULL)
141 				break;
142 			if (index(p, ',') != NULL)
143 			{
144 				message("501", "Source routing not implemented");
145 				Errors++;
146 				break;
147 			}
148 			sendto(p, 1, (ADDRESS *) NULL, &SendQueue);
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 			vrfyqueue = NULL;
195 			sendto(p, 1, (ADDRESS *) NULL, &vrfyqueue);
196 			while (vrfyqueue != NULL)
197 			{
198 				register ADDRESS *a = vrfyqueue->q_next;
199 				char *code;
200 
201 				while (a != NULL && bitset(QDONTSEND, a->q_flags))
202 					a = a->q_next;
203 
204 				if (!bitset(QDONTSEND, vrfyqueue->q_flags))
205 				{
206 					if (a != NULL)
207 						code = "250-";
208 					else
209 						code = "250";
210 					if (vrfyqueue->q_fullname == NULL)
211 						message(code, "<%s>", vrfyqueue->q_paddr);
212 					else
213 						message(code, "%s <%s>",
214 						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
215 				}
216 				else if (a == NULL)
217 					message("554", "Self destructive alias loop");
218 				vrfyqueue = a;
219 			}
220 			break;
221 
222 		  case CMDHELP:		/* help -- give user info */
223 			if (*p == '\0')
224 				p = "SMTP";
225 			help(p);
226 			break;
227 
228 		  case CMDNOOP:		/* noop -- do nothing */
229 			message("200", "OK");
230 			break;
231 
232 		  case CMDQUIT:		/* quit -- leave mail */
233 			message("221", "%s closing connection", HostName);
234 			finis();
235 
236 		  case CMDMRSQ:		/* mrsq -- negotiate protocol */
237 			if (*p == 'R' || *p == 'T')
238 			{
239 				/* recipients first or text first */
240 				message("200", "%c ok, please continue", *p);
241 			}
242 			else if (*p == '?')
243 			{
244 				/* what do I prefer?  anything, anytime */
245 				message("215", "R Recipients first is my choice");
246 			}
247 			else if (*p == '\0')
248 			{
249 				/* no meaningful scheme */
250 				message("200", "okey dokie boobie");
251 			}
252 			else
253 			{
254 				/* bad argument */
255 				message("504", "Scheme unknown");
256 			}
257 			break;
258 
259 # ifdef DEBUG
260 		  case CMDDBGSHOWQ:	/* show queues */
261 			printf("SendQueue=");
262 			printaddr(SendQueue, TRUE);
263 			break;
264 # endif DEBUG
265 
266 		  case CMDERROR:	/* unknown command */
267 			message("500", "Command unrecognized");
268 			break;
269 
270 		  default:
271 			syserr("smtp: unknown code %d", c->cmdcode);
272 			break;
273 		}
274 	}
275 }
276 /*
277 **  SKIPWORD -- skip a fixed word.
278 **
279 **	Parameters:
280 **		p -- place to start looking.
281 **		w -- word to skip.
282 **
283 **	Returns:
284 **		p following w.
285 **		NULL on error.
286 **
287 **	Side Effects:
288 **		clobbers the p data area.
289 */
290 
291 static char *
292 skipword(p, w)
293 	register char *p;
294 	char *w;
295 {
296 	register char *q;
297 	extern bool sameword();
298 
299 	/* find beginning of word */
300 	while (isspace(*p))
301 		p++;
302 	q = p;
303 
304 	/* find end of word */
305 	while (*p != '\0' && *p != ':' && !isspace(*p))
306 		p++;
307 	while (isspace(*p))
308 		*p++ = '\0';
309 	if (*p != ':')
310 	{
311 	  syntax:
312 		message("501", "Syntax error");
313 		Errors++;
314 		return (NULL);
315 	}
316 	*p++ = '\0';
317 	while (isspace(*p))
318 		p++;
319 
320 	/* see if the input word matches desired word */
321 	if (!sameword(q, w))
322 		goto syntax;
323 
324 	return (p);
325 }
326 /*
327 **  HELP -- implement the HELP command.
328 **
329 **	Parameters:
330 **		topic -- the topic we want help for.
331 **
332 **	Returns:
333 **		none.
334 **
335 **	Side Effects:
336 **		outputs the help file to message output.
337 */
338 
339 help(topic)
340 	char *topic;
341 {
342 	register FILE *hf;
343 	int len;
344 	char buf[MAXLINE];
345 	bool noinfo;
346 	extern char *HelpFile;
347 
348 	hf = fopen(HelpFile, "r");
349 	if (hf == NULL)
350 	{
351 		/* no help */
352 		message("502", "HELP not implemented");
353 		return;
354 	}
355 
356 	len = strlen(topic);
357 	makelower(topic);
358 	noinfo = TRUE;
359 
360 	while (fgets(buf, sizeof buf, hf) != NULL)
361 	{
362 		if (strncmp(buf, topic, len) == 0)
363 		{
364 			register char *p;
365 
366 			p = index(buf, '\t');
367 			if (p == NULL)
368 				p = buf;
369 			else
370 				p++;
371 			fixcrlf(p, TRUE);
372 			message("214-", p);
373 			noinfo = FALSE;
374 		}
375 	}
376 
377 	if (noinfo)
378 		message("504", "HELP topic unknown");
379 	else
380 		message("214", "End of HELP info");
381 	(void) fclose(hf);
382 }
383