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