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*65089Seric static char sccsid[] = "@(#)srvrsmtp.c 8.22 (Berkeley) 12/11/93 (with SMTP)"; 1433731Sbostic #else 15*65089Seric static char sccsid[] = "@(#)srvrsmtp.c 8.22 (Berkeley) 12/11/93 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 204549Seric 2133731Sbostic # ifdef SMTP 224556Seric 234549Seric /* 244549Seric ** SMTP -- run the SMTP protocol. 254549Seric ** 264549Seric ** Parameters: 274549Seric ** none. 284549Seric ** 294549Seric ** Returns: 304549Seric ** never. 314549Seric ** 324549Seric ** Side Effects: 334549Seric ** Reads commands from the input channel and processes 344549Seric ** them. 354549Seric */ 364549Seric 374549Seric struct cmd 384549Seric { 394549Seric char *cmdname; /* command name */ 404549Seric int cmdcode; /* internal code, see below */ 414549Seric }; 424549Seric 434549Seric /* values for cmdcode */ 444549Seric # define CMDERROR 0 /* bad command */ 454549Seric # define CMDMAIL 1 /* mail -- designate sender */ 464976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 474549Seric # define CMDDATA 3 /* data -- send message text */ 489339Seric # define CMDRSET 4 /* rset -- reset state */ 499339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5058092Seric # define CMDEXPN 6 /* expn -- expand address */ 519339Seric # define CMDNOOP 7 /* noop -- do nothing */ 529339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 539339Seric # define CMDHELO 9 /* helo -- be polite */ 5458092Seric # define CMDHELP 10 /* help -- give usage info */ 5558323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5658092Seric /* non-standard commands */ 5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 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 */ 10958333Seric auto char *delimptr; 11058714Seric char *id; 11159747Seric int nrcpts; /* number of RCPT commands */ 11263787Seric bool doublequeue; 1138544Seric char inp[MAXLINE]; 11457232Seric char cmdbuf[MAXLINE]; 1157124Seric extern char Version[]; 11624943Seric extern ENVELOPE BlankEnvelope; 1174549Seric 11859066Seric if (fileno(OutChannel) != fileno(stdout)) 1197363Seric { 1207363Seric /* arrange for debugging output to go to remote host */ 12159066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1227363Seric } 12355012Seric settime(e); 12457642Seric CurHostName = RealHostName; 12565017Seric CurSmtpClient = macvalue('_', e); 12665017Seric if (CurSmtpClient == NULL) 12765017Seric CurSmtpClient = RealHostName; 12865017Seric 12965017Seric setproctitle("server %s startup", CurSmtpClient); 13058050Seric expand("\201e", inp, &inp[sizeof inp], e); 13164496Seric if (BrokenSmtpPeers) 13264496Seric { 13364496Seric message("220 %s", inp); 13464496Seric } 13564496Seric else 13664496Seric { 13764496Seric message("220-%s", inp); 13864496Seric message("220 ESMTP spoken here"); 13964496Seric } 14058330Seric protocol = NULL; 14159016Seric sendinghost = macvalue('s', e); 14258082Seric gothello = FALSE; 14358330Seric gotmail = FALSE; 1444549Seric for (;;) 1454549Seric { 14612612Seric /* arrange for backout */ 14712612Seric if (setjmp(TopFrame) > 0 && InChild) 14859058Seric { 14959058Seric QuickAbort = FALSE; 15059058Seric SuprErrs = TRUE; 15112612Seric finis(); 15259058Seric } 15312612Seric QuickAbort = FALSE; 15412612Seric HoldErrs = FALSE; 15551951Seric LogUsrErrs = FALSE; 15663843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 15712612Seric 1587356Seric /* setup for the read */ 15955012Seric e->e_to = NULL; 1604577Seric Errors = 0; 1617275Seric (void) fflush(stdout); 1627356Seric 1637356Seric /* read the input line */ 16461093Seric SmtpPhase = "server cmd read"; 16561093Seric setproctitle("server %s cmd read", CurHostName); 16661093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 16761093Seric SmtpPhase); 1687356Seric 1697685Seric /* handle errors */ 1707356Seric if (p == NULL) 1717356Seric { 1724549Seric /* end of file, just die */ 17358151Seric message("421 %s Lost input channel from %s", 17465017Seric MyHostName, CurSmtpClient); 17555464Seric #ifdef LOG 17663843Seric if (LogLevel > (gotmail ? 1 : 19)) 17755464Seric syslog(LOG_NOTICE, "lost input channel from %s", 17865017Seric CurSmtpClient); 17955464Seric #endif 18058069Seric if (InChild) 18158069Seric ExitStat = EX_QUIT; 1824549Seric finis(); 1834549Seric } 1844549Seric 1854549Seric /* clean up end of line */ 1864558Seric fixcrlf(inp, TRUE); 1874549Seric 1884713Seric /* echo command to transcript */ 18955012Seric if (e->e_xfp != NULL) 19055012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1914713Seric 19259060Seric if (e->e_id == NULL) 19365058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 19459060Seric else 19565058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 19659060Seric 1974549Seric /* break off command */ 19858050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1994549Seric continue; 20057232Seric cmd = cmdbuf; 20158050Seric while (*p != '\0' && 20258050Seric !(isascii(*p) && isspace(*p)) && 20358050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 20424981Seric *cmd++ = *p++; 20524981Seric *cmd = '\0'; 2064549Seric 20725691Seric /* throw away leading whitespace */ 20858050Seric while (isascii(*p) && isspace(*p)) 20925691Seric p++; 21025691Seric 2114549Seric /* decode command */ 2124549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2134549Seric { 21433725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2154549Seric break; 2164549Seric } 2174549Seric 21851954Seric /* reset errors */ 21951954Seric errno = 0; 22051954Seric 2214549Seric /* process command */ 2224549Seric switch (c->cmdcode) 2234549Seric { 2244976Seric case CMDHELO: /* hello -- introduce yourself */ 22558323Seric case CMDEHLO: /* extended hello */ 22658323Seric if (c->cmdcode == CMDEHLO) 22758323Seric { 22858323Seric protocol = "ESMTP"; 22961093Seric SmtpPhase = "server EHLO"; 23058323Seric } 23158323Seric else 23258323Seric { 23358323Seric protocol = "SMTP"; 23461093Seric SmtpPhase = "server HELO"; 23558323Seric } 23659016Seric sendinghost = newstr(p); 23764284Seric if (strcasecmp(p, RealHostName) != 0 && 23864284Seric (strcasecmp(RealHostName, "localhost") != 0 || 23964284Seric strcasecmp(p, MyHostName) != 0)) 24011146Seric { 24158789Seric auth_warning(e, "Host %s claimed to be %s", 24258789Seric RealHostName, p); 24311146Seric } 24458323Seric 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) 27459016Seric sendinghost = RealHostName; 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, 30263753Seric "Host %s didn't use HELO protocol", 30363753Seric RealHostName); 30463753Seric } 30558323Seric if (protocol == NULL) 30658323Seric protocol = "SMTP"; 30758323Seric define('r', protocol, e); 30859016Seric define('s', sendinghost, e); 30955012Seric initsys(e); 31059747Seric nrcpts = 0; 311*65089Seric e->e_flags |= EF_LOGSENDER; 31265058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3139339Seric 3149339Seric /* child -- go do the processing */ 3154549Seric p = skipword(p, "from"); 3164549Seric if (p == NULL) 3174549Seric break; 31857977Seric if (setjmp(TopFrame) > 0) 31958147Seric { 32058147Seric /* this failed -- undo work */ 32158147Seric if (InChild) 32259058Seric { 32359058Seric QuickAbort = FALSE; 32459058Seric SuprErrs = TRUE; 32563787Seric e->e_flags &= ~EF_FATALERRS; 32658147Seric finis(); 32759058Seric } 32857977Seric break; 32958147Seric } 33057977Seric QuickAbort = TRUE; 33158333Seric 33258333Seric /* must parse sender first */ 33358333Seric delimptr = NULL; 33458704Seric setsender(p, e, &delimptr, FALSE); 33558333Seric p = delimptr; 33658333Seric if (p != NULL && *p != '\0') 33758333Seric *p++ = '\0'; 33858333Seric 33958333Seric /* now parse ESMTP arguments */ 34058333Seric msize = 0; 34158333Seric for (; p != NULL && *p != '\0'; p++) 34258333Seric { 34358333Seric char *kp; 34458333Seric char *vp; 34558333Seric 34658333Seric /* locate the beginning of the keyword */ 34758333Seric while (isascii(*p) && isspace(*p)) 34858333Seric p++; 34958333Seric if (*p == '\0') 35058333Seric break; 35158333Seric kp = p; 35258333Seric 35358333Seric /* skip to the value portion */ 35458333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 35558333Seric p++; 35658333Seric if (*p == '=') 35758333Seric { 35858333Seric *p++ = '\0'; 35958333Seric vp = p; 36058333Seric 36158333Seric /* skip to the end of the value */ 36258333Seric while (*p != '\0' && *p != ' ' && 36358333Seric !(isascii(*p) && iscntrl(*p)) && 36458333Seric *p != '=') 36558333Seric p++; 36658333Seric } 36758333Seric 36858333Seric if (*p != '\0') 36958333Seric *p++ = '\0'; 37058333Seric 37158333Seric if (tTd(19, 1)) 37258333Seric printf("MAIL: got arg %s=%s\n", kp, 37358333Seric vp == NULL ? "<null>" : vp); 37458333Seric 37558333Seric if (strcasecmp(kp, "size") == 0) 37658333Seric { 37759093Seric if (vp == NULL) 37858333Seric { 37958333Seric usrerr("501 SIZE requires a value"); 38058333Seric /* NOTREACHED */ 38158333Seric } 38258333Seric msize = atol(vp); 38358333Seric } 38459093Seric else if (strcasecmp(kp, "body") == 0) 38559093Seric { 38659093Seric if (vp == NULL) 38759093Seric { 38859093Seric usrerr("501 BODY requires a value"); 38959093Seric /* NOTREACHED */ 39059093Seric } 39159093Seric # ifdef MIME 39259093Seric if (strcasecmp(vp, "8bitmime") == 0) 39359093Seric { 39459093Seric e->e_bodytype = "8BITMIME"; 39559709Seric SevenBit = FALSE; 39659093Seric } 39759093Seric else if (strcasecmp(vp, "7bit") == 0) 39859093Seric { 39959093Seric e->e_bodytype = "7BIT"; 40059709Seric SevenBit = TRUE; 40159093Seric } 40259093Seric else 40359093Seric { 40459093Seric usrerr("501 Unknown BODY type %s", 40559093Seric vp); 40659093Seric } 40759093Seric # endif 40859093Seric } 40958333Seric else 41058333Seric { 41158333Seric usrerr("501 %s parameter unrecognized", kp); 41258333Seric /* NOTREACHED */ 41358333Seric } 41458333Seric } 41559284Seric 41659284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 41759284Seric { 41859284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 41959284Seric MaxMessageSize); 42059284Seric /* NOTREACHED */ 42159284Seric } 42258333Seric 42358333Seric if (!enoughspace(msize)) 42458333Seric { 42558333Seric message("452 Insufficient disk space; try again later"); 42658333Seric break; 42758333Seric } 42858151Seric message("250 Sender ok"); 42958147Seric gotmail = TRUE; 4304549Seric break; 4314549Seric 4324976Seric case CMDRCPT: /* rcpt -- designate recipient */ 43358850Seric if (!gotmail) 43458850Seric { 43558850Seric usrerr("503 Need MAIL before RCPT"); 43658850Seric break; 43758850Seric } 43861093Seric SmtpPhase = "server RCPT"; 43912612Seric if (setjmp(TopFrame) > 0) 44014785Seric { 44155012Seric e->e_flags &= ~EF_FATALERRS; 44212612Seric break; 44314785Seric } 44412612Seric QuickAbort = TRUE; 44551951Seric LogUsrErrs = TRUE; 44658093Seric 44759699Seric if (e->e_sendmode != SM_DELIVER) 44859699Seric e->e_flags |= EF_VRFYONLY; 44958919Seric 4504549Seric p = skipword(p, "to"); 4514549Seric if (p == NULL) 4524549Seric break; 45364284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 45412612Seric if (a == NULL) 45512612Seric break; 45616886Seric a->q_flags |= QPRIMARY; 45755012Seric a = recipient(a, &e->e_sendqueue, e); 45812612Seric if (Errors != 0) 45912612Seric break; 46012612Seric 46112612Seric /* no errors during parsing, but might be a duplicate */ 46255012Seric e->e_to = p; 46312612Seric if (!bitset(QBADADDR, a->q_flags)) 46459747Seric { 46564718Seric message("250 Recipient ok%s", 46664718Seric bitset(QQUEUEUP, a->q_flags) ? 46764718Seric " (will queue)" : ""); 46859747Seric nrcpts++; 46959747Seric } 47012612Seric else 4714549Seric { 47212612Seric /* punt -- should keep message in ADDRESS.... */ 47358151Seric message("550 Addressee unknown"); 4744549Seric } 47555012Seric e->e_to = NULL; 4764549Seric break; 4774549Seric 4784549Seric case CMDDATA: /* data -- text of mail */ 47961093Seric SmtpPhase = "server DATA"; 48058109Seric if (!gotmail) 4814549Seric { 48258151Seric message("503 Need MAIL command"); 4834976Seric break; 4844549Seric } 48564718Seric else if (nrcpts <= 0) 4864549Seric { 48758151Seric message("503 Need RCPT (recipient)"); 4884976Seric break; 4894549Seric } 4904976Seric 49158929Seric /* check to see if we need to re-expand aliases */ 49263787Seric /* also reset QBADADDR on already-diagnosted addrs */ 49363787Seric doublequeue = FALSE; 49458929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 49558929Seric { 49658929Seric if (bitset(QVERIFIED, a->q_flags)) 49763787Seric { 49863787Seric /* need to re-expand aliases */ 49963787Seric doublequeue = TRUE; 50063787Seric } 50163787Seric if (bitset(QBADADDR, a->q_flags)) 50263787Seric { 50363787Seric /* make this "go away" */ 50463787Seric a->q_flags |= QDONTSEND; 50563787Seric a->q_flags &= ~QBADADDR; 50663787Seric } 50758929Seric } 50858929Seric 5094976Seric /* collect the text of the message */ 51024943Seric SmtpPhase = "collect"; 51164766Seric collect(TRUE, doublequeue, e); 51264766Seric if (Errors != 0) 51364766Seric goto abortmessage; 51463965Seric HoldErrs = TRUE; 5154976Seric 5168238Seric /* 5178238Seric ** Arrange to send to everyone. 5188238Seric ** If sending to multiple people, mail back 5198238Seric ** errors rather than reporting directly. 5208238Seric ** In any case, don't mail back errors for 5218238Seric ** anything that has happened up to 5228238Seric ** now (the other end will do this). 52310197Seric ** Truncate our transcript -- the mail has gotten 52410197Seric ** to us successfully, and if we have 52510197Seric ** to mail this back, it will be easier 52610197Seric ** on the reader. 5278238Seric ** Then send to everyone. 5288238Seric ** Finally give a reply code. If an error has 5298238Seric ** already been given, don't mail a 5308238Seric ** message back. 5319339Seric ** We goose error returns by clearing error bit. 5328238Seric */ 5338238Seric 53424943Seric SmtpPhase = "delivery"; 53563787Seric if (nrcpts != 1 && !doublequeue) 5369378Seric { 5379378Seric HoldErrs = TRUE; 53858734Seric e->e_errormode = EM_MAIL; 5399378Seric } 54055012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 54158714Seric id = e->e_id; 5424976Seric 5434976Seric /* send to all recipients */ 54463787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 54555012Seric e->e_to = NULL; 5464976Seric 5478238Seric /* issue success if appropriate and reset */ 5488238Seric if (Errors == 0 || HoldErrs) 54958855Seric message("250 %s Message accepted for delivery", id); 55059747Seric 55159747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 55259730Seric { 55359730Seric /* avoid sending back an extra message */ 55459730Seric e->e_flags &= ~EF_FATALERRS; 55559747Seric e->e_flags |= EF_CLRQUEUE; 55659730Seric } 5578238Seric else 55858919Seric { 55959747Seric /* from now on, we have to operate silently */ 56059747Seric HoldErrs = TRUE; 56159747Seric e->e_errormode = EM_MAIL; 56259747Seric 56359730Seric /* if we just queued, poke it */ 56463787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 56559730Seric { 56664296Seric extern pid_t dowork(); 56764296Seric 56859730Seric unlockqueue(e); 56964296Seric (void) dowork(id, TRUE, TRUE, e); 57059730Seric } 57158919Seric } 57258883Seric 57359747Seric abortmessage: 5749339Seric /* if in a child, pop back to our parent */ 5759339Seric if (InChild) 5769339Seric finis(); 57724943Seric 57824943Seric /* clean up a bit */ 57958109Seric gotmail = FALSE; 58055012Seric dropenvelope(e); 58158179Seric CurEnv = e = newenvelope(e, CurEnv); 58255012Seric e->e_flags = BlankEnvelope.e_flags; 5834549Seric break; 5844549Seric 5854549Seric case CMDRSET: /* rset -- reset state */ 58658151Seric message("250 Reset state"); 58764359Seric e->e_flags |= EF_CLRQUEUE; 5889339Seric if (InChild) 5899339Seric finis(); 59058109Seric 59158109Seric /* clean up a bit */ 59258109Seric gotmail = FALSE; 59358109Seric dropenvelope(e); 59458179Seric CurEnv = e = newenvelope(e, CurEnv); 5959339Seric break; 5964549Seric 5974549Seric case CMDVRFY: /* vrfy -- verify address */ 59858092Seric case CMDEXPN: /* expn -- expand address */ 59958092Seric vrfy = c->cmdcode == CMDVRFY; 60058092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 60158092Seric PrivacyFlags)) 60258082Seric { 60358412Seric if (vrfy) 60458412Seric message("252 Who's to say?"); 60558412Seric else 60658412Seric message("502 That's none of your business"); 60765017Seric #ifdef LOG 60865017Seric if (LogLevel > 5) 60965017Seric syslog(LOG_INFO, "%s: %s [rejected]", 61065017Seric CurSmtpClient, inp); 61165017Seric #endif 61258082Seric break; 61358082Seric } 61458082Seric else if (!gothello && 61558092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 61658092Seric PrivacyFlags)) 61758082Seric { 61858151Seric message("503 I demand that you introduce yourself first"); 61958082Seric break; 62058082Seric } 62158092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6229339Seric break; 62355173Seric #ifdef LOG 62458020Seric if (LogLevel > 5) 62565017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 62655173Seric #endif 6275003Seric vrfyqueue = NULL; 6287762Seric QuickAbort = TRUE; 62958092Seric if (vrfy) 63058092Seric e->e_flags |= EF_VRFYONLY; 63162373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 63262373Seric *p++; 63362373Seric if (*p == '\0') 63462373Seric { 63562373Seric message("501 Argument required"); 63662373Seric Errors++; 63762373Seric } 63862373Seric else 63962373Seric { 64064284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 64162373Seric } 6427762Seric if (Errors != 0) 6439339Seric { 6449339Seric if (InChild) 6459339Seric finis(); 6467762Seric break; 6479339Seric } 64862373Seric if (vrfyqueue == NULL) 64962373Seric { 65062373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 65162373Seric } 6525003Seric while (vrfyqueue != NULL) 6535003Seric { 65463971Seric a = vrfyqueue; 65563971Seric while ((a = a->q_next) != NULL && 65663971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 65763971Seric continue; 6587685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 65958151Seric printvrfyaddr(vrfyqueue, a == NULL); 66063847Seric vrfyqueue = vrfyqueue->q_next; 6615003Seric } 6629339Seric if (InChild) 6639339Seric finis(); 6644549Seric break; 6654549Seric 6664549Seric case CMDHELP: /* help -- give user info */ 6674577Seric help(p); 6684549Seric break; 6694549Seric 6704549Seric case CMDNOOP: /* noop -- do nothing */ 67164122Seric message("250 OK"); 6724549Seric break; 6734549Seric 6744549Seric case CMDQUIT: /* quit -- leave mail */ 67558151Seric message("221 %s closing connection", MyHostName); 67661051Seric 67761051Seric /* avoid future 050 messages */ 67861051Seric Verbose = FALSE; 67961051Seric 6809339Seric if (InChild) 6819339Seric ExitStat = EX_QUIT; 6824549Seric finis(); 6834549Seric 6848544Seric case CMDVERB: /* set verbose mode */ 68559957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 68659957Seric { 68759957Seric /* this would give out the same info */ 68859957Seric message("502 Verbose unavailable"); 68959957Seric break; 69059957Seric } 6918544Seric Verbose = TRUE; 69258734Seric e->e_sendmode = SM_DELIVER; 69359957Seric message("250 Verbose mode"); 6948544Seric break; 6958544Seric 6969314Seric case CMDONEX: /* doing one transaction only */ 6979378Seric OneXact = TRUE; 69859957Seric message("250 Only one transaction"); 6999314Seric break; 7009314Seric 70136230Skarels # ifdef SMTPDEBUG 7029339Seric case CMDDBGQSHOW: /* show queues */ 7036907Seric printf("Send Queue="); 70455012Seric printaddr(e->e_sendqueue, TRUE); 7055003Seric break; 7067275Seric 7077275Seric case CMDDBGDEBUG: /* set debug mode */ 7087676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7097676Seric tTflag(p); 71058151Seric message("200 Debug set"); 7117275Seric break; 7127275Seric 71336230Skarels # else /* not SMTPDEBUG */ 71436230Skarels case CMDDBGQSHOW: /* show queues */ 71536230Skarels case CMDDBGDEBUG: /* set debug mode */ 71664685Seric # endif /* SMTPDEBUG */ 71764685Seric case CMDLOGBOGUS: /* bogus command */ 71836233Skarels # ifdef LOG 71958308Seric if (LogLevel > 0) 72064685Seric syslog(LOG_CRIT, 72158020Seric "\"%s\" command from %s (%s)", 72236230Skarels c->cmdname, RealHostName, 72358755Seric anynet_ntoa(&RealHostAddr)); 72436233Skarels # endif 72536230Skarels /* FALL THROUGH */ 72636230Skarels 7274549Seric case CMDERROR: /* unknown command */ 72858151Seric message("500 Command unrecognized"); 7294549Seric break; 7304549Seric 7314549Seric default: 73236230Skarels errno = 0; 73358151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7344549Seric break; 7354549Seric } 7364549Seric } 7374549Seric } 7384549Seric /* 7394549Seric ** SKIPWORD -- skip a fixed word. 7404549Seric ** 7414549Seric ** Parameters: 7424549Seric ** p -- place to start looking. 7434549Seric ** w -- word to skip. 7444549Seric ** 7454549Seric ** Returns: 7464549Seric ** p following w. 7474549Seric ** NULL on error. 7484549Seric ** 7494549Seric ** Side Effects: 7504549Seric ** clobbers the p data area. 7514549Seric */ 7524549Seric 7534549Seric static char * 7544549Seric skipword(p, w) 7554549Seric register char *p; 7564549Seric char *w; 7574549Seric { 7584549Seric register char *q; 7594549Seric 7604549Seric /* find beginning of word */ 76158050Seric while (isascii(*p) && isspace(*p)) 7624549Seric p++; 7634549Seric q = p; 7644549Seric 7654549Seric /* find end of word */ 76658050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7674549Seric p++; 76858050Seric while (isascii(*p) && isspace(*p)) 7694549Seric *p++ = '\0'; 7704549Seric if (*p != ':') 7714549Seric { 7724549Seric syntax: 77362373Seric message("501 Syntax error in parameters"); 7744549Seric Errors++; 7754549Seric return (NULL); 7764549Seric } 7774549Seric *p++ = '\0'; 77858050Seric while (isascii(*p) && isspace(*p)) 7794549Seric p++; 7804549Seric 78162373Seric if (*p == '\0') 78262373Seric goto syntax; 78362373Seric 7844549Seric /* see if the input word matches desired word */ 78533725Sbostic if (strcasecmp(q, w)) 7864549Seric goto syntax; 7874549Seric 7884549Seric return (p); 7894549Seric } 7904577Seric /* 79158151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 79258151Seric ** 79358151Seric ** Parameters: 79458151Seric ** a -- the address to print 79558151Seric ** last -- set if this is the last one. 79658151Seric ** 79758151Seric ** Returns: 79858151Seric ** none. 79958151Seric ** 80058151Seric ** Side Effects: 80158151Seric ** Prints the appropriate 250 codes. 80258151Seric */ 80358151Seric 80458151Seric printvrfyaddr(a, last) 80558151Seric register ADDRESS *a; 80658151Seric bool last; 80758151Seric { 80858151Seric char fmtbuf[20]; 80958151Seric 81058151Seric strcpy(fmtbuf, "250"); 81158151Seric fmtbuf[3] = last ? ' ' : '-'; 81258151Seric 81359746Seric if (a->q_fullname == NULL) 81459746Seric { 81559746Seric if (strchr(a->q_user, '@') == NULL) 81659746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 81759746Seric else 81859746Seric strcpy(&fmtbuf[4], "<%s>"); 81959746Seric message(fmtbuf, a->q_user, MyHostName); 82059746Seric } 82158151Seric else 82258151Seric { 82359746Seric if (strchr(a->q_user, '@') == NULL) 82459746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 82559746Seric else 82659746Seric strcpy(&fmtbuf[4], "%s <%s>"); 82759746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 82858151Seric } 82958151Seric } 83058151Seric /* 8314577Seric ** HELP -- implement the HELP command. 8324577Seric ** 8334577Seric ** Parameters: 8344577Seric ** topic -- the topic we want help for. 8354577Seric ** 8364577Seric ** Returns: 8374577Seric ** none. 8384577Seric ** 8394577Seric ** Side Effects: 8404577Seric ** outputs the help file to message output. 8414577Seric */ 8424577Seric 8434577Seric help(topic) 8444577Seric char *topic; 8454577Seric { 8464577Seric register FILE *hf; 8474577Seric int len; 8484577Seric char buf[MAXLINE]; 8494577Seric bool noinfo; 8504577Seric 8518269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8524577Seric { 8534577Seric /* no help */ 85411931Seric errno = 0; 85558151Seric message("502 HELP not implemented"); 8564577Seric return; 8574577Seric } 8584577Seric 85949669Seric if (topic == NULL || *topic == '\0') 86049669Seric topic = "smtp"; 86149669Seric else 86249669Seric makelower(topic); 86349669Seric 8644577Seric len = strlen(topic); 8654577Seric noinfo = TRUE; 8664577Seric 8674577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8684577Seric { 8694577Seric if (strncmp(buf, topic, len) == 0) 8704577Seric { 8714577Seric register char *p; 8724577Seric 87356795Seric p = strchr(buf, '\t'); 8744577Seric if (p == NULL) 8754577Seric p = buf; 8764577Seric else 8774577Seric p++; 8784577Seric fixcrlf(p, TRUE); 87958151Seric message("214-%s", p); 8804577Seric noinfo = FALSE; 8814577Seric } 8824577Seric } 8834577Seric 8844577Seric if (noinfo) 88558151Seric message("504 HELP topic unknown"); 8864577Seric else 88758151Seric message("214 End of HELP info"); 8884628Seric (void) fclose(hf); 8894577Seric } 8908544Seric /* 8919339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8929339Seric ** 8939339Seric ** Parameters: 8949339Seric ** label -- a string used in error messages 8959339Seric ** 8969339Seric ** Returns: 8979339Seric ** zero in the child 8989339Seric ** one in the parent 8999339Seric ** 9009339Seric ** Side Effects: 9019339Seric ** none. 9029339Seric */ 9038544Seric 90455012Seric runinchild(label, e) 9059339Seric char *label; 90655012Seric register ENVELOPE *e; 9079339Seric { 9089339Seric int childpid; 9099339Seric 91016158Seric if (!OneXact) 9119339Seric { 91216158Seric childpid = dofork(); 91316158Seric if (childpid < 0) 91416158Seric { 91516158Seric syserr("%s: cannot fork", label); 91616158Seric return (1); 91716158Seric } 91816158Seric if (childpid > 0) 91916158Seric { 92016158Seric auto int st; 9219339Seric 92216158Seric /* parent -- wait for child to complete */ 92361093Seric setproctitle("server %s child wait", CurHostName); 92416158Seric st = waitfor(childpid); 92516158Seric if (st == -1) 92616158Seric syserr("%s: lost child", label); 92764948Seric else if (!WIFEXITED(st)) 92864948Seric syserr("%s: died on signal %d", 92964948Seric label, st & 0177); 9309339Seric 93116158Seric /* if we exited on a QUIT command, complete the process */ 93216158Seric if (st == (EX_QUIT << 8)) 93316158Seric finis(); 9349339Seric 93516158Seric return (1); 93616158Seric } 93716158Seric else 93816158Seric { 93916158Seric /* child */ 94016158Seric InChild = TRUE; 94125050Seric QuickAbort = FALSE; 94255012Seric clearenvelope(e, FALSE); 94316158Seric } 9449339Seric } 94515256Seric 94616158Seric /* open alias database */ 94760537Seric initmaps(FALSE, e); 94816158Seric 94916158Seric return (0); 9509339Seric } 9519339Seric 95256795Seric # endif /* SMTP */ 953