Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1983 Eric P. Allman | |
3 | * Copyright (c) 1988 Regents of the University of California. | |
4 | * All rights reserved. | |
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 | |
39 | static char sccsid[] = "@(#)srvrsmtp.c 5.31 (Berkeley) 5/10/91 (with SMTP)"; | |
40 | #else | |
41 | static char sccsid[] = "@(#)srvrsmtp.c 5.31 (Berkeley) 5/10/91 (without SMTP)"; | |
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 */ | |
77 | # define CMDHELP 6 /* help -- give usage info */ | |
78 | # define CMDNOOP 7 /* noop -- do nothing */ | |
79 | # define CMDQUIT 8 /* quit -- close connection and die */ | |
80 | # define CMDHELO 9 /* helo -- be polite */ | |
81 | # define CMDONEX 10 /* onex -- sending one transaction only */ | |
82 | # define CMDVERB 11 /* verb -- go into verbose mode */ | |
83 | /* debugging-only commands, only enabled if SMTPDEBUG is defined */ | |
84 | # define CMDDBGQSHOW 12 /* showq -- show send queue */ | |
85 | # define CMDDBGDEBUG 13 /* debug -- set debug mode */ | |
86 | ||
87 | static struct cmd CmdTab[] = | |
88 | { | |
89 | "mail", CMDMAIL, | |
90 | "rcpt", CMDRCPT, | |
91 | "data", CMDDATA, | |
92 | "rset", CMDRSET, | |
93 | "vrfy", CMDVRFY, | |
94 | "expn", CMDVRFY, | |
95 | "help", CMDHELP, | |
96 | "noop", CMDNOOP, | |
97 | "quit", CMDQUIT, | |
98 | "helo", CMDHELO, | |
99 | "verb", CMDVERB, | |
100 | "onex", CMDONEX, | |
101 | /* | |
102 | * remaining commands are here only | |
103 | * to trap and log attempts to use them | |
104 | */ | |
105 | "showq", CMDDBGQSHOW, | |
106 | "debug", CMDDBGDEBUG, | |
107 | NULL, CMDERROR, | |
108 | }; | |
109 | ||
110 | bool InChild = FALSE; /* true if running in a subprocess */ | |
111 | bool OneXact = FALSE; /* one xaction only this run */ | |
112 | ||
113 | #define EX_QUIT 22 /* special code for QUIT command */ | |
114 | ||
115 | smtp() | |
116 | { | |
117 | register char *p; | |
118 | register struct cmd *c; | |
119 | char *cmd; | |
120 | static char *skipword(); | |
121 | bool hasmail; /* mail command received */ | |
122 | auto ADDRESS *vrfyqueue; | |
123 | ADDRESS *a; | |
124 | char *sendinghost; | |
125 | char inp[MAXLINE]; | |
126 | char cmdbuf[100]; | |
127 | extern char Version[]; | |
128 | extern char *macvalue(); | |
129 | extern ADDRESS *recipient(); | |
130 | extern ENVELOPE BlankEnvelope; | |
131 | extern ENVELOPE *newenvelope(); | |
132 | ||
133 | hasmail = FALSE; | |
134 | if (OutChannel != stdout) | |
135 | { | |
136 | /* arrange for debugging output to go to remote host */ | |
137 | (void) close(1); | |
138 | (void) dup(fileno(OutChannel)); | |
139 | } | |
140 | settime(); | |
141 | if (RealHostName != NULL) | |
142 | { | |
143 | CurHostName = RealHostName; | |
144 | setproctitle("srvrsmtp %s", CurHostName); | |
145 | } | |
146 | else | |
147 | { | |
148 | /* this must be us!! */ | |
149 | CurHostName = MyHostName; | |
150 | } | |
151 | expand("\001e", inp, &inp[sizeof inp], CurEnv); | |
152 | message("220", inp); | |
153 | SmtpPhase = "startup"; | |
154 | sendinghost = NULL; | |
155 | for (;;) | |
156 | { | |
157 | /* arrange for backout */ | |
158 | if (setjmp(TopFrame) > 0 && InChild) | |
159 | finis(); | |
160 | QuickAbort = FALSE; | |
161 | HoldErrs = FALSE; | |
162 | ||
163 | /* setup for the read */ | |
164 | CurEnv->e_to = NULL; | |
165 | Errors = 0; | |
166 | (void) fflush(stdout); | |
167 | ||
168 | /* read the input line */ | |
169 | p = sfgets(inp, sizeof inp, InChannel); | |
170 | ||
171 | /* handle errors */ | |
172 | if (p == NULL) | |
173 | { | |
174 | /* end of file, just die */ | |
175 | message("421", "%s Lost input channel from %s", | |
176 | MyHostName, CurHostName); | |
177 | finis(); | |
178 | } | |
179 | ||
180 | /* clean up end of line */ | |
181 | fixcrlf(inp, TRUE); | |
182 | ||
183 | /* echo command to transcript */ | |
184 | if (CurEnv->e_xfp != NULL) | |
185 | fprintf(CurEnv->e_xfp, "<<< %s\n", inp); | |
186 | ||
187 | /* break off command */ | |
188 | for (p = inp; isspace(*p); p++) | |
189 | continue; | |
190 | cmd = p; | |
191 | for (cmd = cmdbuf; *p != '\0' && !isspace(*p); ) | |
192 | *cmd++ = *p++; | |
193 | *cmd = '\0'; | |
194 | ||
195 | /* throw away leading whitespace */ | |
196 | while (isspace(*p)) | |
197 | p++; | |
198 | ||
199 | /* decode command */ | |
200 | for (c = CmdTab; c->cmdname != NULL; c++) | |
201 | { | |
202 | if (!strcasecmp(c->cmdname, cmdbuf)) | |
203 | break; | |
204 | } | |
205 | ||
206 | /* process command */ | |
207 | switch (c->cmdcode) | |
208 | { | |
209 | case CMDHELO: /* hello -- introduce yourself */ | |
210 | SmtpPhase = "HELO"; | |
211 | setproctitle("%s: %s", CurHostName, inp); | |
212 | if (!strcasecmp(p, MyHostName)) | |
213 | { | |
214 | /* | |
215 | * didn't know about alias, | |
216 | * or connected to an echo server | |
217 | */ | |
218 | message("553", "%s config error: mail loops back to myself", | |
219 | MyHostName); | |
220 | break; | |
221 | } | |
222 | if (RealHostName != NULL && strcasecmp(p, RealHostName)) | |
223 | { | |
224 | char hostbuf[MAXNAME]; | |
225 | ||
226 | (void) sprintf(hostbuf, "%s (%s)", p, RealHostName); | |
227 | sendinghost = newstr(hostbuf); | |
228 | } | |
229 | else | |
230 | sendinghost = newstr(p); | |
231 | message("250", "%s Hello %s, pleased to meet you", | |
232 | MyHostName, sendinghost); | |
233 | break; | |
234 | ||
235 | case CMDMAIL: /* mail -- designate sender */ | |
236 | SmtpPhase = "MAIL"; | |
237 | ||
238 | /* force a sending host even if no HELO given */ | |
239 | if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) | |
240 | sendinghost = RealHostName; | |
241 | ||
242 | /* check for validity of this command */ | |
243 | if (hasmail) | |
244 | { | |
245 | message("503", "Sender already specified"); | |
246 | break; | |
247 | } | |
248 | if (InChild) | |
249 | { | |
250 | errno = 0; | |
251 | syserr("Nested MAIL command"); | |
252 | exit(0); | |
253 | } | |
254 | ||
255 | /* fork a subprocess to process this command */ | |
256 | if (runinchild("SMTP-MAIL") > 0) | |
257 | break; | |
258 | define('s', sendinghost, CurEnv); | |
259 | define('r', "SMTP", CurEnv); | |
260 | initsys(); | |
261 | setproctitle("%s %s: %s", CurEnv->e_id, | |
262 | CurHostName, inp); | |
263 | ||
264 | /* child -- go do the processing */ | |
265 | p = skipword(p, "from"); | |
266 | if (p == NULL) | |
267 | break; | |
268 | setsender(p); | |
269 | if (Errors == 0) | |
270 | { | |
271 | message("250", "Sender ok"); | |
272 | hasmail = TRUE; | |
273 | } | |
274 | else if (InChild) | |
275 | finis(); | |
276 | break; | |
277 | ||
278 | case CMDRCPT: /* rcpt -- designate recipient */ | |
279 | SmtpPhase = "RCPT"; | |
280 | setproctitle("%s %s: %s", CurEnv->e_id, | |
281 | CurHostName, inp); | |
282 | if (setjmp(TopFrame) > 0) | |
283 | { | |
284 | CurEnv->e_flags &= ~EF_FATALERRS; | |
285 | break; | |
286 | } | |
287 | QuickAbort = TRUE; | |
288 | p = skipword(p, "to"); | |
289 | if (p == NULL) | |
290 | break; | |
291 | a = parseaddr(p, (ADDRESS *) NULL, 1, '\0'); | |
292 | if (a == NULL) | |
293 | break; | |
294 | a->q_flags |= QPRIMARY; | |
295 | a = recipient(a, &CurEnv->e_sendqueue); | |
296 | if (Errors != 0) | |
297 | break; | |
298 | ||
299 | /* no errors during parsing, but might be a duplicate */ | |
300 | CurEnv->e_to = p; | |
301 | if (!bitset(QBADADDR, a->q_flags)) | |
302 | message("250", "Recipient ok"); | |
303 | else | |
304 | { | |
305 | /* punt -- should keep message in ADDRESS.... */ | |
306 | message("550", "Addressee unknown"); | |
307 | } | |
308 | CurEnv->e_to = NULL; | |
309 | break; | |
310 | ||
311 | case CMDDATA: /* data -- text of mail */ | |
312 | SmtpPhase = "DATA"; | |
313 | if (!hasmail) | |
314 | { | |
315 | message("503", "Need MAIL command"); | |
316 | break; | |
317 | } | |
318 | else if (CurEnv->e_nrcpts <= 0) | |
319 | { | |
320 | message("503", "Need RCPT (recipient)"); | |
321 | break; | |
322 | } | |
323 | ||
324 | /* collect the text of the message */ | |
325 | SmtpPhase = "collect"; | |
326 | setproctitle("%s %s: %s", CurEnv->e_id, | |
327 | CurHostName, inp); | |
328 | collect(TRUE); | |
329 | if (Errors != 0) | |
330 | break; | |
331 | ||
332 | /* | |
333 | ** Arrange to send to everyone. | |
334 | ** If sending to multiple people, mail back | |
335 | ** errors rather than reporting directly. | |
336 | ** In any case, don't mail back errors for | |
337 | ** anything that has happened up to | |
338 | ** now (the other end will do this). | |
339 | ** Truncate our transcript -- the mail has gotten | |
340 | ** to us successfully, and if we have | |
341 | ** to mail this back, it will be easier | |
342 | ** on the reader. | |
343 | ** Then send to everyone. | |
344 | ** Finally give a reply code. If an error has | |
345 | ** already been given, don't mail a | |
346 | ** message back. | |
347 | ** We goose error returns by clearing error bit. | |
348 | */ | |
349 | ||
350 | SmtpPhase = "delivery"; | |
351 | if (CurEnv->e_nrcpts != 1) | |
352 | { | |
353 | HoldErrs = TRUE; | |
354 | ErrorMode = EM_MAIL; | |
355 | } | |
356 | CurEnv->e_flags &= ~EF_FATALERRS; | |
357 | CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); | |
358 | ||
359 | /* send to all recipients */ | |
360 | sendall(CurEnv, SM_DEFAULT); | |
361 | CurEnv->e_to = NULL; | |
362 | ||
363 | /* save statistics */ | |
364 | markstats(CurEnv, (ADDRESS *) NULL); | |
365 | ||
366 | /* issue success if appropriate and reset */ | |
367 | if (Errors == 0 || HoldErrs) | |
368 | message("250", "Ok"); | |
369 | else | |
370 | CurEnv->e_flags &= ~EF_FATALERRS; | |
371 | ||
372 | /* if in a child, pop back to our parent */ | |
373 | if (InChild) | |
374 | finis(); | |
375 | ||
376 | /* clean up a bit */ | |
377 | hasmail = 0; | |
378 | dropenvelope(CurEnv); | |
379 | CurEnv = newenvelope(CurEnv); | |
380 | CurEnv->e_flags = BlankEnvelope.e_flags; | |
381 | break; | |
382 | ||
383 | case CMDRSET: /* rset -- reset state */ | |
384 | message("250", "Reset state"); | |
385 | if (InChild) | |
386 | finis(); | |
387 | break; | |
388 | ||
389 | case CMDVRFY: /* vrfy -- verify address */ | |
390 | if (runinchild("SMTP-VRFY") > 0) | |
391 | break; | |
392 | setproctitle("%s: %s", CurHostName, inp); | |
393 | vrfyqueue = NULL; | |
394 | QuickAbort = TRUE; | |
395 | sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); | |
396 | if (Errors != 0) | |
397 | { | |
398 | if (InChild) | |
399 | finis(); | |
400 | break; | |
401 | } | |
402 | while (vrfyqueue != NULL) | |
403 | { | |
404 | register ADDRESS *a = vrfyqueue->q_next; | |
405 | char *code; | |
406 | ||
407 | while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) | |
408 | a = a->q_next; | |
409 | ||
410 | if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) | |
411 | { | |
412 | if (a != NULL) | |
413 | code = "250-"; | |
414 | else | |
415 | code = "250"; | |
416 | if (vrfyqueue->q_fullname == NULL) | |
417 | message(code, "<%s>", vrfyqueue->q_paddr); | |
418 | else | |
419 | message(code, "%s <%s>", | |
420 | vrfyqueue->q_fullname, vrfyqueue->q_paddr); | |
421 | } | |
422 | else if (a == NULL) | |
423 | message("554", "Self destructive alias loop"); | |
424 | vrfyqueue = a; | |
425 | } | |
426 | if (InChild) | |
427 | finis(); | |
428 | break; | |
429 | ||
430 | case CMDHELP: /* help -- give user info */ | |
431 | help(p); | |
432 | break; | |
433 | ||
434 | case CMDNOOP: /* noop -- do nothing */ | |
435 | message("200", "OK"); | |
436 | break; | |
437 | ||
438 | case CMDQUIT: /* quit -- leave mail */ | |
439 | message("221", "%s closing connection", MyHostName); | |
440 | if (InChild) | |
441 | ExitStat = EX_QUIT; | |
442 | finis(); | |
443 | ||
444 | case CMDVERB: /* set verbose mode */ | |
445 | Verbose = TRUE; | |
446 | SendMode = SM_DELIVER; | |
447 | message("200", "Verbose mode"); | |
448 | break; | |
449 | ||
450 | case CMDONEX: /* doing one transaction only */ | |
451 | OneXact = TRUE; | |
452 | message("200", "Only one transaction"); | |
453 | break; | |
454 | ||
455 | # ifdef SMTPDEBUG | |
456 | case CMDDBGQSHOW: /* show queues */ | |
457 | printf("Send Queue="); | |
458 | printaddr(CurEnv->e_sendqueue, TRUE); | |
459 | break; | |
460 | ||
461 | case CMDDBGDEBUG: /* set debug mode */ | |
462 | tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); | |
463 | tTflag(p); | |
464 | message("200", "Debug set"); | |
465 | break; | |
466 | ||
467 | # else /* not SMTPDEBUG */ | |
468 | ||
469 | case CMDDBGQSHOW: /* show queues */ | |
470 | case CMDDBGDEBUG: /* set debug mode */ | |
471 | # ifdef LOG | |
472 | if (RealHostName != NULL && LogLevel > 0) | |
473 | syslog(LOG_NOTICE, | |
474 | "\"%s\" command from %s (%s)\n", | |
475 | c->cmdname, RealHostName, | |
476 | inet_ntoa(RealHostAddr.sin_addr)); | |
477 | # endif | |
478 | /* FALL THROUGH */ | |
479 | # endif /* SMTPDEBUG */ | |
480 | ||
481 | case CMDERROR: /* unknown command */ | |
482 | message("500", "Command unrecognized"); | |
483 | break; | |
484 | ||
485 | default: | |
486 | errno = 0; | |
487 | syserr("smtp: unknown code %d", c->cmdcode); | |
488 | break; | |
489 | } | |
490 | } | |
491 | } | |
492 | \f/* | |
493 | ** SKIPWORD -- skip a fixed word. | |
494 | ** | |
495 | ** Parameters: | |
496 | ** p -- place to start looking. | |
497 | ** w -- word to skip. | |
498 | ** | |
499 | ** Returns: | |
500 | ** p following w. | |
501 | ** NULL on error. | |
502 | ** | |
503 | ** Side Effects: | |
504 | ** clobbers the p data area. | |
505 | */ | |
506 | ||
507 | static char * | |
508 | skipword(p, w) | |
509 | register char *p; | |
510 | char *w; | |
511 | { | |
512 | register char *q; | |
513 | ||
514 | /* find beginning of word */ | |
515 | while (isspace(*p)) | |
516 | p++; | |
517 | q = p; | |
518 | ||
519 | /* find end of word */ | |
520 | while (*p != '\0' && *p != ':' && !isspace(*p)) | |
521 | p++; | |
522 | while (isspace(*p)) | |
523 | *p++ = '\0'; | |
524 | if (*p != ':') | |
525 | { | |
526 | syntax: | |
527 | message("501", "Syntax error"); | |
528 | Errors++; | |
529 | return (NULL); | |
530 | } | |
531 | *p++ = '\0'; | |
532 | while (isspace(*p)) | |
533 | p++; | |
534 | ||
535 | /* see if the input word matches desired word */ | |
536 | if (strcasecmp(q, w)) | |
537 | goto syntax; | |
538 | ||
539 | return (p); | |
540 | } | |
541 | \f/* | |
542 | ** HELP -- implement the HELP command. | |
543 | ** | |
544 | ** Parameters: | |
545 | ** topic -- the topic we want help for. | |
546 | ** | |
547 | ** Returns: | |
548 | ** none. | |
549 | ** | |
550 | ** Side Effects: | |
551 | ** outputs the help file to message output. | |
552 | */ | |
553 | ||
554 | help(topic) | |
555 | char *topic; | |
556 | { | |
557 | register FILE *hf; | |
558 | int len; | |
559 | char buf[MAXLINE]; | |
560 | bool noinfo; | |
561 | ||
562 | if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) | |
563 | { | |
564 | /* no help */ | |
565 | errno = 0; | |
566 | message("502", "HELP not implemented"); | |
567 | return; | |
568 | } | |
569 | ||
570 | if (topic == NULL || *topic == '\0') | |
571 | topic = "smtp"; | |
572 | else | |
573 | makelower(topic); | |
574 | ||
575 | len = strlen(topic); | |
576 | noinfo = TRUE; | |
577 | ||
578 | while (fgets(buf, sizeof buf, hf) != NULL) | |
579 | { | |
580 | if (strncmp(buf, topic, len) == 0) | |
581 | { | |
582 | register char *p; | |
583 | ||
584 | p = index(buf, '\t'); | |
585 | if (p == NULL) | |
586 | p = buf; | |
587 | else | |
588 | p++; | |
589 | fixcrlf(p, TRUE); | |
590 | message("214-", p); | |
591 | noinfo = FALSE; | |
592 | } | |
593 | } | |
594 | ||
595 | if (noinfo) | |
596 | message("504", "HELP topic unknown"); | |
597 | else | |
598 | message("214", "End of HELP info"); | |
599 | (void) fclose(hf); | |
600 | } | |
601 | \f/* | |
602 | ** RUNINCHILD -- return twice -- once in the child, then in the parent again | |
603 | ** | |
604 | ** Parameters: | |
605 | ** label -- a string used in error messages | |
606 | ** | |
607 | ** Returns: | |
608 | ** zero in the child | |
609 | ** one in the parent | |
610 | ** | |
611 | ** Side Effects: | |
612 | ** none. | |
613 | */ | |
614 | ||
615 | runinchild(label) | |
616 | char *label; | |
617 | { | |
618 | int childpid; | |
619 | ||
620 | if (!OneXact) | |
621 | { | |
622 | childpid = dofork(); | |
623 | if (childpid < 0) | |
624 | { | |
625 | syserr("%s: cannot fork", label); | |
626 | return (1); | |
627 | } | |
628 | if (childpid > 0) | |
629 | { | |
630 | auto int st; | |
631 | ||
632 | /* parent -- wait for child to complete */ | |
633 | st = waitfor(childpid); | |
634 | if (st == -1) | |
635 | syserr("%s: lost child", label); | |
636 | ||
637 | /* if we exited on a QUIT command, complete the process */ | |
638 | if (st == (EX_QUIT << 8)) | |
639 | finis(); | |
640 | ||
641 | return (1); | |
642 | } | |
643 | else | |
644 | { | |
645 | /* child */ | |
646 | InChild = TRUE; | |
647 | QuickAbort = FALSE; | |
648 | clearenvelope(CurEnv, FALSE); | |
649 | } | |
650 | } | |
651 | ||
652 | /* open alias database */ | |
653 | initaliases(AliasFile, FALSE); | |
654 | ||
655 | return (0); | |
656 | } | |
657 | ||
658 | # endif SMTP |