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