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.9 (Berkeley) 07/26/93 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)srvrsmtp.c 8.9 (Berkeley) 07/26/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 /* 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 a = vrfyqueue; 636 while ((a = a->q_next) != NULL && 637 bitset(QDONTSEND|QBADADDR, a->q_flags)) 638 continue; 639 if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 640 printvrfyaddr(vrfyqueue, a == NULL); 641 vrfyqueue = vrfyqueue->q_next; 642 } 643 if (InChild) 644 finis(); 645 break; 646 647 case CMDHELP: /* help -- give user info */ 648 help(p); 649 break; 650 651 case CMDNOOP: /* noop -- do nothing */ 652 message("200 OK"); 653 break; 654 655 case CMDQUIT: /* quit -- leave mail */ 656 message("221 %s closing connection", MyHostName); 657 658 /* avoid future 050 messages */ 659 Verbose = FALSE; 660 661 if (InChild) 662 ExitStat = EX_QUIT; 663 finis(); 664 665 case CMDVERB: /* set verbose mode */ 666 if (bitset(PRIV_NOEXPN, PrivacyFlags)) 667 { 668 /* this would give out the same info */ 669 message("502 Verbose unavailable"); 670 break; 671 } 672 Verbose = TRUE; 673 e->e_sendmode = SM_DELIVER; 674 message("250 Verbose mode"); 675 break; 676 677 case CMDONEX: /* doing one transaction only */ 678 OneXact = TRUE; 679 message("250 Only one transaction"); 680 break; 681 682 # ifdef SMTPDEBUG 683 case CMDDBGQSHOW: /* show queues */ 684 printf("Send Queue="); 685 printaddr(e->e_sendqueue, TRUE); 686 break; 687 688 case CMDDBGDEBUG: /* set debug mode */ 689 tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 690 tTflag(p); 691 message("200 Debug set"); 692 break; 693 694 # else /* not SMTPDEBUG */ 695 696 case CMDDBGQSHOW: /* show queues */ 697 case CMDDBGDEBUG: /* set debug mode */ 698 # ifdef LOG 699 if (LogLevel > 0) 700 syslog(LOG_NOTICE, 701 "\"%s\" command from %s (%s)", 702 c->cmdname, RealHostName, 703 anynet_ntoa(&RealHostAddr)); 704 # endif 705 /* FALL THROUGH */ 706 # endif /* SMTPDEBUG */ 707 708 case CMDERROR: /* unknown command */ 709 message("500 Command unrecognized"); 710 break; 711 712 default: 713 errno = 0; 714 syserr("500 smtp: unknown code %d", c->cmdcode); 715 break; 716 } 717 } 718 } 719 /* 720 ** SKIPWORD -- skip a fixed word. 721 ** 722 ** Parameters: 723 ** p -- place to start looking. 724 ** w -- word to skip. 725 ** 726 ** Returns: 727 ** p following w. 728 ** NULL on error. 729 ** 730 ** Side Effects: 731 ** clobbers the p data area. 732 */ 733 734 static char * 735 skipword(p, w) 736 register char *p; 737 char *w; 738 { 739 register char *q; 740 741 /* find beginning of word */ 742 while (isascii(*p) && isspace(*p)) 743 p++; 744 q = p; 745 746 /* find end of word */ 747 while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 748 p++; 749 while (isascii(*p) && isspace(*p)) 750 *p++ = '\0'; 751 if (*p != ':') 752 { 753 syntax: 754 message("501 Syntax error in parameters"); 755 Errors++; 756 return (NULL); 757 } 758 *p++ = '\0'; 759 while (isascii(*p) && isspace(*p)) 760 p++; 761 762 if (*p == '\0') 763 goto syntax; 764 765 /* see if the input word matches desired word */ 766 if (strcasecmp(q, w)) 767 goto syntax; 768 769 return (p); 770 } 771 /* 772 ** PRINTVRFYADDR -- print an entry in the verify queue 773 ** 774 ** Parameters: 775 ** a -- the address to print 776 ** last -- set if this is the last one. 777 ** 778 ** Returns: 779 ** none. 780 ** 781 ** Side Effects: 782 ** Prints the appropriate 250 codes. 783 */ 784 785 printvrfyaddr(a, last) 786 register ADDRESS *a; 787 bool last; 788 { 789 char fmtbuf[20]; 790 791 strcpy(fmtbuf, "250"); 792 fmtbuf[3] = last ? ' ' : '-'; 793 794 if (a->q_fullname == NULL) 795 { 796 if (strchr(a->q_user, '@') == NULL) 797 strcpy(&fmtbuf[4], "<%s@%s>"); 798 else 799 strcpy(&fmtbuf[4], "<%s>"); 800 message(fmtbuf, a->q_user, MyHostName); 801 } 802 else 803 { 804 if (strchr(a->q_user, '@') == NULL) 805 strcpy(&fmtbuf[4], "%s <%s@%s>"); 806 else 807 strcpy(&fmtbuf[4], "%s <%s>"); 808 message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 809 } 810 } 811 /* 812 ** HELP -- implement the HELP command. 813 ** 814 ** Parameters: 815 ** topic -- the topic we want help for. 816 ** 817 ** Returns: 818 ** none. 819 ** 820 ** Side Effects: 821 ** outputs the help file to message output. 822 */ 823 824 help(topic) 825 char *topic; 826 { 827 register FILE *hf; 828 int len; 829 char buf[MAXLINE]; 830 bool noinfo; 831 832 if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 833 { 834 /* no help */ 835 errno = 0; 836 message("502 HELP not implemented"); 837 return; 838 } 839 840 if (topic == NULL || *topic == '\0') 841 topic = "smtp"; 842 else 843 makelower(topic); 844 845 len = strlen(topic); 846 noinfo = TRUE; 847 848 while (fgets(buf, sizeof buf, hf) != NULL) 849 { 850 if (strncmp(buf, topic, len) == 0) 851 { 852 register char *p; 853 854 p = strchr(buf, '\t'); 855 if (p == NULL) 856 p = buf; 857 else 858 p++; 859 fixcrlf(p, TRUE); 860 message("214-%s", p); 861 noinfo = FALSE; 862 } 863 } 864 865 if (noinfo) 866 message("504 HELP topic unknown"); 867 else 868 message("214 End of HELP info"); 869 (void) fclose(hf); 870 } 871 /* 872 ** RUNINCHILD -- return twice -- once in the child, then in the parent again 873 ** 874 ** Parameters: 875 ** label -- a string used in error messages 876 ** 877 ** Returns: 878 ** zero in the child 879 ** one in the parent 880 ** 881 ** Side Effects: 882 ** none. 883 */ 884 885 runinchild(label, e) 886 char *label; 887 register ENVELOPE *e; 888 { 889 int childpid; 890 891 if (!OneXact) 892 { 893 childpid = dofork(); 894 if (childpid < 0) 895 { 896 syserr("%s: cannot fork", label); 897 return (1); 898 } 899 if (childpid > 0) 900 { 901 auto int st; 902 903 /* parent -- wait for child to complete */ 904 setproctitle("server %s child wait", CurHostName); 905 st = waitfor(childpid); 906 if (st == -1) 907 syserr("%s: lost child", label); 908 909 /* if we exited on a QUIT command, complete the process */ 910 if (st == (EX_QUIT << 8)) 911 finis(); 912 913 return (1); 914 } 915 else 916 { 917 /* child */ 918 InChild = TRUE; 919 QuickAbort = FALSE; 920 clearenvelope(e, FALSE); 921 } 922 } 923 924 /* open alias database */ 925 initmaps(FALSE, e); 926 927 return (0); 928 } 929 930 # endif /* SMTP */ 931