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*66325Seric static char sccsid[] = "@(#)srvrsmtp.c 8.32 (Berkeley) 03/08/94 (with SMTP)"; 1433731Sbostic #else 15*66325Seric static char sccsid[] = "@(#)srvrsmtp.c 8.32 (Berkeley) 03/08/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(); 94*66325Seric extern char RealUserName[]; 9563937Seric 96*66325Seric 9766283Seric #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 9866283Seric 9955012Seric smtp(e) 10055012Seric register ENVELOPE *e; 1014549Seric { 1024549Seric register char *p; 1038544Seric register struct cmd *c; 1044549Seric char *cmd; 1055003Seric auto ADDRESS *vrfyqueue; 10612612Seric ADDRESS *a; 10758109Seric bool gotmail; /* mail command received */ 10858092Seric bool gothello; /* helo command received */ 10958092Seric bool vrfy; /* set if this is a vrfy command */ 11058323Seric char *protocol; /* sending protocol */ 11159016Seric char *sendinghost; /* sending hostname */ 11258333Seric long msize; /* approximate maximum message size */ 11366005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11458333Seric auto char *delimptr; 11558714Seric char *id; 11659747Seric int nrcpts; /* number of RCPT commands */ 11763787Seric bool doublequeue; 11866283Seric int badcommands = 0; /* count of bad commands */ 1198544Seric char inp[MAXLINE]; 12057232Seric char cmdbuf[MAXLINE]; 1217124Seric extern char Version[]; 12224943Seric extern ENVELOPE BlankEnvelope; 1234549Seric 12459066Seric if (fileno(OutChannel) != fileno(stdout)) 1257363Seric { 1267363Seric /* arrange for debugging output to go to remote host */ 12759066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1287363Seric } 12955012Seric settime(e); 13066005Seric peerhostname = RealHostName; 13166005Seric if (peerhostname == NULL) 13266005Seric peerhostname = "localhost"; 13366005Seric CurHostName = peerhostname; 13465017Seric CurSmtpClient = macvalue('_', e); 13565017Seric if (CurSmtpClient == NULL) 13666003Seric CurSmtpClient = CurHostName; 13765017Seric 13865017Seric setproctitle("server %s startup", CurSmtpClient); 13958050Seric expand("\201e", inp, &inp[sizeof inp], e); 14064496Seric if (BrokenSmtpPeers) 14164496Seric { 14264496Seric message("220 %s", inp); 14364496Seric } 14464496Seric else 14564496Seric { 14664496Seric message("220-%s", inp); 14764496Seric message("220 ESMTP spoken here"); 14864496Seric } 14958330Seric protocol = NULL; 15059016Seric sendinghost = macvalue('s', e); 15158082Seric gothello = FALSE; 15258330Seric gotmail = FALSE; 1534549Seric for (;;) 1544549Seric { 15512612Seric /* arrange for backout */ 15665751Seric if (setjmp(TopFrame) > 0) 15759058Seric { 15865751Seric /* if() nesting is necessary for Cray UNICOS */ 15965751Seric if (InChild) 16065751Seric { 16165751Seric QuickAbort = FALSE; 16265751Seric SuprErrs = TRUE; 16365751Seric finis(); 16465751Seric } 16559058Seric } 16612612Seric QuickAbort = FALSE; 16712612Seric HoldErrs = FALSE; 16851951Seric LogUsrErrs = FALSE; 16963843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 17012612Seric 1717356Seric /* setup for the read */ 17255012Seric e->e_to = NULL; 1734577Seric Errors = 0; 1747275Seric (void) fflush(stdout); 1757356Seric 1767356Seric /* read the input line */ 17761093Seric SmtpPhase = "server cmd read"; 17861093Seric setproctitle("server %s cmd read", CurHostName); 17961093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 18061093Seric SmtpPhase); 1817356Seric 1827685Seric /* handle errors */ 1837356Seric if (p == NULL) 1847356Seric { 1854549Seric /* end of file, just die */ 18666017Seric disconnect(1, e); 18758151Seric message("421 %s Lost input channel from %s", 18865017Seric MyHostName, CurSmtpClient); 18955464Seric #ifdef LOG 19063843Seric if (LogLevel > (gotmail ? 1 : 19)) 19155464Seric syslog(LOG_NOTICE, "lost input channel from %s", 19265017Seric CurSmtpClient); 19355464Seric #endif 19458069Seric if (InChild) 19558069Seric ExitStat = EX_QUIT; 1964549Seric finis(); 1974549Seric } 1984549Seric 1994549Seric /* clean up end of line */ 2004558Seric fixcrlf(inp, TRUE); 2014549Seric 2024713Seric /* echo command to transcript */ 20355012Seric if (e->e_xfp != NULL) 20455012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2054713Seric 20659060Seric if (e->e_id == NULL) 20765058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 20859060Seric else 20965058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 21059060Seric 2114549Seric /* break off command */ 21258050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2134549Seric continue; 21457232Seric cmd = cmdbuf; 21558050Seric while (*p != '\0' && 21658050Seric !(isascii(*p) && isspace(*p)) && 21758050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 21824981Seric *cmd++ = *p++; 21924981Seric *cmd = '\0'; 2204549Seric 22125691Seric /* throw away leading whitespace */ 22258050Seric while (isascii(*p) && isspace(*p)) 22325691Seric p++; 22425691Seric 2254549Seric /* decode command */ 2264549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2274549Seric { 22833725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2294549Seric break; 2304549Seric } 2314549Seric 23251954Seric /* reset errors */ 23351954Seric errno = 0; 23451954Seric 2354549Seric /* process command */ 2364549Seric switch (c->cmdcode) 2374549Seric { 2384976Seric case CMDHELO: /* hello -- introduce yourself */ 23958323Seric case CMDEHLO: /* extended hello */ 24058323Seric if (c->cmdcode == CMDEHLO) 24158323Seric { 24258323Seric protocol = "ESMTP"; 24361093Seric SmtpPhase = "server EHLO"; 24458323Seric } 24558323Seric else 24658323Seric { 24758323Seric protocol = "SMTP"; 24861093Seric SmtpPhase = "server HELO"; 24958323Seric } 25059016Seric sendinghost = newstr(p); 25160210Seric gothello = TRUE; 25260210Seric if (c->cmdcode != CMDEHLO) 25360239Seric { 25460239Seric /* print old message and be done with it */ 25560239Seric message("250 %s Hello %s, pleased to meet you", 25665017Seric MyHostName, CurSmtpClient); 25760210Seric break; 25860239Seric } 25960239Seric 26060239Seric /* print extended message and brag */ 26160239Seric message("250-%s Hello %s, pleased to meet you", 26260239Seric MyHostName, p); 26358323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 26458323Seric message("250-EXPN"); 26564359Seric if (MaxMessageSize > 0) 26664359Seric message("250-SIZE %ld", MaxMessageSize); 26759271Seric else 26859271Seric message("250-SIZE"); 26958323Seric message("250 HELP"); 2704976Seric break; 2714976Seric 2724549Seric case CMDMAIL: /* mail -- designate sender */ 27361093Seric SmtpPhase = "server MAIL"; 27424943Seric 2759314Seric /* check for validity of this command */ 27658789Seric if (!gothello) 27758082Seric { 27858957Seric /* set sending host to our known value */ 27959016Seric if (sendinghost == NULL) 28066005Seric sendinghost = peerhostname; 28158957Seric 28258789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 28358821Seric { 28458789Seric message("503 Polite people say HELO first"); 28558821Seric break; 28658821Seric } 28758082Seric } 28858109Seric if (gotmail) 2894558Seric { 29058151Seric message("503 Sender already specified"); 29163843Seric if (InChild) 29263843Seric finis(); 2934558Seric break; 2944558Seric } 2959339Seric if (InChild) 2969339Seric { 29736230Skarels errno = 0; 29858151Seric syserr("503 Nested MAIL command: MAIL %s", p); 29958069Seric finis(); 3009339Seric } 3019339Seric 3029339Seric /* fork a subprocess to process this command */ 30355012Seric if (runinchild("SMTP-MAIL", e) > 0) 3049339Seric break; 30563753Seric if (!gothello) 30663753Seric { 30763753Seric auth_warning(e, 30866005Seric "Host %s didn't use HELO protocol", 30966005Seric peerhostname); 31063753Seric } 31165947Seric #ifdef PICKY_HELO_CHECK 31266005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 31366005Seric (strcasecmp(peerhostname, "localhost") != 0 || 31465823Seric strcasecmp(sendinghost, MyHostName) != 0)) 31565823Seric { 31665823Seric auth_warning(e, "Host %s claimed to be %s", 31766005Seric peerhostname, sendinghost); 31865823Seric } 31965947Seric #endif 32065823Seric 32158323Seric if (protocol == NULL) 32258323Seric protocol = "SMTP"; 32358323Seric define('r', protocol, e); 32459016Seric define('s', sendinghost, e); 32555012Seric initsys(e); 32659747Seric nrcpts = 0; 32765089Seric e->e_flags |= EF_LOGSENDER; 32865058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3299339Seric 3309339Seric /* child -- go do the processing */ 3314549Seric p = skipword(p, "from"); 3324549Seric if (p == NULL) 3334549Seric break; 33457977Seric if (setjmp(TopFrame) > 0) 33558147Seric { 33658147Seric /* this failed -- undo work */ 33758147Seric if (InChild) 33859058Seric { 33959058Seric QuickAbort = FALSE; 34059058Seric SuprErrs = TRUE; 34163787Seric e->e_flags &= ~EF_FATALERRS; 34258147Seric finis(); 34359058Seric } 34457977Seric break; 34558147Seric } 34657977Seric QuickAbort = TRUE; 34758333Seric 34858333Seric /* must parse sender first */ 34958333Seric delimptr = NULL; 35058704Seric setsender(p, e, &delimptr, FALSE); 35158333Seric p = delimptr; 35258333Seric if (p != NULL && *p != '\0') 35358333Seric *p++ = '\0'; 35458333Seric 355*66325Seric /* check for possible spoofing */ 356*66325Seric if (RealUid != 0 && OpMode == MD_SMTP && 357*66325Seric (e->e_from.q_mailer != LocalMailer && 358*66325Seric strcmp(e->e_from.q_user, RealUserName) != 0)) 359*66325Seric { 360*66325Seric auth_warning(e, "%s owned process doing -bs", 361*66325Seric RealUserName); 362*66325Seric } 363*66325Seric 36458333Seric /* now parse ESMTP arguments */ 36558333Seric msize = 0; 36658333Seric for (; p != NULL && *p != '\0'; p++) 36758333Seric { 36858333Seric char *kp; 36966304Seric char *vp = NULL; 37058333Seric 37158333Seric /* locate the beginning of the keyword */ 37258333Seric while (isascii(*p) && isspace(*p)) 37358333Seric p++; 37458333Seric if (*p == '\0') 37558333Seric break; 37658333Seric kp = p; 37758333Seric 37858333Seric /* skip to the value portion */ 37958333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 38058333Seric p++; 38158333Seric if (*p == '=') 38258333Seric { 38358333Seric *p++ = '\0'; 38458333Seric vp = p; 38558333Seric 38658333Seric /* skip to the end of the value */ 38758333Seric while (*p != '\0' && *p != ' ' && 38858333Seric !(isascii(*p) && iscntrl(*p)) && 38958333Seric *p != '=') 39058333Seric p++; 39158333Seric } 39258333Seric 39358333Seric if (*p != '\0') 39458333Seric *p++ = '\0'; 39558333Seric 39658333Seric if (tTd(19, 1)) 39758333Seric printf("MAIL: got arg %s=%s\n", kp, 39858333Seric vp == NULL ? "<null>" : vp); 39958333Seric 40058333Seric if (strcasecmp(kp, "size") == 0) 40158333Seric { 40259093Seric if (vp == NULL) 40358333Seric { 40458333Seric usrerr("501 SIZE requires a value"); 40558333Seric /* NOTREACHED */ 40658333Seric } 40758333Seric msize = atol(vp); 40858333Seric } 40959093Seric else if (strcasecmp(kp, "body") == 0) 41059093Seric { 41159093Seric if (vp == NULL) 41259093Seric { 41359093Seric usrerr("501 BODY requires a value"); 41459093Seric /* NOTREACHED */ 41559093Seric } 41659093Seric # ifdef MIME 41759093Seric if (strcasecmp(vp, "8bitmime") == 0) 41859093Seric { 41959093Seric e->e_bodytype = "8BITMIME"; 42059709Seric SevenBit = FALSE; 42159093Seric } 42259093Seric else if (strcasecmp(vp, "7bit") == 0) 42359093Seric { 42459093Seric e->e_bodytype = "7BIT"; 42559709Seric SevenBit = TRUE; 42659093Seric } 42759093Seric else 42859093Seric { 42959093Seric usrerr("501 Unknown BODY type %s", 43059093Seric vp); 43159093Seric } 43259093Seric # endif 43359093Seric } 43458333Seric else 43558333Seric { 43658333Seric usrerr("501 %s parameter unrecognized", kp); 43758333Seric /* NOTREACHED */ 43858333Seric } 43958333Seric } 44059284Seric 44159284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 44259284Seric { 44359284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 44459284Seric MaxMessageSize); 44559284Seric /* NOTREACHED */ 44659284Seric } 44758333Seric 44858333Seric if (!enoughspace(msize)) 44958333Seric { 45058333Seric message("452 Insufficient disk space; try again later"); 45158333Seric break; 45258333Seric } 45358151Seric message("250 Sender ok"); 45458147Seric gotmail = TRUE; 4554549Seric break; 4564549Seric 4574976Seric case CMDRCPT: /* rcpt -- designate recipient */ 45858850Seric if (!gotmail) 45958850Seric { 46058850Seric usrerr("503 Need MAIL before RCPT"); 46158850Seric break; 46258850Seric } 46361093Seric SmtpPhase = "server RCPT"; 46412612Seric if (setjmp(TopFrame) > 0) 46514785Seric { 46655012Seric e->e_flags &= ~EF_FATALERRS; 46712612Seric break; 46814785Seric } 46912612Seric QuickAbort = TRUE; 47051951Seric LogUsrErrs = TRUE; 47158093Seric 47259699Seric if (e->e_sendmode != SM_DELIVER) 47359699Seric e->e_flags |= EF_VRFYONLY; 47458919Seric 4754549Seric p = skipword(p, "to"); 4764549Seric if (p == NULL) 4774549Seric break; 47864284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 47912612Seric if (a == NULL) 48012612Seric break; 48116886Seric a->q_flags |= QPRIMARY; 48255012Seric a = recipient(a, &e->e_sendqueue, e); 48312612Seric if (Errors != 0) 48412612Seric break; 48512612Seric 48612612Seric /* no errors during parsing, but might be a duplicate */ 48755012Seric e->e_to = p; 48812612Seric if (!bitset(QBADADDR, a->q_flags)) 48959747Seric { 49064718Seric message("250 Recipient ok%s", 49164718Seric bitset(QQUEUEUP, a->q_flags) ? 49264718Seric " (will queue)" : ""); 49359747Seric nrcpts++; 49459747Seric } 49512612Seric else 4964549Seric { 49712612Seric /* punt -- should keep message in ADDRESS.... */ 49858151Seric message("550 Addressee unknown"); 4994549Seric } 50055012Seric e->e_to = NULL; 5014549Seric break; 5024549Seric 5034549Seric case CMDDATA: /* data -- text of mail */ 50461093Seric SmtpPhase = "server DATA"; 50558109Seric if (!gotmail) 5064549Seric { 50758151Seric message("503 Need MAIL command"); 5084976Seric break; 5094549Seric } 51064718Seric else if (nrcpts <= 0) 5114549Seric { 51258151Seric message("503 Need RCPT (recipient)"); 5134976Seric break; 5144549Seric } 5154976Seric 51658929Seric /* check to see if we need to re-expand aliases */ 51763787Seric /* also reset QBADADDR on already-diagnosted addrs */ 51863787Seric doublequeue = FALSE; 51958929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 52058929Seric { 52158929Seric if (bitset(QVERIFIED, a->q_flags)) 52263787Seric { 52363787Seric /* need to re-expand aliases */ 52463787Seric doublequeue = TRUE; 52563787Seric } 52663787Seric if (bitset(QBADADDR, a->q_flags)) 52763787Seric { 52863787Seric /* make this "go away" */ 52963787Seric a->q_flags |= QDONTSEND; 53063787Seric a->q_flags &= ~QBADADDR; 53163787Seric } 53258929Seric } 53358929Seric 5344976Seric /* collect the text of the message */ 53524943Seric SmtpPhase = "collect"; 53664766Seric collect(TRUE, doublequeue, e); 53764766Seric if (Errors != 0) 53864766Seric goto abortmessage; 53963965Seric HoldErrs = TRUE; 5404976Seric 5418238Seric /* 5428238Seric ** Arrange to send to everyone. 5438238Seric ** If sending to multiple people, mail back 5448238Seric ** errors rather than reporting directly. 5458238Seric ** In any case, don't mail back errors for 5468238Seric ** anything that has happened up to 5478238Seric ** now (the other end will do this). 54810197Seric ** Truncate our transcript -- the mail has gotten 54910197Seric ** to us successfully, and if we have 55010197Seric ** to mail this back, it will be easier 55110197Seric ** on the reader. 5528238Seric ** Then send to everyone. 5538238Seric ** Finally give a reply code. If an error has 5548238Seric ** already been given, don't mail a 5558238Seric ** message back. 5569339Seric ** We goose error returns by clearing error bit. 5578238Seric */ 5588238Seric 55924943Seric SmtpPhase = "delivery"; 56063787Seric if (nrcpts != 1 && !doublequeue) 5619378Seric { 5629378Seric HoldErrs = TRUE; 56358734Seric e->e_errormode = EM_MAIL; 5649378Seric } 56555012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 56658714Seric id = e->e_id; 5674976Seric 5684976Seric /* send to all recipients */ 56963787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 57055012Seric e->e_to = NULL; 5714976Seric 5728238Seric /* issue success if appropriate and reset */ 5738238Seric if (Errors == 0 || HoldErrs) 57458855Seric message("250 %s Message accepted for delivery", id); 57559747Seric 57659747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 57759730Seric { 57859730Seric /* avoid sending back an extra message */ 57959730Seric e->e_flags &= ~EF_FATALERRS; 58059747Seric e->e_flags |= EF_CLRQUEUE; 58159730Seric } 5828238Seric else 58358919Seric { 58459747Seric /* from now on, we have to operate silently */ 58559747Seric HoldErrs = TRUE; 58659747Seric e->e_errormode = EM_MAIL; 58759747Seric 58859730Seric /* if we just queued, poke it */ 58963787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 59059730Seric { 59164296Seric extern pid_t dowork(); 59264296Seric 59359730Seric unlockqueue(e); 59464296Seric (void) dowork(id, TRUE, TRUE, e); 59559730Seric } 59658919Seric } 59758883Seric 59859747Seric abortmessage: 5999339Seric /* if in a child, pop back to our parent */ 6009339Seric if (InChild) 6019339Seric finis(); 60224943Seric 60324943Seric /* clean up a bit */ 60458109Seric gotmail = FALSE; 60555012Seric dropenvelope(e); 60658179Seric CurEnv = e = newenvelope(e, CurEnv); 60755012Seric e->e_flags = BlankEnvelope.e_flags; 6084549Seric break; 6094549Seric 6104549Seric case CMDRSET: /* rset -- reset state */ 61158151Seric message("250 Reset state"); 61264359Seric e->e_flags |= EF_CLRQUEUE; 6139339Seric if (InChild) 6149339Seric finis(); 61558109Seric 61658109Seric /* clean up a bit */ 61758109Seric gotmail = FALSE; 61858109Seric dropenvelope(e); 61958179Seric CurEnv = e = newenvelope(e, CurEnv); 6209339Seric break; 6214549Seric 6224549Seric case CMDVRFY: /* vrfy -- verify address */ 62358092Seric case CMDEXPN: /* expn -- expand address */ 62458092Seric vrfy = c->cmdcode == CMDVRFY; 62558092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 62658092Seric PrivacyFlags)) 62758082Seric { 62858412Seric if (vrfy) 62958412Seric message("252 Who's to say?"); 63058412Seric else 63165192Seric message("502 Sorry, we do not allow this operation"); 63265017Seric #ifdef LOG 63365017Seric if (LogLevel > 5) 63465017Seric syslog(LOG_INFO, "%s: %s [rejected]", 63565017Seric CurSmtpClient, inp); 63665017Seric #endif 63758082Seric break; 63858082Seric } 63958082Seric else if (!gothello && 64058092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 64158092Seric PrivacyFlags)) 64258082Seric { 64358151Seric message("503 I demand that you introduce yourself first"); 64458082Seric break; 64558082Seric } 64658092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6479339Seric break; 64855173Seric #ifdef LOG 64958020Seric if (LogLevel > 5) 65065017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 65155173Seric #endif 6525003Seric vrfyqueue = NULL; 6537762Seric QuickAbort = TRUE; 65458092Seric if (vrfy) 65558092Seric e->e_flags |= EF_VRFYONLY; 65662373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 65762373Seric *p++; 65862373Seric if (*p == '\0') 65962373Seric { 66062373Seric message("501 Argument required"); 66162373Seric Errors++; 66262373Seric } 66362373Seric else 66462373Seric { 66564284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 66662373Seric } 6677762Seric if (Errors != 0) 6689339Seric { 6699339Seric if (InChild) 6709339Seric finis(); 6717762Seric break; 6729339Seric } 67362373Seric if (vrfyqueue == NULL) 67462373Seric { 67562373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 67662373Seric } 6775003Seric while (vrfyqueue != NULL) 6785003Seric { 67963971Seric a = vrfyqueue; 68063971Seric while ((a = a->q_next) != NULL && 68163971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 68263971Seric continue; 6837685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 68458151Seric printvrfyaddr(vrfyqueue, a == NULL); 68563847Seric vrfyqueue = vrfyqueue->q_next; 6865003Seric } 6879339Seric if (InChild) 6889339Seric finis(); 6894549Seric break; 6904549Seric 6914549Seric case CMDHELP: /* help -- give user info */ 6924577Seric help(p); 6934549Seric break; 6944549Seric 6954549Seric case CMDNOOP: /* noop -- do nothing */ 69664122Seric message("250 OK"); 6974549Seric break; 6984549Seric 6994549Seric case CMDQUIT: /* quit -- leave mail */ 70058151Seric message("221 %s closing connection", MyHostName); 70161051Seric 70266283Seric doquit: 70361051Seric /* avoid future 050 messages */ 70466017Seric disconnect(1, e); 70561051Seric 7069339Seric if (InChild) 7079339Seric ExitStat = EX_QUIT; 7084549Seric finis(); 7094549Seric 7108544Seric case CMDVERB: /* set verbose mode */ 71159957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 71259957Seric { 71359957Seric /* this would give out the same info */ 71459957Seric message("502 Verbose unavailable"); 71559957Seric break; 71659957Seric } 7178544Seric Verbose = TRUE; 71858734Seric e->e_sendmode = SM_DELIVER; 71959957Seric message("250 Verbose mode"); 7208544Seric break; 7218544Seric 7229314Seric case CMDONEX: /* doing one transaction only */ 7239378Seric OneXact = TRUE; 72459957Seric message("250 Only one transaction"); 7259314Seric break; 7269314Seric 72736230Skarels # ifdef SMTPDEBUG 7289339Seric case CMDDBGQSHOW: /* show queues */ 7296907Seric printf("Send Queue="); 73055012Seric printaddr(e->e_sendqueue, TRUE); 7315003Seric break; 7327275Seric 7337275Seric case CMDDBGDEBUG: /* set debug mode */ 7347676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7357676Seric tTflag(p); 73658151Seric message("200 Debug set"); 7377275Seric break; 7387275Seric 73936230Skarels # else /* not SMTPDEBUG */ 74036230Skarels case CMDDBGQSHOW: /* show queues */ 74136230Skarels case CMDDBGDEBUG: /* set debug mode */ 74264685Seric # endif /* SMTPDEBUG */ 74364685Seric case CMDLOGBOGUS: /* bogus command */ 74436233Skarels # ifdef LOG 74558308Seric if (LogLevel > 0) 74664685Seric syslog(LOG_CRIT, 74758020Seric "\"%s\" command from %s (%s)", 74866005Seric c->cmdname, peerhostname, 74958755Seric anynet_ntoa(&RealHostAddr)); 75036233Skarels # endif 75136230Skarels /* FALL THROUGH */ 75236230Skarels 7534549Seric case CMDERROR: /* unknown command */ 75466283Seric if (++badcommands > MAXBADCOMMANDS) 75566283Seric { 75666283Seric message("421 %s Too many bad commands; closing connection", 75766283Seric MyHostName); 75866283Seric goto doquit; 75966283Seric } 76066283Seric 76158151Seric message("500 Command unrecognized"); 7624549Seric break; 7634549Seric 7644549Seric default: 76536230Skarels errno = 0; 76658151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7674549Seric break; 7684549Seric } 7694549Seric } 7704549Seric } 7714549Seric /* 7724549Seric ** SKIPWORD -- skip a fixed word. 7734549Seric ** 7744549Seric ** Parameters: 7754549Seric ** p -- place to start looking. 7764549Seric ** w -- word to skip. 7774549Seric ** 7784549Seric ** Returns: 7794549Seric ** p following w. 7804549Seric ** NULL on error. 7814549Seric ** 7824549Seric ** Side Effects: 7834549Seric ** clobbers the p data area. 7844549Seric */ 7854549Seric 7864549Seric static char * 7874549Seric skipword(p, w) 7884549Seric register char *p; 7894549Seric char *w; 7904549Seric { 7914549Seric register char *q; 79266005Seric char *firstp = p; 7934549Seric 7944549Seric /* find beginning of word */ 79558050Seric while (isascii(*p) && isspace(*p)) 7964549Seric p++; 7974549Seric q = p; 7984549Seric 7994549Seric /* find end of word */ 80058050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8014549Seric p++; 80258050Seric while (isascii(*p) && isspace(*p)) 8034549Seric *p++ = '\0'; 8044549Seric if (*p != ':') 8054549Seric { 8064549Seric syntax: 80766005Seric message("501 Syntax error in parameters scanning \"%s\"", 80866005Seric firstp); 8094549Seric Errors++; 8104549Seric return (NULL); 8114549Seric } 8124549Seric *p++ = '\0'; 81358050Seric while (isascii(*p) && isspace(*p)) 8144549Seric p++; 8154549Seric 81662373Seric if (*p == '\0') 81762373Seric goto syntax; 81862373Seric 8194549Seric /* see if the input word matches desired word */ 82033725Sbostic if (strcasecmp(q, w)) 8214549Seric goto syntax; 8224549Seric 8234549Seric return (p); 8244549Seric } 8254577Seric /* 82658151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 82758151Seric ** 82858151Seric ** Parameters: 82958151Seric ** a -- the address to print 83058151Seric ** last -- set if this is the last one. 83158151Seric ** 83258151Seric ** Returns: 83358151Seric ** none. 83458151Seric ** 83558151Seric ** Side Effects: 83658151Seric ** Prints the appropriate 250 codes. 83758151Seric */ 83858151Seric 83958151Seric printvrfyaddr(a, last) 84058151Seric register ADDRESS *a; 84158151Seric bool last; 84258151Seric { 84358151Seric char fmtbuf[20]; 84458151Seric 84558151Seric strcpy(fmtbuf, "250"); 84658151Seric fmtbuf[3] = last ? ' ' : '-'; 84758151Seric 84859746Seric if (a->q_fullname == NULL) 84959746Seric { 85059746Seric if (strchr(a->q_user, '@') == NULL) 85159746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 85259746Seric else 85359746Seric strcpy(&fmtbuf[4], "<%s>"); 85459746Seric message(fmtbuf, a->q_user, MyHostName); 85559746Seric } 85658151Seric else 85758151Seric { 85859746Seric if (strchr(a->q_user, '@') == NULL) 85959746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 86059746Seric else 86159746Seric strcpy(&fmtbuf[4], "%s <%s>"); 86259746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 86358151Seric } 86458151Seric } 86558151Seric /* 8664577Seric ** HELP -- implement the HELP command. 8674577Seric ** 8684577Seric ** Parameters: 8694577Seric ** topic -- the topic we want help for. 8704577Seric ** 8714577Seric ** Returns: 8724577Seric ** none. 8734577Seric ** 8744577Seric ** Side Effects: 8754577Seric ** outputs the help file to message output. 8764577Seric */ 8774577Seric 8784577Seric help(topic) 8794577Seric char *topic; 8804577Seric { 8814577Seric register FILE *hf; 8824577Seric int len; 8834577Seric char buf[MAXLINE]; 8844577Seric bool noinfo; 8854577Seric 8868269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8874577Seric { 8884577Seric /* no help */ 88911931Seric errno = 0; 89058151Seric message("502 HELP not implemented"); 8914577Seric return; 8924577Seric } 8934577Seric 89449669Seric if (topic == NULL || *topic == '\0') 89549669Seric topic = "smtp"; 89649669Seric else 89749669Seric makelower(topic); 89849669Seric 8994577Seric len = strlen(topic); 9004577Seric noinfo = TRUE; 9014577Seric 9024577Seric while (fgets(buf, sizeof buf, hf) != NULL) 9034577Seric { 9044577Seric if (strncmp(buf, topic, len) == 0) 9054577Seric { 9064577Seric register char *p; 9074577Seric 90856795Seric p = strchr(buf, '\t'); 9094577Seric if (p == NULL) 9104577Seric p = buf; 9114577Seric else 9124577Seric p++; 9134577Seric fixcrlf(p, TRUE); 91458151Seric message("214-%s", p); 9154577Seric noinfo = FALSE; 9164577Seric } 9174577Seric } 9184577Seric 9194577Seric if (noinfo) 92058151Seric message("504 HELP topic unknown"); 9214577Seric else 92258151Seric message("214 End of HELP info"); 9234628Seric (void) fclose(hf); 9244577Seric } 9258544Seric /* 9269339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 9279339Seric ** 9289339Seric ** Parameters: 9299339Seric ** label -- a string used in error messages 9309339Seric ** 9319339Seric ** Returns: 9329339Seric ** zero in the child 9339339Seric ** one in the parent 9349339Seric ** 9359339Seric ** Side Effects: 9369339Seric ** none. 9379339Seric */ 9388544Seric 93955012Seric runinchild(label, e) 9409339Seric char *label; 94155012Seric register ENVELOPE *e; 9429339Seric { 9439339Seric int childpid; 9449339Seric 94516158Seric if (!OneXact) 9469339Seric { 94716158Seric childpid = dofork(); 94816158Seric if (childpid < 0) 94916158Seric { 95016158Seric syserr("%s: cannot fork", label); 95116158Seric return (1); 95216158Seric } 95316158Seric if (childpid > 0) 95416158Seric { 95516158Seric auto int st; 9569339Seric 95716158Seric /* parent -- wait for child to complete */ 95861093Seric setproctitle("server %s child wait", CurHostName); 95916158Seric st = waitfor(childpid); 96016158Seric if (st == -1) 96116158Seric syserr("%s: lost child", label); 96264948Seric else if (!WIFEXITED(st)) 96364948Seric syserr("%s: died on signal %d", 96464948Seric label, st & 0177); 9659339Seric 96616158Seric /* if we exited on a QUIT command, complete the process */ 96766017Seric if (WEXITSTATUS(st) == EX_QUIT) 96866017Seric { 96966017Seric disconnect(1, e); 97016158Seric finis(); 97166017Seric } 9729339Seric 97316158Seric return (1); 97416158Seric } 97516158Seric else 97616158Seric { 97716158Seric /* child */ 97816158Seric InChild = TRUE; 97925050Seric QuickAbort = FALSE; 98055012Seric clearenvelope(e, FALSE); 98116158Seric } 9829339Seric } 98315256Seric 98416158Seric /* open alias database */ 98560537Seric initmaps(FALSE, e); 98616158Seric 98716158Seric return (0); 9889339Seric } 9899339Seric 99056795Seric # endif /* SMTP */ 991