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