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