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