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