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*66283Seric static char sccsid[] = "@(#)srvrsmtp.c 8.30 (Berkeley) 02/28/94 (with SMTP)"; 1433731Sbostic #else 15*66283Seric static char sccsid[] = "@(#)srvrsmtp.c 8.30 (Berkeley) 02/28/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 95*66283Seric #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 96*66283Seric 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 */ 11166005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11258333Seric auto char *delimptr; 11358714Seric char *id; 11459747Seric int nrcpts; /* number of RCPT commands */ 11563787Seric bool doublequeue; 116*66283Seric int badcommands = 0; /* count of bad commands */ 1178544Seric char inp[MAXLINE]; 11857232Seric char cmdbuf[MAXLINE]; 1197124Seric extern char Version[]; 12024943Seric extern ENVELOPE BlankEnvelope; 1214549Seric 12259066Seric if (fileno(OutChannel) != fileno(stdout)) 1237363Seric { 1247363Seric /* arrange for debugging output to go to remote host */ 12559066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1267363Seric } 12755012Seric settime(e); 12866005Seric peerhostname = RealHostName; 12966005Seric if (peerhostname == NULL) 13066005Seric peerhostname = "localhost"; 13166005Seric CurHostName = peerhostname; 13265017Seric CurSmtpClient = macvalue('_', e); 13365017Seric if (CurSmtpClient == NULL) 13466003Seric CurSmtpClient = CurHostName; 13565017Seric 13665017Seric setproctitle("server %s startup", CurSmtpClient); 13758050Seric expand("\201e", inp, &inp[sizeof inp], e); 13864496Seric if (BrokenSmtpPeers) 13964496Seric { 14064496Seric message("220 %s", inp); 14164496Seric } 14264496Seric else 14364496Seric { 14464496Seric message("220-%s", inp); 14564496Seric message("220 ESMTP spoken here"); 14664496Seric } 14758330Seric protocol = NULL; 14859016Seric sendinghost = macvalue('s', e); 14958082Seric gothello = FALSE; 15058330Seric gotmail = FALSE; 1514549Seric for (;;) 1524549Seric { 15312612Seric /* arrange for backout */ 15465751Seric if (setjmp(TopFrame) > 0) 15559058Seric { 15665751Seric /* if() nesting is necessary for Cray UNICOS */ 15765751Seric if (InChild) 15865751Seric { 15965751Seric QuickAbort = FALSE; 16065751Seric SuprErrs = TRUE; 16165751Seric finis(); 16265751Seric } 16359058Seric } 16412612Seric QuickAbort = FALSE; 16512612Seric HoldErrs = FALSE; 16651951Seric LogUsrErrs = FALSE; 16763843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 16812612Seric 1697356Seric /* setup for the read */ 17055012Seric e->e_to = NULL; 1714577Seric Errors = 0; 1727275Seric (void) fflush(stdout); 1737356Seric 1747356Seric /* read the input line */ 17561093Seric SmtpPhase = "server cmd read"; 17661093Seric setproctitle("server %s cmd read", CurHostName); 17761093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 17861093Seric SmtpPhase); 1797356Seric 1807685Seric /* handle errors */ 1817356Seric if (p == NULL) 1827356Seric { 1834549Seric /* end of file, just die */ 18466017Seric disconnect(1, e); 18558151Seric message("421 %s Lost input channel from %s", 18665017Seric MyHostName, CurSmtpClient); 18755464Seric #ifdef LOG 18863843Seric if (LogLevel > (gotmail ? 1 : 19)) 18955464Seric syslog(LOG_NOTICE, "lost input channel from %s", 19065017Seric CurSmtpClient); 19155464Seric #endif 19258069Seric if (InChild) 19358069Seric ExitStat = EX_QUIT; 1944549Seric finis(); 1954549Seric } 1964549Seric 1974549Seric /* clean up end of line */ 1984558Seric fixcrlf(inp, TRUE); 1994549Seric 2004713Seric /* echo command to transcript */ 20155012Seric if (e->e_xfp != NULL) 20255012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2034713Seric 20459060Seric if (e->e_id == NULL) 20565058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 20659060Seric else 20765058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 20859060Seric 2094549Seric /* break off command */ 21058050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2114549Seric continue; 21257232Seric cmd = cmdbuf; 21358050Seric while (*p != '\0' && 21458050Seric !(isascii(*p) && isspace(*p)) && 21558050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 21624981Seric *cmd++ = *p++; 21724981Seric *cmd = '\0'; 2184549Seric 21925691Seric /* throw away leading whitespace */ 22058050Seric while (isascii(*p) && isspace(*p)) 22125691Seric p++; 22225691Seric 2234549Seric /* decode command */ 2244549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2254549Seric { 22633725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2274549Seric break; 2284549Seric } 2294549Seric 23051954Seric /* reset errors */ 23151954Seric errno = 0; 23251954Seric 2334549Seric /* process command */ 2344549Seric switch (c->cmdcode) 2354549Seric { 2364976Seric case CMDHELO: /* hello -- introduce yourself */ 23758323Seric case CMDEHLO: /* extended hello */ 23858323Seric if (c->cmdcode == CMDEHLO) 23958323Seric { 24058323Seric protocol = "ESMTP"; 24161093Seric SmtpPhase = "server EHLO"; 24258323Seric } 24358323Seric else 24458323Seric { 24558323Seric protocol = "SMTP"; 24661093Seric SmtpPhase = "server HELO"; 24758323Seric } 24859016Seric sendinghost = newstr(p); 24960210Seric gothello = TRUE; 25060210Seric if (c->cmdcode != CMDEHLO) 25160239Seric { 25260239Seric /* print old message and be done with it */ 25360239Seric message("250 %s Hello %s, pleased to meet you", 25465017Seric MyHostName, CurSmtpClient); 25560210Seric break; 25660239Seric } 25760239Seric 25860239Seric /* print extended message and brag */ 25960239Seric message("250-%s Hello %s, pleased to meet you", 26060239Seric MyHostName, p); 26158323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 26258323Seric message("250-EXPN"); 26364359Seric if (MaxMessageSize > 0) 26464359Seric message("250-SIZE %ld", MaxMessageSize); 26559271Seric else 26659271Seric message("250-SIZE"); 26758323Seric message("250 HELP"); 2684976Seric break; 2694976Seric 2704549Seric case CMDMAIL: /* mail -- designate sender */ 27161093Seric SmtpPhase = "server MAIL"; 27224943Seric 2739314Seric /* check for validity of this command */ 27458789Seric if (!gothello) 27558082Seric { 27658957Seric /* set sending host to our known value */ 27759016Seric if (sendinghost == NULL) 27866005Seric sendinghost = peerhostname; 27958957Seric 28058789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 28158821Seric { 28258789Seric message("503 Polite people say HELO first"); 28358821Seric break; 28458821Seric } 28558082Seric } 28658109Seric if (gotmail) 2874558Seric { 28858151Seric message("503 Sender already specified"); 28963843Seric if (InChild) 29063843Seric finis(); 2914558Seric break; 2924558Seric } 2939339Seric if (InChild) 2949339Seric { 29536230Skarels errno = 0; 29658151Seric syserr("503 Nested MAIL command: MAIL %s", p); 29758069Seric finis(); 2989339Seric } 2999339Seric 3009339Seric /* fork a subprocess to process this command */ 30155012Seric if (runinchild("SMTP-MAIL", e) > 0) 3029339Seric break; 30363753Seric if (!gothello) 30463753Seric { 30563753Seric auth_warning(e, 30666005Seric "Host %s didn't use HELO protocol", 30766005Seric peerhostname); 30863753Seric } 30965947Seric #ifdef PICKY_HELO_CHECK 31066005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 31166005Seric (strcasecmp(peerhostname, "localhost") != 0 || 31265823Seric strcasecmp(sendinghost, MyHostName) != 0)) 31365823Seric { 31465823Seric auth_warning(e, "Host %s claimed to be %s", 31566005Seric peerhostname, sendinghost); 31665823Seric } 31765947Seric #endif 31865823Seric 31958323Seric if (protocol == NULL) 32058323Seric protocol = "SMTP"; 32158323Seric define('r', protocol, e); 32259016Seric define('s', sendinghost, e); 32355012Seric initsys(e); 32459747Seric nrcpts = 0; 32565089Seric e->e_flags |= EF_LOGSENDER; 32665058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3279339Seric 3289339Seric /* child -- go do the processing */ 3294549Seric p = skipword(p, "from"); 3304549Seric if (p == NULL) 3314549Seric break; 33257977Seric if (setjmp(TopFrame) > 0) 33358147Seric { 33458147Seric /* this failed -- undo work */ 33558147Seric if (InChild) 33659058Seric { 33759058Seric QuickAbort = FALSE; 33859058Seric SuprErrs = TRUE; 33963787Seric e->e_flags &= ~EF_FATALERRS; 34058147Seric finis(); 34159058Seric } 34257977Seric break; 34358147Seric } 34457977Seric QuickAbort = TRUE; 34558333Seric 34658333Seric /* must parse sender first */ 34758333Seric delimptr = NULL; 34858704Seric setsender(p, e, &delimptr, FALSE); 34958333Seric p = delimptr; 35058333Seric if (p != NULL && *p != '\0') 35158333Seric *p++ = '\0'; 35258333Seric 35358333Seric /* now parse ESMTP arguments */ 35458333Seric msize = 0; 35558333Seric for (; p != NULL && *p != '\0'; p++) 35658333Seric { 35758333Seric char *kp; 35858333Seric char *vp; 35958333Seric 36058333Seric /* locate the beginning of the keyword */ 36158333Seric while (isascii(*p) && isspace(*p)) 36258333Seric p++; 36358333Seric if (*p == '\0') 36458333Seric break; 36558333Seric kp = p; 36658333Seric 36758333Seric /* skip to the value portion */ 36858333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 36958333Seric p++; 37058333Seric if (*p == '=') 37158333Seric { 37258333Seric *p++ = '\0'; 37358333Seric vp = p; 37458333Seric 37558333Seric /* skip to the end of the value */ 37658333Seric while (*p != '\0' && *p != ' ' && 37758333Seric !(isascii(*p) && iscntrl(*p)) && 37858333Seric *p != '=') 37958333Seric p++; 38058333Seric } 38158333Seric 38258333Seric if (*p != '\0') 38358333Seric *p++ = '\0'; 38458333Seric 38558333Seric if (tTd(19, 1)) 38658333Seric printf("MAIL: got arg %s=%s\n", kp, 38758333Seric vp == NULL ? "<null>" : vp); 38858333Seric 38958333Seric if (strcasecmp(kp, "size") == 0) 39058333Seric { 39159093Seric if (vp == NULL) 39258333Seric { 39358333Seric usrerr("501 SIZE requires a value"); 39458333Seric /* NOTREACHED */ 39558333Seric } 39658333Seric msize = atol(vp); 39758333Seric } 39859093Seric else if (strcasecmp(kp, "body") == 0) 39959093Seric { 40059093Seric if (vp == NULL) 40159093Seric { 40259093Seric usrerr("501 BODY requires a value"); 40359093Seric /* NOTREACHED */ 40459093Seric } 40559093Seric # ifdef MIME 40659093Seric if (strcasecmp(vp, "8bitmime") == 0) 40759093Seric { 40859093Seric e->e_bodytype = "8BITMIME"; 40959709Seric SevenBit = FALSE; 41059093Seric } 41159093Seric else if (strcasecmp(vp, "7bit") == 0) 41259093Seric { 41359093Seric e->e_bodytype = "7BIT"; 41459709Seric SevenBit = TRUE; 41559093Seric } 41659093Seric else 41759093Seric { 41859093Seric usrerr("501 Unknown BODY type %s", 41959093Seric vp); 42059093Seric } 42159093Seric # endif 42259093Seric } 42358333Seric else 42458333Seric { 42558333Seric usrerr("501 %s parameter unrecognized", kp); 42658333Seric /* NOTREACHED */ 42758333Seric } 42858333Seric } 42959284Seric 43059284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 43159284Seric { 43259284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 43359284Seric MaxMessageSize); 43459284Seric /* NOTREACHED */ 43559284Seric } 43658333Seric 43758333Seric if (!enoughspace(msize)) 43858333Seric { 43958333Seric message("452 Insufficient disk space; try again later"); 44058333Seric break; 44158333Seric } 44258151Seric message("250 Sender ok"); 44358147Seric gotmail = TRUE; 4444549Seric break; 4454549Seric 4464976Seric case CMDRCPT: /* rcpt -- designate recipient */ 44758850Seric if (!gotmail) 44858850Seric { 44958850Seric usrerr("503 Need MAIL before RCPT"); 45058850Seric break; 45158850Seric } 45261093Seric SmtpPhase = "server RCPT"; 45312612Seric if (setjmp(TopFrame) > 0) 45414785Seric { 45555012Seric e->e_flags &= ~EF_FATALERRS; 45612612Seric break; 45714785Seric } 45812612Seric QuickAbort = TRUE; 45951951Seric LogUsrErrs = TRUE; 46058093Seric 46159699Seric if (e->e_sendmode != SM_DELIVER) 46259699Seric e->e_flags |= EF_VRFYONLY; 46358919Seric 4644549Seric p = skipword(p, "to"); 4654549Seric if (p == NULL) 4664549Seric break; 46764284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 46812612Seric if (a == NULL) 46912612Seric break; 47016886Seric a->q_flags |= QPRIMARY; 47155012Seric a = recipient(a, &e->e_sendqueue, e); 47212612Seric if (Errors != 0) 47312612Seric break; 47412612Seric 47512612Seric /* no errors during parsing, but might be a duplicate */ 47655012Seric e->e_to = p; 47712612Seric if (!bitset(QBADADDR, a->q_flags)) 47859747Seric { 47964718Seric message("250 Recipient ok%s", 48064718Seric bitset(QQUEUEUP, a->q_flags) ? 48164718Seric " (will queue)" : ""); 48259747Seric nrcpts++; 48359747Seric } 48412612Seric else 4854549Seric { 48612612Seric /* punt -- should keep message in ADDRESS.... */ 48758151Seric message("550 Addressee unknown"); 4884549Seric } 48955012Seric e->e_to = NULL; 4904549Seric break; 4914549Seric 4924549Seric case CMDDATA: /* data -- text of mail */ 49361093Seric SmtpPhase = "server DATA"; 49458109Seric if (!gotmail) 4954549Seric { 49658151Seric message("503 Need MAIL command"); 4974976Seric break; 4984549Seric } 49964718Seric else if (nrcpts <= 0) 5004549Seric { 50158151Seric message("503 Need RCPT (recipient)"); 5024976Seric break; 5034549Seric } 5044976Seric 50558929Seric /* check to see if we need to re-expand aliases */ 50663787Seric /* also reset QBADADDR on already-diagnosted addrs */ 50763787Seric doublequeue = FALSE; 50858929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 50958929Seric { 51058929Seric if (bitset(QVERIFIED, a->q_flags)) 51163787Seric { 51263787Seric /* need to re-expand aliases */ 51363787Seric doublequeue = TRUE; 51463787Seric } 51563787Seric if (bitset(QBADADDR, a->q_flags)) 51663787Seric { 51763787Seric /* make this "go away" */ 51863787Seric a->q_flags |= QDONTSEND; 51963787Seric a->q_flags &= ~QBADADDR; 52063787Seric } 52158929Seric } 52258929Seric 5234976Seric /* collect the text of the message */ 52424943Seric SmtpPhase = "collect"; 52564766Seric collect(TRUE, doublequeue, e); 52664766Seric if (Errors != 0) 52764766Seric goto abortmessage; 52863965Seric HoldErrs = TRUE; 5294976Seric 5308238Seric /* 5318238Seric ** Arrange to send to everyone. 5328238Seric ** If sending to multiple people, mail back 5338238Seric ** errors rather than reporting directly. 5348238Seric ** In any case, don't mail back errors for 5358238Seric ** anything that has happened up to 5368238Seric ** now (the other end will do this). 53710197Seric ** Truncate our transcript -- the mail has gotten 53810197Seric ** to us successfully, and if we have 53910197Seric ** to mail this back, it will be easier 54010197Seric ** on the reader. 5418238Seric ** Then send to everyone. 5428238Seric ** Finally give a reply code. If an error has 5438238Seric ** already been given, don't mail a 5448238Seric ** message back. 5459339Seric ** We goose error returns by clearing error bit. 5468238Seric */ 5478238Seric 54824943Seric SmtpPhase = "delivery"; 54963787Seric if (nrcpts != 1 && !doublequeue) 5509378Seric { 5519378Seric HoldErrs = TRUE; 55258734Seric e->e_errormode = EM_MAIL; 5539378Seric } 55455012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 55558714Seric id = e->e_id; 5564976Seric 5574976Seric /* send to all recipients */ 55863787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 55955012Seric e->e_to = NULL; 5604976Seric 5618238Seric /* issue success if appropriate and reset */ 5628238Seric if (Errors == 0 || HoldErrs) 56358855Seric message("250 %s Message accepted for delivery", id); 56459747Seric 56559747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 56659730Seric { 56759730Seric /* avoid sending back an extra message */ 56859730Seric e->e_flags &= ~EF_FATALERRS; 56959747Seric e->e_flags |= EF_CLRQUEUE; 57059730Seric } 5718238Seric else 57258919Seric { 57359747Seric /* from now on, we have to operate silently */ 57459747Seric HoldErrs = TRUE; 57559747Seric e->e_errormode = EM_MAIL; 57659747Seric 57759730Seric /* if we just queued, poke it */ 57863787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 57959730Seric { 58064296Seric extern pid_t dowork(); 58164296Seric 58259730Seric unlockqueue(e); 58364296Seric (void) dowork(id, TRUE, TRUE, e); 58459730Seric } 58558919Seric } 58658883Seric 58759747Seric abortmessage: 5889339Seric /* if in a child, pop back to our parent */ 5899339Seric if (InChild) 5909339Seric finis(); 59124943Seric 59224943Seric /* clean up a bit */ 59358109Seric gotmail = FALSE; 59455012Seric dropenvelope(e); 59558179Seric CurEnv = e = newenvelope(e, CurEnv); 59655012Seric e->e_flags = BlankEnvelope.e_flags; 5974549Seric break; 5984549Seric 5994549Seric case CMDRSET: /* rset -- reset state */ 60058151Seric message("250 Reset state"); 60164359Seric e->e_flags |= EF_CLRQUEUE; 6029339Seric if (InChild) 6039339Seric finis(); 60458109Seric 60558109Seric /* clean up a bit */ 60658109Seric gotmail = FALSE; 60758109Seric dropenvelope(e); 60858179Seric CurEnv = e = newenvelope(e, CurEnv); 6099339Seric break; 6104549Seric 6114549Seric case CMDVRFY: /* vrfy -- verify address */ 61258092Seric case CMDEXPN: /* expn -- expand address */ 61358092Seric vrfy = c->cmdcode == CMDVRFY; 61458092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 61558092Seric PrivacyFlags)) 61658082Seric { 61758412Seric if (vrfy) 61858412Seric message("252 Who's to say?"); 61958412Seric else 62065192Seric message("502 Sorry, we do not allow this operation"); 62165017Seric #ifdef LOG 62265017Seric if (LogLevel > 5) 62365017Seric syslog(LOG_INFO, "%s: %s [rejected]", 62465017Seric CurSmtpClient, inp); 62565017Seric #endif 62658082Seric break; 62758082Seric } 62858082Seric else if (!gothello && 62958092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 63058092Seric PrivacyFlags)) 63158082Seric { 63258151Seric message("503 I demand that you introduce yourself first"); 63358082Seric break; 63458082Seric } 63558092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6369339Seric break; 63755173Seric #ifdef LOG 63858020Seric if (LogLevel > 5) 63965017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 64055173Seric #endif 6415003Seric vrfyqueue = NULL; 6427762Seric QuickAbort = TRUE; 64358092Seric if (vrfy) 64458092Seric e->e_flags |= EF_VRFYONLY; 64562373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 64662373Seric *p++; 64762373Seric if (*p == '\0') 64862373Seric { 64962373Seric message("501 Argument required"); 65062373Seric Errors++; 65162373Seric } 65262373Seric else 65362373Seric { 65464284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 65562373Seric } 6567762Seric if (Errors != 0) 6579339Seric { 6589339Seric if (InChild) 6599339Seric finis(); 6607762Seric break; 6619339Seric } 66262373Seric if (vrfyqueue == NULL) 66362373Seric { 66462373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 66562373Seric } 6665003Seric while (vrfyqueue != NULL) 6675003Seric { 66863971Seric a = vrfyqueue; 66963971Seric while ((a = a->q_next) != NULL && 67063971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 67163971Seric continue; 6727685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 67358151Seric printvrfyaddr(vrfyqueue, a == NULL); 67463847Seric vrfyqueue = vrfyqueue->q_next; 6755003Seric } 6769339Seric if (InChild) 6779339Seric finis(); 6784549Seric break; 6794549Seric 6804549Seric case CMDHELP: /* help -- give user info */ 6814577Seric help(p); 6824549Seric break; 6834549Seric 6844549Seric case CMDNOOP: /* noop -- do nothing */ 68564122Seric message("250 OK"); 6864549Seric break; 6874549Seric 6884549Seric case CMDQUIT: /* quit -- leave mail */ 68958151Seric message("221 %s closing connection", MyHostName); 69061051Seric 691*66283Seric doquit: 69261051Seric /* avoid future 050 messages */ 69366017Seric disconnect(1, e); 69461051Seric 6959339Seric if (InChild) 6969339Seric ExitStat = EX_QUIT; 6974549Seric finis(); 6984549Seric 6998544Seric case CMDVERB: /* set verbose mode */ 70059957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 70159957Seric { 70259957Seric /* this would give out the same info */ 70359957Seric message("502 Verbose unavailable"); 70459957Seric break; 70559957Seric } 7068544Seric Verbose = TRUE; 70758734Seric e->e_sendmode = SM_DELIVER; 70859957Seric message("250 Verbose mode"); 7098544Seric break; 7108544Seric 7119314Seric case CMDONEX: /* doing one transaction only */ 7129378Seric OneXact = TRUE; 71359957Seric message("250 Only one transaction"); 7149314Seric break; 7159314Seric 71636230Skarels # ifdef SMTPDEBUG 7179339Seric case CMDDBGQSHOW: /* show queues */ 7186907Seric printf("Send Queue="); 71955012Seric printaddr(e->e_sendqueue, TRUE); 7205003Seric break; 7217275Seric 7227275Seric case CMDDBGDEBUG: /* set debug mode */ 7237676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7247676Seric tTflag(p); 72558151Seric message("200 Debug set"); 7267275Seric break; 7277275Seric 72836230Skarels # else /* not SMTPDEBUG */ 72936230Skarels case CMDDBGQSHOW: /* show queues */ 73036230Skarels case CMDDBGDEBUG: /* set debug mode */ 73164685Seric # endif /* SMTPDEBUG */ 73264685Seric case CMDLOGBOGUS: /* bogus command */ 73336233Skarels # ifdef LOG 73458308Seric if (LogLevel > 0) 73564685Seric syslog(LOG_CRIT, 73658020Seric "\"%s\" command from %s (%s)", 73766005Seric c->cmdname, peerhostname, 73858755Seric anynet_ntoa(&RealHostAddr)); 73936233Skarels # endif 74036230Skarels /* FALL THROUGH */ 74136230Skarels 7424549Seric case CMDERROR: /* unknown command */ 743*66283Seric if (++badcommands > MAXBADCOMMANDS) 744*66283Seric { 745*66283Seric message("421 %s Too many bad commands; closing connection", 746*66283Seric MyHostName); 747*66283Seric goto doquit; 748*66283Seric } 749*66283Seric 75058151Seric message("500 Command unrecognized"); 7514549Seric break; 7524549Seric 7534549Seric default: 75436230Skarels errno = 0; 75558151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7564549Seric break; 7574549Seric } 7584549Seric } 7594549Seric } 7604549Seric /* 7614549Seric ** SKIPWORD -- skip a fixed word. 7624549Seric ** 7634549Seric ** Parameters: 7644549Seric ** p -- place to start looking. 7654549Seric ** w -- word to skip. 7664549Seric ** 7674549Seric ** Returns: 7684549Seric ** p following w. 7694549Seric ** NULL on error. 7704549Seric ** 7714549Seric ** Side Effects: 7724549Seric ** clobbers the p data area. 7734549Seric */ 7744549Seric 7754549Seric static char * 7764549Seric skipword(p, w) 7774549Seric register char *p; 7784549Seric char *w; 7794549Seric { 7804549Seric register char *q; 78166005Seric char *firstp = p; 7824549Seric 7834549Seric /* find beginning of word */ 78458050Seric while (isascii(*p) && isspace(*p)) 7854549Seric p++; 7864549Seric q = p; 7874549Seric 7884549Seric /* find end of word */ 78958050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7904549Seric p++; 79158050Seric while (isascii(*p) && isspace(*p)) 7924549Seric *p++ = '\0'; 7934549Seric if (*p != ':') 7944549Seric { 7954549Seric syntax: 79666005Seric message("501 Syntax error in parameters scanning \"%s\"", 79766005Seric firstp); 7984549Seric Errors++; 7994549Seric return (NULL); 8004549Seric } 8014549Seric *p++ = '\0'; 80258050Seric while (isascii(*p) && isspace(*p)) 8034549Seric p++; 8044549Seric 80562373Seric if (*p == '\0') 80662373Seric goto syntax; 80762373Seric 8084549Seric /* see if the input word matches desired word */ 80933725Sbostic if (strcasecmp(q, w)) 8104549Seric goto syntax; 8114549Seric 8124549Seric return (p); 8134549Seric } 8144577Seric /* 81558151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 81658151Seric ** 81758151Seric ** Parameters: 81858151Seric ** a -- the address to print 81958151Seric ** last -- set if this is the last one. 82058151Seric ** 82158151Seric ** Returns: 82258151Seric ** none. 82358151Seric ** 82458151Seric ** Side Effects: 82558151Seric ** Prints the appropriate 250 codes. 82658151Seric */ 82758151Seric 82858151Seric printvrfyaddr(a, last) 82958151Seric register ADDRESS *a; 83058151Seric bool last; 83158151Seric { 83258151Seric char fmtbuf[20]; 83358151Seric 83458151Seric strcpy(fmtbuf, "250"); 83558151Seric fmtbuf[3] = last ? ' ' : '-'; 83658151Seric 83759746Seric if (a->q_fullname == NULL) 83859746Seric { 83959746Seric if (strchr(a->q_user, '@') == NULL) 84059746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 84159746Seric else 84259746Seric strcpy(&fmtbuf[4], "<%s>"); 84359746Seric message(fmtbuf, a->q_user, MyHostName); 84459746Seric } 84558151Seric else 84658151Seric { 84759746Seric if (strchr(a->q_user, '@') == NULL) 84859746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 84959746Seric else 85059746Seric strcpy(&fmtbuf[4], "%s <%s>"); 85159746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 85258151Seric } 85358151Seric } 85458151Seric /* 8554577Seric ** HELP -- implement the HELP command. 8564577Seric ** 8574577Seric ** Parameters: 8584577Seric ** topic -- the topic we want help for. 8594577Seric ** 8604577Seric ** Returns: 8614577Seric ** none. 8624577Seric ** 8634577Seric ** Side Effects: 8644577Seric ** outputs the help file to message output. 8654577Seric */ 8664577Seric 8674577Seric help(topic) 8684577Seric char *topic; 8694577Seric { 8704577Seric register FILE *hf; 8714577Seric int len; 8724577Seric char buf[MAXLINE]; 8734577Seric bool noinfo; 8744577Seric 8758269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8764577Seric { 8774577Seric /* no help */ 87811931Seric errno = 0; 87958151Seric message("502 HELP not implemented"); 8804577Seric return; 8814577Seric } 8824577Seric 88349669Seric if (topic == NULL || *topic == '\0') 88449669Seric topic = "smtp"; 88549669Seric else 88649669Seric makelower(topic); 88749669Seric 8884577Seric len = strlen(topic); 8894577Seric noinfo = TRUE; 8904577Seric 8914577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8924577Seric { 8934577Seric if (strncmp(buf, topic, len) == 0) 8944577Seric { 8954577Seric register char *p; 8964577Seric 89756795Seric p = strchr(buf, '\t'); 8984577Seric if (p == NULL) 8994577Seric p = buf; 9004577Seric else 9014577Seric p++; 9024577Seric fixcrlf(p, TRUE); 90358151Seric message("214-%s", p); 9044577Seric noinfo = FALSE; 9054577Seric } 9064577Seric } 9074577Seric 9084577Seric if (noinfo) 90958151Seric message("504 HELP topic unknown"); 9104577Seric else 91158151Seric message("214 End of HELP info"); 9124628Seric (void) fclose(hf); 9134577Seric } 9148544Seric /* 9159339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 9169339Seric ** 9179339Seric ** Parameters: 9189339Seric ** label -- a string used in error messages 9199339Seric ** 9209339Seric ** Returns: 9219339Seric ** zero in the child 9229339Seric ** one in the parent 9239339Seric ** 9249339Seric ** Side Effects: 9259339Seric ** none. 9269339Seric */ 9278544Seric 92855012Seric runinchild(label, e) 9299339Seric char *label; 93055012Seric register ENVELOPE *e; 9319339Seric { 9329339Seric int childpid; 9339339Seric 93416158Seric if (!OneXact) 9359339Seric { 93616158Seric childpid = dofork(); 93716158Seric if (childpid < 0) 93816158Seric { 93916158Seric syserr("%s: cannot fork", label); 94016158Seric return (1); 94116158Seric } 94216158Seric if (childpid > 0) 94316158Seric { 94416158Seric auto int st; 9459339Seric 94616158Seric /* parent -- wait for child to complete */ 94761093Seric setproctitle("server %s child wait", CurHostName); 94816158Seric st = waitfor(childpid); 94916158Seric if (st == -1) 95016158Seric syserr("%s: lost child", label); 95164948Seric else if (!WIFEXITED(st)) 95264948Seric syserr("%s: died on signal %d", 95364948Seric label, st & 0177); 9549339Seric 95516158Seric /* if we exited on a QUIT command, complete the process */ 95666017Seric if (WEXITSTATUS(st) == EX_QUIT) 95766017Seric { 95866017Seric disconnect(1, e); 95916158Seric finis(); 96066017Seric } 9619339Seric 96216158Seric return (1); 96316158Seric } 96416158Seric else 96516158Seric { 96616158Seric /* child */ 96716158Seric InChild = TRUE; 96825050Seric QuickAbort = FALSE; 96955012Seric clearenvelope(e, FALSE); 97016158Seric } 9719339Seric } 97215256Seric 97316158Seric /* open alias database */ 97460537Seric initmaps(FALSE, e); 97516158Seric 97616158Seric return (0); 9779339Seric } 9789339Seric 97956795Seric # endif /* SMTP */ 980