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*66017Seric static char sccsid[] = "@(#)srvrsmtp.c 8.29 (Berkeley) 02/06/94 (with SMTP)"; 1433731Sbostic #else 15*66017Seric static char sccsid[] = "@(#)srvrsmtp.c 8.29 (Berkeley) 02/06/94 (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 */ 5964685Seric /* use this to catch and log "door handle" attempts on your system */ 6064685Seric # 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, 8664685Seric "wiz", CMDLOGBOGUS, 874549Seric NULL, CMDERROR, 884549Seric }; 894549Seric 909378Seric bool OneXact = FALSE; /* one xaction only this run */ 9165017Seric char *CurSmtpClient; /* who's at the other end of channel */ 9211146Seric 9363937Seric static char *skipword(); 9463937Seric 9555012Seric smtp(e) 9655012Seric register ENVELOPE *e; 974549Seric { 984549Seric register char *p; 998544Seric register struct cmd *c; 1004549Seric char *cmd; 1015003Seric auto ADDRESS *vrfyqueue; 10212612Seric ADDRESS *a; 10358109Seric bool gotmail; /* mail command received */ 10458092Seric bool gothello; /* helo command received */ 10558092Seric bool vrfy; /* set if this is a vrfy command */ 10658323Seric char *protocol; /* sending protocol */ 10759016Seric char *sendinghost; /* sending hostname */ 10858333Seric long msize; /* approximate maximum message size */ 10966005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11058333Seric auto char *delimptr; 11158714Seric char *id; 11259747Seric int nrcpts; /* number of RCPT commands */ 11363787Seric bool doublequeue; 1148544Seric char inp[MAXLINE]; 11557232Seric char cmdbuf[MAXLINE]; 1167124Seric extern char Version[]; 11724943Seric extern ENVELOPE BlankEnvelope; 1184549Seric 11959066Seric if (fileno(OutChannel) != fileno(stdout)) 1207363Seric { 1217363Seric /* arrange for debugging output to go to remote host */ 12259066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1237363Seric } 12455012Seric settime(e); 12566005Seric peerhostname = RealHostName; 12666005Seric if (peerhostname == NULL) 12766005Seric peerhostname = "localhost"; 12866005Seric CurHostName = peerhostname; 12965017Seric CurSmtpClient = macvalue('_', e); 13065017Seric if (CurSmtpClient == NULL) 13166003Seric CurSmtpClient = CurHostName; 13265017Seric 13365017Seric setproctitle("server %s startup", CurSmtpClient); 13458050Seric expand("\201e", inp, &inp[sizeof inp], e); 13564496Seric if (BrokenSmtpPeers) 13664496Seric { 13764496Seric message("220 %s", inp); 13864496Seric } 13964496Seric else 14064496Seric { 14164496Seric message("220-%s", inp); 14264496Seric message("220 ESMTP spoken here"); 14364496Seric } 14458330Seric protocol = NULL; 14559016Seric sendinghost = macvalue('s', e); 14658082Seric gothello = FALSE; 14758330Seric gotmail = FALSE; 1484549Seric for (;;) 1494549Seric { 15012612Seric /* arrange for backout */ 15165751Seric if (setjmp(TopFrame) > 0) 15259058Seric { 15365751Seric /* if() nesting is necessary for Cray UNICOS */ 15465751Seric if (InChild) 15565751Seric { 15665751Seric QuickAbort = FALSE; 15765751Seric SuprErrs = TRUE; 15865751Seric finis(); 15965751Seric } 16059058Seric } 16112612Seric QuickAbort = FALSE; 16212612Seric HoldErrs = FALSE; 16351951Seric LogUsrErrs = FALSE; 16463843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 16512612Seric 1667356Seric /* setup for the read */ 16755012Seric e->e_to = NULL; 1684577Seric Errors = 0; 1697275Seric (void) fflush(stdout); 1707356Seric 1717356Seric /* read the input line */ 17261093Seric SmtpPhase = "server cmd read"; 17361093Seric setproctitle("server %s cmd read", CurHostName); 17461093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 17561093Seric SmtpPhase); 1767356Seric 1777685Seric /* handle errors */ 1787356Seric if (p == NULL) 1797356Seric { 1804549Seric /* end of file, just die */ 181*66017Seric disconnect(1, e); 18258151Seric message("421 %s Lost input channel from %s", 18365017Seric MyHostName, CurSmtpClient); 18455464Seric #ifdef LOG 18563843Seric if (LogLevel > (gotmail ? 1 : 19)) 18655464Seric syslog(LOG_NOTICE, "lost input channel from %s", 18765017Seric CurSmtpClient); 18855464Seric #endif 18958069Seric if (InChild) 19058069Seric ExitStat = EX_QUIT; 1914549Seric finis(); 1924549Seric } 1934549Seric 1944549Seric /* clean up end of line */ 1954558Seric fixcrlf(inp, TRUE); 1964549Seric 1974713Seric /* echo command to transcript */ 19855012Seric if (e->e_xfp != NULL) 19955012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2004713Seric 20159060Seric if (e->e_id == NULL) 20265058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 20359060Seric else 20465058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 20559060Seric 2064549Seric /* break off command */ 20758050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2084549Seric continue; 20957232Seric cmd = cmdbuf; 21058050Seric while (*p != '\0' && 21158050Seric !(isascii(*p) && isspace(*p)) && 21258050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 21324981Seric *cmd++ = *p++; 21424981Seric *cmd = '\0'; 2154549Seric 21625691Seric /* throw away leading whitespace */ 21758050Seric while (isascii(*p) && isspace(*p)) 21825691Seric p++; 21925691Seric 2204549Seric /* decode command */ 2214549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2224549Seric { 22333725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2244549Seric break; 2254549Seric } 2264549Seric 22751954Seric /* reset errors */ 22851954Seric errno = 0; 22951954Seric 2304549Seric /* process command */ 2314549Seric switch (c->cmdcode) 2324549Seric { 2334976Seric case CMDHELO: /* hello -- introduce yourself */ 23458323Seric case CMDEHLO: /* extended hello */ 23558323Seric if (c->cmdcode == CMDEHLO) 23658323Seric { 23758323Seric protocol = "ESMTP"; 23861093Seric SmtpPhase = "server EHLO"; 23958323Seric } 24058323Seric else 24158323Seric { 24258323Seric protocol = "SMTP"; 24361093Seric SmtpPhase = "server HELO"; 24458323Seric } 24559016Seric sendinghost = newstr(p); 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", 25165017Seric MyHostName, CurSmtpClient); 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) 27566005Seric sendinghost = peerhostname; 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, 30366005Seric "Host %s didn't use HELO protocol", 30466005Seric peerhostname); 30563753Seric } 30665947Seric #ifdef PICKY_HELO_CHECK 30766005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 30866005Seric (strcasecmp(peerhostname, "localhost") != 0 || 30965823Seric strcasecmp(sendinghost, MyHostName) != 0)) 31065823Seric { 31165823Seric auth_warning(e, "Host %s claimed to be %s", 31266005Seric peerhostname, sendinghost); 31365823Seric } 31465947Seric #endif 31565823Seric 31658323Seric if (protocol == NULL) 31758323Seric protocol = "SMTP"; 31858323Seric define('r', protocol, e); 31959016Seric define('s', sendinghost, e); 32055012Seric initsys(e); 32159747Seric nrcpts = 0; 32265089Seric e->e_flags |= EF_LOGSENDER; 32365058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3249339Seric 3259339Seric /* child -- go do the processing */ 3264549Seric p = skipword(p, "from"); 3274549Seric if (p == NULL) 3284549Seric break; 32957977Seric if (setjmp(TopFrame) > 0) 33058147Seric { 33158147Seric /* this failed -- undo work */ 33258147Seric if (InChild) 33359058Seric { 33459058Seric QuickAbort = FALSE; 33559058Seric SuprErrs = TRUE; 33663787Seric e->e_flags &= ~EF_FATALERRS; 33758147Seric finis(); 33859058Seric } 33957977Seric break; 34058147Seric } 34157977Seric QuickAbort = TRUE; 34258333Seric 34358333Seric /* must parse sender first */ 34458333Seric delimptr = NULL; 34558704Seric setsender(p, e, &delimptr, FALSE); 34658333Seric p = delimptr; 34758333Seric if (p != NULL && *p != '\0') 34858333Seric *p++ = '\0'; 34958333Seric 35058333Seric /* now parse ESMTP arguments */ 35158333Seric msize = 0; 35258333Seric for (; p != NULL && *p != '\0'; p++) 35358333Seric { 35458333Seric char *kp; 35558333Seric char *vp; 35658333Seric 35758333Seric /* locate the beginning of the keyword */ 35858333Seric while (isascii(*p) && isspace(*p)) 35958333Seric p++; 36058333Seric if (*p == '\0') 36158333Seric break; 36258333Seric kp = p; 36358333Seric 36458333Seric /* skip to the value portion */ 36558333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 36658333Seric p++; 36758333Seric if (*p == '=') 36858333Seric { 36958333Seric *p++ = '\0'; 37058333Seric vp = p; 37158333Seric 37258333Seric /* skip to the end of the value */ 37358333Seric while (*p != '\0' && *p != ' ' && 37458333Seric !(isascii(*p) && iscntrl(*p)) && 37558333Seric *p != '=') 37658333Seric p++; 37758333Seric } 37858333Seric 37958333Seric if (*p != '\0') 38058333Seric *p++ = '\0'; 38158333Seric 38258333Seric if (tTd(19, 1)) 38358333Seric printf("MAIL: got arg %s=%s\n", kp, 38458333Seric vp == NULL ? "<null>" : vp); 38558333Seric 38658333Seric if (strcasecmp(kp, "size") == 0) 38758333Seric { 38859093Seric if (vp == NULL) 38958333Seric { 39058333Seric usrerr("501 SIZE requires a value"); 39158333Seric /* NOTREACHED */ 39258333Seric } 39358333Seric msize = atol(vp); 39458333Seric } 39559093Seric else if (strcasecmp(kp, "body") == 0) 39659093Seric { 39759093Seric if (vp == NULL) 39859093Seric { 39959093Seric usrerr("501 BODY requires a value"); 40059093Seric /* NOTREACHED */ 40159093Seric } 40259093Seric # ifdef MIME 40359093Seric if (strcasecmp(vp, "8bitmime") == 0) 40459093Seric { 40559093Seric e->e_bodytype = "8BITMIME"; 40659709Seric SevenBit = FALSE; 40759093Seric } 40859093Seric else if (strcasecmp(vp, "7bit") == 0) 40959093Seric { 41059093Seric e->e_bodytype = "7BIT"; 41159709Seric SevenBit = TRUE; 41259093Seric } 41359093Seric else 41459093Seric { 41559093Seric usrerr("501 Unknown BODY type %s", 41659093Seric vp); 41759093Seric } 41859093Seric # endif 41959093Seric } 42058333Seric else 42158333Seric { 42258333Seric usrerr("501 %s parameter unrecognized", kp); 42358333Seric /* NOTREACHED */ 42458333Seric } 42558333Seric } 42659284Seric 42759284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 42859284Seric { 42959284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 43059284Seric MaxMessageSize); 43159284Seric /* NOTREACHED */ 43259284Seric } 43358333Seric 43458333Seric if (!enoughspace(msize)) 43558333Seric { 43658333Seric message("452 Insufficient disk space; try again later"); 43758333Seric break; 43858333Seric } 43958151Seric message("250 Sender ok"); 44058147Seric gotmail = TRUE; 4414549Seric break; 4424549Seric 4434976Seric case CMDRCPT: /* rcpt -- designate recipient */ 44458850Seric if (!gotmail) 44558850Seric { 44658850Seric usrerr("503 Need MAIL before RCPT"); 44758850Seric break; 44858850Seric } 44961093Seric SmtpPhase = "server RCPT"; 45012612Seric if (setjmp(TopFrame) > 0) 45114785Seric { 45255012Seric e->e_flags &= ~EF_FATALERRS; 45312612Seric break; 45414785Seric } 45512612Seric QuickAbort = TRUE; 45651951Seric LogUsrErrs = TRUE; 45758093Seric 45859699Seric if (e->e_sendmode != SM_DELIVER) 45959699Seric e->e_flags |= EF_VRFYONLY; 46058919Seric 4614549Seric p = skipword(p, "to"); 4624549Seric if (p == NULL) 4634549Seric break; 46464284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 46512612Seric if (a == NULL) 46612612Seric break; 46716886Seric a->q_flags |= QPRIMARY; 46855012Seric a = recipient(a, &e->e_sendqueue, e); 46912612Seric if (Errors != 0) 47012612Seric break; 47112612Seric 47212612Seric /* no errors during parsing, but might be a duplicate */ 47355012Seric e->e_to = p; 47412612Seric if (!bitset(QBADADDR, a->q_flags)) 47559747Seric { 47664718Seric message("250 Recipient ok%s", 47764718Seric bitset(QQUEUEUP, a->q_flags) ? 47864718Seric " (will queue)" : ""); 47959747Seric nrcpts++; 48059747Seric } 48112612Seric else 4824549Seric { 48312612Seric /* punt -- should keep message in ADDRESS.... */ 48458151Seric message("550 Addressee unknown"); 4854549Seric } 48655012Seric e->e_to = NULL; 4874549Seric break; 4884549Seric 4894549Seric case CMDDATA: /* data -- text of mail */ 49061093Seric SmtpPhase = "server DATA"; 49158109Seric if (!gotmail) 4924549Seric { 49358151Seric message("503 Need MAIL command"); 4944976Seric break; 4954549Seric } 49664718Seric else if (nrcpts <= 0) 4974549Seric { 49858151Seric message("503 Need RCPT (recipient)"); 4994976Seric break; 5004549Seric } 5014976Seric 50258929Seric /* check to see if we need to re-expand aliases */ 50363787Seric /* also reset QBADADDR on already-diagnosted addrs */ 50463787Seric doublequeue = FALSE; 50558929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 50658929Seric { 50758929Seric if (bitset(QVERIFIED, a->q_flags)) 50863787Seric { 50963787Seric /* need to re-expand aliases */ 51063787Seric doublequeue = TRUE; 51163787Seric } 51263787Seric if (bitset(QBADADDR, a->q_flags)) 51363787Seric { 51463787Seric /* make this "go away" */ 51563787Seric a->q_flags |= QDONTSEND; 51663787Seric a->q_flags &= ~QBADADDR; 51763787Seric } 51858929Seric } 51958929Seric 5204976Seric /* collect the text of the message */ 52124943Seric SmtpPhase = "collect"; 52264766Seric collect(TRUE, doublequeue, e); 52364766Seric if (Errors != 0) 52464766Seric goto abortmessage; 52563965Seric HoldErrs = TRUE; 5264976Seric 5278238Seric /* 5288238Seric ** Arrange to send to everyone. 5298238Seric ** If sending to multiple people, mail back 5308238Seric ** errors rather than reporting directly. 5318238Seric ** In any case, don't mail back errors for 5328238Seric ** anything that has happened up to 5338238Seric ** now (the other end will do this). 53410197Seric ** Truncate our transcript -- the mail has gotten 53510197Seric ** to us successfully, and if we have 53610197Seric ** to mail this back, it will be easier 53710197Seric ** on the reader. 5388238Seric ** Then send to everyone. 5398238Seric ** Finally give a reply code. If an error has 5408238Seric ** already been given, don't mail a 5418238Seric ** message back. 5429339Seric ** We goose error returns by clearing error bit. 5438238Seric */ 5448238Seric 54524943Seric SmtpPhase = "delivery"; 54663787Seric if (nrcpts != 1 && !doublequeue) 5479378Seric { 5489378Seric HoldErrs = TRUE; 54958734Seric e->e_errormode = EM_MAIL; 5509378Seric } 55155012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 55258714Seric id = e->e_id; 5534976Seric 5544976Seric /* send to all recipients */ 55563787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 55655012Seric e->e_to = NULL; 5574976Seric 5588238Seric /* issue success if appropriate and reset */ 5598238Seric if (Errors == 0 || HoldErrs) 56058855Seric message("250 %s Message accepted for delivery", id); 56159747Seric 56259747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 56359730Seric { 56459730Seric /* avoid sending back an extra message */ 56559730Seric e->e_flags &= ~EF_FATALERRS; 56659747Seric e->e_flags |= EF_CLRQUEUE; 56759730Seric } 5688238Seric else 56958919Seric { 57059747Seric /* from now on, we have to operate silently */ 57159747Seric HoldErrs = TRUE; 57259747Seric e->e_errormode = EM_MAIL; 57359747Seric 57459730Seric /* if we just queued, poke it */ 57563787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 57659730Seric { 57764296Seric extern pid_t dowork(); 57864296Seric 57959730Seric unlockqueue(e); 58064296Seric (void) dowork(id, TRUE, TRUE, e); 58159730Seric } 58258919Seric } 58358883Seric 58459747Seric abortmessage: 5859339Seric /* if in a child, pop back to our parent */ 5869339Seric if (InChild) 5879339Seric finis(); 58824943Seric 58924943Seric /* clean up a bit */ 59058109Seric gotmail = FALSE; 59155012Seric dropenvelope(e); 59258179Seric CurEnv = e = newenvelope(e, CurEnv); 59355012Seric e->e_flags = BlankEnvelope.e_flags; 5944549Seric break; 5954549Seric 5964549Seric case CMDRSET: /* rset -- reset state */ 59758151Seric message("250 Reset state"); 59864359Seric e->e_flags |= EF_CLRQUEUE; 5999339Seric if (InChild) 6009339Seric finis(); 60158109Seric 60258109Seric /* clean up a bit */ 60358109Seric gotmail = FALSE; 60458109Seric dropenvelope(e); 60558179Seric CurEnv = e = newenvelope(e, CurEnv); 6069339Seric break; 6074549Seric 6084549Seric case CMDVRFY: /* vrfy -- verify address */ 60958092Seric case CMDEXPN: /* expn -- expand address */ 61058092Seric vrfy = c->cmdcode == CMDVRFY; 61158092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 61258092Seric PrivacyFlags)) 61358082Seric { 61458412Seric if (vrfy) 61558412Seric message("252 Who's to say?"); 61658412Seric else 61765192Seric message("502 Sorry, we do not allow this operation"); 61865017Seric #ifdef LOG 61965017Seric if (LogLevel > 5) 62065017Seric syslog(LOG_INFO, "%s: %s [rejected]", 62165017Seric CurSmtpClient, inp); 62265017Seric #endif 62358082Seric break; 62458082Seric } 62558082Seric else if (!gothello && 62658092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 62758092Seric PrivacyFlags)) 62858082Seric { 62958151Seric message("503 I demand that you introduce yourself first"); 63058082Seric break; 63158082Seric } 63258092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6339339Seric break; 63455173Seric #ifdef LOG 63558020Seric if (LogLevel > 5) 63665017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 63755173Seric #endif 6385003Seric vrfyqueue = NULL; 6397762Seric QuickAbort = TRUE; 64058092Seric if (vrfy) 64158092Seric e->e_flags |= EF_VRFYONLY; 64262373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 64362373Seric *p++; 64462373Seric if (*p == '\0') 64562373Seric { 64662373Seric message("501 Argument required"); 64762373Seric Errors++; 64862373Seric } 64962373Seric else 65062373Seric { 65164284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 65262373Seric } 6537762Seric if (Errors != 0) 6549339Seric { 6559339Seric if (InChild) 6569339Seric finis(); 6577762Seric break; 6589339Seric } 65962373Seric if (vrfyqueue == NULL) 66062373Seric { 66162373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 66262373Seric } 6635003Seric while (vrfyqueue != NULL) 6645003Seric { 66563971Seric a = vrfyqueue; 66663971Seric while ((a = a->q_next) != NULL && 66763971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 66863971Seric continue; 6697685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 67058151Seric printvrfyaddr(vrfyqueue, a == NULL); 67163847Seric vrfyqueue = vrfyqueue->q_next; 6725003Seric } 6739339Seric if (InChild) 6749339Seric finis(); 6754549Seric break; 6764549Seric 6774549Seric case CMDHELP: /* help -- give user info */ 6784577Seric help(p); 6794549Seric break; 6804549Seric 6814549Seric case CMDNOOP: /* noop -- do nothing */ 68264122Seric message("250 OK"); 6834549Seric break; 6844549Seric 6854549Seric case CMDQUIT: /* quit -- leave mail */ 68658151Seric message("221 %s closing connection", MyHostName); 68761051Seric 68861051Seric /* avoid future 050 messages */ 689*66017Seric disconnect(1, e); 69061051Seric 6919339Seric if (InChild) 6929339Seric ExitStat = EX_QUIT; 6934549Seric finis(); 6944549Seric 6958544Seric case CMDVERB: /* set verbose mode */ 69659957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 69759957Seric { 69859957Seric /* this would give out the same info */ 69959957Seric message("502 Verbose unavailable"); 70059957Seric break; 70159957Seric } 7028544Seric Verbose = TRUE; 70358734Seric e->e_sendmode = SM_DELIVER; 70459957Seric message("250 Verbose mode"); 7058544Seric break; 7068544Seric 7079314Seric case CMDONEX: /* doing one transaction only */ 7089378Seric OneXact = TRUE; 70959957Seric message("250 Only one transaction"); 7109314Seric break; 7119314Seric 71236230Skarels # ifdef SMTPDEBUG 7139339Seric case CMDDBGQSHOW: /* show queues */ 7146907Seric printf("Send Queue="); 71555012Seric printaddr(e->e_sendqueue, TRUE); 7165003Seric break; 7177275Seric 7187275Seric case CMDDBGDEBUG: /* set debug mode */ 7197676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7207676Seric tTflag(p); 72158151Seric message("200 Debug set"); 7227275Seric break; 7237275Seric 72436230Skarels # else /* not SMTPDEBUG */ 72536230Skarels case CMDDBGQSHOW: /* show queues */ 72636230Skarels case CMDDBGDEBUG: /* set debug mode */ 72764685Seric # endif /* SMTPDEBUG */ 72864685Seric case CMDLOGBOGUS: /* bogus command */ 72936233Skarels # ifdef LOG 73058308Seric if (LogLevel > 0) 73164685Seric syslog(LOG_CRIT, 73258020Seric "\"%s\" command from %s (%s)", 73366005Seric c->cmdname, peerhostname, 73458755Seric anynet_ntoa(&RealHostAddr)); 73536233Skarels # endif 73636230Skarels /* FALL THROUGH */ 73736230Skarels 7384549Seric case CMDERROR: /* unknown command */ 73958151Seric message("500 Command unrecognized"); 7404549Seric break; 7414549Seric 7424549Seric default: 74336230Skarels errno = 0; 74458151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7454549Seric break; 7464549Seric } 7474549Seric } 7484549Seric } 7494549Seric /* 7504549Seric ** SKIPWORD -- skip a fixed word. 7514549Seric ** 7524549Seric ** Parameters: 7534549Seric ** p -- place to start looking. 7544549Seric ** w -- word to skip. 7554549Seric ** 7564549Seric ** Returns: 7574549Seric ** p following w. 7584549Seric ** NULL on error. 7594549Seric ** 7604549Seric ** Side Effects: 7614549Seric ** clobbers the p data area. 7624549Seric */ 7634549Seric 7644549Seric static char * 7654549Seric skipword(p, w) 7664549Seric register char *p; 7674549Seric char *w; 7684549Seric { 7694549Seric register char *q; 77066005Seric char *firstp = p; 7714549Seric 7724549Seric /* find beginning of word */ 77358050Seric while (isascii(*p) && isspace(*p)) 7744549Seric p++; 7754549Seric q = p; 7764549Seric 7774549Seric /* find end of word */ 77858050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7794549Seric p++; 78058050Seric while (isascii(*p) && isspace(*p)) 7814549Seric *p++ = '\0'; 7824549Seric if (*p != ':') 7834549Seric { 7844549Seric syntax: 78566005Seric message("501 Syntax error in parameters scanning \"%s\"", 78666005Seric firstp); 7874549Seric Errors++; 7884549Seric return (NULL); 7894549Seric } 7904549Seric *p++ = '\0'; 79158050Seric while (isascii(*p) && isspace(*p)) 7924549Seric p++; 7934549Seric 79462373Seric if (*p == '\0') 79562373Seric goto syntax; 79662373Seric 7974549Seric /* see if the input word matches desired word */ 79833725Sbostic if (strcasecmp(q, w)) 7994549Seric goto syntax; 8004549Seric 8014549Seric return (p); 8024549Seric } 8034577Seric /* 80458151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 80558151Seric ** 80658151Seric ** Parameters: 80758151Seric ** a -- the address to print 80858151Seric ** last -- set if this is the last one. 80958151Seric ** 81058151Seric ** Returns: 81158151Seric ** none. 81258151Seric ** 81358151Seric ** Side Effects: 81458151Seric ** Prints the appropriate 250 codes. 81558151Seric */ 81658151Seric 81758151Seric printvrfyaddr(a, last) 81858151Seric register ADDRESS *a; 81958151Seric bool last; 82058151Seric { 82158151Seric char fmtbuf[20]; 82258151Seric 82358151Seric strcpy(fmtbuf, "250"); 82458151Seric fmtbuf[3] = last ? ' ' : '-'; 82558151Seric 82659746Seric if (a->q_fullname == NULL) 82759746Seric { 82859746Seric if (strchr(a->q_user, '@') == NULL) 82959746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 83059746Seric else 83159746Seric strcpy(&fmtbuf[4], "<%s>"); 83259746Seric message(fmtbuf, a->q_user, MyHostName); 83359746Seric } 83458151Seric else 83558151Seric { 83659746Seric if (strchr(a->q_user, '@') == NULL) 83759746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 83859746Seric else 83959746Seric strcpy(&fmtbuf[4], "%s <%s>"); 84059746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 84158151Seric } 84258151Seric } 84358151Seric /* 8444577Seric ** HELP -- implement the HELP command. 8454577Seric ** 8464577Seric ** Parameters: 8474577Seric ** topic -- the topic we want help for. 8484577Seric ** 8494577Seric ** Returns: 8504577Seric ** none. 8514577Seric ** 8524577Seric ** Side Effects: 8534577Seric ** outputs the help file to message output. 8544577Seric */ 8554577Seric 8564577Seric help(topic) 8574577Seric char *topic; 8584577Seric { 8594577Seric register FILE *hf; 8604577Seric int len; 8614577Seric char buf[MAXLINE]; 8624577Seric bool noinfo; 8634577Seric 8648269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8654577Seric { 8664577Seric /* no help */ 86711931Seric errno = 0; 86858151Seric message("502 HELP not implemented"); 8694577Seric return; 8704577Seric } 8714577Seric 87249669Seric if (topic == NULL || *topic == '\0') 87349669Seric topic = "smtp"; 87449669Seric else 87549669Seric makelower(topic); 87649669Seric 8774577Seric len = strlen(topic); 8784577Seric noinfo = TRUE; 8794577Seric 8804577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8814577Seric { 8824577Seric if (strncmp(buf, topic, len) == 0) 8834577Seric { 8844577Seric register char *p; 8854577Seric 88656795Seric p = strchr(buf, '\t'); 8874577Seric if (p == NULL) 8884577Seric p = buf; 8894577Seric else 8904577Seric p++; 8914577Seric fixcrlf(p, TRUE); 89258151Seric message("214-%s", p); 8934577Seric noinfo = FALSE; 8944577Seric } 8954577Seric } 8964577Seric 8974577Seric if (noinfo) 89858151Seric message("504 HELP topic unknown"); 8994577Seric else 90058151Seric message("214 End of HELP info"); 9014628Seric (void) fclose(hf); 9024577Seric } 9038544Seric /* 9049339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 9059339Seric ** 9069339Seric ** Parameters: 9079339Seric ** label -- a string used in error messages 9089339Seric ** 9099339Seric ** Returns: 9109339Seric ** zero in the child 9119339Seric ** one in the parent 9129339Seric ** 9139339Seric ** Side Effects: 9149339Seric ** none. 9159339Seric */ 9168544Seric 91755012Seric runinchild(label, e) 9189339Seric char *label; 91955012Seric register ENVELOPE *e; 9209339Seric { 9219339Seric int childpid; 9229339Seric 92316158Seric if (!OneXact) 9249339Seric { 92516158Seric childpid = dofork(); 92616158Seric if (childpid < 0) 92716158Seric { 92816158Seric syserr("%s: cannot fork", label); 92916158Seric return (1); 93016158Seric } 93116158Seric if (childpid > 0) 93216158Seric { 93316158Seric auto int st; 9349339Seric 93516158Seric /* parent -- wait for child to complete */ 93661093Seric setproctitle("server %s child wait", CurHostName); 93716158Seric st = waitfor(childpid); 93816158Seric if (st == -1) 93916158Seric syserr("%s: lost child", label); 94064948Seric else if (!WIFEXITED(st)) 94164948Seric syserr("%s: died on signal %d", 94264948Seric label, st & 0177); 9439339Seric 94416158Seric /* if we exited on a QUIT command, complete the process */ 945*66017Seric if (WEXITSTATUS(st) == EX_QUIT) 946*66017Seric { 947*66017Seric disconnect(1, e); 94816158Seric finis(); 949*66017Seric } 9509339Seric 95116158Seric return (1); 95216158Seric } 95316158Seric else 95416158Seric { 95516158Seric /* child */ 95616158Seric InChild = TRUE; 95725050Seric QuickAbort = FALSE; 95855012Seric clearenvelope(e, FALSE); 95916158Seric } 9609339Seric } 96115256Seric 96216158Seric /* open alias database */ 96360537Seric initmaps(FALSE, e); 96416158Seric 96516158Seric return (0); 9669339Seric } 9679339Seric 96856795Seric # endif /* SMTP */ 969