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