Commit | Line | Data |
---|---|---|
b2a81223 | 1 | /* |
dc45ba8c | 2 | * Copyright (c) 1983 Eric P. Allman |
bee79b64 KB |
3 | * Copyright (c) 1988 Regents of the University of California. |
4 | * All rights reserved. | |
5 | * | |
417f7a11 | 6 | * %sccs.include.redist.c% |
bee79b64 KB |
7 | */ |
8 | ||
9 | # include "sendmail.h" | |
b2a81223 | 10 | |
bee79b64 KB |
11 | #ifndef lint |
12 | #ifdef SMTP | |
fba945cd | 13 | static char sccsid[] = "@(#)srvrsmtp.c 6.52 (Berkeley) %G% (with SMTP)"; |
bee79b64 | 14 | #else |
fba945cd | 15 | static char sccsid[] = "@(#)srvrsmtp.c 6.52 (Berkeley) %G% (without SMTP)"; |
bee79b64 KB |
16 | #endif |
17 | #endif /* not lint */ | |
b2a81223 | 18 | |
e6f08ab1 | 19 | # include <errno.h> |
671d4b5c | 20 | # include <signal.h> |
6b861048 | 21 | |
bee79b64 | 22 | # ifdef SMTP |
d727056e | 23 | |
6b861048 EA |
24 | /* |
25 | ** SMTP -- run the SMTP protocol. | |
26 | ** | |
27 | ** Parameters: | |
28 | ** none. | |
29 | ** | |
30 | ** Returns: | |
31 | ** never. | |
32 | ** | |
33 | ** Side Effects: | |
34 | ** Reads commands from the input channel and processes | |
35 | ** them. | |
36 | */ | |
37 | ||
38 | struct cmd | |
39 | { | |
40 | char *cmdname; /* command name */ | |
41 | int cmdcode; /* internal code, see below */ | |
42 | }; | |
43 | ||
44 | /* values for cmdcode */ | |
45 | # define CMDERROR 0 /* bad command */ | |
46 | # define CMDMAIL 1 /* mail -- designate sender */ | |
4a4ebe09 | 47 | # define CMDRCPT 2 /* rcpt -- designate recipient */ |
6b861048 | 48 | # define CMDDATA 3 /* data -- send message text */ |
7d7fdf93 | 49 | # define CMDHOPS 4 /* hops -- specify hop count */ |
e6f08ab1 EA |
50 | # define CMDRSET 4 /* rset -- reset state */ |
51 | # define CMDVRFY 5 /* vrfy -- verify address */ | |
8f48def8 | 52 | # define CMDEXPN 6 /* expn -- expand address */ |
e6f08ab1 EA |
53 | # define CMDNOOP 7 /* noop -- do nothing */ |
54 | # define CMDQUIT 8 /* quit -- close connection and die */ | |
55 | # define CMDHELO 9 /* helo -- be polite */ | |
8f48def8 | 56 | # define CMDHELP 10 /* help -- give usage info */ |
34f2d20e | 57 | # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ |
8f48def8 EA |
58 | /* non-standard commands */ |
59 | # define CMDONEX 16 /* onex -- sending one transaction only */ | |
60 | # define CMDVERB 17 /* verb -- go into verbose mode */ | |
ab4889ea | 61 | /* debugging-only commands, only enabled if SMTPDEBUG is defined */ |
8f48def8 EA |
62 | # define CMDDBGQSHOW 24 /* showq -- show send queue */ |
63 | # define CMDDBGDEBUG 25 /* debug -- set debug mode */ | |
6b861048 EA |
64 | |
65 | static struct cmd CmdTab[] = | |
66 | { | |
67 | "mail", CMDMAIL, | |
4a4ebe09 | 68 | "rcpt", CMDRCPT, |
6b861048 | 69 | "data", CMDDATA, |
6b861048 EA |
70 | "rset", CMDRSET, |
71 | "vrfy", CMDVRFY, | |
8f48def8 | 72 | "expn", CMDEXPN, |
633a2e02 | 73 | "expn", CMDVRFY, |
6b861048 EA |
74 | "help", CMDHELP, |
75 | "noop", CMDNOOP, | |
76 | "quit", CMDQUIT, | |
4a4ebe09 | 77 | "helo", CMDHELO, |
34f2d20e | 78 | "ehlo", CMDEHLO, |
e8ad767d | 79 | "verb", CMDVERB, |
8fe4fb9b | 80 | "onex", CMDONEX, |
7d7fdf93 | 81 | "hops", CMDHOPS, |
ab4889ea MK |
82 | /* |
83 | * remaining commands are here only | |
84 | * to trap and log attempts to use them | |
85 | */ | |
e6f08ab1 | 86 | "showq", CMDDBGQSHOW, |
ad20d67b | 87 | "debug", CMDDBGDEBUG, |
6b861048 EA |
88 | NULL, CMDERROR, |
89 | }; | |
90 | ||
e6f08ab1 | 91 | bool InChild = FALSE; /* true if running in a subprocess */ |
7338e3d4 | 92 | bool OneXact = FALSE; /* one xaction only this run */ |
1972fb40 | 93 | |
e6f08ab1 | 94 | #define EX_QUIT 22 /* special code for QUIT command */ |
e8ad767d | 95 | |
a4076aed EA |
96 | smtp(e) |
97 | register ENVELOPE *e; | |
6b861048 | 98 | { |
6b861048 | 99 | register char *p; |
e8ad767d | 100 | register struct cmd *c; |
6b861048 | 101 | char *cmd; |
0df908a9 | 102 | static char *skipword(); |
abae7b2d EA |
103 | extern ADDRESS *sendto(); |
104 | ADDRESS *a; | |
6b861048 | 105 | |
abae7b2d | 106 | hasmail = FALSE; |
20219e3f | 107 | if (fileno(OutChannel) != fileno(stdout)) |
1f1cc003 EA |
108 | { |
109 | /* arrange for debugging output to go to remote host */ | |
20219e3f | 110 | (void) dup2(fileno(OutChannel), fileno(stdout)); |
1f1cc003 | 111 | } |
a4076aed | 112 | settime(e); |
6e99f903 | 113 | CurHostName = RealHostName; |
ade7da2a | 114 | setproctitle("srvrsmtp %s startup", CurHostName); |
2bee003d | 115 | expand("\201e", inp, &inp[sizeof inp], e); |
b6edea3d | 116 | message("220 %s", inp); |
6fd6d536 | 117 | protocol = NULL; |
37444fe1 | 118 | sendinghost = macvalue('s', e); |
1c7897ef | 119 | gothello = FALSE; |
6fd6d536 | 120 | gotmail = FALSE; |
6b861048 EA |
121 | for (;;) |
122 | { | |
d344c0b7 EA |
123 | /* arrange for backout */ |
124 | if (setjmp(TopFrame) > 0 && InChild) | |
b8bf5eba EA |
125 | { |
126 | QuickAbort = FALSE; | |
127 | SuprErrs = TRUE; | |
d344c0b7 | 128 | finis(); |
b8bf5eba | 129 | } |
d344c0b7 EA |
130 | QuickAbort = FALSE; |
131 | HoldErrs = FALSE; | |
f61c3c40 | 132 | LogUsrErrs = FALSE; |
8f48def8 | 133 | e->e_flags &= ~EF_VRFYONLY; |
d344c0b7 | 134 | |
37eaaadb | 135 | /* setup for the read */ |
a4076aed | 136 | e->e_to = NULL; |
34d37b7d | 137 | Errors = 0; |
09eb49d8 | 138 | (void) fflush(stdout); |
37eaaadb | 139 | |
37eaaadb | 140 | /* read the input line */ |
ade7da2a EA |
141 | SmtpPhase = "srvrsmtp cmd read"; |
142 | setproctitle("srvrsmtp %s cmd read", CurHostName); | |
3b87200d | 143 | p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand); |
37eaaadb | 144 | |
2439b900 | 145 | /* handle errors */ |
37eaaadb | 146 | if (p == NULL) |
6b861048 EA |
147 | { |
148 | /* end of file, just die */ | |
b6edea3d | 149 | message("421 %s Lost input channel from %s", |
57c97d4a | 150 | MyHostName, CurHostName); |
6d6f9196 | 151 | #ifdef LOG |
68f7099c | 152 | if (LogLevel > 1) |
6d6f9196 EA |
153 | syslog(LOG_NOTICE, "lost input channel from %s", |
154 | CurHostName); | |
155 | #endif | |
074b0256 EA |
156 | if (InChild) |
157 | ExitStat = EX_QUIT; | |
6b861048 EA |
158 | finis(); |
159 | } | |
160 | ||
161 | /* clean up end of line */ | |
2768afe3 | 162 | fixcrlf(inp, TRUE); |
6b861048 | 163 | |
49086753 | 164 | /* echo command to transcript */ |
a4076aed EA |
165 | if (e->e_xfp != NULL) |
166 | fprintf(e->e_xfp, "<<< %s\n", inp); | |
49086753 | 167 | |
ade7da2a EA |
168 | if (e->e_id == NULL) |
169 | setproctitle("%s: %s", CurHostName, inp); | |
170 | else | |
171 | setproctitle("%s %s: %s", e->e_id, CurHostName, inp); | |
172 | ||
6b861048 | 173 | /* break off command */ |
2bee003d | 174 | for (p = inp; isascii(*p) && isspace(*p); p++) |
6b861048 | 175 | continue; |
f43b04ce | 176 | cmd = cmdbuf; |
2bee003d EA |
177 | while (*p != '\0' && |
178 | !(isascii(*p) && isspace(*p)) && | |
179 | cmd < &cmdbuf[sizeof cmdbuf - 2]) | |
a0225d08 EA |
180 | *cmd++ = *p++; |
181 | *cmd = '\0'; | |
6b861048 | 182 | |
a1a07282 | 183 | /* throw away leading whitespace */ |
2bee003d | 184 | while (isascii(*p) && isspace(*p)) |
a1a07282 EA |
185 | p++; |
186 | ||
6b861048 EA |
187 | /* decode command */ |
188 | for (c = CmdTab; c->cmdname != NULL; c++) | |
189 | { | |
ed73ef1d | 190 | if (!strcasecmp(c->cmdname, cmdbuf)) |
6b861048 EA |
191 | break; |
192 | } | |
193 | ||
5ae51cd5 EA |
194 | /* reset errors */ |
195 | errno = 0; | |
196 | ||
6b861048 EA |
197 | /* process command */ |
198 | switch (c->cmdcode) | |
199 | { | |
4a4ebe09 | 200 | case CMDHELO: /* hello -- introduce yourself */ |
34f2d20e EA |
201 | case CMDEHLO: /* extended hello */ |
202 | if (c->cmdcode == CMDEHLO) | |
203 | { | |
204 | protocol = "ESMTP"; | |
205 | SmtpPhase = "EHLO"; | |
206 | } | |
207 | else | |
208 | { | |
209 | protocol = "SMTP"; | |
210 | SmtpPhase = "HELO"; | |
211 | } | |
37444fe1 | 212 | sendinghost = newstr(p); |
5973222c | 213 | if (strcasecmp(p, RealHostName) != 0) |
1972fb40 | 214 | { |
94bc039a EA |
215 | auth_warning(e, "Host %s claimed to be %s", |
216 | RealHostName, p); | |
1972fb40 | 217 | } |
a9bac7a9 EA |
218 | p = macvalue('_', e); |
219 | if (p == NULL) | |
37444fe1 | 220 | p = RealHostName; |
abae7b2d | 221 | message("250", "%s Hello %s, pleased to meet you", HostName, p); |
4a4ebe09 EA |
222 | break; |
223 | ||
6b861048 | 224 | case CMDMAIL: /* mail -- designate sender */ |
2e3062fe EA |
225 | SmtpPhase = "MAIL"; |
226 | ||
8fe4fb9b | 227 | /* check for validity of this command */ |
94bc039a | 228 | if (!gothello) |
1c7897ef | 229 | { |
a9bac7a9 | 230 | /* set sending host to our known value */ |
37444fe1 EA |
231 | if (sendinghost == NULL) |
232 | sendinghost = RealHostName; | |
a9bac7a9 | 233 | |
94bc039a | 234 | if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) |
f967c46a | 235 | { |
94bc039a | 236 | message("503 Polite people say HELO first"); |
f967c46a EA |
237 | break; |
238 | } | |
94bc039a | 239 | else |
f967c46a | 240 | { |
94bc039a EA |
241 | auth_warning(e, |
242 | "Host %s didn't use HELO protocol", | |
243 | RealHostName); | |
f967c46a | 244 | } |
1c7897ef | 245 | } |
3b87200d | 246 | if (gotmail) |
2768afe3 | 247 | { |
b6edea3d | 248 | message("503 Sender already specified"); |
2768afe3 EA |
249 | break; |
250 | } | |
e6f08ab1 EA |
251 | if (InChild) |
252 | { | |
ab4889ea | 253 | errno = 0; |
b6edea3d | 254 | syserr("503 Nested MAIL command: MAIL %s", p); |
074b0256 | 255 | finis(); |
e6f08ab1 EA |
256 | } |
257 | ||
258 | /* fork a subprocess to process this command */ | |
a4076aed | 259 | if (runinchild("SMTP-MAIL", e) > 0) |
e6f08ab1 | 260 | break; |
34f2d20e EA |
261 | if (protocol == NULL) |
262 | protocol = "SMTP"; | |
263 | define('r', protocol, e); | |
37444fe1 | 264 | define('s', sendinghost, e); |
a4076aed | 265 | initsys(e); |
fba945cd | 266 | nrcpts = 0; |
7aa1e2bc | 267 | setproctitle("%s %s: %s", e->e_id, CurHostName, inp); |
e6f08ab1 EA |
268 | |
269 | /* child -- go do the processing */ | |
6b861048 EA |
270 | p = skipword(p, "from"); |
271 | if (p == NULL) | |
272 | break; | |
aa102c71 | 273 | if (setjmp(TopFrame) > 0) |
182c060a EA |
274 | { |
275 | /* this failed -- undo work */ | |
276 | if (InChild) | |
b8bf5eba EA |
277 | { |
278 | QuickAbort = FALSE; | |
279 | SuprErrs = TRUE; | |
182c060a | 280 | finis(); |
b8bf5eba | 281 | } |
aa102c71 | 282 | break; |
182c060a | 283 | } |
aa102c71 | 284 | QuickAbort = TRUE; |
9e2cf26f EA |
285 | |
286 | /* must parse sender first */ | |
287 | delimptr = NULL; | |
4a2da288 | 288 | setsender(p, e, &delimptr, FALSE); |
9e2cf26f EA |
289 | p = delimptr; |
290 | if (p != NULL && *p != '\0') | |
291 | *p++ = '\0'; | |
292 | ||
293 | /* now parse ESMTP arguments */ | |
294 | msize = 0; | |
295 | for (; p != NULL && *p != '\0'; p++) | |
296 | { | |
297 | char *kp; | |
298 | char *vp; | |
299 | ||
300 | /* locate the beginning of the keyword */ | |
301 | while (isascii(*p) && isspace(*p)) | |
302 | p++; | |
303 | if (*p == '\0') | |
304 | break; | |
305 | kp = p; | |
306 | ||
307 | /* skip to the value portion */ | |
308 | while (isascii(*p) && isalnum(*p) || *p == '-') | |
309 | p++; | |
310 | if (*p == '=') | |
311 | { | |
312 | *p++ = '\0'; | |
313 | vp = p; | |
314 | ||
315 | /* skip to the end of the value */ | |
316 | while (*p != '\0' && *p != ' ' && | |
317 | !(isascii(*p) && iscntrl(*p)) && | |
318 | *p != '=') | |
319 | p++; | |
320 | } | |
321 | ||
322 | if (*p != '\0') | |
323 | *p++ = '\0'; | |
324 | ||
325 | if (tTd(19, 1)) | |
326 | printf("MAIL: got arg %s=%s\n", kp, | |
327 | vp == NULL ? "<null>" : vp); | |
328 | ||
329 | if (strcasecmp(kp, "size") == 0) | |
330 | { | |
96bfbc2c | 331 | if (vp == NULL) |
9e2cf26f EA |
332 | { |
333 | usrerr("501 SIZE requires a value"); | |
334 | /* NOTREACHED */ | |
335 | } | |
336 | msize = atol(vp); | |
337 | } | |
96bfbc2c EA |
338 | else if (strcasecmp(kp, "body") == 0) |
339 | { | |
340 | if (vp == NULL) | |
341 | { | |
342 | usrerr("501 BODY requires a value"); | |
343 | /* NOTREACHED */ | |
344 | } | |
345 | # ifdef MIME | |
346 | if (strcasecmp(vp, "8bitmime") == 0) | |
347 | { | |
348 | e->e_bodytype = "8BITMIME"; | |
6a7569d3 | 349 | SevenBit = FALSE; |
96bfbc2c EA |
350 | } |
351 | else if (strcasecmp(vp, "7bit") == 0) | |
352 | { | |
353 | e->e_bodytype = "7BIT"; | |
6a7569d3 | 354 | SevenBit = TRUE; |
96bfbc2c EA |
355 | } |
356 | else | |
357 | { | |
358 | usrerr("501 Unknown BODY type %s", | |
359 | vp); | |
360 | } | |
361 | # endif | |
362 | } | |
9e2cf26f EA |
363 | else |
364 | { | |
365 | usrerr("501 %s parameter unrecognized", kp); | |
366 | /* NOTREACHED */ | |
367 | } | |
368 | } | |
346fe280 EA |
369 | |
370 | if (MaxMessageSize > 0 && msize > MaxMessageSize) | |
371 | { | |
372 | usrerr("552 Message size exceeds fixed maximum message size (%ld)", | |
373 | MaxMessageSize); | |
374 | /* NOTREACHED */ | |
375 | } | |
9e2cf26f EA |
376 | |
377 | if (!enoughspace(msize)) | |
378 | { | |
379 | message("452 Insufficient disk space; try again later"); | |
380 | break; | |
381 | } | |
b6edea3d | 382 | message("250 Sender ok"); |
182c060a | 383 | gotmail = TRUE; |
506a2500 EA |
384 | |
385 | /* optimize: non-interactive, don't expand aliases */ | |
28a8a6ef | 386 | if (e->e_sendmode != SM_DELIVER) |
506a2500 EA |
387 | e->e_flags |= EF_VRFYONLY; |
388 | ||
6b861048 EA |
389 | break; |
390 | ||
4a4ebe09 | 391 | case CMDRCPT: /* rcpt -- designate recipient */ |
d7f41a7b EA |
392 | if (!gotmail) |
393 | { | |
394 | usrerr("503 Need MAIL before RCPT"); | |
395 | break; | |
396 | } | |
2e3062fe | 397 | SmtpPhase = "RCPT"; |
d344c0b7 | 398 | if (setjmp(TopFrame) > 0) |
9bfb75c1 | 399 | { |
a4076aed | 400 | e->e_flags &= ~EF_FATALERRS; |
d344c0b7 | 401 | break; |
9bfb75c1 | 402 | } |
d344c0b7 | 403 | QuickAbort = TRUE; |
f61c3c40 | 404 | LogUsrErrs = TRUE; |
6b861048 EA |
405 | p = skipword(p, "to"); |
406 | if (p == NULL) | |
407 | break; | |
abae7b2d EA |
408 | a = sendto(p, 1, (ADDRESS *) NULL, 0); |
409 | # ifdef DEBUG | |
410 | if (Debug > 1) | |
411 | printaddr(a, TRUE); | |
412 | # endif DEBUG | |
d344c0b7 EA |
413 | if (Errors != 0) |
414 | break; | |
415 | ||
416 | /* no errors during parsing, but might be a duplicate */ | |
a4076aed | 417 | e->e_to = p; |
d344c0b7 | 418 | if (!bitset(QBADADDR, a->q_flags)) |
fba945cd | 419 | { |
b6edea3d | 420 | message("250 Recipient ok"); |
fba945cd EA |
421 | nrcpts++; |
422 | } | |
d344c0b7 | 423 | else |
6b861048 | 424 | { |
d344c0b7 | 425 | /* punt -- should keep message in ADDRESS.... */ |
b6edea3d | 426 | message("550 Addressee unknown"); |
6b861048 | 427 | } |
a4076aed | 428 | e->e_to = NULL; |
6b861048 EA |
429 | break; |
430 | ||
431 | case CMDDATA: /* data -- text of mail */ | |
2e3062fe | 432 | SmtpPhase = "DATA"; |
3b87200d | 433 | if (!gotmail) |
4a4ebe09 | 434 | { |
b6edea3d | 435 | message("503 Need MAIL command"); |
4a4ebe09 EA |
436 | break; |
437 | } | |
a4076aed | 438 | else if (e->e_nrcpts <= 0) |
6b861048 | 439 | { |
b6edea3d | 440 | message("503 Need RCPT (recipient)"); |
4a4ebe09 | 441 | break; |
6b861048 | 442 | } |
4a4ebe09 | 443 | |
959cf51d EA |
444 | /* check to see if we need to re-expand aliases */ |
445 | for (a = e->e_sendqueue; a != NULL; a = a->q_next) | |
446 | { | |
447 | if (bitset(QVERIFIED, a->q_flags)) | |
448 | break; | |
449 | } | |
450 | ||
4a4ebe09 | 451 | /* collect the text of the message */ |
2e3062fe | 452 | SmtpPhase = "collect"; |
959cf51d | 453 | collect(TRUE, a != NULL, e); |
2f6a8a78 | 454 | e->e_flags &= ~EF_FATALERRS; |
4a4ebe09 | 455 | if (Errors != 0) |
fba945cd | 456 | goto abortmessage; |
4a4ebe09 | 457 | |
8eedb496 EA |
458 | /* |
459 | ** Arrange to send to everyone. | |
460 | ** If sending to multiple people, mail back | |
461 | ** errors rather than reporting directly. | |
462 | ** In any case, don't mail back errors for | |
463 | ** anything that has happened up to | |
464 | ** now (the other end will do this). | |
bcf74f25 EA |
465 | ** Truncate our transcript -- the mail has gotten |
466 | ** to us successfully, and if we have | |
467 | ** to mail this back, it will be easier | |
468 | ** on the reader. | |
8eedb496 EA |
469 | ** Then send to everyone. |
470 | ** Finally give a reply code. If an error has | |
471 | ** already been given, don't mail a | |
472 | ** message back. | |
e6f08ab1 | 473 | ** We goose error returns by clearing error bit. |
8eedb496 EA |
474 | */ |
475 | ||
2e3062fe | 476 | SmtpPhase = "delivery"; |
fba945cd | 477 | if (nrcpts != 1 && a == NULL) |
7338e3d4 EA |
478 | { |
479 | HoldErrs = TRUE; | |
8c8e8e94 | 480 | e->e_errormode = EM_MAIL; |
7338e3d4 | 481 | } |
a4076aed | 482 | e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); |
4aed8f3a | 483 | id = e->e_id; |
4a4ebe09 EA |
484 | |
485 | /* send to all recipients */ | |
6366413a | 486 | sendall(e, Verbose ? SM_DELIVER : SM_QUEUE); |
a4076aed | 487 | e->e_to = NULL; |
4a4ebe09 | 488 | |
666b39ad | 489 | /* save statistics */ |
a4076aed | 490 | markstats(e, (ADDRESS *) NULL); |
666b39ad | 491 | |
6366413a EA |
492 | unlockqueue(e); |
493 | ||
8eedb496 EA |
494 | /* issue success if appropriate and reset */ |
495 | if (Errors == 0 || HoldErrs) | |
c10e5f89 | 496 | message("250 %s Message accepted for delivery", id); |
fba945cd EA |
497 | |
498 | if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) | |
2f6a8a78 EA |
499 | { |
500 | /* avoid sending back an extra message */ | |
a4076aed | 501 | e->e_flags &= ~EF_FATALERRS; |
fba945cd | 502 | e->e_flags |= EF_CLRQUEUE; |
2f6a8a78 EA |
503 | } |
504 | else | |
a891bb7e | 505 | { |
fba945cd EA |
506 | /* from now on, we have to operate silently */ |
507 | HoldErrs = TRUE; | |
508 | e->e_errormode = EM_MAIL; | |
509 | ||
2f6a8a78 EA |
510 | /* if we just queued, poke it */ |
511 | if (a != NULL && e->e_sendmode != SM_QUEUE) | |
512 | { | |
513 | unlockqueue(e); | |
514 | dowork(id, TRUE, TRUE, e); | |
515 | e->e_id = NULL; | |
516 | } | |
a891bb7e EA |
517 | } |
518 | ||
6366413a EA |
519 | /* now make it really happen */ |
520 | if (!Verbose && e->e_sendmode != SM_QUEUE) | |
521 | dowork(id, TRUE, e); | |
522 | ||
fba945cd | 523 | abortmessage: |
e6f08ab1 EA |
524 | /* if in a child, pop back to our parent */ |
525 | if (InChild) | |
526 | finis(); | |
2e3062fe EA |
527 | |
528 | /* clean up a bit */ | |
3b87200d | 529 | gotmail = FALSE; |
a4076aed | 530 | dropenvelope(e); |
fda58daa | 531 | CurEnv = e = newenvelope(e, CurEnv); |
a4076aed | 532 | e->e_flags = BlankEnvelope.e_flags; |
6b861048 EA |
533 | break; |
534 | ||
535 | case CMDRSET: /* rset -- reset state */ | |
b6edea3d | 536 | message("250 Reset state"); |
e6f08ab1 EA |
537 | if (InChild) |
538 | finis(); | |
3b87200d EA |
539 | |
540 | /* clean up a bit */ | |
541 | gotmail = FALSE; | |
542 | dropenvelope(e); | |
fda58daa | 543 | CurEnv = e = newenvelope(e, CurEnv); |
e6f08ab1 | 544 | break; |
6b861048 EA |
545 | |
546 | case CMDVRFY: /* vrfy -- verify address */ | |
8f48def8 EA |
547 | case CMDEXPN: /* expn -- expand address */ |
548 | vrfy = c->cmdcode == CMDVRFY; | |
549 | if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, | |
550 | PrivacyFlags)) | |
1c7897ef | 551 | { |
efae59f3 EA |
552 | if (vrfy) |
553 | message("252 Who's to say?"); | |
554 | else | |
555 | message("502 That's none of your business"); | |
1c7897ef EA |
556 | break; |
557 | } | |
558 | else if (!gothello && | |
8f48def8 EA |
559 | bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, |
560 | PrivacyFlags)) | |
1c7897ef | 561 | { |
b6edea3d | 562 | message("503 I demand that you introduce yourself first"); |
1c7897ef EA |
563 | break; |
564 | } | |
8f48def8 | 565 | if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) |
e6f08ab1 | 566 | break; |
01e8020e | 567 | #ifdef LOG |
68f7099c | 568 | if (LogLevel > 5) |
01e8020e EA |
569 | syslog(LOG_INFO, "%s: %s", CurHostName, inp); |
570 | #endif | |
abae7b2d | 571 | paddrtree(a); |
6b861048 EA |
572 | break; |
573 | ||
574 | case CMDHELP: /* help -- give user info */ | |
34d37b7d | 575 | help(p); |
6b861048 EA |
576 | break; |
577 | ||
578 | case CMDNOOP: /* noop -- do nothing */ | |
b6edea3d | 579 | message("200 OK"); |
6b861048 EA |
580 | break; |
581 | ||
582 | case CMDQUIT: /* quit -- leave mail */ | |
b6edea3d | 583 | message("221 %s closing connection", MyHostName); |
e6f08ab1 EA |
584 | if (InChild) |
585 | ExitStat = EX_QUIT; | |
6b861048 EA |
586 | finis(); |
587 | ||
e8ad767d EA |
588 | case CMDVERB: /* set verbose mode */ |
589 | Verbose = TRUE; | |
8c8e8e94 | 590 | e->e_sendmode = SM_DELIVER; |
b6edea3d | 591 | message("200 Verbose mode"); |
e8ad767d EA |
592 | break; |
593 | ||
8fe4fb9b | 594 | case CMDONEX: /* doing one transaction only */ |
7338e3d4 | 595 | OneXact = TRUE; |
b6edea3d | 596 | message("200 Only one transaction"); |
8fe4fb9b EA |
597 | break; |
598 | ||
ab4889ea | 599 | # ifdef SMTPDEBUG |
e6f08ab1 | 600 | case CMDDBGQSHOW: /* show queues */ |
2654b031 | 601 | printf("Send Queue="); |
a4076aed | 602 | printaddr(e->e_sendqueue, TRUE); |
d4f42161 | 603 | break; |
09eb49d8 EA |
604 | |
605 | case CMDDBGDEBUG: /* set debug mode */ | |
9678c96d EA |
606 | tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); |
607 | tTflag(p); | |
b6edea3d | 608 | message("200 Debug set"); |
09eb49d8 EA |
609 | break; |
610 | ||
ab4889ea MK |
611 | # else /* not SMTPDEBUG */ |
612 | ||
613 | case CMDDBGQSHOW: /* show queues */ | |
614 | case CMDDBGDEBUG: /* set debug mode */ | |
2e15a2d8 | 615 | # ifdef LOG |
5973222c | 616 | if (LogLevel > 0) |
ab4889ea | 617 | syslog(LOG_NOTICE, |
68f7099c | 618 | "\"%s\" command from %s (%s)", |
ab4889ea | 619 | c->cmdname, RealHostName, |
3341995c | 620 | anynet_ntoa(&RealHostAddr)); |
2e15a2d8 | 621 | # endif |
ab4889ea MK |
622 | /* FALL THROUGH */ |
623 | # endif /* SMTPDEBUG */ | |
d4f42161 | 624 | |
6b861048 | 625 | case CMDERROR: /* unknown command */ |
b6edea3d | 626 | message("500 Command unrecognized"); |
6b861048 EA |
627 | break; |
628 | ||
629 | default: | |
ab4889ea | 630 | errno = 0; |
b6edea3d | 631 | syserr("500 smtp: unknown code %d", c->cmdcode); |
6b861048 EA |
632 | break; |
633 | } | |
634 | } | |
635 | } | |
636 | \f/* | |
637 | ** SKIPWORD -- skip a fixed word. | |
638 | ** | |
639 | ** Parameters: | |
640 | ** p -- place to start looking. | |
641 | ** w -- word to skip. | |
642 | ** | |
643 | ** Returns: | |
644 | ** p following w. | |
645 | ** NULL on error. | |
646 | ** | |
647 | ** Side Effects: | |
648 | ** clobbers the p data area. | |
649 | */ | |
650 | ||
651 | static char * | |
652 | skipword(p, w) | |
653 | register char *p; | |
654 | char *w; | |
655 | { | |
656 | register char *q; | |
6b861048 EA |
657 | |
658 | /* find beginning of word */ | |
2bee003d | 659 | while (isascii(*p) && isspace(*p)) |
6b861048 EA |
660 | p++; |
661 | q = p; | |
662 | ||
663 | /* find end of word */ | |
2bee003d | 664 | while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) |
6b861048 | 665 | p++; |
2bee003d | 666 | while (isascii(*p) && isspace(*p)) |
6b861048 EA |
667 | *p++ = '\0'; |
668 | if (*p != ':') | |
669 | { | |
670 | syntax: | |
b6edea3d | 671 | message("501 Syntax error"); |
6b861048 EA |
672 | Errors++; |
673 | return (NULL); | |
674 | } | |
675 | *p++ = '\0'; | |
2bee003d | 676 | while (isascii(*p) && isspace(*p)) |
6b861048 EA |
677 | p++; |
678 | ||
679 | /* see if the input word matches desired word */ | |
ed73ef1d | 680 | if (strcasecmp(q, w)) |
6b861048 EA |
681 | goto syntax; |
682 | ||
683 | return (p); | |
684 | } | |
34d37b7d | 685 | \f/* |
b6edea3d EA |
686 | ** PRINTVRFYADDR -- print an entry in the verify queue |
687 | ** | |
688 | ** Parameters: | |
689 | ** a -- the address to print | |
690 | ** last -- set if this is the last one. | |
691 | ** | |
692 | ** Returns: | |
693 | ** none. | |
694 | ** | |
695 | ** Side Effects: | |
696 | ** Prints the appropriate 250 codes. | |
697 | */ | |
698 | ||
699 | printvrfyaddr(a, last) | |
700 | register ADDRESS *a; | |
701 | bool last; | |
702 | { | |
703 | char fmtbuf[20]; | |
704 | ||
705 | strcpy(fmtbuf, "250"); | |
706 | fmtbuf[3] = last ? ' ' : '-'; | |
707 | ||
ac820be5 EA |
708 | if (a->q_fullname == NULL) |
709 | { | |
710 | if (strchr(a->q_user, '@') == NULL) | |
711 | strcpy(&fmtbuf[4], "<%s@%s>"); | |
712 | else | |
713 | strcpy(&fmtbuf[4], "<%s>"); | |
714 | message(fmtbuf, a->q_user, MyHostName); | |
715 | } | |
b6edea3d EA |
716 | else |
717 | { | |
ac820be5 EA |
718 | if (strchr(a->q_user, '@') == NULL) |
719 | strcpy(&fmtbuf[4], "%s <%s@%s>"); | |
720 | else | |
721 | strcpy(&fmtbuf[4], "%s <%s>"); | |
722 | message(fmtbuf, a->q_fullname, a->q_user, MyHostName); | |
b6edea3d | 723 | } |
b6edea3d EA |
724 | } |
725 | \f/* | |
34d37b7d EA |
726 | ** HELP -- implement the HELP command. |
727 | ** | |
728 | ** Parameters: | |
729 | ** topic -- the topic we want help for. | |
730 | ** | |
731 | ** Returns: | |
732 | ** none. | |
733 | ** | |
734 | ** Side Effects: | |
735 | ** outputs the help file to message output. | |
736 | */ | |
737 | ||
738 | help(topic) | |
739 | char *topic; | |
740 | { | |
741 | register FILE *hf; | |
742 | int len; | |
743 | char buf[MAXLINE]; | |
744 | bool noinfo; | |
745 | ||
c1e24818 | 746 | if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) |
34d37b7d EA |
747 | { |
748 | /* no help */ | |
1b932f66 | 749 | errno = 0; |
b6edea3d | 750 | message("502 HELP not implemented"); |
34d37b7d EA |
751 | return; |
752 | } | |
753 | ||
e97afd4a EA |
754 | if (topic == NULL || *topic == '\0') |
755 | topic = "smtp"; | |
756 | else | |
757 | makelower(topic); | |
758 | ||
34d37b7d | 759 | len = strlen(topic); |
34d37b7d EA |
760 | noinfo = TRUE; |
761 | ||
762 | while (fgets(buf, sizeof buf, hf) != NULL) | |
763 | { | |
764 | if (strncmp(buf, topic, len) == 0) | |
765 | { | |
766 | register char *p; | |
767 | ||
f3d8f6d6 | 768 | p = strchr(buf, '\t'); |
34d37b7d EA |
769 | if (p == NULL) |
770 | p = buf; | |
771 | else | |
772 | p++; | |
773 | fixcrlf(p, TRUE); | |
b6edea3d | 774 | message("214-%s", p); |
34d37b7d EA |
775 | noinfo = FALSE; |
776 | } | |
777 | } | |
778 | ||
779 | if (noinfo) | |
b6edea3d | 780 | message("504 HELP topic unknown"); |
34d37b7d | 781 | else |
b6edea3d | 782 | message("214 End of HELP info"); |
ed45aae1 | 783 | (void) fclose(hf); |
34d37b7d | 784 | } |
abae7b2d | 785 | \f/* |
e6f08ab1 EA |
786 | ** RUNINCHILD -- return twice -- once in the child, then in the parent again |
787 | ** | |
788 | ** Parameters: | |
789 | ** label -- a string used in error messages | |
790 | ** | |
791 | ** Returns: | |
792 | ** zero in the child | |
793 | ** one in the parent | |
794 | ** | |
795 | ** Side Effects: | |
796 | ** none. | |
797 | */ | |
798 | ||
a4076aed | 799 | runinchild(label, e) |
e6f08ab1 | 800 | char *label; |
a4076aed | 801 | register ENVELOPE *e; |
e6f08ab1 EA |
802 | { |
803 | int childpid; | |
804 | ||
c1a66acf | 805 | if (!OneXact) |
e6f08ab1 | 806 | { |
c1a66acf EA |
807 | childpid = dofork(); |
808 | if (childpid < 0) | |
809 | { | |
810 | syserr("%s: cannot fork", label); | |
811 | return (1); | |
812 | } | |
813 | if (childpid > 0) | |
814 | { | |
815 | auto int st; | |
e6f08ab1 | 816 | |
c1a66acf | 817 | /* parent -- wait for child to complete */ |
ade7da2a | 818 | setproctitle("srvrsmtp %s child wait", CurHostName); |
c1a66acf EA |
819 | st = waitfor(childpid); |
820 | if (st == -1) | |
821 | syserr("%s: lost child", label); | |
e6f08ab1 | 822 | |
c1a66acf EA |
823 | /* if we exited on a QUIT command, complete the process */ |
824 | if (st == (EX_QUIT << 8)) | |
825 | finis(); | |
e6f08ab1 | 826 | |
c1a66acf EA |
827 | return (1); |
828 | } | |
829 | else | |
830 | { | |
831 | /* child */ | |
832 | InChild = TRUE; | |
57c97d4a | 833 | QuickAbort = FALSE; |
a4076aed | 834 | clearenvelope(e, FALSE); |
c1a66acf | 835 | } |
e6f08ab1 | 836 | } |
55f0da62 | 837 | |
c1a66acf | 838 | /* open alias database */ |
595aeb43 | 839 | initaliases(FALSE, e); |
c1a66acf EA |
840 | |
841 | return (0); | |
e6f08ab1 EA |
842 | } |
843 | \f/* | |
abae7b2d EA |
844 | ** PADDRTREE -- print address tree |
845 | ** | |
846 | ** Used by VRFY and EXPD to dump the tree of addresses produced. | |
847 | ** | |
848 | ** Parameters: | |
849 | ** a -- address of root. | |
850 | ** | |
851 | ** Returns: | |
852 | ** none. | |
853 | ** | |
854 | ** Side Effects: | |
855 | ** prints the tree in a nice order. | |
856 | */ | |
857 | ||
858 | paddrtree(a) | |
859 | register ADDRESS *a; | |
860 | { | |
861 | static ADDRESS *prev; | |
862 | static int lev; | |
863 | ||
864 | if (a == NULL) | |
865 | return; | |
866 | lev++; | |
867 | if (!bitset(QDONTSEND, a->q_flags)) | |
868 | { | |
869 | if (prev != NULL) | |
870 | { | |
871 | if (prev->q_fullname != NULL) | |
872 | message("250-", "%s <%s>", prev->q_fullname, prev->q_paddr); | |
873 | else | |
874 | message("250-", "<%s>", prev->q_paddr); | |
875 | } | |
876 | prev = a; | |
877 | } | |
878 | paddrtree(a->q_child); | |
879 | paddrtree(a->q_sibling); | |
880 | if (--lev <= 0) | |
881 | { | |
882 | if (prev != NULL) | |
883 | { | |
884 | /* last one */ | |
885 | if (prev->q_fullname != NULL) | |
886 | message("250", "%s <%s>", prev->q_fullname, prev->q_paddr); | |
887 | else | |
888 | message("250", "<%s>", prev->q_paddr); | |
889 | prev = NULL; | |
890 | } | |
891 | else | |
892 | message("550", "User unknown"); | |
893 | } | |
894 | } | |
884a20cb | 895 | |
f3d8f6d6 | 896 | # endif /* SMTP */ |