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