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