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