Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1983 Eric P. Allman | |
6f14531a RG |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
15637ed4 RG |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * 3. All advertising materials mentioning features or use of this software | |
15 | * must display the following acknowledgement: | |
16 | * This product includes software developed by the University of | |
17 | * California, Berkeley and its contributors. | |
18 | * 4. Neither the name of the University nor the names of its contributors | |
19 | * may be used to endorse or promote products derived from this software | |
20 | * without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | # include "sendmail.h" | |
36 | ||
37 | #ifndef lint | |
38 | #ifdef SMTP | |
042b8fbf | 39 | static char sccsid[] = "@(#)srvrsmtp.c 8.23 (Berkeley) 12/21/93 (with SMTP)"; |
15637ed4 | 40 | #else |
042b8fbf | 41 | static char sccsid[] = "@(#)srvrsmtp.c 8.23 (Berkeley) 12/21/93 (without SMTP)"; |
15637ed4 RG |
42 | #endif |
43 | #endif /* not lint */ | |
44 | ||
45 | # include <errno.h> | |
15637ed4 RG |
46 | |
47 | # ifdef SMTP | |
48 | ||
49 | /* | |
50 | ** SMTP -- run the SMTP protocol. | |
51 | ** | |
52 | ** Parameters: | |
53 | ** none. | |
54 | ** | |
55 | ** Returns: | |
56 | ** never. | |
57 | ** | |
58 | ** Side Effects: | |
59 | ** Reads commands from the input channel and processes | |
60 | ** them. | |
61 | */ | |
62 | ||
63 | struct cmd | |
64 | { | |
65 | char *cmdname; /* command name */ | |
66 | int cmdcode; /* internal code, see below */ | |
67 | }; | |
68 | ||
69 | /* values for cmdcode */ | |
70 | # define CMDERROR 0 /* bad command */ | |
71 | # define CMDMAIL 1 /* mail -- designate sender */ | |
72 | # define CMDRCPT 2 /* rcpt -- designate recipient */ | |
73 | # define CMDDATA 3 /* data -- send message text */ | |
74 | # define CMDRSET 4 /* rset -- reset state */ | |
75 | # define CMDVRFY 5 /* vrfy -- verify address */ | |
6f14531a | 76 | # define CMDEXPN 6 /* expn -- expand address */ |
15637ed4 RG |
77 | # define CMDNOOP 7 /* noop -- do nothing */ |
78 | # define CMDQUIT 8 /* quit -- close connection and die */ | |
79 | # define CMDHELO 9 /* helo -- be polite */ | |
6f14531a RG |
80 | # define CMDHELP 10 /* help -- give usage info */ |
81 | # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ | |
82 | /* non-standard commands */ | |
83 | # define CMDONEX 16 /* onex -- sending one transaction only */ | |
84 | # define CMDVERB 17 /* verb -- go into verbose mode */ | |
d747e748 JH |
85 | /* use this to catch and log "door handle" attempts on your system */ |
86 | # define CMDLOGBOGUS 23 /* bogus command that should be logged */ | |
15637ed4 | 87 | /* debugging-only commands, only enabled if SMTPDEBUG is defined */ |
6f14531a RG |
88 | # define CMDDBGQSHOW 24 /* showq -- show send queue */ |
89 | # define CMDDBGDEBUG 25 /* debug -- set debug mode */ | |
15637ed4 RG |
90 | |
91 | static struct cmd CmdTab[] = | |
92 | { | |
93 | "mail", CMDMAIL, | |
94 | "rcpt", CMDRCPT, | |
95 | "data", CMDDATA, | |
96 | "rset", CMDRSET, | |
97 | "vrfy", CMDVRFY, | |
6f14531a | 98 | "expn", CMDEXPN, |
15637ed4 RG |
99 | "help", CMDHELP, |
100 | "noop", CMDNOOP, | |
101 | "quit", CMDQUIT, | |
102 | "helo", CMDHELO, | |
6f14531a | 103 | "ehlo", CMDEHLO, |
15637ed4 RG |
104 | "verb", CMDVERB, |
105 | "onex", CMDONEX, | |
106 | /* | |
107 | * remaining commands are here only | |
108 | * to trap and log attempts to use them | |
109 | */ | |
110 | "showq", CMDDBGQSHOW, | |
111 | "debug", CMDDBGDEBUG, | |
d747e748 | 112 | "wiz", CMDLOGBOGUS, |
15637ed4 RG |
113 | NULL, CMDERROR, |
114 | }; | |
115 | ||
15637ed4 | 116 | bool OneXact = FALSE; /* one xaction only this run */ |
042b8fbf | 117 | char *CurSmtpClient; /* who's at the other end of channel */ |
15637ed4 | 118 | |
d747e748 | 119 | static char *skipword(); |
15637ed4 | 120 | |
6f14531a RG |
121 | smtp(e) |
122 | register ENVELOPE *e; | |
15637ed4 RG |
123 | { |
124 | register char *p; | |
125 | register struct cmd *c; | |
126 | char *cmd; | |
15637ed4 RG |
127 | auto ADDRESS *vrfyqueue; |
128 | ADDRESS *a; | |
6f14531a RG |
129 | bool gotmail; /* mail command received */ |
130 | bool gothello; /* helo command received */ | |
131 | bool vrfy; /* set if this is a vrfy command */ | |
132 | char *protocol; /* sending protocol */ | |
133 | char *sendinghost; /* sending hostname */ | |
134 | long msize; /* approximate maximum message size */ | |
135 | auto char *delimptr; | |
136 | char *id; | |
137 | int nrcpts; /* number of RCPT commands */ | |
3a363396 | 138 | bool doublequeue; |
15637ed4 | 139 | char inp[MAXLINE]; |
6f14531a | 140 | char cmdbuf[MAXLINE]; |
15637ed4 | 141 | extern char Version[]; |
15637ed4 | 142 | extern ENVELOPE BlankEnvelope; |
15637ed4 | 143 | |
6f14531a | 144 | if (fileno(OutChannel) != fileno(stdout)) |
15637ed4 RG |
145 | { |
146 | /* arrange for debugging output to go to remote host */ | |
6f14531a | 147 | (void) dup2(fileno(OutChannel), fileno(stdout)); |
15637ed4 | 148 | } |
6f14531a RG |
149 | settime(e); |
150 | CurHostName = RealHostName; | |
042b8fbf AM |
151 | CurSmtpClient = macvalue('_', e); |
152 | if (CurSmtpClient == NULL) | |
153 | CurSmtpClient = RealHostName; | |
154 | ||
155 | setproctitle("server %s startup", CurSmtpClient); | |
6f14531a | 156 | expand("\201e", inp, &inp[sizeof inp], e); |
d747e748 JH |
157 | if (BrokenSmtpPeers) |
158 | { | |
159 | message("220 %s", inp); | |
160 | } | |
161 | else | |
162 | { | |
163 | message("220-%s", inp); | |
164 | message("220 ESMTP spoken here"); | |
165 | } | |
6f14531a RG |
166 | protocol = NULL; |
167 | sendinghost = macvalue('s', e); | |
168 | gothello = FALSE; | |
169 | gotmail = FALSE; | |
15637ed4 RG |
170 | for (;;) |
171 | { | |
172 | /* arrange for backout */ | |
173 | if (setjmp(TopFrame) > 0 && InChild) | |
6f14531a RG |
174 | { |
175 | QuickAbort = FALSE; | |
176 | SuprErrs = TRUE; | |
15637ed4 | 177 | finis(); |
6f14531a | 178 | } |
15637ed4 RG |
179 | QuickAbort = FALSE; |
180 | HoldErrs = FALSE; | |
6f14531a | 181 | LogUsrErrs = FALSE; |
d747e748 | 182 | e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); |
15637ed4 RG |
183 | |
184 | /* setup for the read */ | |
6f14531a | 185 | e->e_to = NULL; |
15637ed4 RG |
186 | Errors = 0; |
187 | (void) fflush(stdout); | |
188 | ||
189 | /* read the input line */ | |
6f14531a RG |
190 | SmtpPhase = "server cmd read"; |
191 | setproctitle("server %s cmd read", CurHostName); | |
192 | p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, | |
193 | SmtpPhase); | |
15637ed4 RG |
194 | |
195 | /* handle errors */ | |
196 | if (p == NULL) | |
197 | { | |
198 | /* end of file, just die */ | |
6f14531a | 199 | message("421 %s Lost input channel from %s", |
042b8fbf | 200 | MyHostName, CurSmtpClient); |
6f14531a | 201 | #ifdef LOG |
d747e748 | 202 | if (LogLevel > (gotmail ? 1 : 19)) |
6f14531a | 203 | syslog(LOG_NOTICE, "lost input channel from %s", |
042b8fbf | 204 | CurSmtpClient); |
6f14531a RG |
205 | #endif |
206 | if (InChild) | |
207 | ExitStat = EX_QUIT; | |
15637ed4 RG |
208 | finis(); |
209 | } | |
210 | ||
211 | /* clean up end of line */ | |
212 | fixcrlf(inp, TRUE); | |
213 | ||
214 | /* echo command to transcript */ | |
6f14531a RG |
215 | if (e->e_xfp != NULL) |
216 | fprintf(e->e_xfp, "<<< %s\n", inp); | |
217 | ||
218 | if (e->e_id == NULL) | |
042b8fbf | 219 | setproctitle("%s: %.80s", CurSmtpClient, inp); |
6f14531a | 220 | else |
042b8fbf | 221 | setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); |
15637ed4 RG |
222 | |
223 | /* break off command */ | |
6f14531a | 224 | for (p = inp; isascii(*p) && isspace(*p); p++) |
15637ed4 | 225 | continue; |
6f14531a RG |
226 | cmd = cmdbuf; |
227 | while (*p != '\0' && | |
228 | !(isascii(*p) && isspace(*p)) && | |
229 | cmd < &cmdbuf[sizeof cmdbuf - 2]) | |
15637ed4 RG |
230 | *cmd++ = *p++; |
231 | *cmd = '\0'; | |
232 | ||
233 | /* throw away leading whitespace */ | |
6f14531a | 234 | while (isascii(*p) && isspace(*p)) |
15637ed4 RG |
235 | p++; |
236 | ||
237 | /* decode command */ | |
238 | for (c = CmdTab; c->cmdname != NULL; c++) | |
239 | { | |
240 | if (!strcasecmp(c->cmdname, cmdbuf)) | |
241 | break; | |
242 | } | |
243 | ||
6f14531a RG |
244 | /* reset errors */ |
245 | errno = 0; | |
246 | ||
15637ed4 RG |
247 | /* process command */ |
248 | switch (c->cmdcode) | |
249 | { | |
250 | case CMDHELO: /* hello -- introduce yourself */ | |
6f14531a RG |
251 | case CMDEHLO: /* extended hello */ |
252 | if (c->cmdcode == CMDEHLO) | |
15637ed4 | 253 | { |
6f14531a RG |
254 | protocol = "ESMTP"; |
255 | SmtpPhase = "server EHLO"; | |
15637ed4 | 256 | } |
6f14531a | 257 | else |
15637ed4 | 258 | { |
6f14531a RG |
259 | protocol = "SMTP"; |
260 | SmtpPhase = "server HELO"; | |
261 | } | |
262 | sendinghost = newstr(p); | |
d747e748 JH |
263 | if (strcasecmp(p, RealHostName) != 0 && |
264 | (strcasecmp(RealHostName, "localhost") != 0 || | |
265 | strcasecmp(p, MyHostName) != 0)) | |
6f14531a RG |
266 | { |
267 | auth_warning(e, "Host %s claimed to be %s", | |
268 | RealHostName, p); | |
269 | } | |
15637ed4 | 270 | |
6f14531a RG |
271 | gothello = TRUE; |
272 | if (c->cmdcode != CMDEHLO) | |
273 | { | |
274 | /* print old message and be done with it */ | |
275 | message("250 %s Hello %s, pleased to meet you", | |
042b8fbf | 276 | MyHostName, CurSmtpClient); |
6f14531a | 277 | break; |
15637ed4 | 278 | } |
6f14531a RG |
279 | |
280 | /* print extended message and brag */ | |
281 | message("250-%s Hello %s, pleased to meet you", | |
282 | MyHostName, p); | |
283 | if (!bitset(PRIV_NOEXPN, PrivacyFlags)) | |
284 | message("250-EXPN"); | |
285 | if (MaxMessageSize > 0) | |
286 | message("250-SIZE %ld", MaxMessageSize); | |
15637ed4 | 287 | else |
6f14531a RG |
288 | message("250-SIZE"); |
289 | message("250 HELP"); | |
15637ed4 RG |
290 | break; |
291 | ||
292 | case CMDMAIL: /* mail -- designate sender */ | |
6f14531a | 293 | SmtpPhase = "server MAIL"; |
15637ed4 RG |
294 | |
295 | /* check for validity of this command */ | |
6f14531a | 296 | if (!gothello) |
15637ed4 | 297 | { |
6f14531a RG |
298 | /* set sending host to our known value */ |
299 | if (sendinghost == NULL) | |
300 | sendinghost = RealHostName; | |
301 | ||
302 | if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) | |
303 | { | |
304 | message("503 Polite people say HELO first"); | |
305 | break; | |
306 | } | |
6f14531a RG |
307 | } |
308 | if (gotmail) | |
309 | { | |
310 | message("503 Sender already specified"); | |
d747e748 JH |
311 | if (InChild) |
312 | finis(); | |
15637ed4 RG |
313 | break; |
314 | } | |
315 | if (InChild) | |
316 | { | |
317 | errno = 0; | |
6f14531a RG |
318 | syserr("503 Nested MAIL command: MAIL %s", p); |
319 | finis(); | |
15637ed4 RG |
320 | } |
321 | ||
322 | /* fork a subprocess to process this command */ | |
6f14531a | 323 | if (runinchild("SMTP-MAIL", e) > 0) |
15637ed4 | 324 | break; |
3a363396 NW |
325 | if (!gothello) |
326 | { | |
327 | auth_warning(e, | |
328 | "Host %s didn't use HELO protocol", | |
329 | RealHostName); | |
330 | } | |
6f14531a RG |
331 | if (protocol == NULL) |
332 | protocol = "SMTP"; | |
333 | define('r', protocol, e); | |
334 | define('s', sendinghost, e); | |
335 | initsys(e); | |
336 | nrcpts = 0; | |
042b8fbf AM |
337 | e->e_flags |= EF_LOGSENDER; |
338 | setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); | |
15637ed4 RG |
339 | |
340 | /* child -- go do the processing */ | |
341 | p = skipword(p, "from"); | |
342 | if (p == NULL) | |
343 | break; | |
6f14531a | 344 | if (setjmp(TopFrame) > 0) |
15637ed4 | 345 | { |
6f14531a RG |
346 | /* this failed -- undo work */ |
347 | if (InChild) | |
348 | { | |
349 | QuickAbort = FALSE; | |
350 | SuprErrs = TRUE; | |
3a363396 | 351 | e->e_flags &= ~EF_FATALERRS; |
6f14531a RG |
352 | finis(); |
353 | } | |
354 | break; | |
15637ed4 | 355 | } |
6f14531a RG |
356 | QuickAbort = TRUE; |
357 | ||
358 | /* must parse sender first */ | |
359 | delimptr = NULL; | |
360 | setsender(p, e, &delimptr, FALSE); | |
361 | p = delimptr; | |
362 | if (p != NULL && *p != '\0') | |
363 | *p++ = '\0'; | |
364 | ||
365 | /* now parse ESMTP arguments */ | |
366 | msize = 0; | |
367 | for (; p != NULL && *p != '\0'; p++) | |
368 | { | |
369 | char *kp; | |
370 | char *vp; | |
371 | ||
372 | /* locate the beginning of the keyword */ | |
373 | while (isascii(*p) && isspace(*p)) | |
374 | p++; | |
375 | if (*p == '\0') | |
376 | break; | |
377 | kp = p; | |
378 | ||
379 | /* skip to the value portion */ | |
380 | while (isascii(*p) && isalnum(*p) || *p == '-') | |
381 | p++; | |
382 | if (*p == '=') | |
383 | { | |
384 | *p++ = '\0'; | |
385 | vp = p; | |
386 | ||
387 | /* skip to the end of the value */ | |
388 | while (*p != '\0' && *p != ' ' && | |
389 | !(isascii(*p) && iscntrl(*p)) && | |
390 | *p != '=') | |
391 | p++; | |
392 | } | |
393 | ||
394 | if (*p != '\0') | |
395 | *p++ = '\0'; | |
396 | ||
397 | if (tTd(19, 1)) | |
398 | printf("MAIL: got arg %s=%s\n", kp, | |
399 | vp == NULL ? "<null>" : vp); | |
400 | ||
401 | if (strcasecmp(kp, "size") == 0) | |
402 | { | |
403 | if (vp == NULL) | |
404 | { | |
405 | usrerr("501 SIZE requires a value"); | |
406 | /* NOTREACHED */ | |
407 | } | |
408 | msize = atol(vp); | |
409 | } | |
410 | else if (strcasecmp(kp, "body") == 0) | |
411 | { | |
412 | if (vp == NULL) | |
413 | { | |
414 | usrerr("501 BODY requires a value"); | |
415 | /* NOTREACHED */ | |
416 | } | |
417 | # ifdef MIME | |
418 | if (strcasecmp(vp, "8bitmime") == 0) | |
419 | { | |
420 | e->e_bodytype = "8BITMIME"; | |
421 | SevenBit = FALSE; | |
422 | } | |
423 | else if (strcasecmp(vp, "7bit") == 0) | |
424 | { | |
425 | e->e_bodytype = "7BIT"; | |
426 | SevenBit = TRUE; | |
427 | } | |
428 | else | |
429 | { | |
430 | usrerr("501 Unknown BODY type %s", | |
431 | vp); | |
432 | } | |
433 | # endif | |
434 | } | |
435 | else | |
436 | { | |
437 | usrerr("501 %s parameter unrecognized", kp); | |
438 | /* NOTREACHED */ | |
439 | } | |
440 | } | |
441 | ||
442 | if (MaxMessageSize > 0 && msize > MaxMessageSize) | |
443 | { | |
444 | usrerr("552 Message size exceeds fixed maximum message size (%ld)", | |
445 | MaxMessageSize); | |
446 | /* NOTREACHED */ | |
447 | } | |
448 | ||
449 | if (!enoughspace(msize)) | |
450 | { | |
451 | message("452 Insufficient disk space; try again later"); | |
452 | break; | |
453 | } | |
454 | message("250 Sender ok"); | |
455 | gotmail = TRUE; | |
15637ed4 RG |
456 | break; |
457 | ||
458 | case CMDRCPT: /* rcpt -- designate recipient */ | |
6f14531a RG |
459 | if (!gotmail) |
460 | { | |
461 | usrerr("503 Need MAIL before RCPT"); | |
462 | break; | |
463 | } | |
464 | SmtpPhase = "server RCPT"; | |
15637ed4 RG |
465 | if (setjmp(TopFrame) > 0) |
466 | { | |
6f14531a | 467 | e->e_flags &= ~EF_FATALERRS; |
15637ed4 RG |
468 | break; |
469 | } | |
470 | QuickAbort = TRUE; | |
6f14531a RG |
471 | LogUsrErrs = TRUE; |
472 | ||
473 | if (e->e_sendmode != SM_DELIVER) | |
474 | e->e_flags |= EF_VRFYONLY; | |
475 | ||
15637ed4 RG |
476 | p = skipword(p, "to"); |
477 | if (p == NULL) | |
478 | break; | |
d747e748 | 479 | a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); |
15637ed4 RG |
480 | if (a == NULL) |
481 | break; | |
482 | a->q_flags |= QPRIMARY; | |
6f14531a | 483 | a = recipient(a, &e->e_sendqueue, e); |
15637ed4 RG |
484 | if (Errors != 0) |
485 | break; | |
486 | ||
487 | /* no errors during parsing, but might be a duplicate */ | |
6f14531a | 488 | e->e_to = p; |
15637ed4 | 489 | if (!bitset(QBADADDR, a->q_flags)) |
6f14531a | 490 | { |
d747e748 JH |
491 | message("250 Recipient ok%s", |
492 | bitset(QQUEUEUP, a->q_flags) ? | |
493 | " (will queue)" : ""); | |
6f14531a RG |
494 | nrcpts++; |
495 | } | |
15637ed4 RG |
496 | else |
497 | { | |
498 | /* punt -- should keep message in ADDRESS.... */ | |
6f14531a | 499 | message("550 Addressee unknown"); |
15637ed4 | 500 | } |
6f14531a | 501 | e->e_to = NULL; |
15637ed4 RG |
502 | break; |
503 | ||
504 | case CMDDATA: /* data -- text of mail */ | |
6f14531a RG |
505 | SmtpPhase = "server DATA"; |
506 | if (!gotmail) | |
15637ed4 | 507 | { |
6f14531a | 508 | message("503 Need MAIL command"); |
15637ed4 RG |
509 | break; |
510 | } | |
d747e748 | 511 | else if (nrcpts <= 0) |
15637ed4 | 512 | { |
6f14531a | 513 | message("503 Need RCPT (recipient)"); |
15637ed4 RG |
514 | break; |
515 | } | |
516 | ||
6f14531a | 517 | /* check to see if we need to re-expand aliases */ |
3a363396 NW |
518 | /* also reset QBADADDR on already-diagnosted addrs */ |
519 | doublequeue = FALSE; | |
6f14531a RG |
520 | for (a = e->e_sendqueue; a != NULL; a = a->q_next) |
521 | { | |
522 | if (bitset(QVERIFIED, a->q_flags)) | |
3a363396 NW |
523 | { |
524 | /* need to re-expand aliases */ | |
525 | doublequeue = TRUE; | |
526 | } | |
527 | if (bitset(QBADADDR, a->q_flags)) | |
528 | { | |
529 | /* make this "go away" */ | |
530 | a->q_flags |= QDONTSEND; | |
531 | a->q_flags &= ~QBADADDR; | |
532 | } | |
6f14531a RG |
533 | } |
534 | ||
15637ed4 RG |
535 | /* collect the text of the message */ |
536 | SmtpPhase = "collect"; | |
3a363396 | 537 | collect(TRUE, doublequeue, e); |
69fc843f AM |
538 | if (Errors != 0) |
539 | goto abortmessage; | |
540 | HoldErrs = TRUE; | |
15637ed4 RG |
541 | |
542 | /* | |
543 | ** Arrange to send to everyone. | |
544 | ** If sending to multiple people, mail back | |
545 | ** errors rather than reporting directly. | |
546 | ** In any case, don't mail back errors for | |
547 | ** anything that has happened up to | |
548 | ** now (the other end will do this). | |
549 | ** Truncate our transcript -- the mail has gotten | |
550 | ** to us successfully, and if we have | |
551 | ** to mail this back, it will be easier | |
552 | ** on the reader. | |
553 | ** Then send to everyone. | |
554 | ** Finally give a reply code. If an error has | |
555 | ** already been given, don't mail a | |
556 | ** message back. | |
557 | ** We goose error returns by clearing error bit. | |
558 | */ | |
559 | ||
560 | SmtpPhase = "delivery"; | |
3a363396 | 561 | if (nrcpts != 1 && !doublequeue) |
15637ed4 RG |
562 | { |
563 | HoldErrs = TRUE; | |
6f14531a | 564 | e->e_errormode = EM_MAIL; |
15637ed4 | 565 | } |
6f14531a RG |
566 | e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); |
567 | id = e->e_id; | |
15637ed4 RG |
568 | |
569 | /* send to all recipients */ | |
3a363396 | 570 | sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); |
6f14531a | 571 | e->e_to = NULL; |
15637ed4 | 572 | |
15637ed4 RG |
573 | /* issue success if appropriate and reset */ |
574 | if (Errors == 0 || HoldErrs) | |
6f14531a RG |
575 | message("250 %s Message accepted for delivery", id); |
576 | ||
577 | if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) | |
578 | { | |
579 | /* avoid sending back an extra message */ | |
580 | e->e_flags &= ~EF_FATALERRS; | |
581 | e->e_flags |= EF_CLRQUEUE; | |
582 | } | |
15637ed4 | 583 | else |
6f14531a RG |
584 | { |
585 | /* from now on, we have to operate silently */ | |
586 | HoldErrs = TRUE; | |
587 | e->e_errormode = EM_MAIL; | |
588 | ||
589 | /* if we just queued, poke it */ | |
3a363396 | 590 | if (doublequeue && e->e_sendmode != SM_QUEUE) |
6f14531a | 591 | { |
d747e748 JH |
592 | extern pid_t dowork(); |
593 | ||
6f14531a | 594 | unlockqueue(e); |
d747e748 | 595 | (void) dowork(id, TRUE, TRUE, e); |
6f14531a RG |
596 | } |
597 | } | |
15637ed4 | 598 | |
6f14531a | 599 | abortmessage: |
15637ed4 RG |
600 | /* if in a child, pop back to our parent */ |
601 | if (InChild) | |
602 | finis(); | |
603 | ||
604 | /* clean up a bit */ | |
6f14531a RG |
605 | gotmail = FALSE; |
606 | dropenvelope(e); | |
607 | CurEnv = e = newenvelope(e, CurEnv); | |
608 | e->e_flags = BlankEnvelope.e_flags; | |
15637ed4 RG |
609 | break; |
610 | ||
611 | case CMDRSET: /* rset -- reset state */ | |
6f14531a | 612 | message("250 Reset state"); |
d747e748 | 613 | e->e_flags |= EF_CLRQUEUE; |
15637ed4 RG |
614 | if (InChild) |
615 | finis(); | |
6f14531a RG |
616 | |
617 | /* clean up a bit */ | |
618 | gotmail = FALSE; | |
619 | dropenvelope(e); | |
620 | CurEnv = e = newenvelope(e, CurEnv); | |
15637ed4 RG |
621 | break; |
622 | ||
623 | case CMDVRFY: /* vrfy -- verify address */ | |
6f14531a RG |
624 | case CMDEXPN: /* expn -- expand address */ |
625 | vrfy = c->cmdcode == CMDVRFY; | |
626 | if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, | |
627 | PrivacyFlags)) | |
628 | { | |
629 | if (vrfy) | |
630 | message("252 Who's to say?"); | |
631 | else | |
042b8fbf AM |
632 | message("502 Sorry, we do not allow this operation"); |
633 | #ifdef LOG | |
634 | if (LogLevel > 5) | |
635 | syslog(LOG_INFO, "%s: %s [rejected]", | |
636 | CurSmtpClient, inp); | |
637 | #endif | |
15637ed4 | 638 | break; |
6f14531a RG |
639 | } |
640 | else if (!gothello && | |
641 | bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, | |
642 | PrivacyFlags)) | |
643 | { | |
644 | message("503 I demand that you introduce yourself first"); | |
645 | break; | |
646 | } | |
647 | if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) | |
648 | break; | |
649 | #ifdef LOG | |
650 | if (LogLevel > 5) | |
042b8fbf | 651 | syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); |
6f14531a | 652 | #endif |
15637ed4 RG |
653 | vrfyqueue = NULL; |
654 | QuickAbort = TRUE; | |
6f14531a RG |
655 | if (vrfy) |
656 | e->e_flags |= EF_VRFYONLY; | |
657 | while (*p != '\0' && isascii(*p) && isspace(*p)) | |
658 | *p++; | |
659 | if (*p == '\0') | |
660 | { | |
661 | message("501 Argument required"); | |
662 | Errors++; | |
663 | } | |
664 | else | |
665 | { | |
d747e748 | 666 | (void) sendtolist(p, NULLADDR, &vrfyqueue, e); |
6f14531a | 667 | } |
15637ed4 RG |
668 | if (Errors != 0) |
669 | { | |
670 | if (InChild) | |
671 | finis(); | |
672 | break; | |
673 | } | |
6f14531a RG |
674 | if (vrfyqueue == NULL) |
675 | { | |
676 | message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); | |
677 | } | |
15637ed4 RG |
678 | while (vrfyqueue != NULL) |
679 | { | |
d747e748 JH |
680 | a = vrfyqueue; |
681 | while ((a = a->q_next) != NULL && | |
682 | bitset(QDONTSEND|QBADADDR, a->q_flags)) | |
683 | continue; | |
15637ed4 | 684 | if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) |
6f14531a | 685 | printvrfyaddr(vrfyqueue, a == NULL); |
d747e748 | 686 | vrfyqueue = vrfyqueue->q_next; |
15637ed4 RG |
687 | } |
688 | if (InChild) | |
689 | finis(); | |
690 | break; | |
691 | ||
692 | case CMDHELP: /* help -- give user info */ | |
693 | help(p); | |
694 | break; | |
695 | ||
696 | case CMDNOOP: /* noop -- do nothing */ | |
d747e748 | 697 | message("250 OK"); |
15637ed4 RG |
698 | break; |
699 | ||
700 | case CMDQUIT: /* quit -- leave mail */ | |
6f14531a RG |
701 | message("221 %s closing connection", MyHostName); |
702 | ||
703 | /* avoid future 050 messages */ | |
704 | Verbose = FALSE; | |
705 | ||
15637ed4 RG |
706 | if (InChild) |
707 | ExitStat = EX_QUIT; | |
708 | finis(); | |
709 | ||
710 | case CMDVERB: /* set verbose mode */ | |
6f14531a RG |
711 | if (bitset(PRIV_NOEXPN, PrivacyFlags)) |
712 | { | |
713 | /* this would give out the same info */ | |
714 | message("502 Verbose unavailable"); | |
715 | break; | |
716 | } | |
15637ed4 | 717 | Verbose = TRUE; |
6f14531a RG |
718 | e->e_sendmode = SM_DELIVER; |
719 | message("250 Verbose mode"); | |
15637ed4 RG |
720 | break; |
721 | ||
722 | case CMDONEX: /* doing one transaction only */ | |
723 | OneXact = TRUE; | |
6f14531a | 724 | message("250 Only one transaction"); |
15637ed4 RG |
725 | break; |
726 | ||
727 | # ifdef SMTPDEBUG | |
728 | case CMDDBGQSHOW: /* show queues */ | |
729 | printf("Send Queue="); | |
6f14531a | 730 | printaddr(e->e_sendqueue, TRUE); |
15637ed4 RG |
731 | break; |
732 | ||
733 | case CMDDBGDEBUG: /* set debug mode */ | |
734 | tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); | |
735 | tTflag(p); | |
6f14531a | 736 | message("200 Debug set"); |
15637ed4 RG |
737 | break; |
738 | ||
739 | # else /* not SMTPDEBUG */ | |
15637ed4 RG |
740 | case CMDDBGQSHOW: /* show queues */ |
741 | case CMDDBGDEBUG: /* set debug mode */ | |
d747e748 JH |
742 | # endif /* SMTPDEBUG */ |
743 | case CMDLOGBOGUS: /* bogus command */ | |
15637ed4 | 744 | # ifdef LOG |
6f14531a | 745 | if (LogLevel > 0) |
d747e748 | 746 | syslog(LOG_CRIT, |
6f14531a | 747 | "\"%s\" command from %s (%s)", |
15637ed4 | 748 | c->cmdname, RealHostName, |
6f14531a | 749 | anynet_ntoa(&RealHostAddr)); |
15637ed4 RG |
750 | # endif |
751 | /* FALL THROUGH */ | |
15637ed4 RG |
752 | |
753 | case CMDERROR: /* unknown command */ | |
6f14531a | 754 | message("500 Command unrecognized"); |
15637ed4 RG |
755 | break; |
756 | ||
757 | default: | |
758 | errno = 0; | |
6f14531a | 759 | syserr("500 smtp: unknown code %d", c->cmdcode); |
15637ed4 RG |
760 | break; |
761 | } | |
762 | } | |
763 | } | |
764 | \f/* | |
765 | ** SKIPWORD -- skip a fixed word. | |
766 | ** | |
767 | ** Parameters: | |
768 | ** p -- place to start looking. | |
769 | ** w -- word to skip. | |
770 | ** | |
771 | ** Returns: | |
772 | ** p following w. | |
773 | ** NULL on error. | |
774 | ** | |
775 | ** Side Effects: | |
776 | ** clobbers the p data area. | |
777 | */ | |
778 | ||
779 | static char * | |
780 | skipword(p, w) | |
781 | register char *p; | |
782 | char *w; | |
783 | { | |
784 | register char *q; | |
785 | ||
786 | /* find beginning of word */ | |
6f14531a | 787 | while (isascii(*p) && isspace(*p)) |
15637ed4 RG |
788 | p++; |
789 | q = p; | |
790 | ||
791 | /* find end of word */ | |
6f14531a | 792 | while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) |
15637ed4 | 793 | p++; |
6f14531a | 794 | while (isascii(*p) && isspace(*p)) |
15637ed4 RG |
795 | *p++ = '\0'; |
796 | if (*p != ':') | |
797 | { | |
798 | syntax: | |
6f14531a | 799 | message("501 Syntax error in parameters"); |
15637ed4 RG |
800 | Errors++; |
801 | return (NULL); | |
802 | } | |
803 | *p++ = '\0'; | |
6f14531a | 804 | while (isascii(*p) && isspace(*p)) |
15637ed4 RG |
805 | p++; |
806 | ||
6f14531a RG |
807 | if (*p == '\0') |
808 | goto syntax; | |
809 | ||
15637ed4 RG |
810 | /* see if the input word matches desired word */ |
811 | if (strcasecmp(q, w)) | |
812 | goto syntax; | |
813 | ||
814 | return (p); | |
815 | } | |
816 | \f/* | |
6f14531a RG |
817 | ** PRINTVRFYADDR -- print an entry in the verify queue |
818 | ** | |
819 | ** Parameters: | |
820 | ** a -- the address to print | |
821 | ** last -- set if this is the last one. | |
822 | ** | |
823 | ** Returns: | |
824 | ** none. | |
825 | ** | |
826 | ** Side Effects: | |
827 | ** Prints the appropriate 250 codes. | |
828 | */ | |
829 | ||
830 | printvrfyaddr(a, last) | |
831 | register ADDRESS *a; | |
832 | bool last; | |
833 | { | |
834 | char fmtbuf[20]; | |
835 | ||
836 | strcpy(fmtbuf, "250"); | |
837 | fmtbuf[3] = last ? ' ' : '-'; | |
838 | ||
839 | if (a->q_fullname == NULL) | |
840 | { | |
841 | if (strchr(a->q_user, '@') == NULL) | |
842 | strcpy(&fmtbuf[4], "<%s@%s>"); | |
843 | else | |
844 | strcpy(&fmtbuf[4], "<%s>"); | |
845 | message(fmtbuf, a->q_user, MyHostName); | |
846 | } | |
847 | else | |
848 | { | |
849 | if (strchr(a->q_user, '@') == NULL) | |
850 | strcpy(&fmtbuf[4], "%s <%s@%s>"); | |
851 | else | |
852 | strcpy(&fmtbuf[4], "%s <%s>"); | |
853 | message(fmtbuf, a->q_fullname, a->q_user, MyHostName); | |
854 | } | |
855 | } | |
856 | \f/* | |
15637ed4 RG |
857 | ** HELP -- implement the HELP command. |
858 | ** | |
859 | ** Parameters: | |
860 | ** topic -- the topic we want help for. | |
861 | ** | |
862 | ** Returns: | |
863 | ** none. | |
864 | ** | |
865 | ** Side Effects: | |
866 | ** outputs the help file to message output. | |
867 | */ | |
868 | ||
869 | help(topic) | |
870 | char *topic; | |
871 | { | |
872 | register FILE *hf; | |
873 | int len; | |
874 | char buf[MAXLINE]; | |
875 | bool noinfo; | |
876 | ||
877 | if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) | |
878 | { | |
879 | /* no help */ | |
880 | errno = 0; | |
6f14531a | 881 | message("502 HELP not implemented"); |
15637ed4 RG |
882 | return; |
883 | } | |
884 | ||
885 | if (topic == NULL || *topic == '\0') | |
886 | topic = "smtp"; | |
887 | else | |
888 | makelower(topic); | |
889 | ||
890 | len = strlen(topic); | |
891 | noinfo = TRUE; | |
892 | ||
893 | while (fgets(buf, sizeof buf, hf) != NULL) | |
894 | { | |
895 | if (strncmp(buf, topic, len) == 0) | |
896 | { | |
897 | register char *p; | |
898 | ||
6f14531a | 899 | p = strchr(buf, '\t'); |
15637ed4 RG |
900 | if (p == NULL) |
901 | p = buf; | |
902 | else | |
903 | p++; | |
904 | fixcrlf(p, TRUE); | |
6f14531a | 905 | message("214-%s", p); |
15637ed4 RG |
906 | noinfo = FALSE; |
907 | } | |
908 | } | |
909 | ||
910 | if (noinfo) | |
6f14531a | 911 | message("504 HELP topic unknown"); |
15637ed4 | 912 | else |
6f14531a | 913 | message("214 End of HELP info"); |
15637ed4 RG |
914 | (void) fclose(hf); |
915 | } | |
916 | \f/* | |
917 | ** RUNINCHILD -- return twice -- once in the child, then in the parent again | |
918 | ** | |
919 | ** Parameters: | |
920 | ** label -- a string used in error messages | |
921 | ** | |
922 | ** Returns: | |
923 | ** zero in the child | |
924 | ** one in the parent | |
925 | ** | |
926 | ** Side Effects: | |
927 | ** none. | |
928 | */ | |
929 | ||
6f14531a | 930 | runinchild(label, e) |
15637ed4 | 931 | char *label; |
6f14531a | 932 | register ENVELOPE *e; |
15637ed4 RG |
933 | { |
934 | int childpid; | |
935 | ||
936 | if (!OneXact) | |
937 | { | |
938 | childpid = dofork(); | |
939 | if (childpid < 0) | |
940 | { | |
941 | syserr("%s: cannot fork", label); | |
942 | return (1); | |
943 | } | |
944 | if (childpid > 0) | |
945 | { | |
946 | auto int st; | |
947 | ||
948 | /* parent -- wait for child to complete */ | |
6f14531a | 949 | setproctitle("server %s child wait", CurHostName); |
15637ed4 RG |
950 | st = waitfor(childpid); |
951 | if (st == -1) | |
952 | syserr("%s: lost child", label); | |
042b8fbf AM |
953 | else if (!WIFEXITED(st)) |
954 | syserr("%s: died on signal %d", | |
955 | label, st & 0177); | |
15637ed4 RG |
956 | |
957 | /* if we exited on a QUIT command, complete the process */ | |
958 | if (st == (EX_QUIT << 8)) | |
959 | finis(); | |
960 | ||
961 | return (1); | |
962 | } | |
963 | else | |
964 | { | |
965 | /* child */ | |
966 | InChild = TRUE; | |
967 | QuickAbort = FALSE; | |
6f14531a | 968 | clearenvelope(e, FALSE); |
15637ed4 RG |
969 | } |
970 | } | |
971 | ||
972 | /* open alias database */ | |
6f14531a | 973 | initmaps(FALSE, e); |
15637ed4 RG |
974 | |
975 | return (0); | |
976 | } | |
977 | ||
6f14531a | 978 | # endif /* SMTP */ |