122712Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362532Sbostic * Copyright (c) 1988, 1993 462532Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822712Sdist 933731Sbostic # include "sendmail.h" 1022712Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*64685Seric static char sccsid[] = "@(#)srvrsmtp.c 8.16 (Berkeley) 09/30/93 (with SMTP)"; 1433731Sbostic #else 15*64685Seric static char sccsid[] = "@(#)srvrsmtp.c 8.16 (Berkeley) 09/30/93 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 204549Seric 2133731Sbostic # ifdef SMTP 224556Seric 234549Seric /* 244549Seric ** SMTP -- run the SMTP protocol. 254549Seric ** 264549Seric ** Parameters: 274549Seric ** none. 284549Seric ** 294549Seric ** Returns: 304549Seric ** never. 314549Seric ** 324549Seric ** Side Effects: 334549Seric ** Reads commands from the input channel and processes 344549Seric ** them. 354549Seric */ 364549Seric 374549Seric struct cmd 384549Seric { 394549Seric char *cmdname; /* command name */ 404549Seric int cmdcode; /* internal code, see below */ 414549Seric }; 424549Seric 434549Seric /* values for cmdcode */ 444549Seric # define CMDERROR 0 /* bad command */ 454549Seric # define CMDMAIL 1 /* mail -- designate sender */ 464976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 474549Seric # define CMDDATA 3 /* data -- send message text */ 489339Seric # define CMDRSET 4 /* rset -- reset state */ 499339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5058092Seric # define CMDEXPN 6 /* expn -- expand address */ 519339Seric # define CMDNOOP 7 /* noop -- do nothing */ 529339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 539339Seric # define CMDHELO 9 /* helo -- be polite */ 5458092Seric # define CMDHELP 10 /* help -- give usage info */ 5558323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5658092Seric /* non-standard commands */ 5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 59*64685Seric /* use this to catch and log "door handle" attempts on your system */ 60*64685Seric # define CMDLOGBOGUS 23 /* bogus command that should be logged */ 6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6258092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6358092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 644549Seric 654549Seric static struct cmd CmdTab[] = 664549Seric { 674549Seric "mail", CMDMAIL, 684976Seric "rcpt", CMDRCPT, 694549Seric "data", CMDDATA, 704549Seric "rset", CMDRSET, 714549Seric "vrfy", CMDVRFY, 7258092Seric "expn", CMDEXPN, 734549Seric "help", CMDHELP, 744549Seric "noop", CMDNOOP, 754549Seric "quit", CMDQUIT, 764976Seric "helo", CMDHELO, 7758323Seric "ehlo", CMDEHLO, 788544Seric "verb", CMDVERB, 799314Seric "onex", CMDONEX, 8036230Skarels /* 8136230Skarels * remaining commands are here only 8236230Skarels * to trap and log attempts to use them 8336230Skarels */ 849339Seric "showq", CMDDBGQSHOW, 858544Seric "debug", CMDDBGDEBUG, 86*64685Seric "wiz", CMDLOGBOGUS, 874549Seric NULL, CMDERROR, 884549Seric }; 894549Seric 909339Seric bool InChild = FALSE; /* true if running in a subprocess */ 919378Seric bool OneXact = FALSE; /* one xaction only this run */ 9211146Seric 939339Seric #define EX_QUIT 22 /* special code for QUIT command */ 948544Seric 9563937Seric static char *skipword(); 9663937Seric 9755012Seric smtp(e) 9855012Seric register ENVELOPE *e; 994549Seric { 1004549Seric register char *p; 1018544Seric register struct cmd *c; 1024549Seric char *cmd; 1035003Seric auto ADDRESS *vrfyqueue; 10412612Seric ADDRESS *a; 10558109Seric bool gotmail; /* mail command received */ 10658092Seric bool gothello; /* helo command received */ 10758092Seric bool vrfy; /* set if this is a vrfy command */ 10858323Seric char *protocol; /* sending protocol */ 10959016Seric char *sendinghost; /* sending hostname */ 11058333Seric long msize; /* approximate maximum message size */ 11158333Seric auto char *delimptr; 11258714Seric char *id; 11359747Seric int nrcpts; /* number of RCPT commands */ 11463787Seric bool doublequeue; 1158544Seric char inp[MAXLINE]; 11657232Seric char cmdbuf[MAXLINE]; 1177124Seric extern char Version[]; 11824943Seric extern ENVELOPE BlankEnvelope; 1194549Seric 12059066Seric if (fileno(OutChannel) != fileno(stdout)) 1217363Seric { 1227363Seric /* arrange for debugging output to go to remote host */ 12359066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1247363Seric } 12555012Seric settime(e); 12657642Seric CurHostName = RealHostName; 12761093Seric setproctitle("server %s startup", CurHostName); 12858050Seric expand("\201e", inp, &inp[sizeof inp], e); 12964496Seric if (BrokenSmtpPeers) 13064496Seric { 13164496Seric message("220 %s", inp); 13264496Seric } 13364496Seric else 13464496Seric { 13564496Seric message("220-%s", inp); 13664496Seric message("220 ESMTP spoken here"); 13764496Seric } 13858330Seric protocol = NULL; 13959016Seric sendinghost = macvalue('s', e); 14058082Seric gothello = FALSE; 14158330Seric gotmail = FALSE; 1424549Seric for (;;) 1434549Seric { 14412612Seric /* arrange for backout */ 14512612Seric if (setjmp(TopFrame) > 0 && InChild) 14659058Seric { 14759058Seric QuickAbort = FALSE; 14859058Seric SuprErrs = TRUE; 14912612Seric finis(); 15059058Seric } 15112612Seric QuickAbort = FALSE; 15212612Seric HoldErrs = FALSE; 15351951Seric LogUsrErrs = FALSE; 15463843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 15512612Seric 1567356Seric /* setup for the read */ 15755012Seric e->e_to = NULL; 1584577Seric Errors = 0; 1597275Seric (void) fflush(stdout); 1607356Seric 1617356Seric /* read the input line */ 16261093Seric SmtpPhase = "server cmd read"; 16361093Seric setproctitle("server %s cmd read", CurHostName); 16461093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 16561093Seric SmtpPhase); 1667356Seric 1677685Seric /* handle errors */ 1687356Seric if (p == NULL) 1697356Seric { 1704549Seric /* end of file, just die */ 17158151Seric message("421 %s Lost input channel from %s", 17225050Seric MyHostName, CurHostName); 17355464Seric #ifdef LOG 17463843Seric if (LogLevel > (gotmail ? 1 : 19)) 17555464Seric syslog(LOG_NOTICE, "lost input channel from %s", 17655464Seric CurHostName); 17755464Seric #endif 17858069Seric if (InChild) 17958069Seric ExitStat = EX_QUIT; 1804549Seric finis(); 1814549Seric } 1824549Seric 1834549Seric /* clean up end of line */ 1844558Seric fixcrlf(inp, TRUE); 1854549Seric 1864713Seric /* echo command to transcript */ 18755012Seric if (e->e_xfp != NULL) 18855012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1894713Seric 19059060Seric if (e->e_id == NULL) 19159060Seric setproctitle("%s: %s", CurHostName, inp); 19259060Seric else 19359060Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 19459060Seric 1954549Seric /* break off command */ 19658050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1974549Seric continue; 19857232Seric cmd = cmdbuf; 19958050Seric while (*p != '\0' && 20058050Seric !(isascii(*p) && isspace(*p)) && 20158050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 20224981Seric *cmd++ = *p++; 20324981Seric *cmd = '\0'; 2044549Seric 20525691Seric /* throw away leading whitespace */ 20658050Seric while (isascii(*p) && isspace(*p)) 20725691Seric p++; 20825691Seric 2094549Seric /* decode command */ 2104549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2114549Seric { 21233725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2134549Seric break; 2144549Seric } 2154549Seric 21651954Seric /* reset errors */ 21751954Seric errno = 0; 21851954Seric 2194549Seric /* process command */ 2204549Seric switch (c->cmdcode) 2214549Seric { 2224976Seric case CMDHELO: /* hello -- introduce yourself */ 22358323Seric case CMDEHLO: /* extended hello */ 22458323Seric if (c->cmdcode == CMDEHLO) 22558323Seric { 22658323Seric protocol = "ESMTP"; 22761093Seric SmtpPhase = "server EHLO"; 22858323Seric } 22958323Seric else 23058323Seric { 23158323Seric protocol = "SMTP"; 23261093Seric SmtpPhase = "server HELO"; 23358323Seric } 23459016Seric sendinghost = newstr(p); 23564284Seric if (strcasecmp(p, RealHostName) != 0 && 23664284Seric (strcasecmp(RealHostName, "localhost") != 0 || 23764284Seric strcasecmp(p, MyHostName) != 0)) 23811146Seric { 23958789Seric auth_warning(e, "Host %s claimed to be %s", 24058789Seric RealHostName, p); 24111146Seric } 24258957Seric p = macvalue('_', e); 24358957Seric if (p == NULL) 24459016Seric p = RealHostName; 24558323Seric 24660210Seric gothello = TRUE; 24760210Seric if (c->cmdcode != CMDEHLO) 24860239Seric { 24960239Seric /* print old message and be done with it */ 25060239Seric message("250 %s Hello %s, pleased to meet you", 25160239Seric MyHostName, p); 25260210Seric break; 25360239Seric } 25460239Seric 25560239Seric /* print extended message and brag */ 25660239Seric message("250-%s Hello %s, pleased to meet you", 25760239Seric MyHostName, p); 25858323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 25958323Seric message("250-EXPN"); 26064359Seric if (MaxMessageSize > 0) 26164359Seric message("250-SIZE %ld", MaxMessageSize); 26259271Seric else 26359271Seric message("250-SIZE"); 26458323Seric message("250 HELP"); 2654976Seric break; 2664976Seric 2674549Seric case CMDMAIL: /* mail -- designate sender */ 26861093Seric SmtpPhase = "server MAIL"; 26924943Seric 2709314Seric /* check for validity of this command */ 27158789Seric if (!gothello) 27258082Seric { 27358957Seric /* set sending host to our known value */ 27459016Seric if (sendinghost == NULL) 27559016Seric sendinghost = RealHostName; 27658957Seric 27758789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 27858821Seric { 27958789Seric message("503 Polite people say HELO first"); 28058821Seric break; 28158821Seric } 28258082Seric } 28358109Seric if (gotmail) 2844558Seric { 28558151Seric message("503 Sender already specified"); 28663843Seric if (InChild) 28763843Seric finis(); 2884558Seric break; 2894558Seric } 2909339Seric if (InChild) 2919339Seric { 29236230Skarels errno = 0; 29358151Seric syserr("503 Nested MAIL command: MAIL %s", p); 29458069Seric finis(); 2959339Seric } 2969339Seric 2979339Seric /* fork a subprocess to process this command */ 29855012Seric if (runinchild("SMTP-MAIL", e) > 0) 2999339Seric break; 30063753Seric if (!gothello) 30163753Seric { 30263753Seric auth_warning(e, 30363753Seric "Host %s didn't use HELO protocol", 30463753Seric RealHostName); 30563753Seric } 30658323Seric if (protocol == NULL) 30758323Seric protocol = "SMTP"; 30858323Seric define('r', protocol, e); 30959016Seric define('s', sendinghost, e); 31055012Seric initsys(e); 31159747Seric nrcpts = 0; 31257389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 3139339Seric 3149339Seric /* child -- go do the processing */ 3154549Seric p = skipword(p, "from"); 3164549Seric if (p == NULL) 3174549Seric break; 31857977Seric if (setjmp(TopFrame) > 0) 31958147Seric { 32058147Seric /* this failed -- undo work */ 32158147Seric if (InChild) 32259058Seric { 32359058Seric QuickAbort = FALSE; 32459058Seric SuprErrs = TRUE; 32563787Seric e->e_flags &= ~EF_FATALERRS; 32658147Seric finis(); 32759058Seric } 32857977Seric break; 32958147Seric } 33057977Seric QuickAbort = TRUE; 33158333Seric 33258333Seric /* must parse sender first */ 33358333Seric delimptr = NULL; 33458704Seric setsender(p, e, &delimptr, FALSE); 33558333Seric p = delimptr; 33658333Seric if (p != NULL && *p != '\0') 33758333Seric *p++ = '\0'; 33858333Seric 33958333Seric /* now parse ESMTP arguments */ 34058333Seric msize = 0; 34158333Seric for (; p != NULL && *p != '\0'; p++) 34258333Seric { 34358333Seric char *kp; 34458333Seric char *vp; 34558333Seric 34658333Seric /* locate the beginning of the keyword */ 34758333Seric while (isascii(*p) && isspace(*p)) 34858333Seric p++; 34958333Seric if (*p == '\0') 35058333Seric break; 35158333Seric kp = p; 35258333Seric 35358333Seric /* skip to the value portion */ 35458333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 35558333Seric p++; 35658333Seric if (*p == '=') 35758333Seric { 35858333Seric *p++ = '\0'; 35958333Seric vp = p; 36058333Seric 36158333Seric /* skip to the end of the value */ 36258333Seric while (*p != '\0' && *p != ' ' && 36358333Seric !(isascii(*p) && iscntrl(*p)) && 36458333Seric *p != '=') 36558333Seric p++; 36658333Seric } 36758333Seric 36858333Seric if (*p != '\0') 36958333Seric *p++ = '\0'; 37058333Seric 37158333Seric if (tTd(19, 1)) 37258333Seric printf("MAIL: got arg %s=%s\n", kp, 37358333Seric vp == NULL ? "<null>" : vp); 37458333Seric 37558333Seric if (strcasecmp(kp, "size") == 0) 37658333Seric { 37759093Seric if (vp == NULL) 37858333Seric { 37958333Seric usrerr("501 SIZE requires a value"); 38058333Seric /* NOTREACHED */ 38158333Seric } 38258333Seric msize = atol(vp); 38358333Seric } 38459093Seric else if (strcasecmp(kp, "body") == 0) 38559093Seric { 38659093Seric if (vp == NULL) 38759093Seric { 38859093Seric usrerr("501 BODY requires a value"); 38959093Seric /* NOTREACHED */ 39059093Seric } 39159093Seric # ifdef MIME 39259093Seric if (strcasecmp(vp, "8bitmime") == 0) 39359093Seric { 39459093Seric e->e_bodytype = "8BITMIME"; 39559709Seric SevenBit = FALSE; 39659093Seric } 39759093Seric else if (strcasecmp(vp, "7bit") == 0) 39859093Seric { 39959093Seric e->e_bodytype = "7BIT"; 40059709Seric SevenBit = TRUE; 40159093Seric } 40259093Seric else 40359093Seric { 40459093Seric usrerr("501 Unknown BODY type %s", 40559093Seric vp); 40659093Seric } 40759093Seric # endif 40859093Seric } 40958333Seric else 41058333Seric { 41158333Seric usrerr("501 %s parameter unrecognized", kp); 41258333Seric /* NOTREACHED */ 41358333Seric } 41458333Seric } 41559284Seric 41659284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 41759284Seric { 41859284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 41959284Seric MaxMessageSize); 42059284Seric /* NOTREACHED */ 42159284Seric } 42258333Seric 42358333Seric if (!enoughspace(msize)) 42458333Seric { 42558333Seric message("452 Insufficient disk space; try again later"); 42658333Seric break; 42758333Seric } 42858151Seric message("250 Sender ok"); 42958147Seric gotmail = TRUE; 4304549Seric break; 4314549Seric 4324976Seric case CMDRCPT: /* rcpt -- designate recipient */ 43358850Seric if (!gotmail) 43458850Seric { 43558850Seric usrerr("503 Need MAIL before RCPT"); 43658850Seric break; 43758850Seric } 43861093Seric SmtpPhase = "server RCPT"; 43912612Seric if (setjmp(TopFrame) > 0) 44014785Seric { 44155012Seric e->e_flags &= ~EF_FATALERRS; 44212612Seric break; 44314785Seric } 44412612Seric QuickAbort = TRUE; 44551951Seric LogUsrErrs = TRUE; 44658093Seric 44759699Seric if (e->e_sendmode != SM_DELIVER) 44859699Seric e->e_flags |= EF_VRFYONLY; 44958919Seric 4504549Seric p = skipword(p, "to"); 4514549Seric if (p == NULL) 4524549Seric break; 45364284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 45412612Seric if (a == NULL) 45512612Seric break; 45616886Seric a->q_flags |= QPRIMARY; 45755012Seric a = recipient(a, &e->e_sendqueue, e); 45812612Seric if (Errors != 0) 45912612Seric break; 46012612Seric 46112612Seric /* no errors during parsing, but might be a duplicate */ 46255012Seric e->e_to = p; 46312612Seric if (!bitset(QBADADDR, a->q_flags)) 46459747Seric { 46558151Seric message("250 Recipient ok"); 46659747Seric nrcpts++; 46759747Seric } 46812612Seric else 4694549Seric { 47012612Seric /* punt -- should keep message in ADDRESS.... */ 47158151Seric message("550 Addressee unknown"); 4724549Seric } 47355012Seric e->e_to = NULL; 4744549Seric break; 4754549Seric 4764549Seric case CMDDATA: /* data -- text of mail */ 47761093Seric SmtpPhase = "server DATA"; 47858109Seric if (!gotmail) 4794549Seric { 48058151Seric message("503 Need MAIL command"); 4814976Seric break; 4824549Seric } 48355012Seric else if (e->e_nrcpts <= 0) 4844549Seric { 48558151Seric message("503 Need RCPT (recipient)"); 4864976Seric break; 4874549Seric } 4884976Seric 48958929Seric /* check to see if we need to re-expand aliases */ 49063787Seric /* also reset QBADADDR on already-diagnosted addrs */ 49163787Seric doublequeue = FALSE; 49258929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 49358929Seric { 49458929Seric if (bitset(QVERIFIED, a->q_flags)) 49563787Seric { 49663787Seric /* need to re-expand aliases */ 49763787Seric doublequeue = TRUE; 49863787Seric } 49963787Seric if (bitset(QBADADDR, a->q_flags)) 50063787Seric { 50163787Seric /* make this "go away" */ 50263787Seric a->q_flags |= QDONTSEND; 50363787Seric a->q_flags &= ~QBADADDR; 50463787Seric } 50558929Seric } 50658929Seric 5074976Seric /* collect the text of the message */ 50824943Seric SmtpPhase = "collect"; 50963965Seric HoldErrs = TRUE; 51063787Seric collect(TRUE, doublequeue, e); 5114976Seric 5128238Seric /* 5138238Seric ** Arrange to send to everyone. 5148238Seric ** If sending to multiple people, mail back 5158238Seric ** errors rather than reporting directly. 5168238Seric ** In any case, don't mail back errors for 5178238Seric ** anything that has happened up to 5188238Seric ** now (the other end will do this). 51910197Seric ** Truncate our transcript -- the mail has gotten 52010197Seric ** to us successfully, and if we have 52110197Seric ** to mail this back, it will be easier 52210197Seric ** on the reader. 5238238Seric ** Then send to everyone. 5248238Seric ** Finally give a reply code. If an error has 5258238Seric ** already been given, don't mail a 5268238Seric ** message back. 5279339Seric ** We goose error returns by clearing error bit. 5288238Seric */ 5298238Seric 53024943Seric SmtpPhase = "delivery"; 53163787Seric if (nrcpts != 1 && !doublequeue) 5329378Seric { 5339378Seric HoldErrs = TRUE; 53458734Seric e->e_errormode = EM_MAIL; 5359378Seric } 53655012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 53758714Seric id = e->e_id; 5384976Seric 5394976Seric /* send to all recipients */ 54063787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 54155012Seric e->e_to = NULL; 5424976Seric 5438238Seric /* issue success if appropriate and reset */ 5448238Seric if (Errors == 0 || HoldErrs) 54558855Seric message("250 %s Message accepted for delivery", id); 54659747Seric 54759747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 54859730Seric { 54959730Seric /* avoid sending back an extra message */ 55059730Seric e->e_flags &= ~EF_FATALERRS; 55159747Seric e->e_flags |= EF_CLRQUEUE; 55259730Seric } 5538238Seric else 55458919Seric { 55559747Seric /* from now on, we have to operate silently */ 55659747Seric HoldErrs = TRUE; 55759747Seric e->e_errormode = EM_MAIL; 55859747Seric 55959730Seric /* if we just queued, poke it */ 56063787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 56159730Seric { 56264296Seric extern pid_t dowork(); 56364296Seric 56459730Seric unlockqueue(e); 56564296Seric (void) dowork(id, TRUE, TRUE, e); 56659730Seric } 56758919Seric } 56858883Seric 56959747Seric abortmessage: 5709339Seric /* if in a child, pop back to our parent */ 5719339Seric if (InChild) 5729339Seric finis(); 57324943Seric 57424943Seric /* clean up a bit */ 57558109Seric gotmail = FALSE; 57655012Seric dropenvelope(e); 57758179Seric CurEnv = e = newenvelope(e, CurEnv); 57855012Seric e->e_flags = BlankEnvelope.e_flags; 5794549Seric break; 5804549Seric 5814549Seric case CMDRSET: /* rset -- reset state */ 58258151Seric message("250 Reset state"); 58364359Seric e->e_flags |= EF_CLRQUEUE; 5849339Seric if (InChild) 5859339Seric finis(); 58658109Seric 58758109Seric /* clean up a bit */ 58858109Seric gotmail = FALSE; 58958109Seric dropenvelope(e); 59058179Seric CurEnv = e = newenvelope(e, CurEnv); 5919339Seric break; 5924549Seric 5934549Seric case CMDVRFY: /* vrfy -- verify address */ 59458092Seric case CMDEXPN: /* expn -- expand address */ 59558092Seric vrfy = c->cmdcode == CMDVRFY; 59658092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 59758092Seric PrivacyFlags)) 59858082Seric { 59958412Seric if (vrfy) 60058412Seric message("252 Who's to say?"); 60158412Seric else 60258412Seric message("502 That's none of your business"); 60358082Seric break; 60458082Seric } 60558082Seric else if (!gothello && 60658092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 60758092Seric PrivacyFlags)) 60858082Seric { 60958151Seric message("503 I demand that you introduce yourself first"); 61058082Seric break; 61158082Seric } 61258092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6139339Seric break; 61455173Seric #ifdef LOG 61558020Seric if (LogLevel > 5) 61655173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 61755173Seric #endif 6185003Seric vrfyqueue = NULL; 6197762Seric QuickAbort = TRUE; 62058092Seric if (vrfy) 62158092Seric e->e_flags |= EF_VRFYONLY; 62262373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 62362373Seric *p++; 62462373Seric if (*p == '\0') 62562373Seric { 62662373Seric message("501 Argument required"); 62762373Seric Errors++; 62862373Seric } 62962373Seric else 63062373Seric { 63164284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 63262373Seric } 6337762Seric if (Errors != 0) 6349339Seric { 6359339Seric if (InChild) 6369339Seric finis(); 6377762Seric break; 6389339Seric } 63962373Seric if (vrfyqueue == NULL) 64062373Seric { 64162373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 64262373Seric } 6435003Seric while (vrfyqueue != NULL) 6445003Seric { 64563971Seric a = vrfyqueue; 64663971Seric while ((a = a->q_next) != NULL && 64763971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 64863971Seric continue; 6497685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 65058151Seric printvrfyaddr(vrfyqueue, a == NULL); 65163847Seric vrfyqueue = vrfyqueue->q_next; 6525003Seric } 6539339Seric if (InChild) 6549339Seric finis(); 6554549Seric break; 6564549Seric 6574549Seric case CMDHELP: /* help -- give user info */ 6584577Seric help(p); 6594549Seric break; 6604549Seric 6614549Seric case CMDNOOP: /* noop -- do nothing */ 66264122Seric message("250 OK"); 6634549Seric break; 6644549Seric 6654549Seric case CMDQUIT: /* quit -- leave mail */ 66658151Seric message("221 %s closing connection", MyHostName); 66761051Seric 66861051Seric /* avoid future 050 messages */ 66961051Seric Verbose = FALSE; 67061051Seric 6719339Seric if (InChild) 6729339Seric ExitStat = EX_QUIT; 6734549Seric finis(); 6744549Seric 6758544Seric case CMDVERB: /* set verbose mode */ 67659957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 67759957Seric { 67859957Seric /* this would give out the same info */ 67959957Seric message("502 Verbose unavailable"); 68059957Seric break; 68159957Seric } 6828544Seric Verbose = TRUE; 68358734Seric e->e_sendmode = SM_DELIVER; 68459957Seric message("250 Verbose mode"); 6858544Seric break; 6868544Seric 6879314Seric case CMDONEX: /* doing one transaction only */ 6889378Seric OneXact = TRUE; 68959957Seric message("250 Only one transaction"); 6909314Seric break; 6919314Seric 69236230Skarels # ifdef SMTPDEBUG 6939339Seric case CMDDBGQSHOW: /* show queues */ 6946907Seric printf("Send Queue="); 69555012Seric printaddr(e->e_sendqueue, TRUE); 6965003Seric break; 6977275Seric 6987275Seric case CMDDBGDEBUG: /* set debug mode */ 6997676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7007676Seric tTflag(p); 70158151Seric message("200 Debug set"); 7027275Seric break; 7037275Seric 70436230Skarels # else /* not SMTPDEBUG */ 70536230Skarels case CMDDBGQSHOW: /* show queues */ 70636230Skarels case CMDDBGDEBUG: /* set debug mode */ 707*64685Seric # endif /* SMTPDEBUG */ 708*64685Seric case CMDLOGBOGUS: /* bogus command */ 70936233Skarels # ifdef LOG 71058308Seric if (LogLevel > 0) 711*64685Seric syslog(LOG_CRIT, 71258020Seric "\"%s\" command from %s (%s)", 71336230Skarels c->cmdname, RealHostName, 71458755Seric anynet_ntoa(&RealHostAddr)); 71536233Skarels # endif 71636230Skarels /* FALL THROUGH */ 71736230Skarels 7184549Seric case CMDERROR: /* unknown command */ 71958151Seric message("500 Command unrecognized"); 7204549Seric break; 7214549Seric 7224549Seric default: 72336230Skarels errno = 0; 72458151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7254549Seric break; 7264549Seric } 7274549Seric } 7284549Seric } 7294549Seric /* 7304549Seric ** SKIPWORD -- skip a fixed word. 7314549Seric ** 7324549Seric ** Parameters: 7334549Seric ** p -- place to start looking. 7344549Seric ** w -- word to skip. 7354549Seric ** 7364549Seric ** Returns: 7374549Seric ** p following w. 7384549Seric ** NULL on error. 7394549Seric ** 7404549Seric ** Side Effects: 7414549Seric ** clobbers the p data area. 7424549Seric */ 7434549Seric 7444549Seric static char * 7454549Seric skipword(p, w) 7464549Seric register char *p; 7474549Seric char *w; 7484549Seric { 7494549Seric register char *q; 7504549Seric 7514549Seric /* find beginning of word */ 75258050Seric while (isascii(*p) && isspace(*p)) 7534549Seric p++; 7544549Seric q = p; 7554549Seric 7564549Seric /* find end of word */ 75758050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7584549Seric p++; 75958050Seric while (isascii(*p) && isspace(*p)) 7604549Seric *p++ = '\0'; 7614549Seric if (*p != ':') 7624549Seric { 7634549Seric syntax: 76462373Seric message("501 Syntax error in parameters"); 7654549Seric Errors++; 7664549Seric return (NULL); 7674549Seric } 7684549Seric *p++ = '\0'; 76958050Seric while (isascii(*p) && isspace(*p)) 7704549Seric p++; 7714549Seric 77262373Seric if (*p == '\0') 77362373Seric goto syntax; 77462373Seric 7754549Seric /* see if the input word matches desired word */ 77633725Sbostic if (strcasecmp(q, w)) 7774549Seric goto syntax; 7784549Seric 7794549Seric return (p); 7804549Seric } 7814577Seric /* 78258151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 78358151Seric ** 78458151Seric ** Parameters: 78558151Seric ** a -- the address to print 78658151Seric ** last -- set if this is the last one. 78758151Seric ** 78858151Seric ** Returns: 78958151Seric ** none. 79058151Seric ** 79158151Seric ** Side Effects: 79258151Seric ** Prints the appropriate 250 codes. 79358151Seric */ 79458151Seric 79558151Seric printvrfyaddr(a, last) 79658151Seric register ADDRESS *a; 79758151Seric bool last; 79858151Seric { 79958151Seric char fmtbuf[20]; 80058151Seric 80158151Seric strcpy(fmtbuf, "250"); 80258151Seric fmtbuf[3] = last ? ' ' : '-'; 80358151Seric 80459746Seric if (a->q_fullname == NULL) 80559746Seric { 80659746Seric if (strchr(a->q_user, '@') == NULL) 80759746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 80859746Seric else 80959746Seric strcpy(&fmtbuf[4], "<%s>"); 81059746Seric message(fmtbuf, a->q_user, MyHostName); 81159746Seric } 81258151Seric else 81358151Seric { 81459746Seric if (strchr(a->q_user, '@') == NULL) 81559746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 81659746Seric else 81759746Seric strcpy(&fmtbuf[4], "%s <%s>"); 81859746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 81958151Seric } 82058151Seric } 82158151Seric /* 8224577Seric ** HELP -- implement the HELP command. 8234577Seric ** 8244577Seric ** Parameters: 8254577Seric ** topic -- the topic we want help for. 8264577Seric ** 8274577Seric ** Returns: 8284577Seric ** none. 8294577Seric ** 8304577Seric ** Side Effects: 8314577Seric ** outputs the help file to message output. 8324577Seric */ 8334577Seric 8344577Seric help(topic) 8354577Seric char *topic; 8364577Seric { 8374577Seric register FILE *hf; 8384577Seric int len; 8394577Seric char buf[MAXLINE]; 8404577Seric bool noinfo; 8414577Seric 8428269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8434577Seric { 8444577Seric /* no help */ 84511931Seric errno = 0; 84658151Seric message("502 HELP not implemented"); 8474577Seric return; 8484577Seric } 8494577Seric 85049669Seric if (topic == NULL || *topic == '\0') 85149669Seric topic = "smtp"; 85249669Seric else 85349669Seric makelower(topic); 85449669Seric 8554577Seric len = strlen(topic); 8564577Seric noinfo = TRUE; 8574577Seric 8584577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8594577Seric { 8604577Seric if (strncmp(buf, topic, len) == 0) 8614577Seric { 8624577Seric register char *p; 8634577Seric 86456795Seric p = strchr(buf, '\t'); 8654577Seric if (p == NULL) 8664577Seric p = buf; 8674577Seric else 8684577Seric p++; 8694577Seric fixcrlf(p, TRUE); 87058151Seric message("214-%s", p); 8714577Seric noinfo = FALSE; 8724577Seric } 8734577Seric } 8744577Seric 8754577Seric if (noinfo) 87658151Seric message("504 HELP topic unknown"); 8774577Seric else 87858151Seric message("214 End of HELP info"); 8794628Seric (void) fclose(hf); 8804577Seric } 8818544Seric /* 8829339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8839339Seric ** 8849339Seric ** Parameters: 8859339Seric ** label -- a string used in error messages 8869339Seric ** 8879339Seric ** Returns: 8889339Seric ** zero in the child 8899339Seric ** one in the parent 8909339Seric ** 8919339Seric ** Side Effects: 8929339Seric ** none. 8939339Seric */ 8948544Seric 89555012Seric runinchild(label, e) 8969339Seric char *label; 89755012Seric register ENVELOPE *e; 8989339Seric { 8999339Seric int childpid; 9009339Seric 90116158Seric if (!OneXact) 9029339Seric { 90316158Seric childpid = dofork(); 90416158Seric if (childpid < 0) 90516158Seric { 90616158Seric syserr("%s: cannot fork", label); 90716158Seric return (1); 90816158Seric } 90916158Seric if (childpid > 0) 91016158Seric { 91116158Seric auto int st; 9129339Seric 91316158Seric /* parent -- wait for child to complete */ 91461093Seric setproctitle("server %s child wait", CurHostName); 91516158Seric st = waitfor(childpid); 91616158Seric if (st == -1) 91716158Seric syserr("%s: lost child", label); 9189339Seric 91916158Seric /* if we exited on a QUIT command, complete the process */ 92016158Seric if (st == (EX_QUIT << 8)) 92116158Seric finis(); 9229339Seric 92316158Seric return (1); 92416158Seric } 92516158Seric else 92616158Seric { 92716158Seric /* child */ 92816158Seric InChild = TRUE; 92925050Seric QuickAbort = FALSE; 93055012Seric clearenvelope(e, FALSE); 93116158Seric } 9329339Seric } 93315256Seric 93416158Seric /* open alias database */ 93560537Seric initmaps(FALSE, e); 93616158Seric 93716158Seric return (0); 9389339Seric } 9399339Seric 94056795Seric # endif /* SMTP */ 941