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*66005Seric static char sccsid[] = "@(#)srvrsmtp.c 8.28 (Berkeley) 02/05/94 (with SMTP)"; 1433731Sbostic #else 15*66005Seric static char sccsid[] = "@(#)srvrsmtp.c 8.28 (Berkeley) 02/05/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 */ 109*66005Seric 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); 125*66005Seric peerhostname = RealHostName; 126*66005Seric if (peerhostname == NULL) 127*66005Seric peerhostname = "localhost"; 128*66005Seric 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 */ 18158151Seric message("421 %s Lost input channel from %s", 18265017Seric MyHostName, CurSmtpClient); 18355464Seric #ifdef LOG 18463843Seric if (LogLevel > (gotmail ? 1 : 19)) 18555464Seric syslog(LOG_NOTICE, "lost input channel from %s", 18665017Seric CurSmtpClient); 18755464Seric #endif 18858069Seric if (InChild) 18958069Seric ExitStat = EX_QUIT; 1904549Seric finis(); 1914549Seric } 1924549Seric 1934549Seric /* clean up end of line */ 1944558Seric fixcrlf(inp, TRUE); 1954549Seric 1964713Seric /* echo command to transcript */ 19755012Seric if (e->e_xfp != NULL) 19855012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1994713Seric 20059060Seric if (e->e_id == NULL) 20165058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 20259060Seric else 20365058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 20459060Seric 2054549Seric /* break off command */ 20658050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2074549Seric continue; 20857232Seric cmd = cmdbuf; 20958050Seric while (*p != '\0' && 21058050Seric !(isascii(*p) && isspace(*p)) && 21158050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 21224981Seric *cmd++ = *p++; 21324981Seric *cmd = '\0'; 2144549Seric 21525691Seric /* throw away leading whitespace */ 21658050Seric while (isascii(*p) && isspace(*p)) 21725691Seric p++; 21825691Seric 2194549Seric /* decode command */ 2204549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2214549Seric { 22233725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2234549Seric break; 2244549Seric } 2254549Seric 22651954Seric /* reset errors */ 22751954Seric errno = 0; 22851954Seric 2294549Seric /* process command */ 2304549Seric switch (c->cmdcode) 2314549Seric { 2324976Seric case CMDHELO: /* hello -- introduce yourself */ 23358323Seric case CMDEHLO: /* extended hello */ 23458323Seric if (c->cmdcode == CMDEHLO) 23558323Seric { 23658323Seric protocol = "ESMTP"; 23761093Seric SmtpPhase = "server EHLO"; 23858323Seric } 23958323Seric else 24058323Seric { 24158323Seric protocol = "SMTP"; 24261093Seric SmtpPhase = "server HELO"; 24358323Seric } 24459016Seric sendinghost = newstr(p); 24560210Seric gothello = TRUE; 24660210Seric if (c->cmdcode != CMDEHLO) 24760239Seric { 24860239Seric /* print old message and be done with it */ 24960239Seric message("250 %s Hello %s, pleased to meet you", 25065017Seric MyHostName, CurSmtpClient); 25160210Seric break; 25260239Seric } 25360239Seric 25460239Seric /* print extended message and brag */ 25560239Seric message("250-%s Hello %s, pleased to meet you", 25660239Seric MyHostName, p); 25758323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 25858323Seric message("250-EXPN"); 25964359Seric if (MaxMessageSize > 0) 26064359Seric message("250-SIZE %ld", MaxMessageSize); 26159271Seric else 26259271Seric message("250-SIZE"); 26358323Seric message("250 HELP"); 2644976Seric break; 2654976Seric 2664549Seric case CMDMAIL: /* mail -- designate sender */ 26761093Seric SmtpPhase = "server MAIL"; 26824943Seric 2699314Seric /* check for validity of this command */ 27058789Seric if (!gothello) 27158082Seric { 27258957Seric /* set sending host to our known value */ 27359016Seric if (sendinghost == NULL) 274*66005Seric sendinghost = peerhostname; 27558957Seric 27658789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 27758821Seric { 27858789Seric message("503 Polite people say HELO first"); 27958821Seric break; 28058821Seric } 28158082Seric } 28258109Seric if (gotmail) 2834558Seric { 28458151Seric message("503 Sender already specified"); 28563843Seric if (InChild) 28663843Seric finis(); 2874558Seric break; 2884558Seric } 2899339Seric if (InChild) 2909339Seric { 29136230Skarels errno = 0; 29258151Seric syserr("503 Nested MAIL command: MAIL %s", p); 29358069Seric finis(); 2949339Seric } 2959339Seric 2969339Seric /* fork a subprocess to process this command */ 29755012Seric if (runinchild("SMTP-MAIL", e) > 0) 2989339Seric break; 29963753Seric if (!gothello) 30063753Seric { 30163753Seric auth_warning(e, 302*66005Seric "Host %s didn't use HELO protocol", 303*66005Seric peerhostname); 30463753Seric } 30565947Seric #ifdef PICKY_HELO_CHECK 306*66005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 307*66005Seric (strcasecmp(peerhostname, "localhost") != 0 || 30865823Seric strcasecmp(sendinghost, MyHostName) != 0)) 30965823Seric { 31065823Seric auth_warning(e, "Host %s claimed to be %s", 311*66005Seric peerhostname, sendinghost); 31265823Seric } 31365947Seric #endif 31465823Seric 31558323Seric if (protocol == NULL) 31658323Seric protocol = "SMTP"; 31758323Seric define('r', protocol, e); 31859016Seric define('s', sendinghost, e); 31955012Seric initsys(e); 32059747Seric nrcpts = 0; 32165089Seric e->e_flags |= EF_LOGSENDER; 32265058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3239339Seric 3249339Seric /* child -- go do the processing */ 3254549Seric p = skipword(p, "from"); 3264549Seric if (p == NULL) 3274549Seric break; 32857977Seric if (setjmp(TopFrame) > 0) 32958147Seric { 33058147Seric /* this failed -- undo work */ 33158147Seric if (InChild) 33259058Seric { 33359058Seric QuickAbort = FALSE; 33459058Seric SuprErrs = TRUE; 33563787Seric e->e_flags &= ~EF_FATALERRS; 33658147Seric finis(); 33759058Seric } 33857977Seric break; 33958147Seric } 34057977Seric QuickAbort = TRUE; 34158333Seric 34258333Seric /* must parse sender first */ 34358333Seric delimptr = NULL; 34458704Seric setsender(p, e, &delimptr, FALSE); 34558333Seric p = delimptr; 34658333Seric if (p != NULL && *p != '\0') 34758333Seric *p++ = '\0'; 34858333Seric 34958333Seric /* now parse ESMTP arguments */ 35058333Seric msize = 0; 35158333Seric for (; p != NULL && *p != '\0'; p++) 35258333Seric { 35358333Seric char *kp; 35458333Seric char *vp; 35558333Seric 35658333Seric /* locate the beginning of the keyword */ 35758333Seric while (isascii(*p) && isspace(*p)) 35858333Seric p++; 35958333Seric if (*p == '\0') 36058333Seric break; 36158333Seric kp = p; 36258333Seric 36358333Seric /* skip to the value portion */ 36458333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 36558333Seric p++; 36658333Seric if (*p == '=') 36758333Seric { 36858333Seric *p++ = '\0'; 36958333Seric vp = p; 37058333Seric 37158333Seric /* skip to the end of the value */ 37258333Seric while (*p != '\0' && *p != ' ' && 37358333Seric !(isascii(*p) && iscntrl(*p)) && 37458333Seric *p != '=') 37558333Seric p++; 37658333Seric } 37758333Seric 37858333Seric if (*p != '\0') 37958333Seric *p++ = '\0'; 38058333Seric 38158333Seric if (tTd(19, 1)) 38258333Seric printf("MAIL: got arg %s=%s\n", kp, 38358333Seric vp == NULL ? "<null>" : vp); 38458333Seric 38558333Seric if (strcasecmp(kp, "size") == 0) 38658333Seric { 38759093Seric if (vp == NULL) 38858333Seric { 38958333Seric usrerr("501 SIZE requires a value"); 39058333Seric /* NOTREACHED */ 39158333Seric } 39258333Seric msize = atol(vp); 39358333Seric } 39459093Seric else if (strcasecmp(kp, "body") == 0) 39559093Seric { 39659093Seric if (vp == NULL) 39759093Seric { 39859093Seric usrerr("501 BODY requires a value"); 39959093Seric /* NOTREACHED */ 40059093Seric } 40159093Seric # ifdef MIME 40259093Seric if (strcasecmp(vp, "8bitmime") == 0) 40359093Seric { 40459093Seric e->e_bodytype = "8BITMIME"; 40559709Seric SevenBit = FALSE; 40659093Seric } 40759093Seric else if (strcasecmp(vp, "7bit") == 0) 40859093Seric { 40959093Seric e->e_bodytype = "7BIT"; 41059709Seric SevenBit = TRUE; 41159093Seric } 41259093Seric else 41359093Seric { 41459093Seric usrerr("501 Unknown BODY type %s", 41559093Seric vp); 41659093Seric } 41759093Seric # endif 41859093Seric } 41958333Seric else 42058333Seric { 42158333Seric usrerr("501 %s parameter unrecognized", kp); 42258333Seric /* NOTREACHED */ 42358333Seric } 42458333Seric } 42559284Seric 42659284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 42759284Seric { 42859284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 42959284Seric MaxMessageSize); 43059284Seric /* NOTREACHED */ 43159284Seric } 43258333Seric 43358333Seric if (!enoughspace(msize)) 43458333Seric { 43558333Seric message("452 Insufficient disk space; try again later"); 43658333Seric break; 43758333Seric } 43858151Seric message("250 Sender ok"); 43958147Seric gotmail = TRUE; 4404549Seric break; 4414549Seric 4424976Seric case CMDRCPT: /* rcpt -- designate recipient */ 44358850Seric if (!gotmail) 44458850Seric { 44558850Seric usrerr("503 Need MAIL before RCPT"); 44658850Seric break; 44758850Seric } 44861093Seric SmtpPhase = "server RCPT"; 44912612Seric if (setjmp(TopFrame) > 0) 45014785Seric { 45155012Seric e->e_flags &= ~EF_FATALERRS; 45212612Seric break; 45314785Seric } 45412612Seric QuickAbort = TRUE; 45551951Seric LogUsrErrs = TRUE; 45658093Seric 45759699Seric if (e->e_sendmode != SM_DELIVER) 45859699Seric e->e_flags |= EF_VRFYONLY; 45958919Seric 4604549Seric p = skipword(p, "to"); 4614549Seric if (p == NULL) 4624549Seric break; 46364284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 46412612Seric if (a == NULL) 46512612Seric break; 46616886Seric a->q_flags |= QPRIMARY; 46755012Seric a = recipient(a, &e->e_sendqueue, e); 46812612Seric if (Errors != 0) 46912612Seric break; 47012612Seric 47112612Seric /* no errors during parsing, but might be a duplicate */ 47255012Seric e->e_to = p; 47312612Seric if (!bitset(QBADADDR, a->q_flags)) 47459747Seric { 47564718Seric message("250 Recipient ok%s", 47664718Seric bitset(QQUEUEUP, a->q_flags) ? 47764718Seric " (will queue)" : ""); 47859747Seric nrcpts++; 47959747Seric } 48012612Seric else 4814549Seric { 48212612Seric /* punt -- should keep message in ADDRESS.... */ 48358151Seric message("550 Addressee unknown"); 4844549Seric } 48555012Seric e->e_to = NULL; 4864549Seric break; 4874549Seric 4884549Seric case CMDDATA: /* data -- text of mail */ 48961093Seric SmtpPhase = "server DATA"; 49058109Seric if (!gotmail) 4914549Seric { 49258151Seric message("503 Need MAIL command"); 4934976Seric break; 4944549Seric } 49564718Seric else if (nrcpts <= 0) 4964549Seric { 49758151Seric message("503 Need RCPT (recipient)"); 4984976Seric break; 4994549Seric } 5004976Seric 50158929Seric /* check to see if we need to re-expand aliases */ 50263787Seric /* also reset QBADADDR on already-diagnosted addrs */ 50363787Seric doublequeue = FALSE; 50458929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 50558929Seric { 50658929Seric if (bitset(QVERIFIED, a->q_flags)) 50763787Seric { 50863787Seric /* need to re-expand aliases */ 50963787Seric doublequeue = TRUE; 51063787Seric } 51163787Seric if (bitset(QBADADDR, a->q_flags)) 51263787Seric { 51363787Seric /* make this "go away" */ 51463787Seric a->q_flags |= QDONTSEND; 51563787Seric a->q_flags &= ~QBADADDR; 51663787Seric } 51758929Seric } 51858929Seric 5194976Seric /* collect the text of the message */ 52024943Seric SmtpPhase = "collect"; 52164766Seric collect(TRUE, doublequeue, e); 52264766Seric if (Errors != 0) 52364766Seric goto abortmessage; 52463965Seric HoldErrs = TRUE; 5254976Seric 5268238Seric /* 5278238Seric ** Arrange to send to everyone. 5288238Seric ** If sending to multiple people, mail back 5298238Seric ** errors rather than reporting directly. 5308238Seric ** In any case, don't mail back errors for 5318238Seric ** anything that has happened up to 5328238Seric ** now (the other end will do this). 53310197Seric ** Truncate our transcript -- the mail has gotten 53410197Seric ** to us successfully, and if we have 53510197Seric ** to mail this back, it will be easier 53610197Seric ** on the reader. 5378238Seric ** Then send to everyone. 5388238Seric ** Finally give a reply code. If an error has 5398238Seric ** already been given, don't mail a 5408238Seric ** message back. 5419339Seric ** We goose error returns by clearing error bit. 5428238Seric */ 5438238Seric 54424943Seric SmtpPhase = "delivery"; 54563787Seric if (nrcpts != 1 && !doublequeue) 5469378Seric { 5479378Seric HoldErrs = TRUE; 54858734Seric e->e_errormode = EM_MAIL; 5499378Seric } 55055012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 55158714Seric id = e->e_id; 5524976Seric 5534976Seric /* send to all recipients */ 55463787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 55555012Seric e->e_to = NULL; 5564976Seric 5578238Seric /* issue success if appropriate and reset */ 5588238Seric if (Errors == 0 || HoldErrs) 55958855Seric message("250 %s Message accepted for delivery", id); 56059747Seric 56159747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 56259730Seric { 56359730Seric /* avoid sending back an extra message */ 56459730Seric e->e_flags &= ~EF_FATALERRS; 56559747Seric e->e_flags |= EF_CLRQUEUE; 56659730Seric } 5678238Seric else 56858919Seric { 56959747Seric /* from now on, we have to operate silently */ 57059747Seric HoldErrs = TRUE; 57159747Seric e->e_errormode = EM_MAIL; 57259747Seric 57359730Seric /* if we just queued, poke it */ 57463787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 57559730Seric { 57664296Seric extern pid_t dowork(); 57764296Seric 57859730Seric unlockqueue(e); 57964296Seric (void) dowork(id, TRUE, TRUE, e); 58059730Seric } 58158919Seric } 58258883Seric 58359747Seric abortmessage: 5849339Seric /* if in a child, pop back to our parent */ 5859339Seric if (InChild) 5869339Seric finis(); 58724943Seric 58824943Seric /* clean up a bit */ 58958109Seric gotmail = FALSE; 59055012Seric dropenvelope(e); 59158179Seric CurEnv = e = newenvelope(e, CurEnv); 59255012Seric e->e_flags = BlankEnvelope.e_flags; 5934549Seric break; 5944549Seric 5954549Seric case CMDRSET: /* rset -- reset state */ 59658151Seric message("250 Reset state"); 59764359Seric e->e_flags |= EF_CLRQUEUE; 5989339Seric if (InChild) 5999339Seric finis(); 60058109Seric 60158109Seric /* clean up a bit */ 60258109Seric gotmail = FALSE; 60358109Seric dropenvelope(e); 60458179Seric CurEnv = e = newenvelope(e, CurEnv); 6059339Seric break; 6064549Seric 6074549Seric case CMDVRFY: /* vrfy -- verify address */ 60858092Seric case CMDEXPN: /* expn -- expand address */ 60958092Seric vrfy = c->cmdcode == CMDVRFY; 61058092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 61158092Seric PrivacyFlags)) 61258082Seric { 61358412Seric if (vrfy) 61458412Seric message("252 Who's to say?"); 61558412Seric else 61665192Seric message("502 Sorry, we do not allow this operation"); 61765017Seric #ifdef LOG 61865017Seric if (LogLevel > 5) 61965017Seric syslog(LOG_INFO, "%s: %s [rejected]", 62065017Seric CurSmtpClient, inp); 62165017Seric #endif 62258082Seric break; 62358082Seric } 62458082Seric else if (!gothello && 62558092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 62658092Seric PrivacyFlags)) 62758082Seric { 62858151Seric message("503 I demand that you introduce yourself first"); 62958082Seric break; 63058082Seric } 63158092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6329339Seric break; 63355173Seric #ifdef LOG 63458020Seric if (LogLevel > 5) 63565017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 63655173Seric #endif 6375003Seric vrfyqueue = NULL; 6387762Seric QuickAbort = TRUE; 63958092Seric if (vrfy) 64058092Seric e->e_flags |= EF_VRFYONLY; 64162373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 64262373Seric *p++; 64362373Seric if (*p == '\0') 64462373Seric { 64562373Seric message("501 Argument required"); 64662373Seric Errors++; 64762373Seric } 64862373Seric else 64962373Seric { 65064284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 65162373Seric } 6527762Seric if (Errors != 0) 6539339Seric { 6549339Seric if (InChild) 6559339Seric finis(); 6567762Seric break; 6579339Seric } 65862373Seric if (vrfyqueue == NULL) 65962373Seric { 66062373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 66162373Seric } 6625003Seric while (vrfyqueue != NULL) 6635003Seric { 66463971Seric a = vrfyqueue; 66563971Seric while ((a = a->q_next) != NULL && 66663971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 66763971Seric continue; 6687685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 66958151Seric printvrfyaddr(vrfyqueue, a == NULL); 67063847Seric vrfyqueue = vrfyqueue->q_next; 6715003Seric } 6729339Seric if (InChild) 6739339Seric finis(); 6744549Seric break; 6754549Seric 6764549Seric case CMDHELP: /* help -- give user info */ 6774577Seric help(p); 6784549Seric break; 6794549Seric 6804549Seric case CMDNOOP: /* noop -- do nothing */ 68164122Seric message("250 OK"); 6824549Seric break; 6834549Seric 6844549Seric case CMDQUIT: /* quit -- leave mail */ 68558151Seric message("221 %s closing connection", MyHostName); 68661051Seric 68761051Seric /* avoid future 050 messages */ 68861051Seric Verbose = FALSE; 68961051Seric 6909339Seric if (InChild) 6919339Seric ExitStat = EX_QUIT; 6924549Seric finis(); 6934549Seric 6948544Seric case CMDVERB: /* set verbose mode */ 69559957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 69659957Seric { 69759957Seric /* this would give out the same info */ 69859957Seric message("502 Verbose unavailable"); 69959957Seric break; 70059957Seric } 7018544Seric Verbose = TRUE; 70258734Seric e->e_sendmode = SM_DELIVER; 70359957Seric message("250 Verbose mode"); 7048544Seric break; 7058544Seric 7069314Seric case CMDONEX: /* doing one transaction only */ 7079378Seric OneXact = TRUE; 70859957Seric message("250 Only one transaction"); 7099314Seric break; 7109314Seric 71136230Skarels # ifdef SMTPDEBUG 7129339Seric case CMDDBGQSHOW: /* show queues */ 7136907Seric printf("Send Queue="); 71455012Seric printaddr(e->e_sendqueue, TRUE); 7155003Seric break; 7167275Seric 7177275Seric case CMDDBGDEBUG: /* set debug mode */ 7187676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7197676Seric tTflag(p); 72058151Seric message("200 Debug set"); 7217275Seric break; 7227275Seric 72336230Skarels # else /* not SMTPDEBUG */ 72436230Skarels case CMDDBGQSHOW: /* show queues */ 72536230Skarels case CMDDBGDEBUG: /* set debug mode */ 72664685Seric # endif /* SMTPDEBUG */ 72764685Seric case CMDLOGBOGUS: /* bogus command */ 72836233Skarels # ifdef LOG 72958308Seric if (LogLevel > 0) 73064685Seric syslog(LOG_CRIT, 73158020Seric "\"%s\" command from %s (%s)", 732*66005Seric c->cmdname, peerhostname, 73358755Seric anynet_ntoa(&RealHostAddr)); 73436233Skarels # endif 73536230Skarels /* FALL THROUGH */ 73636230Skarels 7374549Seric case CMDERROR: /* unknown command */ 73858151Seric message("500 Command unrecognized"); 7394549Seric break; 7404549Seric 7414549Seric default: 74236230Skarels errno = 0; 74358151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7444549Seric break; 7454549Seric } 7464549Seric } 7474549Seric } 7484549Seric /* 7494549Seric ** SKIPWORD -- skip a fixed word. 7504549Seric ** 7514549Seric ** Parameters: 7524549Seric ** p -- place to start looking. 7534549Seric ** w -- word to skip. 7544549Seric ** 7554549Seric ** Returns: 7564549Seric ** p following w. 7574549Seric ** NULL on error. 7584549Seric ** 7594549Seric ** Side Effects: 7604549Seric ** clobbers the p data area. 7614549Seric */ 7624549Seric 7634549Seric static char * 7644549Seric skipword(p, w) 7654549Seric register char *p; 7664549Seric char *w; 7674549Seric { 7684549Seric register char *q; 769*66005Seric char *firstp = p; 7704549Seric 7714549Seric /* find beginning of word */ 77258050Seric while (isascii(*p) && isspace(*p)) 7734549Seric p++; 7744549Seric q = p; 7754549Seric 7764549Seric /* find end of word */ 77758050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7784549Seric p++; 77958050Seric while (isascii(*p) && isspace(*p)) 7804549Seric *p++ = '\0'; 7814549Seric if (*p != ':') 7824549Seric { 7834549Seric syntax: 784*66005Seric message("501 Syntax error in parameters scanning \"%s\"", 785*66005Seric firstp); 7864549Seric Errors++; 7874549Seric return (NULL); 7884549Seric } 7894549Seric *p++ = '\0'; 79058050Seric while (isascii(*p) && isspace(*p)) 7914549Seric p++; 7924549Seric 79362373Seric if (*p == '\0') 79462373Seric goto syntax; 79562373Seric 7964549Seric /* see if the input word matches desired word */ 79733725Sbostic if (strcasecmp(q, w)) 7984549Seric goto syntax; 7994549Seric 8004549Seric return (p); 8014549Seric } 8024577Seric /* 80358151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 80458151Seric ** 80558151Seric ** Parameters: 80658151Seric ** a -- the address to print 80758151Seric ** last -- set if this is the last one. 80858151Seric ** 80958151Seric ** Returns: 81058151Seric ** none. 81158151Seric ** 81258151Seric ** Side Effects: 81358151Seric ** Prints the appropriate 250 codes. 81458151Seric */ 81558151Seric 81658151Seric printvrfyaddr(a, last) 81758151Seric register ADDRESS *a; 81858151Seric bool last; 81958151Seric { 82058151Seric char fmtbuf[20]; 82158151Seric 82258151Seric strcpy(fmtbuf, "250"); 82358151Seric fmtbuf[3] = last ? ' ' : '-'; 82458151Seric 82559746Seric if (a->q_fullname == NULL) 82659746Seric { 82759746Seric if (strchr(a->q_user, '@') == NULL) 82859746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 82959746Seric else 83059746Seric strcpy(&fmtbuf[4], "<%s>"); 83159746Seric message(fmtbuf, a->q_user, MyHostName); 83259746Seric } 83358151Seric else 83458151Seric { 83559746Seric if (strchr(a->q_user, '@') == NULL) 83659746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 83759746Seric else 83859746Seric strcpy(&fmtbuf[4], "%s <%s>"); 83959746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 84058151Seric } 84158151Seric } 84258151Seric /* 8434577Seric ** HELP -- implement the HELP command. 8444577Seric ** 8454577Seric ** Parameters: 8464577Seric ** topic -- the topic we want help for. 8474577Seric ** 8484577Seric ** Returns: 8494577Seric ** none. 8504577Seric ** 8514577Seric ** Side Effects: 8524577Seric ** outputs the help file to message output. 8534577Seric */ 8544577Seric 8554577Seric help(topic) 8564577Seric char *topic; 8574577Seric { 8584577Seric register FILE *hf; 8594577Seric int len; 8604577Seric char buf[MAXLINE]; 8614577Seric bool noinfo; 8624577Seric 8638269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8644577Seric { 8654577Seric /* no help */ 86611931Seric errno = 0; 86758151Seric message("502 HELP not implemented"); 8684577Seric return; 8694577Seric } 8704577Seric 87149669Seric if (topic == NULL || *topic == '\0') 87249669Seric topic = "smtp"; 87349669Seric else 87449669Seric makelower(topic); 87549669Seric 8764577Seric len = strlen(topic); 8774577Seric noinfo = TRUE; 8784577Seric 8794577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8804577Seric { 8814577Seric if (strncmp(buf, topic, len) == 0) 8824577Seric { 8834577Seric register char *p; 8844577Seric 88556795Seric p = strchr(buf, '\t'); 8864577Seric if (p == NULL) 8874577Seric p = buf; 8884577Seric else 8894577Seric p++; 8904577Seric fixcrlf(p, TRUE); 89158151Seric message("214-%s", p); 8924577Seric noinfo = FALSE; 8934577Seric } 8944577Seric } 8954577Seric 8964577Seric if (noinfo) 89758151Seric message("504 HELP topic unknown"); 8984577Seric else 89958151Seric message("214 End of HELP info"); 9004628Seric (void) fclose(hf); 9014577Seric } 9028544Seric /* 9039339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 9049339Seric ** 9059339Seric ** Parameters: 9069339Seric ** label -- a string used in error messages 9079339Seric ** 9089339Seric ** Returns: 9099339Seric ** zero in the child 9109339Seric ** one in the parent 9119339Seric ** 9129339Seric ** Side Effects: 9139339Seric ** none. 9149339Seric */ 9158544Seric 91655012Seric runinchild(label, e) 9179339Seric char *label; 91855012Seric register ENVELOPE *e; 9199339Seric { 9209339Seric int childpid; 9219339Seric 92216158Seric if (!OneXact) 9239339Seric { 92416158Seric childpid = dofork(); 92516158Seric if (childpid < 0) 92616158Seric { 92716158Seric syserr("%s: cannot fork", label); 92816158Seric return (1); 92916158Seric } 93016158Seric if (childpid > 0) 93116158Seric { 93216158Seric auto int st; 9339339Seric 93416158Seric /* parent -- wait for child to complete */ 93561093Seric setproctitle("server %s child wait", CurHostName); 93616158Seric st = waitfor(childpid); 93716158Seric if (st == -1) 93816158Seric syserr("%s: lost child", label); 93964948Seric else if (!WIFEXITED(st)) 94064948Seric syserr("%s: died on signal %d", 94164948Seric label, st & 0177); 9429339Seric 94316158Seric /* if we exited on a QUIT command, complete the process */ 94416158Seric if (st == (EX_QUIT << 8)) 94516158Seric finis(); 9469339Seric 94716158Seric return (1); 94816158Seric } 94916158Seric else 95016158Seric { 95116158Seric /* child */ 95216158Seric InChild = TRUE; 95325050Seric QuickAbort = FALSE; 95455012Seric clearenvelope(e, FALSE); 95516158Seric } 9569339Seric } 95715256Seric 95816158Seric /* open alias database */ 95960537Seric initmaps(FALSE, e); 96016158Seric 96116158Seric return (0); 9629339Seric } 9639339Seric 96456795Seric # endif /* SMTP */ 965