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.6 (Berkeley) 07/19/93 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)srvrsmtp.c 8.6 (Berkeley) 07/19/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 static char *skipword(); 94 95 smtp(e) 96 register ENVELOPE *e; 97 { 98 register char *p; 99 register struct cmd *c; 100 char *cmd; 101 auto ADDRESS *vrfyqueue; 102 ADDRESS *a; 103 bool gotmail; /* mail command received */ 104 bool gothello; /* helo command received */ 105 bool vrfy; /* set if this is a vrfy command */ 106 char *protocol; /* sending protocol */ 107 char *sendinghost; /* sending hostname */ 108 long msize; /* approximate maximum message size */ 109 auto char *delimptr; 110 char *id; 111 int nrcpts; /* number of RCPT commands */ 112 bool doublequeue; 113 char inp[MAXLINE]; 114 char cmdbuf[MAXLINE]; 115 extern char Version[]; 116 extern ENVELOPE BlankEnvelope; 117 118 if (fileno(OutChannel) != fileno(stdout)) 119 { 120 /* arrange for debugging output to go to remote host */ 121 (void) dup2(fileno(OutChannel), fileno(stdout)); 122 } 123 settime(e); 124 CurHostName = RealHostName; 125 setproctitle("server %s startup", CurHostName); 126 expand("\201e", inp, &inp[sizeof inp], e); 127 message("220-%s", inp); 128 message("220 ESMTP spoken here"); 129 protocol = NULL; 130 sendinghost = macvalue('s', e); 131 gothello = FALSE; 132 gotmail = FALSE; 133 for (;;) 134 { 135 /* arrange for backout */ 136 if (setjmp(TopFrame) > 0 && InChild) 137 { 138 QuickAbort = FALSE; 139 SuprErrs = TRUE; 140 finis(); 141 } 142 QuickAbort = FALSE; 143 HoldErrs = FALSE; 144 LogUsrErrs = FALSE; 145 e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 146 147 /* setup for the read */ 148 e->e_to = NULL; 149 Errors = 0; 150 (void) fflush(stdout); 151 152 /* read the input line */ 153 SmtpPhase = "server cmd read"; 154 setproctitle("server %s cmd read", CurHostName); 155 p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 156 SmtpPhase); 157 158 /* handle errors */ 159 if (p == NULL) 160 { 161 /* end of file, just die */ 162 message("421 %s Lost input channel from %s", 163 MyHostName, CurHostName); 164 #ifdef LOG 165 if (LogLevel > (gotmail ? 1 : 19)) 166 syslog(LOG_NOTICE, "lost input channel from %s", 167 CurHostName); 168 #endif 169 if (InChild) 170 ExitStat = EX_QUIT; 171 finis(); 172 } 173 174 /* clean up end of line */ 175 fixcrlf(inp, TRUE); 176 177 /* echo command to transcript */ 178 if (e->e_xfp != NULL) 179 fprintf(e->e_xfp, "<<< %s\n", inp); 180 181 if (e->e_id == NULL) 182 setproctitle("%s: %s", CurHostName, inp); 183 else 184 setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 185 186 /* break off command */ 187 for (p = inp; isascii(*p) && isspace(*p); p++) 188 continue; 189 cmd = cmdbuf; 190 while (*p != '\0' && 191 !(isascii(*p) && isspace(*p)) && 192 cmd < &cmdbuf[sizeof cmdbuf - 2]) 193 *cmd++ = *p++; 194 *cmd = '\0'; 195 196 /* throw away leading whitespace */ 197 while (isascii(*p) && isspace(*p)) 198 p++; 199 200 /* decode command */ 201 for (c = CmdTab; c->cmdname != NULL; c++) 202 { 203 if (!strcasecmp(c->cmdname, cmdbuf)) 204 break; 205 } 206 207 /* reset errors */ 208 errno = 0; 209 210 /* process command */ 211 switch (c->cmdcode) 212 { 213 case CMDHELO: /* hello -- introduce yourself */ 214 case CMDEHLO: /* extended hello */ 215 if (c->cmdcode == CMDEHLO) 216 { 217 protocol = "ESMTP"; 218 SmtpPhase = "server EHLO"; 219 } 220 else 221 { 222 protocol = "SMTP"; 223 SmtpPhase = "server HELO"; 224 } 225 sendinghost = newstr(p); 226 if (strcasecmp(p, RealHostName) != 0) 227 { 228 auth_warning(e, "Host %s claimed to be %s", 229 RealHostName, p); 230 } 231 p = macvalue('_', e); 232 if (p == NULL) 233 p = RealHostName; 234 235 gothello = TRUE; 236 if (c->cmdcode != CMDEHLO) 237 { 238 /* print old message and be done with it */ 239 message("250 %s Hello %s, pleased to meet you", 240 MyHostName, p); 241 break; 242 } 243 244 /* print extended message and brag */ 245 message("250-%s Hello %s, pleased to meet you", 246 MyHostName, p); 247 if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 248 message("250-EXPN"); 249 if (MaxMessageSize > 0) 250 message("250-SIZE %ld", MaxMessageSize); 251 else 252 message("250-SIZE"); 253 message("250 HELP"); 254 break; 255 256 case CMDMAIL: /* mail -- designate sender */ 257 SmtpPhase = "server MAIL"; 258 259 /* check for validity of this command */ 260 if (!gothello) 261 { 262 /* set sending host to our known value */ 263 if (sendinghost == NULL) 264 sendinghost = RealHostName; 265 266 if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 267 { 268 message("503 Polite people say HELO first"); 269 break; 270 } 271 } 272 if (gotmail) 273 { 274 message("503 Sender already specified"); 275 if (InChild) 276 finis(); 277 break; 278 } 279 if (InChild) 280 { 281 errno = 0; 282 syserr("503 Nested MAIL command: MAIL %s", p); 283 finis(); 284 } 285 286 /* fork a subprocess to process this command */ 287 if (runinchild("SMTP-MAIL", e) > 0) 288 break; 289 if (!gothello) 290 { 291 auth_warning(e, 292 "Host %s didn't use HELO protocol", 293 RealHostName); 294 } 295 if (protocol == NULL) 296 protocol = "SMTP"; 297 define('r', protocol, e); 298 define('s', sendinghost, e); 299 initsys(e); 300 nrcpts = 0; 301 setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 302 303 /* child -- go do the processing */ 304 p = skipword(p, "from"); 305 if (p == NULL) 306 break; 307 if (setjmp(TopFrame) > 0) 308 { 309 /* this failed -- undo work */ 310 if (InChild) 311 { 312 QuickAbort = FALSE; 313 SuprErrs = TRUE; 314 e->e_flags &= ~EF_FATALERRS; 315 finis(); 316 } 317 break; 318 } 319 QuickAbort = TRUE; 320 321 /* must parse sender first */ 322 delimptr = NULL; 323 setsender(p, e, &delimptr, FALSE); 324 p = delimptr; 325 if (p != NULL && *p != '\0') 326 *p++ = '\0'; 327 328 /* now parse ESMTP arguments */ 329 msize = 0; 330 for (; p != NULL && *p != '\0'; p++) 331 { 332 char *kp; 333 char *vp; 334 335 /* locate the beginning of the keyword */ 336 while (isascii(*p) && isspace(*p)) 337 p++; 338 if (*p == '\0') 339 break; 340 kp = p; 341 342 /* skip to the value portion */ 343 while (isascii(*p) && isalnum(*p) || *p == '-') 344 p++; 345 if (*p == '=') 346 { 347 *p++ = '\0'; 348 vp = p; 349 350 /* skip to the end of the value */ 351 while (*p != '\0' && *p != ' ' && 352 !(isascii(*p) && iscntrl(*p)) && 353 *p != '=') 354 p++; 355 } 356 357 if (*p != '\0') 358 *p++ = '\0'; 359 360 if (tTd(19, 1)) 361 printf("MAIL: got arg %s=%s\n", kp, 362 vp == NULL ? "<null>" : vp); 363 364 if (strcasecmp(kp, "size") == 0) 365 { 366 if (vp == NULL) 367 { 368 usrerr("501 SIZE requires a value"); 369 /* NOTREACHED */ 370 } 371 msize = atol(vp); 372 } 373 else if (strcasecmp(kp, "body") == 0) 374 { 375 if (vp == NULL) 376 { 377 usrerr("501 BODY requires a value"); 378 /* NOTREACHED */ 379 } 380 # ifdef MIME 381 if (strcasecmp(vp, "8bitmime") == 0) 382 { 383 e->e_bodytype = "8BITMIME"; 384 SevenBit = FALSE; 385 } 386 else if (strcasecmp(vp, "7bit") == 0) 387 { 388 e->e_bodytype = "7BIT"; 389 SevenBit = TRUE; 390 } 391 else 392 { 393 usrerr("501 Unknown BODY type %s", 394 vp); 395 } 396 # endif 397 } 398 else 399 { 400 usrerr("501 %s parameter unrecognized", kp); 401 /* NOTREACHED */ 402 } 403 } 404 405 if (MaxMessageSize > 0 && msize > MaxMessageSize) 406 { 407 usrerr("552 Message size exceeds fixed maximum message size (%ld)", 408 MaxMessageSize); 409 /* NOTREACHED */ 410 } 411 412 if (!enoughspace(msize)) 413 { 414 message("452 Insufficient disk space; try again later"); 415 break; 416 } 417 message("250 Sender ok"); 418 gotmail = TRUE; 419 break; 420 421 case CMDRCPT: /* rcpt -- designate recipient */ 422 if (!gotmail) 423 { 424 usrerr("503 Need MAIL before RCPT"); 425 break; 426 } 427 SmtpPhase = "server RCPT"; 428 if (setjmp(TopFrame) > 0) 429 { 430 e->e_flags &= ~EF_FATALERRS; 431 break; 432 } 433 QuickAbort = TRUE; 434 LogUsrErrs = TRUE; 435 436 if (e->e_sendmode != SM_DELIVER) 437 e->e_flags |= EF_VRFYONLY; 438 439 p = skipword(p, "to"); 440 if (p == NULL) 441 break; 442 a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); 443 if (a == NULL) 444 break; 445 a->q_flags |= QPRIMARY; 446 a = recipient(a, &e->e_sendqueue, e); 447 if (Errors != 0) 448 break; 449 450 /* no errors during parsing, but might be a duplicate */ 451 e->e_to = p; 452 if (!bitset(QBADADDR, a->q_flags)) 453 { 454 message("250 Recipient ok"); 455 nrcpts++; 456 } 457 else 458 { 459 /* punt -- should keep message in ADDRESS.... */ 460 message("550 Addressee unknown"); 461 } 462 e->e_to = NULL; 463 break; 464 465 case CMDDATA: /* data -- text of mail */ 466 SmtpPhase = "server DATA"; 467 if (!gotmail) 468 { 469 message("503 Need MAIL command"); 470 break; 471 } 472 else if (e->e_nrcpts <= 0) 473 { 474 message("503 Need RCPT (recipient)"); 475 break; 476 } 477 478 /* check to see if we need to re-expand aliases */ 479 /* also reset QBADADDR on already-diagnosted addrs */ 480 doublequeue = FALSE; 481 for (a = e->e_sendqueue; a != NULL; a = a->q_next) 482 { 483 if (bitset(QVERIFIED, a->q_flags)) 484 { 485 /* need to re-expand aliases */ 486 doublequeue = TRUE; 487 } 488 if (bitset(QBADADDR, a->q_flags)) 489 { 490 /* make this "go away" */ 491 a->q_flags |= QDONTSEND; 492 a->q_flags &= ~QBADADDR; 493 } 494 } 495 496 /* collect the text of the message */ 497 SmtpPhase = "collect"; 498 SuprErrs = TRUE; 499 collect(TRUE, doublequeue, e); 500 if (Errors != 0) 501 goto abortmessage; 502 503 /* 504 ** Arrange to send to everyone. 505 ** If sending to multiple people, mail back 506 ** errors rather than reporting directly. 507 ** In any case, don't mail back errors for 508 ** anything that has happened up to 509 ** now (the other end will do this). 510 ** Truncate our transcript -- the mail has gotten 511 ** to us successfully, and if we have 512 ** to mail this back, it will be easier 513 ** on the reader. 514 ** Then send to everyone. 515 ** Finally give a reply code. If an error has 516 ** already been given, don't mail a 517 ** message back. 518 ** We goose error returns by clearing error bit. 519 */ 520 521 SmtpPhase = "delivery"; 522 if (nrcpts != 1 && !doublequeue) 523 { 524 HoldErrs = TRUE; 525 e->e_errormode = EM_MAIL; 526 } 527 e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 528 id = e->e_id; 529 530 /* send to all recipients */ 531 sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 532 e->e_to = NULL; 533 SuprErrs = FALSE; 534 535 /* save statistics */ 536 markstats(e, (ADDRESS *) NULL); 537 538 /* issue success if appropriate and reset */ 539 if (Errors == 0 || HoldErrs) 540 message("250 %s Message accepted for delivery", id); 541 542 if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 543 { 544 /* avoid sending back an extra message */ 545 e->e_flags &= ~EF_FATALERRS; 546 e->e_flags |= EF_CLRQUEUE; 547 } 548 else 549 { 550 /* from now on, we have to operate silently */ 551 HoldErrs = TRUE; 552 e->e_errormode = EM_MAIL; 553 554 /* if we just queued, poke it */ 555 if (doublequeue && e->e_sendmode != SM_QUEUE) 556 { 557 unlockqueue(e); 558 dowork(id, TRUE, TRUE, e); 559 e->e_id = NULL; 560 } 561 } 562 563 abortmessage: 564 /* if in a child, pop back to our parent */ 565 if (InChild) 566 finis(); 567 568 /* clean up a bit */ 569 gotmail = FALSE; 570 dropenvelope(e); 571 CurEnv = e = newenvelope(e, CurEnv); 572 e->e_flags = BlankEnvelope.e_flags; 573 break; 574 575 case CMDRSET: /* rset -- reset state */ 576 message("250 Reset state"); 577 if (InChild) 578 finis(); 579 580 /* clean up a bit */ 581 gotmail = FALSE; 582 dropenvelope(e); 583 CurEnv = e = newenvelope(e, CurEnv); 584 break; 585 586 case CMDVRFY: /* vrfy -- verify address */ 587 case CMDEXPN: /* expn -- expand address */ 588 vrfy = c->cmdcode == CMDVRFY; 589 if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 590 PrivacyFlags)) 591 { 592 if (vrfy) 593 message("252 Who's to say?"); 594 else 595 message("502 That's none of your business"); 596 break; 597 } 598 else if (!gothello && 599 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 600 PrivacyFlags)) 601 { 602 message("503 I demand that you introduce yourself first"); 603 break; 604 } 605 if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 606 break; 607 #ifdef LOG 608 if (LogLevel > 5) 609 syslog(LOG_INFO, "%s: %s", CurHostName, inp); 610 #endif 611 vrfyqueue = NULL; 612 QuickAbort = TRUE; 613 if (vrfy) 614 e->e_flags |= EF_VRFYONLY; 615 while (*p != '\0' && isascii(*p) && isspace(*p)) 616 *p++; 617 if (*p == '\0') 618 { 619 message("501 Argument required"); 620 Errors++; 621 } 622 else 623 { 624 (void) sendtolist(p, (ADDRESS *) NULL, 625 &vrfyqueue, e); 626 } 627 if (Errors != 0) 628 { 629 if (InChild) 630 finis(); 631 break; 632 } 633 if (vrfyqueue == NULL) 634 { 635 message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 636 } 637 while (vrfyqueue != NULL) 638 { 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