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