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