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