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