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*65947Seric static char sccsid[] = "@(#)srvrsmtp.c 8.26 (Berkeley) 01/31/94 (with SMTP)"; 1433731Sbostic #else 15*65947Seric static char sccsid[] = "@(#)srvrsmtp.c 8.26 (Berkeley) 01/31/94 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 204549Seric 2133731Sbostic # ifdef SMTP 224556Seric 234549Seric /* 244549Seric ** SMTP -- run the SMTP protocol. 254549Seric ** 264549Seric ** Parameters: 274549Seric ** none. 284549Seric ** 294549Seric ** Returns: 304549Seric ** never. 314549Seric ** 324549Seric ** Side Effects: 334549Seric ** Reads commands from the input channel and processes 344549Seric ** them. 354549Seric */ 364549Seric 374549Seric struct cmd 384549Seric { 394549Seric char *cmdname; /* command name */ 404549Seric int cmdcode; /* internal code, see below */ 414549Seric }; 424549Seric 434549Seric /* values for cmdcode */ 444549Seric # define CMDERROR 0 /* bad command */ 454549Seric # define CMDMAIL 1 /* mail -- designate sender */ 464976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 474549Seric # define CMDDATA 3 /* data -- send message text */ 489339Seric # define CMDRSET 4 /* rset -- reset state */ 499339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5058092Seric # define CMDEXPN 6 /* expn -- expand address */ 519339Seric # define CMDNOOP 7 /* noop -- do nothing */ 529339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 539339Seric # define CMDHELO 9 /* helo -- be polite */ 5458092Seric # define CMDHELP 10 /* help -- give usage info */ 5558323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5658092Seric /* non-standard commands */ 5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 5964685Seric /* use this to catch and log "door handle" attempts on your system */ 6064685Seric # define CMDLOGBOGUS 23 /* bogus command that should be logged */ 6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6258092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6358092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 644549Seric 654549Seric static struct cmd CmdTab[] = 664549Seric { 674549Seric "mail", CMDMAIL, 684976Seric "rcpt", CMDRCPT, 694549Seric "data", CMDDATA, 704549Seric "rset", CMDRSET, 714549Seric "vrfy", CMDVRFY, 7258092Seric "expn", CMDEXPN, 734549Seric "help", CMDHELP, 744549Seric "noop", CMDNOOP, 754549Seric "quit", CMDQUIT, 764976Seric "helo", CMDHELO, 7758323Seric "ehlo", CMDEHLO, 788544Seric "verb", CMDVERB, 799314Seric "onex", CMDONEX, 8036230Skarels /* 8136230Skarels * remaining commands are here only 8236230Skarels * to trap and log attempts to use them 8336230Skarels */ 849339Seric "showq", CMDDBGQSHOW, 858544Seric "debug", CMDDBGDEBUG, 8664685Seric "wiz", CMDLOGBOGUS, 874549Seric NULL, CMDERROR, 884549Seric }; 894549Seric 909378Seric bool OneXact = FALSE; /* one xaction only this run */ 9165017Seric char *CurSmtpClient; /* who's at the other end of channel */ 9211146Seric 9363937Seric static char *skipword(); 9463937Seric 9555012Seric smtp(e) 9655012Seric register ENVELOPE *e; 974549Seric { 984549Seric register char *p; 998544Seric register struct cmd *c; 1004549Seric char *cmd; 1015003Seric auto ADDRESS *vrfyqueue; 10212612Seric ADDRESS *a; 10358109Seric bool gotmail; /* mail command received */ 10458092Seric bool gothello; /* helo command received */ 10558092Seric bool vrfy; /* set if this is a vrfy command */ 10658323Seric char *protocol; /* sending protocol */ 10759016Seric char *sendinghost; /* sending hostname */ 10858333Seric long msize; /* approximate maximum message size */ 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 */ 14765751Seric if (setjmp(TopFrame) > 0) 14859058Seric { 14965751Seric /* if() nesting is necessary for Cray UNICOS */ 15065751Seric if (InChild) 15165751Seric { 15265751Seric QuickAbort = FALSE; 15365751Seric SuprErrs = TRUE; 15465751Seric finis(); 15565751Seric } 15659058Seric } 15712612Seric QuickAbort = FALSE; 15812612Seric HoldErrs = FALSE; 15951951Seric LogUsrErrs = FALSE; 16063843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 16112612Seric 1627356Seric /* setup for the read */ 16355012Seric e->e_to = NULL; 1644577Seric Errors = 0; 1657275Seric (void) fflush(stdout); 1667356Seric 1677356Seric /* read the input line */ 16861093Seric SmtpPhase = "server cmd read"; 16961093Seric setproctitle("server %s cmd read", CurHostName); 17061093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 17161093Seric SmtpPhase); 1727356Seric 1737685Seric /* handle errors */ 1747356Seric if (p == NULL) 1757356Seric { 1764549Seric /* end of file, just die */ 17758151Seric message("421 %s Lost input channel from %s", 17865017Seric MyHostName, CurSmtpClient); 17955464Seric #ifdef LOG 18063843Seric if (LogLevel > (gotmail ? 1 : 19)) 18155464Seric syslog(LOG_NOTICE, "lost input channel from %s", 18265017Seric CurSmtpClient); 18355464Seric #endif 18458069Seric if (InChild) 18558069Seric ExitStat = EX_QUIT; 1864549Seric finis(); 1874549Seric } 1884549Seric 1894549Seric /* clean up end of line */ 1904558Seric fixcrlf(inp, TRUE); 1914549Seric 1924713Seric /* echo command to transcript */ 19355012Seric if (e->e_xfp != NULL) 19455012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1954713Seric 19659060Seric if (e->e_id == NULL) 19765058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 19859060Seric else 19965058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 20059060Seric 2014549Seric /* break off command */ 20258050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2034549Seric continue; 20457232Seric cmd = cmdbuf; 20558050Seric while (*p != '\0' && 20658050Seric !(isascii(*p) && isspace(*p)) && 20758050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 20824981Seric *cmd++ = *p++; 20924981Seric *cmd = '\0'; 2104549Seric 21125691Seric /* throw away leading whitespace */ 21258050Seric while (isascii(*p) && isspace(*p)) 21325691Seric p++; 21425691Seric 2154549Seric /* decode command */ 2164549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2174549Seric { 21833725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2194549Seric break; 2204549Seric } 2214549Seric 22251954Seric /* reset errors */ 22351954Seric errno = 0; 22451954Seric 2254549Seric /* process command */ 2264549Seric switch (c->cmdcode) 2274549Seric { 2284976Seric case CMDHELO: /* hello -- introduce yourself */ 22958323Seric case CMDEHLO: /* extended hello */ 23058323Seric if (c->cmdcode == CMDEHLO) 23158323Seric { 23258323Seric protocol = "ESMTP"; 23361093Seric SmtpPhase = "server EHLO"; 23458323Seric } 23558323Seric else 23658323Seric { 23758323Seric protocol = "SMTP"; 23861093Seric SmtpPhase = "server HELO"; 23958323Seric } 24059016Seric sendinghost = newstr(p); 24160210Seric gothello = TRUE; 24260210Seric if (c->cmdcode != CMDEHLO) 24360239Seric { 24460239Seric /* print old message and be done with it */ 24560239Seric message("250 %s Hello %s, pleased to meet you", 24665017Seric MyHostName, CurSmtpClient); 24760210Seric break; 24860239Seric } 24960239Seric 25060239Seric /* print extended message and brag */ 25160239Seric message("250-%s Hello %s, pleased to meet you", 25260239Seric MyHostName, p); 25358323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 25458323Seric message("250-EXPN"); 25564359Seric if (MaxMessageSize > 0) 25664359Seric message("250-SIZE %ld", MaxMessageSize); 25759271Seric else 25859271Seric message("250-SIZE"); 25958323Seric message("250 HELP"); 2604976Seric break; 2614976Seric 2624549Seric case CMDMAIL: /* mail -- designate sender */ 26361093Seric SmtpPhase = "server MAIL"; 26424943Seric 2659314Seric /* check for validity of this command */ 26658789Seric if (!gothello) 26758082Seric { 26858957Seric /* set sending host to our known value */ 26959016Seric if (sendinghost == NULL) 27059016Seric sendinghost = RealHostName; 27158957Seric 27258789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 27358821Seric { 27458789Seric message("503 Polite people say HELO first"); 27558821Seric break; 27658821Seric } 27758082Seric } 27858109Seric if (gotmail) 2794558Seric { 28058151Seric message("503 Sender already specified"); 28163843Seric if (InChild) 28263843Seric finis(); 2834558Seric break; 2844558Seric } 2859339Seric if (InChild) 2869339Seric { 28736230Skarels errno = 0; 28858151Seric syserr("503 Nested MAIL command: MAIL %s", p); 28958069Seric finis(); 2909339Seric } 2919339Seric 2929339Seric /* fork a subprocess to process this command */ 29355012Seric if (runinchild("SMTP-MAIL", e) > 0) 2949339Seric break; 29563753Seric if (!gothello) 29663753Seric { 29763753Seric auth_warning(e, 29863753Seric "Host %s didn't use HELO protocol", 29963753Seric RealHostName); 30063753Seric } 301*65947Seric #ifdef PICKY_HELO_CHECK 30265823Seric if (strcasecmp(sendinghost, RealHostName) != 0 && 30365823Seric (strcasecmp(RealHostName, "localhost") != 0 || 30465823Seric strcasecmp(sendinghost, MyHostName) != 0)) 30565823Seric { 30665823Seric auth_warning(e, "Host %s claimed to be %s", 30765823Seric RealHostName, sendinghost); 30865823Seric } 309*65947Seric #endif 31065823Seric 31158323Seric if (protocol == NULL) 31258323Seric protocol = "SMTP"; 31358323Seric define('r', protocol, e); 31459016Seric define('s', sendinghost, e); 31555012Seric initsys(e); 31659747Seric nrcpts = 0; 31765089Seric e->e_flags |= EF_LOGSENDER; 31865058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3199339Seric 3209339Seric /* child -- go do the processing */ 3214549Seric p = skipword(p, "from"); 3224549Seric if (p == NULL) 3234549Seric break; 32457977Seric if (setjmp(TopFrame) > 0) 32558147Seric { 32658147Seric /* this failed -- undo work */ 32758147Seric if (InChild) 32859058Seric { 32959058Seric QuickAbort = FALSE; 33059058Seric SuprErrs = TRUE; 33163787Seric e->e_flags &= ~EF_FATALERRS; 33258147Seric finis(); 33359058Seric } 33457977Seric break; 33558147Seric } 33657977Seric QuickAbort = TRUE; 33758333Seric 33858333Seric /* must parse sender first */ 33958333Seric delimptr = NULL; 34058704Seric setsender(p, e, &delimptr, FALSE); 34158333Seric p = delimptr; 34258333Seric if (p != NULL && *p != '\0') 34358333Seric *p++ = '\0'; 34458333Seric 34558333Seric /* now parse ESMTP arguments */ 34658333Seric msize = 0; 34758333Seric for (; p != NULL && *p != '\0'; p++) 34858333Seric { 34958333Seric char *kp; 35058333Seric char *vp; 35158333Seric 35258333Seric /* locate the beginning of the keyword */ 35358333Seric while (isascii(*p) && isspace(*p)) 35458333Seric p++; 35558333Seric if (*p == '\0') 35658333Seric break; 35758333Seric kp = p; 35858333Seric 35958333Seric /* skip to the value portion */ 36058333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 36158333Seric p++; 36258333Seric if (*p == '=') 36358333Seric { 36458333Seric *p++ = '\0'; 36558333Seric vp = p; 36658333Seric 36758333Seric /* skip to the end of the value */ 36858333Seric while (*p != '\0' && *p != ' ' && 36958333Seric !(isascii(*p) && iscntrl(*p)) && 37058333Seric *p != '=') 37158333Seric p++; 37258333Seric } 37358333Seric 37458333Seric if (*p != '\0') 37558333Seric *p++ = '\0'; 37658333Seric 37758333Seric if (tTd(19, 1)) 37858333Seric printf("MAIL: got arg %s=%s\n", kp, 37958333Seric vp == NULL ? "<null>" : vp); 38058333Seric 38158333Seric if (strcasecmp(kp, "size") == 0) 38258333Seric { 38359093Seric if (vp == NULL) 38458333Seric { 38558333Seric usrerr("501 SIZE requires a value"); 38658333Seric /* NOTREACHED */ 38758333Seric } 38858333Seric msize = atol(vp); 38958333Seric } 39059093Seric else if (strcasecmp(kp, "body") == 0) 39159093Seric { 39259093Seric if (vp == NULL) 39359093Seric { 39459093Seric usrerr("501 BODY requires a value"); 39559093Seric /* NOTREACHED */ 39659093Seric } 39759093Seric # ifdef MIME 39859093Seric if (strcasecmp(vp, "8bitmime") == 0) 39959093Seric { 40059093Seric e->e_bodytype = "8BITMIME"; 40159709Seric SevenBit = FALSE; 40259093Seric } 40359093Seric else if (strcasecmp(vp, "7bit") == 0) 40459093Seric { 40559093Seric e->e_bodytype = "7BIT"; 40659709Seric SevenBit = TRUE; 40759093Seric } 40859093Seric else 40959093Seric { 41059093Seric usrerr("501 Unknown BODY type %s", 41159093Seric vp); 41259093Seric } 41359093Seric # endif 41459093Seric } 41558333Seric else 41658333Seric { 41758333Seric usrerr("501 %s parameter unrecognized", kp); 41858333Seric /* NOTREACHED */ 41958333Seric } 42058333Seric } 42159284Seric 42259284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 42359284Seric { 42459284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 42559284Seric MaxMessageSize); 42659284Seric /* NOTREACHED */ 42759284Seric } 42858333Seric 42958333Seric if (!enoughspace(msize)) 43058333Seric { 43158333Seric message("452 Insufficient disk space; try again later"); 43258333Seric break; 43358333Seric } 43458151Seric message("250 Sender ok"); 43558147Seric gotmail = TRUE; 4364549Seric break; 4374549Seric 4384976Seric case CMDRCPT: /* rcpt -- designate recipient */ 43958850Seric if (!gotmail) 44058850Seric { 44158850Seric usrerr("503 Need MAIL before RCPT"); 44258850Seric break; 44358850Seric } 44461093Seric SmtpPhase = "server RCPT"; 44512612Seric if (setjmp(TopFrame) > 0) 44614785Seric { 44755012Seric e->e_flags &= ~EF_FATALERRS; 44812612Seric break; 44914785Seric } 45012612Seric QuickAbort = TRUE; 45151951Seric LogUsrErrs = TRUE; 45258093Seric 45359699Seric if (e->e_sendmode != SM_DELIVER) 45459699Seric e->e_flags |= EF_VRFYONLY; 45558919Seric 4564549Seric p = skipword(p, "to"); 4574549Seric if (p == NULL) 4584549Seric break; 45964284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 46012612Seric if (a == NULL) 46112612Seric break; 46216886Seric a->q_flags |= QPRIMARY; 46355012Seric a = recipient(a, &e->e_sendqueue, e); 46412612Seric if (Errors != 0) 46512612Seric break; 46612612Seric 46712612Seric /* no errors during parsing, but might be a duplicate */ 46855012Seric e->e_to = p; 46912612Seric if (!bitset(QBADADDR, a->q_flags)) 47059747Seric { 47164718Seric message("250 Recipient ok%s", 47264718Seric bitset(QQUEUEUP, a->q_flags) ? 47364718Seric " (will queue)" : ""); 47459747Seric nrcpts++; 47559747Seric } 47612612Seric else 4774549Seric { 47812612Seric /* punt -- should keep message in ADDRESS.... */ 47958151Seric message("550 Addressee unknown"); 4804549Seric } 48155012Seric e->e_to = NULL; 4824549Seric break; 4834549Seric 4844549Seric case CMDDATA: /* data -- text of mail */ 48561093Seric SmtpPhase = "server DATA"; 48658109Seric if (!gotmail) 4874549Seric { 48858151Seric message("503 Need MAIL command"); 4894976Seric break; 4904549Seric } 49164718Seric else if (nrcpts <= 0) 4924549Seric { 49358151Seric message("503 Need RCPT (recipient)"); 4944976Seric break; 4954549Seric } 4964976Seric 49758929Seric /* check to see if we need to re-expand aliases */ 49863787Seric /* also reset QBADADDR on already-diagnosted addrs */ 49963787Seric doublequeue = FALSE; 50058929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 50158929Seric { 50258929Seric if (bitset(QVERIFIED, a->q_flags)) 50363787Seric { 50463787Seric /* need to re-expand aliases */ 50563787Seric doublequeue = TRUE; 50663787Seric } 50763787Seric if (bitset(QBADADDR, a->q_flags)) 50863787Seric { 50963787Seric /* make this "go away" */ 51063787Seric a->q_flags |= QDONTSEND; 51163787Seric a->q_flags &= ~QBADADDR; 51263787Seric } 51358929Seric } 51458929Seric 5154976Seric /* collect the text of the message */ 51624943Seric SmtpPhase = "collect"; 51764766Seric collect(TRUE, doublequeue, e); 51864766Seric if (Errors != 0) 51964766Seric goto abortmessage; 52063965Seric HoldErrs = TRUE; 5214976Seric 5228238Seric /* 5238238Seric ** Arrange to send to everyone. 5248238Seric ** If sending to multiple people, mail back 5258238Seric ** errors rather than reporting directly. 5268238Seric ** In any case, don't mail back errors for 5278238Seric ** anything that has happened up to 5288238Seric ** now (the other end will do this). 52910197Seric ** Truncate our transcript -- the mail has gotten 53010197Seric ** to us successfully, and if we have 53110197Seric ** to mail this back, it will be easier 53210197Seric ** on the reader. 5338238Seric ** Then send to everyone. 5348238Seric ** Finally give a reply code. If an error has 5358238Seric ** already been given, don't mail a 5368238Seric ** message back. 5379339Seric ** We goose error returns by clearing error bit. 5388238Seric */ 5398238Seric 54024943Seric SmtpPhase = "delivery"; 54163787Seric if (nrcpts != 1 && !doublequeue) 5429378Seric { 5439378Seric HoldErrs = TRUE; 54458734Seric e->e_errormode = EM_MAIL; 5459378Seric } 54655012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 54758714Seric id = e->e_id; 5484976Seric 5494976Seric /* send to all recipients */ 55063787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 55155012Seric e->e_to = NULL; 5524976Seric 5538238Seric /* issue success if appropriate and reset */ 5548238Seric if (Errors == 0 || HoldErrs) 55558855Seric message("250 %s Message accepted for delivery", id); 55659747Seric 55759747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 55859730Seric { 55959730Seric /* avoid sending back an extra message */ 56059730Seric e->e_flags &= ~EF_FATALERRS; 56159747Seric e->e_flags |= EF_CLRQUEUE; 56259730Seric } 5638238Seric else 56458919Seric { 56559747Seric /* from now on, we have to operate silently */ 56659747Seric HoldErrs = TRUE; 56759747Seric e->e_errormode = EM_MAIL; 56859747Seric 56959730Seric /* if we just queued, poke it */ 57063787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 57159730Seric { 57264296Seric extern pid_t dowork(); 57364296Seric 57459730Seric unlockqueue(e); 57564296Seric (void) dowork(id, TRUE, TRUE, e); 57659730Seric } 57758919Seric } 57858883Seric 57959747Seric abortmessage: 5809339Seric /* if in a child, pop back to our parent */ 5819339Seric if (InChild) 5829339Seric finis(); 58324943Seric 58424943Seric /* clean up a bit */ 58558109Seric gotmail = FALSE; 58655012Seric dropenvelope(e); 58758179Seric CurEnv = e = newenvelope(e, CurEnv); 58855012Seric e->e_flags = BlankEnvelope.e_flags; 5894549Seric break; 5904549Seric 5914549Seric case CMDRSET: /* rset -- reset state */ 59258151Seric message("250 Reset state"); 59364359Seric e->e_flags |= EF_CLRQUEUE; 5949339Seric if (InChild) 5959339Seric finis(); 59658109Seric 59758109Seric /* clean up a bit */ 59858109Seric gotmail = FALSE; 59958109Seric dropenvelope(e); 60058179Seric CurEnv = e = newenvelope(e, CurEnv); 6019339Seric break; 6024549Seric 6034549Seric case CMDVRFY: /* vrfy -- verify address */ 60458092Seric case CMDEXPN: /* expn -- expand address */ 60558092Seric vrfy = c->cmdcode == CMDVRFY; 60658092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 60758092Seric PrivacyFlags)) 60858082Seric { 60958412Seric if (vrfy) 61058412Seric message("252 Who's to say?"); 61158412Seric else 61265192Seric message("502 Sorry, we do not allow this operation"); 61365017Seric #ifdef LOG 61465017Seric if (LogLevel > 5) 61565017Seric syslog(LOG_INFO, "%s: %s [rejected]", 61665017Seric CurSmtpClient, inp); 61765017Seric #endif 61858082Seric break; 61958082Seric } 62058082Seric else if (!gothello && 62158092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 62258092Seric PrivacyFlags)) 62358082Seric { 62458151Seric message("503 I demand that you introduce yourself first"); 62558082Seric break; 62658082Seric } 62758092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6289339Seric break; 62955173Seric #ifdef LOG 63058020Seric if (LogLevel > 5) 63165017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 63255173Seric #endif 6335003Seric vrfyqueue = NULL; 6347762Seric QuickAbort = TRUE; 63558092Seric if (vrfy) 63658092Seric e->e_flags |= EF_VRFYONLY; 63762373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 63862373Seric *p++; 63962373Seric if (*p == '\0') 64062373Seric { 64162373Seric message("501 Argument required"); 64262373Seric Errors++; 64362373Seric } 64462373Seric else 64562373Seric { 64664284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 64762373Seric } 6487762Seric if (Errors != 0) 6499339Seric { 6509339Seric if (InChild) 6519339Seric finis(); 6527762Seric break; 6539339Seric } 65462373Seric if (vrfyqueue == NULL) 65562373Seric { 65662373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 65762373Seric } 6585003Seric while (vrfyqueue != NULL) 6595003Seric { 66063971Seric a = vrfyqueue; 66163971Seric while ((a = a->q_next) != NULL && 66263971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 66363971Seric continue; 6647685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 66558151Seric printvrfyaddr(vrfyqueue, a == NULL); 66663847Seric vrfyqueue = vrfyqueue->q_next; 6675003Seric } 6689339Seric if (InChild) 6699339Seric finis(); 6704549Seric break; 6714549Seric 6724549Seric case CMDHELP: /* help -- give user info */ 6734577Seric help(p); 6744549Seric break; 6754549Seric 6764549Seric case CMDNOOP: /* noop -- do nothing */ 67764122Seric message("250 OK"); 6784549Seric break; 6794549Seric 6804549Seric case CMDQUIT: /* quit -- leave mail */ 68158151Seric message("221 %s closing connection", MyHostName); 68261051Seric 68361051Seric /* avoid future 050 messages */ 68461051Seric Verbose = FALSE; 68561051Seric 6869339Seric if (InChild) 6879339Seric ExitStat = EX_QUIT; 6884549Seric finis(); 6894549Seric 6908544Seric case CMDVERB: /* set verbose mode */ 69159957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 69259957Seric { 69359957Seric /* this would give out the same info */ 69459957Seric message("502 Verbose unavailable"); 69559957Seric break; 69659957Seric } 6978544Seric Verbose = TRUE; 69858734Seric e->e_sendmode = SM_DELIVER; 69959957Seric message("250 Verbose mode"); 7008544Seric break; 7018544Seric 7029314Seric case CMDONEX: /* doing one transaction only */ 7039378Seric OneXact = TRUE; 70459957Seric message("250 Only one transaction"); 7059314Seric break; 7069314Seric 70736230Skarels # ifdef SMTPDEBUG 7089339Seric case CMDDBGQSHOW: /* show queues */ 7096907Seric printf("Send Queue="); 71055012Seric printaddr(e->e_sendqueue, TRUE); 7115003Seric break; 7127275Seric 7137275Seric case CMDDBGDEBUG: /* set debug mode */ 7147676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7157676Seric tTflag(p); 71658151Seric message("200 Debug set"); 7177275Seric break; 7187275Seric 71936230Skarels # else /* not SMTPDEBUG */ 72036230Skarels case CMDDBGQSHOW: /* show queues */ 72136230Skarels case CMDDBGDEBUG: /* set debug mode */ 72264685Seric # endif /* SMTPDEBUG */ 72364685Seric case CMDLOGBOGUS: /* bogus command */ 72436233Skarels # ifdef LOG 72558308Seric if (LogLevel > 0) 72664685Seric syslog(LOG_CRIT, 72758020Seric "\"%s\" command from %s (%s)", 72836230Skarels c->cmdname, RealHostName, 72958755Seric anynet_ntoa(&RealHostAddr)); 73036233Skarels # endif 73136230Skarels /* FALL THROUGH */ 73236230Skarels 7334549Seric case CMDERROR: /* unknown command */ 73458151Seric message("500 Command unrecognized"); 7354549Seric break; 7364549Seric 7374549Seric default: 73836230Skarels errno = 0; 73958151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7404549Seric break; 7414549Seric } 7424549Seric } 7434549Seric } 7444549Seric /* 7454549Seric ** SKIPWORD -- skip a fixed word. 7464549Seric ** 7474549Seric ** Parameters: 7484549Seric ** p -- place to start looking. 7494549Seric ** w -- word to skip. 7504549Seric ** 7514549Seric ** Returns: 7524549Seric ** p following w. 7534549Seric ** NULL on error. 7544549Seric ** 7554549Seric ** Side Effects: 7564549Seric ** clobbers the p data area. 7574549Seric */ 7584549Seric 7594549Seric static char * 7604549Seric skipword(p, w) 7614549Seric register char *p; 7624549Seric char *w; 7634549Seric { 7644549Seric register char *q; 7654549Seric 7664549Seric /* find beginning of word */ 76758050Seric while (isascii(*p) && isspace(*p)) 7684549Seric p++; 7694549Seric q = p; 7704549Seric 7714549Seric /* find end of word */ 77258050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7734549Seric p++; 77458050Seric while (isascii(*p) && isspace(*p)) 7754549Seric *p++ = '\0'; 7764549Seric if (*p != ':') 7774549Seric { 7784549Seric syntax: 77962373Seric message("501 Syntax error in parameters"); 7804549Seric Errors++; 7814549Seric return (NULL); 7824549Seric } 7834549Seric *p++ = '\0'; 78458050Seric while (isascii(*p) && isspace(*p)) 7854549Seric p++; 7864549Seric 78762373Seric if (*p == '\0') 78862373Seric goto syntax; 78962373Seric 7904549Seric /* see if the input word matches desired word */ 79133725Sbostic if (strcasecmp(q, w)) 7924549Seric goto syntax; 7934549Seric 7944549Seric return (p); 7954549Seric } 7964577Seric /* 79758151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 79858151Seric ** 79958151Seric ** Parameters: 80058151Seric ** a -- the address to print 80158151Seric ** last -- set if this is the last one. 80258151Seric ** 80358151Seric ** Returns: 80458151Seric ** none. 80558151Seric ** 80658151Seric ** Side Effects: 80758151Seric ** Prints the appropriate 250 codes. 80858151Seric */ 80958151Seric 81058151Seric printvrfyaddr(a, last) 81158151Seric register ADDRESS *a; 81258151Seric bool last; 81358151Seric { 81458151Seric char fmtbuf[20]; 81558151Seric 81658151Seric strcpy(fmtbuf, "250"); 81758151Seric fmtbuf[3] = last ? ' ' : '-'; 81858151Seric 81959746Seric if (a->q_fullname == NULL) 82059746Seric { 82159746Seric if (strchr(a->q_user, '@') == NULL) 82259746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 82359746Seric else 82459746Seric strcpy(&fmtbuf[4], "<%s>"); 82559746Seric message(fmtbuf, a->q_user, MyHostName); 82659746Seric } 82758151Seric else 82858151Seric { 82959746Seric if (strchr(a->q_user, '@') == NULL) 83059746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 83159746Seric else 83259746Seric strcpy(&fmtbuf[4], "%s <%s>"); 83359746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 83458151Seric } 83558151Seric } 83658151Seric /* 8374577Seric ** HELP -- implement the HELP command. 8384577Seric ** 8394577Seric ** Parameters: 8404577Seric ** topic -- the topic we want help for. 8414577Seric ** 8424577Seric ** Returns: 8434577Seric ** none. 8444577Seric ** 8454577Seric ** Side Effects: 8464577Seric ** outputs the help file to message output. 8474577Seric */ 8484577Seric 8494577Seric help(topic) 8504577Seric char *topic; 8514577Seric { 8524577Seric register FILE *hf; 8534577Seric int len; 8544577Seric char buf[MAXLINE]; 8554577Seric bool noinfo; 8564577Seric 8578269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8584577Seric { 8594577Seric /* no help */ 86011931Seric errno = 0; 86158151Seric message("502 HELP not implemented"); 8624577Seric return; 8634577Seric } 8644577Seric 86549669Seric if (topic == NULL || *topic == '\0') 86649669Seric topic = "smtp"; 86749669Seric else 86849669Seric makelower(topic); 86949669Seric 8704577Seric len = strlen(topic); 8714577Seric noinfo = TRUE; 8724577Seric 8734577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8744577Seric { 8754577Seric if (strncmp(buf, topic, len) == 0) 8764577Seric { 8774577Seric register char *p; 8784577Seric 87956795Seric p = strchr(buf, '\t'); 8804577Seric if (p == NULL) 8814577Seric p = buf; 8824577Seric else 8834577Seric p++; 8844577Seric fixcrlf(p, TRUE); 88558151Seric message("214-%s", p); 8864577Seric noinfo = FALSE; 8874577Seric } 8884577Seric } 8894577Seric 8904577Seric if (noinfo) 89158151Seric message("504 HELP topic unknown"); 8924577Seric else 89358151Seric message("214 End of HELP info"); 8944628Seric (void) fclose(hf); 8954577Seric } 8968544Seric /* 8979339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8989339Seric ** 8999339Seric ** Parameters: 9009339Seric ** label -- a string used in error messages 9019339Seric ** 9029339Seric ** Returns: 9039339Seric ** zero in the child 9049339Seric ** one in the parent 9059339Seric ** 9069339Seric ** Side Effects: 9079339Seric ** none. 9089339Seric */ 9098544Seric 91055012Seric runinchild(label, e) 9119339Seric char *label; 91255012Seric register ENVELOPE *e; 9139339Seric { 9149339Seric int childpid; 9159339Seric 91616158Seric if (!OneXact) 9179339Seric { 91816158Seric childpid = dofork(); 91916158Seric if (childpid < 0) 92016158Seric { 92116158Seric syserr("%s: cannot fork", label); 92216158Seric return (1); 92316158Seric } 92416158Seric if (childpid > 0) 92516158Seric { 92616158Seric auto int st; 9279339Seric 92816158Seric /* parent -- wait for child to complete */ 92961093Seric setproctitle("server %s child wait", CurHostName); 93016158Seric st = waitfor(childpid); 93116158Seric if (st == -1) 93216158Seric syserr("%s: lost child", label); 93364948Seric else if (!WIFEXITED(st)) 93464948Seric syserr("%s: died on signal %d", 93564948Seric label, st & 0177); 9369339Seric 93716158Seric /* if we exited on a QUIT command, complete the process */ 93816158Seric if (st == (EX_QUIT << 8)) 93916158Seric finis(); 9409339Seric 94116158Seric return (1); 94216158Seric } 94316158Seric else 94416158Seric { 94516158Seric /* child */ 94616158Seric InChild = TRUE; 94725050Seric QuickAbort = FALSE; 94855012Seric clearenvelope(e, FALSE); 94916158Seric } 9509339Seric } 95115256Seric 95216158Seric /* open alias database */ 95360537Seric initmaps(FALSE, e); 95416158Seric 95516158Seric return (0); 9569339Seric } 9579339Seric 95856795Seric # endif /* SMTP */ 959