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