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