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*65751Seric static char sccsid[] = "@(#)srvrsmtp.c 8.24 (Berkeley) 01/15/94 (with SMTP)"; 1433731Sbostic #else 15*65751Seric static char sccsid[] = "@(#)srvrsmtp.c 8.24 (Berkeley) 01/15/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 */ 147*65751Seric if (setjmp(TopFrame) > 0) 14859058Seric { 149*65751Seric /* if() nesting is necessary for Cray UNICOS */ 150*65751Seric if (InChild) 151*65751Seric { 152*65751Seric QuickAbort = FALSE; 153*65751Seric SuprErrs = TRUE; 154*65751Seric finis(); 155*65751Seric } 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); 24164284Seric if (strcasecmp(p, RealHostName) != 0 && 24264284Seric (strcasecmp(RealHostName, "localhost") != 0 || 24364284Seric strcasecmp(p, MyHostName) != 0)) 24411146Seric { 24558789Seric auth_warning(e, "Host %s claimed to be %s", 24658789Seric RealHostName, p); 24711146Seric } 24858323Seric 24960210Seric gothello = TRUE; 25060210Seric if (c->cmdcode != CMDEHLO) 25160239Seric { 25260239Seric /* print old message and be done with it */ 25360239Seric message("250 %s Hello %s, pleased to meet you", 25465017Seric MyHostName, CurSmtpClient); 25560210Seric break; 25660239Seric } 25760239Seric 25860239Seric /* print extended message and brag */ 25960239Seric message("250-%s Hello %s, pleased to meet you", 26060239Seric MyHostName, p); 26158323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 26258323Seric message("250-EXPN"); 26364359Seric if (MaxMessageSize > 0) 26464359Seric message("250-SIZE %ld", MaxMessageSize); 26559271Seric else 26659271Seric message("250-SIZE"); 26758323Seric message("250 HELP"); 2684976Seric break; 2694976Seric 2704549Seric case CMDMAIL: /* mail -- designate sender */ 27161093Seric SmtpPhase = "server MAIL"; 27224943Seric 2739314Seric /* check for validity of this command */ 27458789Seric if (!gothello) 27558082Seric { 27658957Seric /* set sending host to our known value */ 27759016Seric if (sendinghost == NULL) 27859016Seric sendinghost = RealHostName; 27958957Seric 28058789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 28158821Seric { 28258789Seric message("503 Polite people say HELO first"); 28358821Seric break; 28458821Seric } 28558082Seric } 28658109Seric if (gotmail) 2874558Seric { 28858151Seric message("503 Sender already specified"); 28963843Seric if (InChild) 29063843Seric finis(); 2914558Seric break; 2924558Seric } 2939339Seric if (InChild) 2949339Seric { 29536230Skarels errno = 0; 29658151Seric syserr("503 Nested MAIL command: MAIL %s", p); 29758069Seric finis(); 2989339Seric } 2999339Seric 3009339Seric /* fork a subprocess to process this command */ 30155012Seric if (runinchild("SMTP-MAIL", e) > 0) 3029339Seric break; 30363753Seric if (!gothello) 30463753Seric { 30563753Seric auth_warning(e, 30663753Seric "Host %s didn't use HELO protocol", 30763753Seric RealHostName); 30863753Seric } 30958323Seric if (protocol == NULL) 31058323Seric protocol = "SMTP"; 31158323Seric define('r', protocol, e); 31259016Seric define('s', sendinghost, e); 31355012Seric initsys(e); 31459747Seric nrcpts = 0; 31565089Seric e->e_flags |= EF_LOGSENDER; 31665058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3179339Seric 3189339Seric /* child -- go do the processing */ 3194549Seric p = skipword(p, "from"); 3204549Seric if (p == NULL) 3214549Seric break; 32257977Seric if (setjmp(TopFrame) > 0) 32358147Seric { 32458147Seric /* this failed -- undo work */ 32558147Seric if (InChild) 32659058Seric { 32759058Seric QuickAbort = FALSE; 32859058Seric SuprErrs = TRUE; 32963787Seric e->e_flags &= ~EF_FATALERRS; 33058147Seric finis(); 33159058Seric } 33257977Seric break; 33358147Seric } 33457977Seric QuickAbort = TRUE; 33558333Seric 33658333Seric /* must parse sender first */ 33758333Seric delimptr = NULL; 33858704Seric setsender(p, e, &delimptr, FALSE); 33958333Seric p = delimptr; 34058333Seric if (p != NULL && *p != '\0') 34158333Seric *p++ = '\0'; 34258333Seric 34358333Seric /* now parse ESMTP arguments */ 34458333Seric msize = 0; 34558333Seric for (; p != NULL && *p != '\0'; p++) 34658333Seric { 34758333Seric char *kp; 34858333Seric char *vp; 34958333Seric 35058333Seric /* locate the beginning of the keyword */ 35158333Seric while (isascii(*p) && isspace(*p)) 35258333Seric p++; 35358333Seric if (*p == '\0') 35458333Seric break; 35558333Seric kp = p; 35658333Seric 35758333Seric /* skip to the value portion */ 35858333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 35958333Seric p++; 36058333Seric if (*p == '=') 36158333Seric { 36258333Seric *p++ = '\0'; 36358333Seric vp = p; 36458333Seric 36558333Seric /* skip to the end of the value */ 36658333Seric while (*p != '\0' && *p != ' ' && 36758333Seric !(isascii(*p) && iscntrl(*p)) && 36858333Seric *p != '=') 36958333Seric p++; 37058333Seric } 37158333Seric 37258333Seric if (*p != '\0') 37358333Seric *p++ = '\0'; 37458333Seric 37558333Seric if (tTd(19, 1)) 37658333Seric printf("MAIL: got arg %s=%s\n", kp, 37758333Seric vp == NULL ? "<null>" : vp); 37858333Seric 37958333Seric if (strcasecmp(kp, "size") == 0) 38058333Seric { 38159093Seric if (vp == NULL) 38258333Seric { 38358333Seric usrerr("501 SIZE requires a value"); 38458333Seric /* NOTREACHED */ 38558333Seric } 38658333Seric msize = atol(vp); 38758333Seric } 38859093Seric else if (strcasecmp(kp, "body") == 0) 38959093Seric { 39059093Seric if (vp == NULL) 39159093Seric { 39259093Seric usrerr("501 BODY requires a value"); 39359093Seric /* NOTREACHED */ 39459093Seric } 39559093Seric # ifdef MIME 39659093Seric if (strcasecmp(vp, "8bitmime") == 0) 39759093Seric { 39859093Seric e->e_bodytype = "8BITMIME"; 39959709Seric SevenBit = FALSE; 40059093Seric } 40159093Seric else if (strcasecmp(vp, "7bit") == 0) 40259093Seric { 40359093Seric e->e_bodytype = "7BIT"; 40459709Seric SevenBit = TRUE; 40559093Seric } 40659093Seric else 40759093Seric { 40859093Seric usrerr("501 Unknown BODY type %s", 40959093Seric vp); 41059093Seric } 41159093Seric # endif 41259093Seric } 41358333Seric else 41458333Seric { 41558333Seric usrerr("501 %s parameter unrecognized", kp); 41658333Seric /* NOTREACHED */ 41758333Seric } 41858333Seric } 41959284Seric 42059284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 42159284Seric { 42259284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 42359284Seric MaxMessageSize); 42459284Seric /* NOTREACHED */ 42559284Seric } 42658333Seric 42758333Seric if (!enoughspace(msize)) 42858333Seric { 42958333Seric message("452 Insufficient disk space; try again later"); 43058333Seric break; 43158333Seric } 43258151Seric message("250 Sender ok"); 43358147Seric gotmail = TRUE; 4344549Seric break; 4354549Seric 4364976Seric case CMDRCPT: /* rcpt -- designate recipient */ 43758850Seric if (!gotmail) 43858850Seric { 43958850Seric usrerr("503 Need MAIL before RCPT"); 44058850Seric break; 44158850Seric } 44261093Seric SmtpPhase = "server RCPT"; 44312612Seric if (setjmp(TopFrame) > 0) 44414785Seric { 44555012Seric e->e_flags &= ~EF_FATALERRS; 44612612Seric break; 44714785Seric } 44812612Seric QuickAbort = TRUE; 44951951Seric LogUsrErrs = TRUE; 45058093Seric 45159699Seric if (e->e_sendmode != SM_DELIVER) 45259699Seric e->e_flags |= EF_VRFYONLY; 45358919Seric 4544549Seric p = skipword(p, "to"); 4554549Seric if (p == NULL) 4564549Seric break; 45764284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 45812612Seric if (a == NULL) 45912612Seric break; 46016886Seric a->q_flags |= QPRIMARY; 46155012Seric a = recipient(a, &e->e_sendqueue, e); 46212612Seric if (Errors != 0) 46312612Seric break; 46412612Seric 46512612Seric /* no errors during parsing, but might be a duplicate */ 46655012Seric e->e_to = p; 46712612Seric if (!bitset(QBADADDR, a->q_flags)) 46859747Seric { 46964718Seric message("250 Recipient ok%s", 47064718Seric bitset(QQUEUEUP, a->q_flags) ? 47164718Seric " (will queue)" : ""); 47259747Seric nrcpts++; 47359747Seric } 47412612Seric else 4754549Seric { 47612612Seric /* punt -- should keep message in ADDRESS.... */ 47758151Seric message("550 Addressee unknown"); 4784549Seric } 47955012Seric e->e_to = NULL; 4804549Seric break; 4814549Seric 4824549Seric case CMDDATA: /* data -- text of mail */ 48361093Seric SmtpPhase = "server DATA"; 48458109Seric if (!gotmail) 4854549Seric { 48658151Seric message("503 Need MAIL command"); 4874976Seric break; 4884549Seric } 48964718Seric else if (nrcpts <= 0) 4904549Seric { 49158151Seric message("503 Need RCPT (recipient)"); 4924976Seric break; 4934549Seric } 4944976Seric 49558929Seric /* check to see if we need to re-expand aliases */ 49663787Seric /* also reset QBADADDR on already-diagnosted addrs */ 49763787Seric doublequeue = FALSE; 49858929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 49958929Seric { 50058929Seric if (bitset(QVERIFIED, a->q_flags)) 50163787Seric { 50263787Seric /* need to re-expand aliases */ 50363787Seric doublequeue = TRUE; 50463787Seric } 50563787Seric if (bitset(QBADADDR, a->q_flags)) 50663787Seric { 50763787Seric /* make this "go away" */ 50863787Seric a->q_flags |= QDONTSEND; 50963787Seric a->q_flags &= ~QBADADDR; 51063787Seric } 51158929Seric } 51258929Seric 5134976Seric /* collect the text of the message */ 51424943Seric SmtpPhase = "collect"; 51564766Seric collect(TRUE, doublequeue, e); 51664766Seric if (Errors != 0) 51764766Seric goto abortmessage; 51863965Seric HoldErrs = TRUE; 5194976Seric 5208238Seric /* 5218238Seric ** Arrange to send to everyone. 5228238Seric ** If sending to multiple people, mail back 5238238Seric ** errors rather than reporting directly. 5248238Seric ** In any case, don't mail back errors for 5258238Seric ** anything that has happened up to 5268238Seric ** now (the other end will do this). 52710197Seric ** Truncate our transcript -- the mail has gotten 52810197Seric ** to us successfully, and if we have 52910197Seric ** to mail this back, it will be easier 53010197Seric ** on the reader. 5318238Seric ** Then send to everyone. 5328238Seric ** Finally give a reply code. If an error has 5338238Seric ** already been given, don't mail a 5348238Seric ** message back. 5359339Seric ** We goose error returns by clearing error bit. 5368238Seric */ 5378238Seric 53824943Seric SmtpPhase = "delivery"; 53963787Seric if (nrcpts != 1 && !doublequeue) 5409378Seric { 5419378Seric HoldErrs = TRUE; 54258734Seric e->e_errormode = EM_MAIL; 5439378Seric } 54455012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 54558714Seric id = e->e_id; 5464976Seric 5474976Seric /* send to all recipients */ 54863787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 54955012Seric e->e_to = NULL; 5504976Seric 5518238Seric /* issue success if appropriate and reset */ 5528238Seric if (Errors == 0 || HoldErrs) 55358855Seric message("250 %s Message accepted for delivery", id); 55459747Seric 55559747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 55659730Seric { 55759730Seric /* avoid sending back an extra message */ 55859730Seric e->e_flags &= ~EF_FATALERRS; 55959747Seric e->e_flags |= EF_CLRQUEUE; 56059730Seric } 5618238Seric else 56258919Seric { 56359747Seric /* from now on, we have to operate silently */ 56459747Seric HoldErrs = TRUE; 56559747Seric e->e_errormode = EM_MAIL; 56659747Seric 56759730Seric /* if we just queued, poke it */ 56863787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 56959730Seric { 57064296Seric extern pid_t dowork(); 57164296Seric 57259730Seric unlockqueue(e); 57364296Seric (void) dowork(id, TRUE, TRUE, e); 57459730Seric } 57558919Seric } 57658883Seric 57759747Seric abortmessage: 5789339Seric /* if in a child, pop back to our parent */ 5799339Seric if (InChild) 5809339Seric finis(); 58124943Seric 58224943Seric /* clean up a bit */ 58358109Seric gotmail = FALSE; 58455012Seric dropenvelope(e); 58558179Seric CurEnv = e = newenvelope(e, CurEnv); 58655012Seric e->e_flags = BlankEnvelope.e_flags; 5874549Seric break; 5884549Seric 5894549Seric case CMDRSET: /* rset -- reset state */ 59058151Seric message("250 Reset state"); 59164359Seric e->e_flags |= EF_CLRQUEUE; 5929339Seric if (InChild) 5939339Seric finis(); 59458109Seric 59558109Seric /* clean up a bit */ 59658109Seric gotmail = FALSE; 59758109Seric dropenvelope(e); 59858179Seric CurEnv = e = newenvelope(e, CurEnv); 5999339Seric break; 6004549Seric 6014549Seric case CMDVRFY: /* vrfy -- verify address */ 60258092Seric case CMDEXPN: /* expn -- expand address */ 60358092Seric vrfy = c->cmdcode == CMDVRFY; 60458092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 60558092Seric PrivacyFlags)) 60658082Seric { 60758412Seric if (vrfy) 60858412Seric message("252 Who's to say?"); 60958412Seric else 61065192Seric message("502 Sorry, we do not allow this operation"); 61165017Seric #ifdef LOG 61265017Seric if (LogLevel > 5) 61365017Seric syslog(LOG_INFO, "%s: %s [rejected]", 61465017Seric CurSmtpClient, inp); 61565017Seric #endif 61658082Seric break; 61758082Seric } 61858082Seric else if (!gothello && 61958092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 62058092Seric PrivacyFlags)) 62158082Seric { 62258151Seric message("503 I demand that you introduce yourself first"); 62358082Seric break; 62458082Seric } 62558092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6269339Seric break; 62755173Seric #ifdef LOG 62858020Seric if (LogLevel > 5) 62965017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 63055173Seric #endif 6315003Seric vrfyqueue = NULL; 6327762Seric QuickAbort = TRUE; 63358092Seric if (vrfy) 63458092Seric e->e_flags |= EF_VRFYONLY; 63562373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 63662373Seric *p++; 63762373Seric if (*p == '\0') 63862373Seric { 63962373Seric message("501 Argument required"); 64062373Seric Errors++; 64162373Seric } 64262373Seric else 64362373Seric { 64464284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 64562373Seric } 6467762Seric if (Errors != 0) 6479339Seric { 6489339Seric if (InChild) 6499339Seric finis(); 6507762Seric break; 6519339Seric } 65262373Seric if (vrfyqueue == NULL) 65362373Seric { 65462373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 65562373Seric } 6565003Seric while (vrfyqueue != NULL) 6575003Seric { 65863971Seric a = vrfyqueue; 65963971Seric while ((a = a->q_next) != NULL && 66063971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 66163971Seric continue; 6627685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 66358151Seric printvrfyaddr(vrfyqueue, a == NULL); 66463847Seric vrfyqueue = vrfyqueue->q_next; 6655003Seric } 6669339Seric if (InChild) 6679339Seric finis(); 6684549Seric break; 6694549Seric 6704549Seric case CMDHELP: /* help -- give user info */ 6714577Seric help(p); 6724549Seric break; 6734549Seric 6744549Seric case CMDNOOP: /* noop -- do nothing */ 67564122Seric message("250 OK"); 6764549Seric break; 6774549Seric 6784549Seric case CMDQUIT: /* quit -- leave mail */ 67958151Seric message("221 %s closing connection", MyHostName); 68061051Seric 68161051Seric /* avoid future 050 messages */ 68261051Seric Verbose = FALSE; 68361051Seric 6849339Seric if (InChild) 6859339Seric ExitStat = EX_QUIT; 6864549Seric finis(); 6874549Seric 6888544Seric case CMDVERB: /* set verbose mode */ 68959957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 69059957Seric { 69159957Seric /* this would give out the same info */ 69259957Seric message("502 Verbose unavailable"); 69359957Seric break; 69459957Seric } 6958544Seric Verbose = TRUE; 69658734Seric e->e_sendmode = SM_DELIVER; 69759957Seric message("250 Verbose mode"); 6988544Seric break; 6998544Seric 7009314Seric case CMDONEX: /* doing one transaction only */ 7019378Seric OneXact = TRUE; 70259957Seric message("250 Only one transaction"); 7039314Seric break; 7049314Seric 70536230Skarels # ifdef SMTPDEBUG 7069339Seric case CMDDBGQSHOW: /* show queues */ 7076907Seric printf("Send Queue="); 70855012Seric printaddr(e->e_sendqueue, TRUE); 7095003Seric break; 7107275Seric 7117275Seric case CMDDBGDEBUG: /* set debug mode */ 7127676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7137676Seric tTflag(p); 71458151Seric message("200 Debug set"); 7157275Seric break; 7167275Seric 71736230Skarels # else /* not SMTPDEBUG */ 71836230Skarels case CMDDBGQSHOW: /* show queues */ 71936230Skarels case CMDDBGDEBUG: /* set debug mode */ 72064685Seric # endif /* SMTPDEBUG */ 72164685Seric case CMDLOGBOGUS: /* bogus command */ 72236233Skarels # ifdef LOG 72358308Seric if (LogLevel > 0) 72464685Seric syslog(LOG_CRIT, 72558020Seric "\"%s\" command from %s (%s)", 72636230Skarels c->cmdname, RealHostName, 72758755Seric anynet_ntoa(&RealHostAddr)); 72836233Skarels # endif 72936230Skarels /* FALL THROUGH */ 73036230Skarels 7314549Seric case CMDERROR: /* unknown command */ 73258151Seric message("500 Command unrecognized"); 7334549Seric break; 7344549Seric 7354549Seric default: 73636230Skarels errno = 0; 73758151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7384549Seric break; 7394549Seric } 7404549Seric } 7414549Seric } 7424549Seric /* 7434549Seric ** SKIPWORD -- skip a fixed word. 7444549Seric ** 7454549Seric ** Parameters: 7464549Seric ** p -- place to start looking. 7474549Seric ** w -- word to skip. 7484549Seric ** 7494549Seric ** Returns: 7504549Seric ** p following w. 7514549Seric ** NULL on error. 7524549Seric ** 7534549Seric ** Side Effects: 7544549Seric ** clobbers the p data area. 7554549Seric */ 7564549Seric 7574549Seric static char * 7584549Seric skipword(p, w) 7594549Seric register char *p; 7604549Seric char *w; 7614549Seric { 7624549Seric register char *q; 7634549Seric 7644549Seric /* find beginning of word */ 76558050Seric while (isascii(*p) && isspace(*p)) 7664549Seric p++; 7674549Seric q = p; 7684549Seric 7694549Seric /* find end of word */ 77058050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7714549Seric p++; 77258050Seric while (isascii(*p) && isspace(*p)) 7734549Seric *p++ = '\0'; 7744549Seric if (*p != ':') 7754549Seric { 7764549Seric syntax: 77762373Seric message("501 Syntax error in parameters"); 7784549Seric Errors++; 7794549Seric return (NULL); 7804549Seric } 7814549Seric *p++ = '\0'; 78258050Seric while (isascii(*p) && isspace(*p)) 7834549Seric p++; 7844549Seric 78562373Seric if (*p == '\0') 78662373Seric goto syntax; 78762373Seric 7884549Seric /* see if the input word matches desired word */ 78933725Sbostic if (strcasecmp(q, w)) 7904549Seric goto syntax; 7914549Seric 7924549Seric return (p); 7934549Seric } 7944577Seric /* 79558151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 79658151Seric ** 79758151Seric ** Parameters: 79858151Seric ** a -- the address to print 79958151Seric ** last -- set if this is the last one. 80058151Seric ** 80158151Seric ** Returns: 80258151Seric ** none. 80358151Seric ** 80458151Seric ** Side Effects: 80558151Seric ** Prints the appropriate 250 codes. 80658151Seric */ 80758151Seric 80858151Seric printvrfyaddr(a, last) 80958151Seric register ADDRESS *a; 81058151Seric bool last; 81158151Seric { 81258151Seric char fmtbuf[20]; 81358151Seric 81458151Seric strcpy(fmtbuf, "250"); 81558151Seric fmtbuf[3] = last ? ' ' : '-'; 81658151Seric 81759746Seric if (a->q_fullname == NULL) 81859746Seric { 81959746Seric if (strchr(a->q_user, '@') == NULL) 82059746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 82159746Seric else 82259746Seric strcpy(&fmtbuf[4], "<%s>"); 82359746Seric message(fmtbuf, a->q_user, MyHostName); 82459746Seric } 82558151Seric else 82658151Seric { 82759746Seric if (strchr(a->q_user, '@') == NULL) 82859746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 82959746Seric else 83059746Seric strcpy(&fmtbuf[4], "%s <%s>"); 83159746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 83258151Seric } 83358151Seric } 83458151Seric /* 8354577Seric ** HELP -- implement the HELP command. 8364577Seric ** 8374577Seric ** Parameters: 8384577Seric ** topic -- the topic we want help for. 8394577Seric ** 8404577Seric ** Returns: 8414577Seric ** none. 8424577Seric ** 8434577Seric ** Side Effects: 8444577Seric ** outputs the help file to message output. 8454577Seric */ 8464577Seric 8474577Seric help(topic) 8484577Seric char *topic; 8494577Seric { 8504577Seric register FILE *hf; 8514577Seric int len; 8524577Seric char buf[MAXLINE]; 8534577Seric bool noinfo; 8544577Seric 8558269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8564577Seric { 8574577Seric /* no help */ 85811931Seric errno = 0; 85958151Seric message("502 HELP not implemented"); 8604577Seric return; 8614577Seric } 8624577Seric 86349669Seric if (topic == NULL || *topic == '\0') 86449669Seric topic = "smtp"; 86549669Seric else 86649669Seric makelower(topic); 86749669Seric 8684577Seric len = strlen(topic); 8694577Seric noinfo = TRUE; 8704577Seric 8714577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8724577Seric { 8734577Seric if (strncmp(buf, topic, len) == 0) 8744577Seric { 8754577Seric register char *p; 8764577Seric 87756795Seric p = strchr(buf, '\t'); 8784577Seric if (p == NULL) 8794577Seric p = buf; 8804577Seric else 8814577Seric p++; 8824577Seric fixcrlf(p, TRUE); 88358151Seric message("214-%s", p); 8844577Seric noinfo = FALSE; 8854577Seric } 8864577Seric } 8874577Seric 8884577Seric if (noinfo) 88958151Seric message("504 HELP topic unknown"); 8904577Seric else 89158151Seric message("214 End of HELP info"); 8924628Seric (void) fclose(hf); 8934577Seric } 8948544Seric /* 8959339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8969339Seric ** 8979339Seric ** Parameters: 8989339Seric ** label -- a string used in error messages 8999339Seric ** 9009339Seric ** Returns: 9019339Seric ** zero in the child 9029339Seric ** one in the parent 9039339Seric ** 9049339Seric ** Side Effects: 9059339Seric ** none. 9069339Seric */ 9078544Seric 90855012Seric runinchild(label, e) 9099339Seric char *label; 91055012Seric register ENVELOPE *e; 9119339Seric { 9129339Seric int childpid; 9139339Seric 91416158Seric if (!OneXact) 9159339Seric { 91616158Seric childpid = dofork(); 91716158Seric if (childpid < 0) 91816158Seric { 91916158Seric syserr("%s: cannot fork", label); 92016158Seric return (1); 92116158Seric } 92216158Seric if (childpid > 0) 92316158Seric { 92416158Seric auto int st; 9259339Seric 92616158Seric /* parent -- wait for child to complete */ 92761093Seric setproctitle("server %s child wait", CurHostName); 92816158Seric st = waitfor(childpid); 92916158Seric if (st == -1) 93016158Seric syserr("%s: lost child", label); 93164948Seric else if (!WIFEXITED(st)) 93264948Seric syserr("%s: died on signal %d", 93364948Seric label, st & 0177); 9349339Seric 93516158Seric /* if we exited on a QUIT command, complete the process */ 93616158Seric if (st == (EX_QUIT << 8)) 93716158Seric finis(); 9389339Seric 93916158Seric return (1); 94016158Seric } 94116158Seric else 94216158Seric { 94316158Seric /* child */ 94416158Seric InChild = TRUE; 94525050Seric QuickAbort = FALSE; 94655012Seric clearenvelope(e, FALSE); 94716158Seric } 9489339Seric } 94915256Seric 95016158Seric /* open alias database */ 95160537Seric initmaps(FALSE, e); 95216158Seric 95316158Seric return (0); 9549339Seric } 9559339Seric 95656795Seric # endif /* SMTP */ 957