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