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