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