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*66003Seric static char sccsid[] = "@(#)srvrsmtp.c 8.27 (Berkeley) 02/05/94 (with SMTP)"; 1433731Sbostic #else 15*66003Seric static char sccsid[] = "@(#)srvrsmtp.c 8.27 (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 95*66003Seric #define REALHOSTNAME (RealHostName == NULL ? "localhost" : RealHostName) 96*66003Seric 9755012Seric smtp(e) 9855012Seric register ENVELOPE *e; 994549Seric { 1004549Seric register char *p; 1018544Seric register struct cmd *c; 1024549Seric char *cmd; 1035003Seric auto ADDRESS *vrfyqueue; 10412612Seric ADDRESS *a; 10558109Seric bool gotmail; /* mail command received */ 10658092Seric bool gothello; /* helo command received */ 10758092Seric bool vrfy; /* set if this is a vrfy command */ 10858323Seric char *protocol; /* sending protocol */ 10959016Seric char *sendinghost; /* sending hostname */ 11058333Seric long msize; /* approximate maximum message size */ 11158333Seric auto char *delimptr; 11258714Seric char *id; 11359747Seric int nrcpts; /* number of RCPT commands */ 11463787Seric bool doublequeue; 1158544Seric char inp[MAXLINE]; 11657232Seric char cmdbuf[MAXLINE]; 1177124Seric extern char Version[]; 11824943Seric extern ENVELOPE BlankEnvelope; 1194549Seric 12059066Seric if (fileno(OutChannel) != fileno(stdout)) 1217363Seric { 1227363Seric /* arrange for debugging output to go to remote host */ 12359066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1247363Seric } 12555012Seric settime(e); 126*66003Seric CurHostName = REALHOSTNAME; 12765017Seric CurSmtpClient = macvalue('_', e); 12865017Seric if (CurSmtpClient == NULL) 129*66003Seric CurSmtpClient = CurHostName; 13065017Seric 13165017Seric setproctitle("server %s startup", CurSmtpClient); 13258050Seric expand("\201e", inp, &inp[sizeof inp], e); 13364496Seric if (BrokenSmtpPeers) 13464496Seric { 13564496Seric message("220 %s", inp); 13664496Seric } 13764496Seric else 13864496Seric { 13964496Seric message("220-%s", inp); 14064496Seric message("220 ESMTP spoken here"); 14164496Seric } 14258330Seric protocol = NULL; 14359016Seric sendinghost = macvalue('s', e); 14458082Seric gothello = FALSE; 14558330Seric gotmail = FALSE; 1464549Seric for (;;) 1474549Seric { 14812612Seric /* arrange for backout */ 14965751Seric if (setjmp(TopFrame) > 0) 15059058Seric { 15165751Seric /* if() nesting is necessary for Cray UNICOS */ 15265751Seric if (InChild) 15365751Seric { 15465751Seric QuickAbort = FALSE; 15565751Seric SuprErrs = TRUE; 15665751Seric finis(); 15765751Seric } 15859058Seric } 15912612Seric QuickAbort = FALSE; 16012612Seric HoldErrs = FALSE; 16151951Seric LogUsrErrs = FALSE; 16263843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 16312612Seric 1647356Seric /* setup for the read */ 16555012Seric e->e_to = NULL; 1664577Seric Errors = 0; 1677275Seric (void) fflush(stdout); 1687356Seric 1697356Seric /* read the input line */ 17061093Seric SmtpPhase = "server cmd read"; 17161093Seric setproctitle("server %s cmd read", CurHostName); 17261093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 17361093Seric SmtpPhase); 1747356Seric 1757685Seric /* handle errors */ 1767356Seric if (p == NULL) 1777356Seric { 1784549Seric /* end of file, just die */ 17958151Seric message("421 %s Lost input channel from %s", 18065017Seric MyHostName, CurSmtpClient); 18155464Seric #ifdef LOG 18263843Seric if (LogLevel > (gotmail ? 1 : 19)) 18355464Seric syslog(LOG_NOTICE, "lost input channel from %s", 18465017Seric CurSmtpClient); 18555464Seric #endif 18658069Seric if (InChild) 18758069Seric ExitStat = EX_QUIT; 1884549Seric finis(); 1894549Seric } 1904549Seric 1914549Seric /* clean up end of line */ 1924558Seric fixcrlf(inp, TRUE); 1934549Seric 1944713Seric /* echo command to transcript */ 19555012Seric if (e->e_xfp != NULL) 19655012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1974713Seric 19859060Seric if (e->e_id == NULL) 19965058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 20059060Seric else 20165058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 20259060Seric 2034549Seric /* break off command */ 20458050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2054549Seric continue; 20657232Seric cmd = cmdbuf; 20758050Seric while (*p != '\0' && 20858050Seric !(isascii(*p) && isspace(*p)) && 20958050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 21024981Seric *cmd++ = *p++; 21124981Seric *cmd = '\0'; 2124549Seric 21325691Seric /* throw away leading whitespace */ 21458050Seric while (isascii(*p) && isspace(*p)) 21525691Seric p++; 21625691Seric 2174549Seric /* decode command */ 2184549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2194549Seric { 22033725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2214549Seric break; 2224549Seric } 2234549Seric 22451954Seric /* reset errors */ 22551954Seric errno = 0; 22651954Seric 2274549Seric /* process command */ 2284549Seric switch (c->cmdcode) 2294549Seric { 2304976Seric case CMDHELO: /* hello -- introduce yourself */ 23158323Seric case CMDEHLO: /* extended hello */ 23258323Seric if (c->cmdcode == CMDEHLO) 23358323Seric { 23458323Seric protocol = "ESMTP"; 23561093Seric SmtpPhase = "server EHLO"; 23658323Seric } 23758323Seric else 23858323Seric { 23958323Seric protocol = "SMTP"; 24061093Seric SmtpPhase = "server HELO"; 24158323Seric } 24259016Seric sendinghost = newstr(p); 24360210Seric gothello = TRUE; 24460210Seric if (c->cmdcode != CMDEHLO) 24560239Seric { 24660239Seric /* print old message and be done with it */ 24760239Seric message("250 %s Hello %s, pleased to meet you", 24865017Seric MyHostName, CurSmtpClient); 24960210Seric break; 25060239Seric } 25160239Seric 25260239Seric /* print extended message and brag */ 25360239Seric message("250-%s Hello %s, pleased to meet you", 25460239Seric MyHostName, p); 25558323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 25658323Seric message("250-EXPN"); 25764359Seric if (MaxMessageSize > 0) 25864359Seric message("250-SIZE %ld", MaxMessageSize); 25959271Seric else 26059271Seric message("250-SIZE"); 26158323Seric message("250 HELP"); 2624976Seric break; 2634976Seric 2644549Seric case CMDMAIL: /* mail -- designate sender */ 26561093Seric SmtpPhase = "server MAIL"; 26624943Seric 2679314Seric /* check for validity of this command */ 26858789Seric if (!gothello) 26958082Seric { 27058957Seric /* set sending host to our known value */ 27159016Seric if (sendinghost == NULL) 272*66003Seric sendinghost = REALHOSTNAME; 27358957Seric 27458789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 27558821Seric { 27658789Seric message("503 Polite people say HELO first"); 27758821Seric break; 27858821Seric } 27958082Seric } 28058109Seric if (gotmail) 2814558Seric { 28258151Seric message("503 Sender already specified"); 28363843Seric if (InChild) 28463843Seric finis(); 2854558Seric break; 2864558Seric } 2879339Seric if (InChild) 2889339Seric { 28936230Skarels errno = 0; 29058151Seric syserr("503 Nested MAIL command: MAIL %s", p); 29158069Seric finis(); 2929339Seric } 2939339Seric 2949339Seric /* fork a subprocess to process this command */ 29555012Seric if (runinchild("SMTP-MAIL", e) > 0) 2969339Seric break; 297*66003Seric p = REALHOSTNAME; 29863753Seric if (!gothello) 29963753Seric { 30063753Seric auth_warning(e, 301*66003Seric "Host %s didn't use HELO protocol", p); 30263753Seric } 30365947Seric #ifdef PICKY_HELO_CHECK 304*66003Seric if (strcasecmp(sendinghost, p) != 0 && 305*66003Seric (strcasecmp(p, "localhost") != 0 || 30665823Seric strcasecmp(sendinghost, MyHostName) != 0)) 30765823Seric { 30865823Seric auth_warning(e, "Host %s claimed to be %s", 309*66003Seric p, sendinghost); 31065823Seric } 31165947Seric #endif 31265823Seric 31358323Seric if (protocol == NULL) 31458323Seric protocol = "SMTP"; 31558323Seric define('r', protocol, e); 31659016Seric define('s', sendinghost, e); 31755012Seric initsys(e); 31859747Seric nrcpts = 0; 31965089Seric e->e_flags |= EF_LOGSENDER; 32065058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3219339Seric 3229339Seric /* child -- go do the processing */ 3234549Seric p = skipword(p, "from"); 3244549Seric if (p == NULL) 3254549Seric break; 32657977Seric if (setjmp(TopFrame) > 0) 32758147Seric { 32858147Seric /* this failed -- undo work */ 32958147Seric if (InChild) 33059058Seric { 33159058Seric QuickAbort = FALSE; 33259058Seric SuprErrs = TRUE; 33363787Seric e->e_flags &= ~EF_FATALERRS; 33458147Seric finis(); 33559058Seric } 33657977Seric break; 33758147Seric } 33857977Seric QuickAbort = TRUE; 33958333Seric 34058333Seric /* must parse sender first */ 34158333Seric delimptr = NULL; 34258704Seric setsender(p, e, &delimptr, FALSE); 34358333Seric p = delimptr; 34458333Seric if (p != NULL && *p != '\0') 34558333Seric *p++ = '\0'; 34658333Seric 34758333Seric /* now parse ESMTP arguments */ 34858333Seric msize = 0; 34958333Seric for (; p != NULL && *p != '\0'; p++) 35058333Seric { 35158333Seric char *kp; 35258333Seric char *vp; 35358333Seric 35458333Seric /* locate the beginning of the keyword */ 35558333Seric while (isascii(*p) && isspace(*p)) 35658333Seric p++; 35758333Seric if (*p == '\0') 35858333Seric break; 35958333Seric kp = p; 36058333Seric 36158333Seric /* skip to the value portion */ 36258333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 36358333Seric p++; 36458333Seric if (*p == '=') 36558333Seric { 36658333Seric *p++ = '\0'; 36758333Seric vp = p; 36858333Seric 36958333Seric /* skip to the end of the value */ 37058333Seric while (*p != '\0' && *p != ' ' && 37158333Seric !(isascii(*p) && iscntrl(*p)) && 37258333Seric *p != '=') 37358333Seric p++; 37458333Seric } 37558333Seric 37658333Seric if (*p != '\0') 37758333Seric *p++ = '\0'; 37858333Seric 37958333Seric if (tTd(19, 1)) 38058333Seric printf("MAIL: got arg %s=%s\n", kp, 38158333Seric vp == NULL ? "<null>" : vp); 38258333Seric 38358333Seric if (strcasecmp(kp, "size") == 0) 38458333Seric { 38559093Seric if (vp == NULL) 38658333Seric { 38758333Seric usrerr("501 SIZE requires a value"); 38858333Seric /* NOTREACHED */ 38958333Seric } 39058333Seric msize = atol(vp); 39158333Seric } 39259093Seric else if (strcasecmp(kp, "body") == 0) 39359093Seric { 39459093Seric if (vp == NULL) 39559093Seric { 39659093Seric usrerr("501 BODY requires a value"); 39759093Seric /* NOTREACHED */ 39859093Seric } 39959093Seric # ifdef MIME 40059093Seric if (strcasecmp(vp, "8bitmime") == 0) 40159093Seric { 40259093Seric e->e_bodytype = "8BITMIME"; 40359709Seric SevenBit = FALSE; 40459093Seric } 40559093Seric else if (strcasecmp(vp, "7bit") == 0) 40659093Seric { 40759093Seric e->e_bodytype = "7BIT"; 40859709Seric SevenBit = TRUE; 40959093Seric } 41059093Seric else 41159093Seric { 41259093Seric usrerr("501 Unknown BODY type %s", 41359093Seric vp); 41459093Seric } 41559093Seric # endif 41659093Seric } 41758333Seric else 41858333Seric { 41958333Seric usrerr("501 %s parameter unrecognized", kp); 42058333Seric /* NOTREACHED */ 42158333Seric } 42258333Seric } 42359284Seric 42459284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 42559284Seric { 42659284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 42759284Seric MaxMessageSize); 42859284Seric /* NOTREACHED */ 42959284Seric } 43058333Seric 43158333Seric if (!enoughspace(msize)) 43258333Seric { 43358333Seric message("452 Insufficient disk space; try again later"); 43458333Seric break; 43558333Seric } 43658151Seric message("250 Sender ok"); 43758147Seric gotmail = TRUE; 4384549Seric break; 4394549Seric 4404976Seric case CMDRCPT: /* rcpt -- designate recipient */ 44158850Seric if (!gotmail) 44258850Seric { 44358850Seric usrerr("503 Need MAIL before RCPT"); 44458850Seric break; 44558850Seric } 44661093Seric SmtpPhase = "server RCPT"; 44712612Seric if (setjmp(TopFrame) > 0) 44814785Seric { 44955012Seric e->e_flags &= ~EF_FATALERRS; 45012612Seric break; 45114785Seric } 45212612Seric QuickAbort = TRUE; 45351951Seric LogUsrErrs = TRUE; 45458093Seric 45559699Seric if (e->e_sendmode != SM_DELIVER) 45659699Seric e->e_flags |= EF_VRFYONLY; 45758919Seric 4584549Seric p = skipword(p, "to"); 4594549Seric if (p == NULL) 4604549Seric break; 46164284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 46212612Seric if (a == NULL) 46312612Seric break; 46416886Seric a->q_flags |= QPRIMARY; 46555012Seric a = recipient(a, &e->e_sendqueue, e); 46612612Seric if (Errors != 0) 46712612Seric break; 46812612Seric 46912612Seric /* no errors during parsing, but might be a duplicate */ 47055012Seric e->e_to = p; 47112612Seric if (!bitset(QBADADDR, a->q_flags)) 47259747Seric { 47364718Seric message("250 Recipient ok%s", 47464718Seric bitset(QQUEUEUP, a->q_flags) ? 47564718Seric " (will queue)" : ""); 47659747Seric nrcpts++; 47759747Seric } 47812612Seric else 4794549Seric { 48012612Seric /* punt -- should keep message in ADDRESS.... */ 48158151Seric message("550 Addressee unknown"); 4824549Seric } 48355012Seric e->e_to = NULL; 4844549Seric break; 4854549Seric 4864549Seric case CMDDATA: /* data -- text of mail */ 48761093Seric SmtpPhase = "server DATA"; 48858109Seric if (!gotmail) 4894549Seric { 49058151Seric message("503 Need MAIL command"); 4914976Seric break; 4924549Seric } 49364718Seric else if (nrcpts <= 0) 4944549Seric { 49558151Seric message("503 Need RCPT (recipient)"); 4964976Seric break; 4974549Seric } 4984976Seric 49958929Seric /* check to see if we need to re-expand aliases */ 50063787Seric /* also reset QBADADDR on already-diagnosted addrs */ 50163787Seric doublequeue = FALSE; 50258929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 50358929Seric { 50458929Seric if (bitset(QVERIFIED, a->q_flags)) 50563787Seric { 50663787Seric /* need to re-expand aliases */ 50763787Seric doublequeue = TRUE; 50863787Seric } 50963787Seric if (bitset(QBADADDR, a->q_flags)) 51063787Seric { 51163787Seric /* make this "go away" */ 51263787Seric a->q_flags |= QDONTSEND; 51363787Seric a->q_flags &= ~QBADADDR; 51463787Seric } 51558929Seric } 51658929Seric 5174976Seric /* collect the text of the message */ 51824943Seric SmtpPhase = "collect"; 51964766Seric collect(TRUE, doublequeue, e); 52064766Seric if (Errors != 0) 52164766Seric goto abortmessage; 52263965Seric HoldErrs = TRUE; 5234976Seric 5248238Seric /* 5258238Seric ** Arrange to send to everyone. 5268238Seric ** If sending to multiple people, mail back 5278238Seric ** errors rather than reporting directly. 5288238Seric ** In any case, don't mail back errors for 5298238Seric ** anything that has happened up to 5308238Seric ** now (the other end will do this). 53110197Seric ** Truncate our transcript -- the mail has gotten 53210197Seric ** to us successfully, and if we have 53310197Seric ** to mail this back, it will be easier 53410197Seric ** on the reader. 5358238Seric ** Then send to everyone. 5368238Seric ** Finally give a reply code. If an error has 5378238Seric ** already been given, don't mail a 5388238Seric ** message back. 5399339Seric ** We goose error returns by clearing error bit. 5408238Seric */ 5418238Seric 54224943Seric SmtpPhase = "delivery"; 54363787Seric if (nrcpts != 1 && !doublequeue) 5449378Seric { 5459378Seric HoldErrs = TRUE; 54658734Seric e->e_errormode = EM_MAIL; 5479378Seric } 54855012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 54958714Seric id = e->e_id; 5504976Seric 5514976Seric /* send to all recipients */ 55263787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 55355012Seric e->e_to = NULL; 5544976Seric 5558238Seric /* issue success if appropriate and reset */ 5568238Seric if (Errors == 0 || HoldErrs) 55758855Seric message("250 %s Message accepted for delivery", id); 55859747Seric 55959747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 56059730Seric { 56159730Seric /* avoid sending back an extra message */ 56259730Seric e->e_flags &= ~EF_FATALERRS; 56359747Seric e->e_flags |= EF_CLRQUEUE; 56459730Seric } 5658238Seric else 56658919Seric { 56759747Seric /* from now on, we have to operate silently */ 56859747Seric HoldErrs = TRUE; 56959747Seric e->e_errormode = EM_MAIL; 57059747Seric 57159730Seric /* if we just queued, poke it */ 57263787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 57359730Seric { 57464296Seric extern pid_t dowork(); 57564296Seric 57659730Seric unlockqueue(e); 57764296Seric (void) dowork(id, TRUE, TRUE, e); 57859730Seric } 57958919Seric } 58058883Seric 58159747Seric abortmessage: 5829339Seric /* if in a child, pop back to our parent */ 5839339Seric if (InChild) 5849339Seric finis(); 58524943Seric 58624943Seric /* clean up a bit */ 58758109Seric gotmail = FALSE; 58855012Seric dropenvelope(e); 58958179Seric CurEnv = e = newenvelope(e, CurEnv); 59055012Seric e->e_flags = BlankEnvelope.e_flags; 5914549Seric break; 5924549Seric 5934549Seric case CMDRSET: /* rset -- reset state */ 59458151Seric message("250 Reset state"); 59564359Seric e->e_flags |= EF_CLRQUEUE; 5969339Seric if (InChild) 5979339Seric finis(); 59858109Seric 59958109Seric /* clean up a bit */ 60058109Seric gotmail = FALSE; 60158109Seric dropenvelope(e); 60258179Seric CurEnv = e = newenvelope(e, CurEnv); 6039339Seric break; 6044549Seric 6054549Seric case CMDVRFY: /* vrfy -- verify address */ 60658092Seric case CMDEXPN: /* expn -- expand address */ 60758092Seric vrfy = c->cmdcode == CMDVRFY; 60858092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 60958092Seric PrivacyFlags)) 61058082Seric { 61158412Seric if (vrfy) 61258412Seric message("252 Who's to say?"); 61358412Seric else 61465192Seric message("502 Sorry, we do not allow this operation"); 61565017Seric #ifdef LOG 61665017Seric if (LogLevel > 5) 61765017Seric syslog(LOG_INFO, "%s: %s [rejected]", 61865017Seric CurSmtpClient, inp); 61965017Seric #endif 62058082Seric break; 62158082Seric } 62258082Seric else if (!gothello && 62358092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 62458092Seric PrivacyFlags)) 62558082Seric { 62658151Seric message("503 I demand that you introduce yourself first"); 62758082Seric break; 62858082Seric } 62958092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6309339Seric break; 63155173Seric #ifdef LOG 63258020Seric if (LogLevel > 5) 63365017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 63455173Seric #endif 6355003Seric vrfyqueue = NULL; 6367762Seric QuickAbort = TRUE; 63758092Seric if (vrfy) 63858092Seric e->e_flags |= EF_VRFYONLY; 63962373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 64062373Seric *p++; 64162373Seric if (*p == '\0') 64262373Seric { 64362373Seric message("501 Argument required"); 64462373Seric Errors++; 64562373Seric } 64662373Seric else 64762373Seric { 64864284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 64962373Seric } 6507762Seric if (Errors != 0) 6519339Seric { 6529339Seric if (InChild) 6539339Seric finis(); 6547762Seric break; 6559339Seric } 65662373Seric if (vrfyqueue == NULL) 65762373Seric { 65862373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 65962373Seric } 6605003Seric while (vrfyqueue != NULL) 6615003Seric { 66263971Seric a = vrfyqueue; 66363971Seric while ((a = a->q_next) != NULL && 66463971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 66563971Seric continue; 6667685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 66758151Seric printvrfyaddr(vrfyqueue, a == NULL); 66863847Seric vrfyqueue = vrfyqueue->q_next; 6695003Seric } 6709339Seric if (InChild) 6719339Seric finis(); 6724549Seric break; 6734549Seric 6744549Seric case CMDHELP: /* help -- give user info */ 6754577Seric help(p); 6764549Seric break; 6774549Seric 6784549Seric case CMDNOOP: /* noop -- do nothing */ 67964122Seric message("250 OK"); 6804549Seric break; 6814549Seric 6824549Seric case CMDQUIT: /* quit -- leave mail */ 68358151Seric message("221 %s closing connection", MyHostName); 68461051Seric 68561051Seric /* avoid future 050 messages */ 68661051Seric Verbose = FALSE; 68761051Seric 6889339Seric if (InChild) 6899339Seric ExitStat = EX_QUIT; 6904549Seric finis(); 6914549Seric 6928544Seric case CMDVERB: /* set verbose mode */ 69359957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 69459957Seric { 69559957Seric /* this would give out the same info */ 69659957Seric message("502 Verbose unavailable"); 69759957Seric break; 69859957Seric } 6998544Seric Verbose = TRUE; 70058734Seric e->e_sendmode = SM_DELIVER; 70159957Seric message("250 Verbose mode"); 7028544Seric break; 7038544Seric 7049314Seric case CMDONEX: /* doing one transaction only */ 7059378Seric OneXact = TRUE; 70659957Seric message("250 Only one transaction"); 7079314Seric break; 7089314Seric 70936230Skarels # ifdef SMTPDEBUG 7109339Seric case CMDDBGQSHOW: /* show queues */ 7116907Seric printf("Send Queue="); 71255012Seric printaddr(e->e_sendqueue, TRUE); 7135003Seric break; 7147275Seric 7157275Seric case CMDDBGDEBUG: /* set debug mode */ 7167676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7177676Seric tTflag(p); 71858151Seric message("200 Debug set"); 7197275Seric break; 7207275Seric 72136230Skarels # else /* not SMTPDEBUG */ 72236230Skarels case CMDDBGQSHOW: /* show queues */ 72336230Skarels case CMDDBGDEBUG: /* set debug mode */ 72464685Seric # endif /* SMTPDEBUG */ 72564685Seric case CMDLOGBOGUS: /* bogus command */ 72636233Skarels # ifdef LOG 72758308Seric if (LogLevel > 0) 72864685Seric syslog(LOG_CRIT, 72958020Seric "\"%s\" command from %s (%s)", 730*66003Seric c->cmdname, REALHOSTNAME, 73158755Seric anynet_ntoa(&RealHostAddr)); 73236233Skarels # endif 73336230Skarels /* FALL THROUGH */ 73436230Skarels 7354549Seric case CMDERROR: /* unknown command */ 73658151Seric message("500 Command unrecognized"); 7374549Seric break; 7384549Seric 7394549Seric default: 74036230Skarels errno = 0; 74158151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7424549Seric break; 7434549Seric } 7444549Seric } 7454549Seric } 7464549Seric /* 7474549Seric ** SKIPWORD -- skip a fixed word. 7484549Seric ** 7494549Seric ** Parameters: 7504549Seric ** p -- place to start looking. 7514549Seric ** w -- word to skip. 7524549Seric ** 7534549Seric ** Returns: 7544549Seric ** p following w. 7554549Seric ** NULL on error. 7564549Seric ** 7574549Seric ** Side Effects: 7584549Seric ** clobbers the p data area. 7594549Seric */ 7604549Seric 7614549Seric static char * 7624549Seric skipword(p, w) 7634549Seric register char *p; 7644549Seric char *w; 7654549Seric { 7664549Seric register char *q; 7674549Seric 7684549Seric /* find beginning of word */ 76958050Seric while (isascii(*p) && isspace(*p)) 7704549Seric p++; 7714549Seric q = p; 7724549Seric 7734549Seric /* find end of word */ 77458050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7754549Seric p++; 77658050Seric while (isascii(*p) && isspace(*p)) 7774549Seric *p++ = '\0'; 7784549Seric if (*p != ':') 7794549Seric { 7804549Seric syntax: 78162373Seric message("501 Syntax error in parameters"); 7824549Seric Errors++; 7834549Seric return (NULL); 7844549Seric } 7854549Seric *p++ = '\0'; 78658050Seric while (isascii(*p) && isspace(*p)) 7874549Seric p++; 7884549Seric 78962373Seric if (*p == '\0') 79062373Seric goto syntax; 79162373Seric 7924549Seric /* see if the input word matches desired word */ 79333725Sbostic if (strcasecmp(q, w)) 7944549Seric goto syntax; 7954549Seric 7964549Seric return (p); 7974549Seric } 7984577Seric /* 79958151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 80058151Seric ** 80158151Seric ** Parameters: 80258151Seric ** a -- the address to print 80358151Seric ** last -- set if this is the last one. 80458151Seric ** 80558151Seric ** Returns: 80658151Seric ** none. 80758151Seric ** 80858151Seric ** Side Effects: 80958151Seric ** Prints the appropriate 250 codes. 81058151Seric */ 81158151Seric 81258151Seric printvrfyaddr(a, last) 81358151Seric register ADDRESS *a; 81458151Seric bool last; 81558151Seric { 81658151Seric char fmtbuf[20]; 81758151Seric 81858151Seric strcpy(fmtbuf, "250"); 81958151Seric fmtbuf[3] = last ? ' ' : '-'; 82058151Seric 82159746Seric if (a->q_fullname == NULL) 82259746Seric { 82359746Seric if (strchr(a->q_user, '@') == NULL) 82459746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 82559746Seric else 82659746Seric strcpy(&fmtbuf[4], "<%s>"); 82759746Seric message(fmtbuf, a->q_user, MyHostName); 82859746Seric } 82958151Seric else 83058151Seric { 83159746Seric if (strchr(a->q_user, '@') == NULL) 83259746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 83359746Seric else 83459746Seric strcpy(&fmtbuf[4], "%s <%s>"); 83559746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 83658151Seric } 83758151Seric } 83858151Seric /* 8394577Seric ** HELP -- implement the HELP command. 8404577Seric ** 8414577Seric ** Parameters: 8424577Seric ** topic -- the topic we want help for. 8434577Seric ** 8444577Seric ** Returns: 8454577Seric ** none. 8464577Seric ** 8474577Seric ** Side Effects: 8484577Seric ** outputs the help file to message output. 8494577Seric */ 8504577Seric 8514577Seric help(topic) 8524577Seric char *topic; 8534577Seric { 8544577Seric register FILE *hf; 8554577Seric int len; 8564577Seric char buf[MAXLINE]; 8574577Seric bool noinfo; 8584577Seric 8598269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8604577Seric { 8614577Seric /* no help */ 86211931Seric errno = 0; 86358151Seric message("502 HELP not implemented"); 8644577Seric return; 8654577Seric } 8664577Seric 86749669Seric if (topic == NULL || *topic == '\0') 86849669Seric topic = "smtp"; 86949669Seric else 87049669Seric makelower(topic); 87149669Seric 8724577Seric len = strlen(topic); 8734577Seric noinfo = TRUE; 8744577Seric 8754577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8764577Seric { 8774577Seric if (strncmp(buf, topic, len) == 0) 8784577Seric { 8794577Seric register char *p; 8804577Seric 88156795Seric p = strchr(buf, '\t'); 8824577Seric if (p == NULL) 8834577Seric p = buf; 8844577Seric else 8854577Seric p++; 8864577Seric fixcrlf(p, TRUE); 88758151Seric message("214-%s", p); 8884577Seric noinfo = FALSE; 8894577Seric } 8904577Seric } 8914577Seric 8924577Seric if (noinfo) 89358151Seric message("504 HELP topic unknown"); 8944577Seric else 89558151Seric message("214 End of HELP info"); 8964628Seric (void) fclose(hf); 8974577Seric } 8988544Seric /* 8999339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 9009339Seric ** 9019339Seric ** Parameters: 9029339Seric ** label -- a string used in error messages 9039339Seric ** 9049339Seric ** Returns: 9059339Seric ** zero in the child 9069339Seric ** one in the parent 9079339Seric ** 9089339Seric ** Side Effects: 9099339Seric ** none. 9109339Seric */ 9118544Seric 91255012Seric runinchild(label, e) 9139339Seric char *label; 91455012Seric register ENVELOPE *e; 9159339Seric { 9169339Seric int childpid; 9179339Seric 91816158Seric if (!OneXact) 9199339Seric { 92016158Seric childpid = dofork(); 92116158Seric if (childpid < 0) 92216158Seric { 92316158Seric syserr("%s: cannot fork", label); 92416158Seric return (1); 92516158Seric } 92616158Seric if (childpid > 0) 92716158Seric { 92816158Seric auto int st; 9299339Seric 93016158Seric /* parent -- wait for child to complete */ 93161093Seric setproctitle("server %s child wait", CurHostName); 93216158Seric st = waitfor(childpid); 93316158Seric if (st == -1) 93416158Seric syserr("%s: lost child", label); 93564948Seric else if (!WIFEXITED(st)) 93664948Seric syserr("%s: died on signal %d", 93764948Seric label, st & 0177); 9389339Seric 93916158Seric /* if we exited on a QUIT command, complete the process */ 94016158Seric if (st == (EX_QUIT << 8)) 94116158Seric finis(); 9429339Seric 94316158Seric return (1); 94416158Seric } 94516158Seric else 94616158Seric { 94716158Seric /* child */ 94816158Seric InChild = TRUE; 94925050Seric QuickAbort = FALSE; 95055012Seric clearenvelope(e, FALSE); 95116158Seric } 9529339Seric } 95315256Seric 95416158Seric /* open alias database */ 95560537Seric initmaps(FALSE, e); 95616158Seric 95716158Seric return (0); 9589339Seric } 9599339Seric 96056795Seric # endif /* SMTP */ 961