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