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