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