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*64359Seric static char sccsid[] = "@(#)srvrsmtp.c 8.14 (Berkeley) 08/26/93 (with SMTP)"; 1433731Sbostic #else 15*64359Seric static char sccsid[] = "@(#)srvrsmtp.c 8.14 (Berkeley) 08/26/93 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 204549Seric 2133731Sbostic # ifdef SMTP 224556Seric 234549Seric /* 244549Seric ** SMTP -- run the SMTP protocol. 254549Seric ** 264549Seric ** Parameters: 274549Seric ** none. 284549Seric ** 294549Seric ** Returns: 304549Seric ** never. 314549Seric ** 324549Seric ** Side Effects: 334549Seric ** Reads commands from the input channel and processes 344549Seric ** them. 354549Seric */ 364549Seric 374549Seric struct cmd 384549Seric { 394549Seric char *cmdname; /* command name */ 404549Seric int cmdcode; /* internal code, see below */ 414549Seric }; 424549Seric 434549Seric /* values for cmdcode */ 444549Seric # define CMDERROR 0 /* bad command */ 454549Seric # define CMDMAIL 1 /* mail -- designate sender */ 464976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 474549Seric # define CMDDATA 3 /* data -- send message text */ 489339Seric # define CMDRSET 4 /* rset -- reset state */ 499339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5058092Seric # define CMDEXPN 6 /* expn -- expand address */ 519339Seric # define CMDNOOP 7 /* noop -- do nothing */ 529339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 539339Seric # define CMDHELO 9 /* helo -- be polite */ 5458092Seric # define CMDHELP 10 /* help -- give usage info */ 5558323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5658092Seric /* non-standard commands */ 5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 5936230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6058092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6158092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 624549Seric 634549Seric static struct cmd CmdTab[] = 644549Seric { 654549Seric "mail", CMDMAIL, 664976Seric "rcpt", CMDRCPT, 674549Seric "data", CMDDATA, 684549Seric "rset", CMDRSET, 694549Seric "vrfy", CMDVRFY, 7058092Seric "expn", CMDEXPN, 714549Seric "help", CMDHELP, 724549Seric "noop", CMDNOOP, 734549Seric "quit", CMDQUIT, 744976Seric "helo", CMDHELO, 7558323Seric "ehlo", CMDEHLO, 768544Seric "verb", CMDVERB, 779314Seric "onex", CMDONEX, 7836230Skarels /* 7936230Skarels * remaining commands are here only 8036230Skarels * to trap and log attempts to use them 8136230Skarels */ 829339Seric "showq", CMDDBGQSHOW, 838544Seric "debug", CMDDBGDEBUG, 844549Seric NULL, CMDERROR, 854549Seric }; 864549Seric 879339Seric bool InChild = FALSE; /* true if running in a subprocess */ 889378Seric bool OneXact = FALSE; /* one xaction only this run */ 8911146Seric 909339Seric #define EX_QUIT 22 /* special code for QUIT command */ 918544Seric 9263937Seric static char *skipword(); 9363937Seric 9455012Seric smtp(e) 9555012Seric register ENVELOPE *e; 964549Seric { 974549Seric register char *p; 988544Seric register struct cmd *c; 994549Seric char *cmd; 1005003Seric auto ADDRESS *vrfyqueue; 10112612Seric ADDRESS *a; 10258109Seric bool gotmail; /* mail command received */ 10358092Seric bool gothello; /* helo command received */ 10458092Seric bool vrfy; /* set if this is a vrfy command */ 10558323Seric char *protocol; /* sending protocol */ 10659016Seric char *sendinghost; /* sending hostname */ 10758333Seric long msize; /* approximate maximum message size */ 10858333Seric auto char *delimptr; 10958714Seric char *id; 11059747Seric int nrcpts; /* number of RCPT commands */ 11163787Seric bool doublequeue; 1128544Seric char inp[MAXLINE]; 11357232Seric char cmdbuf[MAXLINE]; 1147124Seric extern char Version[]; 11524943Seric extern ENVELOPE BlankEnvelope; 1164549Seric 11759066Seric if (fileno(OutChannel) != fileno(stdout)) 1187363Seric { 1197363Seric /* arrange for debugging output to go to remote host */ 12059066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1217363Seric } 12255012Seric settime(e); 12357642Seric CurHostName = RealHostName; 12461093Seric setproctitle("server %s startup", CurHostName); 12558050Seric expand("\201e", inp, &inp[sizeof inp], e); 12660210Seric message("220-%s", inp); 12760210Seric message("220 ESMTP spoken here"); 12858330Seric protocol = NULL; 12959016Seric sendinghost = macvalue('s', e); 13058082Seric gothello = FALSE; 13158330Seric gotmail = FALSE; 1324549Seric for (;;) 1334549Seric { 13412612Seric /* arrange for backout */ 13512612Seric if (setjmp(TopFrame) > 0 && InChild) 13659058Seric { 13759058Seric QuickAbort = FALSE; 13859058Seric SuprErrs = TRUE; 13912612Seric finis(); 14059058Seric } 14112612Seric QuickAbort = FALSE; 14212612Seric HoldErrs = FALSE; 14351951Seric LogUsrErrs = FALSE; 14463843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 14512612Seric 1467356Seric /* setup for the read */ 14755012Seric e->e_to = NULL; 1484577Seric Errors = 0; 1497275Seric (void) fflush(stdout); 1507356Seric 1517356Seric /* read the input line */ 15261093Seric SmtpPhase = "server cmd read"; 15361093Seric setproctitle("server %s cmd read", CurHostName); 15461093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 15561093Seric SmtpPhase); 1567356Seric 1577685Seric /* handle errors */ 1587356Seric if (p == NULL) 1597356Seric { 1604549Seric /* end of file, just die */ 16158151Seric message("421 %s Lost input channel from %s", 16225050Seric MyHostName, CurHostName); 16355464Seric #ifdef LOG 16463843Seric if (LogLevel > (gotmail ? 1 : 19)) 16555464Seric syslog(LOG_NOTICE, "lost input channel from %s", 16655464Seric CurHostName); 16755464Seric #endif 16858069Seric if (InChild) 16958069Seric ExitStat = EX_QUIT; 1704549Seric finis(); 1714549Seric } 1724549Seric 1734549Seric /* clean up end of line */ 1744558Seric fixcrlf(inp, TRUE); 1754549Seric 1764713Seric /* echo command to transcript */ 17755012Seric if (e->e_xfp != NULL) 17855012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1794713Seric 18059060Seric if (e->e_id == NULL) 18159060Seric setproctitle("%s: %s", CurHostName, inp); 18259060Seric else 18359060Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 18459060Seric 1854549Seric /* break off command */ 18658050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1874549Seric continue; 18857232Seric cmd = cmdbuf; 18958050Seric while (*p != '\0' && 19058050Seric !(isascii(*p) && isspace(*p)) && 19158050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 19224981Seric *cmd++ = *p++; 19324981Seric *cmd = '\0'; 1944549Seric 19525691Seric /* throw away leading whitespace */ 19658050Seric while (isascii(*p) && isspace(*p)) 19725691Seric p++; 19825691Seric 1994549Seric /* decode command */ 2004549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2014549Seric { 20233725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2034549Seric break; 2044549Seric } 2054549Seric 20651954Seric /* reset errors */ 20751954Seric errno = 0; 20851954Seric 2094549Seric /* process command */ 2104549Seric switch (c->cmdcode) 2114549Seric { 2124976Seric case CMDHELO: /* hello -- introduce yourself */ 21358323Seric case CMDEHLO: /* extended hello */ 21458323Seric if (c->cmdcode == CMDEHLO) 21558323Seric { 21658323Seric protocol = "ESMTP"; 21761093Seric SmtpPhase = "server EHLO"; 21858323Seric } 21958323Seric else 22058323Seric { 22158323Seric protocol = "SMTP"; 22261093Seric SmtpPhase = "server HELO"; 22358323Seric } 22459016Seric sendinghost = newstr(p); 22564284Seric if (strcasecmp(p, RealHostName) != 0 && 22664284Seric (strcasecmp(RealHostName, "localhost") != 0 || 22764284Seric strcasecmp(p, MyHostName) != 0)) 22811146Seric { 22958789Seric auth_warning(e, "Host %s claimed to be %s", 23058789Seric RealHostName, p); 23111146Seric } 23258957Seric p = macvalue('_', e); 23358957Seric if (p == NULL) 23459016Seric p = RealHostName; 23558323Seric 23660210Seric gothello = TRUE; 23760210Seric if (c->cmdcode != CMDEHLO) 23860239Seric { 23960239Seric /* print old message and be done with it */ 24060239Seric message("250 %s Hello %s, pleased to meet you", 24160239Seric MyHostName, p); 24260210Seric break; 24360239Seric } 24460239Seric 24560239Seric /* print extended message and brag */ 24660239Seric message("250-%s Hello %s, pleased to meet you", 24760239Seric MyHostName, p); 24858323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 24958323Seric message("250-EXPN"); 250*64359Seric if (MaxMessageSize > 0) 251*64359Seric message("250-SIZE %ld", MaxMessageSize); 25259271Seric else 25359271Seric message("250-SIZE"); 25458323Seric message("250 HELP"); 2554976Seric break; 2564976Seric 2574549Seric case CMDMAIL: /* mail -- designate sender */ 25861093Seric SmtpPhase = "server MAIL"; 25924943Seric 2609314Seric /* check for validity of this command */ 26158789Seric if (!gothello) 26258082Seric { 26358957Seric /* set sending host to our known value */ 26459016Seric if (sendinghost == NULL) 26559016Seric sendinghost = RealHostName; 26658957Seric 26758789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 26858821Seric { 26958789Seric message("503 Polite people say HELO first"); 27058821Seric break; 27158821Seric } 27258082Seric } 27358109Seric if (gotmail) 2744558Seric { 27558151Seric message("503 Sender already specified"); 27663843Seric if (InChild) 27763843Seric finis(); 2784558Seric break; 2794558Seric } 2809339Seric if (InChild) 2819339Seric { 28236230Skarels errno = 0; 28358151Seric syserr("503 Nested MAIL command: MAIL %s", p); 28458069Seric finis(); 2859339Seric } 2869339Seric 2879339Seric /* fork a subprocess to process this command */ 28855012Seric if (runinchild("SMTP-MAIL", e) > 0) 2899339Seric break; 29063753Seric if (!gothello) 29163753Seric { 29263753Seric auth_warning(e, 29363753Seric "Host %s didn't use HELO protocol", 29463753Seric RealHostName); 29563753Seric } 29658323Seric if (protocol == NULL) 29758323Seric protocol = "SMTP"; 29858323Seric define('r', protocol, e); 29959016Seric define('s', sendinghost, e); 30055012Seric initsys(e); 30159747Seric nrcpts = 0; 30257389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 3039339Seric 3049339Seric /* child -- go do the processing */ 3054549Seric p = skipword(p, "from"); 3064549Seric if (p == NULL) 3074549Seric break; 30857977Seric if (setjmp(TopFrame) > 0) 30958147Seric { 31058147Seric /* this failed -- undo work */ 31158147Seric if (InChild) 31259058Seric { 31359058Seric QuickAbort = FALSE; 31459058Seric SuprErrs = TRUE; 31563787Seric e->e_flags &= ~EF_FATALERRS; 31658147Seric finis(); 31759058Seric } 31857977Seric break; 31958147Seric } 32057977Seric QuickAbort = TRUE; 32158333Seric 32258333Seric /* must parse sender first */ 32358333Seric delimptr = NULL; 32458704Seric setsender(p, e, &delimptr, FALSE); 32558333Seric p = delimptr; 32658333Seric if (p != NULL && *p != '\0') 32758333Seric *p++ = '\0'; 32858333Seric 32958333Seric /* now parse ESMTP arguments */ 33058333Seric msize = 0; 33158333Seric for (; p != NULL && *p != '\0'; p++) 33258333Seric { 33358333Seric char *kp; 33458333Seric char *vp; 33558333Seric 33658333Seric /* locate the beginning of the keyword */ 33758333Seric while (isascii(*p) && isspace(*p)) 33858333Seric p++; 33958333Seric if (*p == '\0') 34058333Seric break; 34158333Seric kp = p; 34258333Seric 34358333Seric /* skip to the value portion */ 34458333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 34558333Seric p++; 34658333Seric if (*p == '=') 34758333Seric { 34858333Seric *p++ = '\0'; 34958333Seric vp = p; 35058333Seric 35158333Seric /* skip to the end of the value */ 35258333Seric while (*p != '\0' && *p != ' ' && 35358333Seric !(isascii(*p) && iscntrl(*p)) && 35458333Seric *p != '=') 35558333Seric p++; 35658333Seric } 35758333Seric 35858333Seric if (*p != '\0') 35958333Seric *p++ = '\0'; 36058333Seric 36158333Seric if (tTd(19, 1)) 36258333Seric printf("MAIL: got arg %s=%s\n", kp, 36358333Seric vp == NULL ? "<null>" : vp); 36458333Seric 36558333Seric if (strcasecmp(kp, "size") == 0) 36658333Seric { 36759093Seric if (vp == NULL) 36858333Seric { 36958333Seric usrerr("501 SIZE requires a value"); 37058333Seric /* NOTREACHED */ 37158333Seric } 37258333Seric msize = atol(vp); 37358333Seric } 37459093Seric else if (strcasecmp(kp, "body") == 0) 37559093Seric { 37659093Seric if (vp == NULL) 37759093Seric { 37859093Seric usrerr("501 BODY requires a value"); 37959093Seric /* NOTREACHED */ 38059093Seric } 38159093Seric # ifdef MIME 38259093Seric if (strcasecmp(vp, "8bitmime") == 0) 38359093Seric { 38459093Seric e->e_bodytype = "8BITMIME"; 38559709Seric SevenBit = FALSE; 38659093Seric } 38759093Seric else if (strcasecmp(vp, "7bit") == 0) 38859093Seric { 38959093Seric e->e_bodytype = "7BIT"; 39059709Seric SevenBit = TRUE; 39159093Seric } 39259093Seric else 39359093Seric { 39459093Seric usrerr("501 Unknown BODY type %s", 39559093Seric vp); 39659093Seric } 39759093Seric # endif 39859093Seric } 39958333Seric else 40058333Seric { 40158333Seric usrerr("501 %s parameter unrecognized", kp); 40258333Seric /* NOTREACHED */ 40358333Seric } 40458333Seric } 40559284Seric 40659284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 40759284Seric { 40859284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 40959284Seric MaxMessageSize); 41059284Seric /* NOTREACHED */ 41159284Seric } 41258333Seric 41358333Seric if (!enoughspace(msize)) 41458333Seric { 41558333Seric message("452 Insufficient disk space; try again later"); 41658333Seric break; 41758333Seric } 41858151Seric message("250 Sender ok"); 41958147Seric gotmail = TRUE; 4204549Seric break; 4214549Seric 4224976Seric case CMDRCPT: /* rcpt -- designate recipient */ 42358850Seric if (!gotmail) 42458850Seric { 42558850Seric usrerr("503 Need MAIL before RCPT"); 42658850Seric break; 42758850Seric } 42861093Seric SmtpPhase = "server RCPT"; 42912612Seric if (setjmp(TopFrame) > 0) 43014785Seric { 43155012Seric e->e_flags &= ~EF_FATALERRS; 43212612Seric break; 43314785Seric } 43412612Seric QuickAbort = TRUE; 43551951Seric LogUsrErrs = TRUE; 43658093Seric 43759699Seric if (e->e_sendmode != SM_DELIVER) 43859699Seric e->e_flags |= EF_VRFYONLY; 43958919Seric 4404549Seric p = skipword(p, "to"); 4414549Seric if (p == NULL) 4424549Seric break; 44364284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 44412612Seric if (a == NULL) 44512612Seric break; 44616886Seric a->q_flags |= QPRIMARY; 44755012Seric a = recipient(a, &e->e_sendqueue, e); 44812612Seric if (Errors != 0) 44912612Seric break; 45012612Seric 45112612Seric /* no errors during parsing, but might be a duplicate */ 45255012Seric e->e_to = p; 45312612Seric if (!bitset(QBADADDR, a->q_flags)) 45459747Seric { 45558151Seric message("250 Recipient ok"); 45659747Seric nrcpts++; 45759747Seric } 45812612Seric else 4594549Seric { 46012612Seric /* punt -- should keep message in ADDRESS.... */ 46158151Seric message("550 Addressee unknown"); 4624549Seric } 46355012Seric e->e_to = NULL; 4644549Seric break; 4654549Seric 4664549Seric case CMDDATA: /* data -- text of mail */ 46761093Seric SmtpPhase = "server DATA"; 46858109Seric if (!gotmail) 4694549Seric { 47058151Seric message("503 Need MAIL command"); 4714976Seric break; 4724549Seric } 47355012Seric else if (e->e_nrcpts <= 0) 4744549Seric { 47558151Seric message("503 Need RCPT (recipient)"); 4764976Seric break; 4774549Seric } 4784976Seric 47958929Seric /* check to see if we need to re-expand aliases */ 48063787Seric /* also reset QBADADDR on already-diagnosted addrs */ 48163787Seric doublequeue = FALSE; 48258929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 48358929Seric { 48458929Seric if (bitset(QVERIFIED, a->q_flags)) 48563787Seric { 48663787Seric /* need to re-expand aliases */ 48763787Seric doublequeue = TRUE; 48863787Seric } 48963787Seric if (bitset(QBADADDR, a->q_flags)) 49063787Seric { 49163787Seric /* make this "go away" */ 49263787Seric a->q_flags |= QDONTSEND; 49363787Seric a->q_flags &= ~QBADADDR; 49463787Seric } 49558929Seric } 49658929Seric 4974976Seric /* collect the text of the message */ 49824943Seric SmtpPhase = "collect"; 49963965Seric HoldErrs = TRUE; 50063787Seric collect(TRUE, doublequeue, e); 5014976Seric 5028238Seric /* 5038238Seric ** Arrange to send to everyone. 5048238Seric ** If sending to multiple people, mail back 5058238Seric ** errors rather than reporting directly. 5068238Seric ** In any case, don't mail back errors for 5078238Seric ** anything that has happened up to 5088238Seric ** now (the other end will do this). 50910197Seric ** Truncate our transcript -- the mail has gotten 51010197Seric ** to us successfully, and if we have 51110197Seric ** to mail this back, it will be easier 51210197Seric ** on the reader. 5138238Seric ** Then send to everyone. 5148238Seric ** Finally give a reply code. If an error has 5158238Seric ** already been given, don't mail a 5168238Seric ** message back. 5179339Seric ** We goose error returns by clearing error bit. 5188238Seric */ 5198238Seric 52024943Seric SmtpPhase = "delivery"; 52163787Seric if (nrcpts != 1 && !doublequeue) 5229378Seric { 5239378Seric HoldErrs = TRUE; 52458734Seric e->e_errormode = EM_MAIL; 5259378Seric } 52655012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 52758714Seric id = e->e_id; 5284976Seric 5294976Seric /* send to all recipients */ 53063787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 53155012Seric e->e_to = NULL; 5324976Seric 5338238Seric /* issue success if appropriate and reset */ 5348238Seric if (Errors == 0 || HoldErrs) 53558855Seric message("250 %s Message accepted for delivery", id); 53659747Seric 53759747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 53859730Seric { 53959730Seric /* avoid sending back an extra message */ 54059730Seric e->e_flags &= ~EF_FATALERRS; 54159747Seric e->e_flags |= EF_CLRQUEUE; 54259730Seric } 5438238Seric else 54458919Seric { 54559747Seric /* from now on, we have to operate silently */ 54659747Seric HoldErrs = TRUE; 54759747Seric e->e_errormode = EM_MAIL; 54859747Seric 54959730Seric /* if we just queued, poke it */ 55063787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 55159730Seric { 55264296Seric extern pid_t dowork(); 55364296Seric 55459730Seric unlockqueue(e); 55564296Seric (void) dowork(id, TRUE, TRUE, e); 55659730Seric } 55758919Seric } 55858883Seric 55959747Seric abortmessage: 5609339Seric /* if in a child, pop back to our parent */ 5619339Seric if (InChild) 5629339Seric finis(); 56324943Seric 56424943Seric /* clean up a bit */ 56558109Seric gotmail = FALSE; 56655012Seric dropenvelope(e); 56758179Seric CurEnv = e = newenvelope(e, CurEnv); 56855012Seric e->e_flags = BlankEnvelope.e_flags; 5694549Seric break; 5704549Seric 5714549Seric case CMDRSET: /* rset -- reset state */ 57258151Seric message("250 Reset state"); 573*64359Seric e->e_flags |= EF_CLRQUEUE; 5749339Seric if (InChild) 5759339Seric finis(); 57658109Seric 57758109Seric /* clean up a bit */ 57858109Seric gotmail = FALSE; 57958109Seric dropenvelope(e); 58058179Seric CurEnv = e = newenvelope(e, CurEnv); 5819339Seric break; 5824549Seric 5834549Seric case CMDVRFY: /* vrfy -- verify address */ 58458092Seric case CMDEXPN: /* expn -- expand address */ 58558092Seric vrfy = c->cmdcode == CMDVRFY; 58658092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 58758092Seric PrivacyFlags)) 58858082Seric { 58958412Seric if (vrfy) 59058412Seric message("252 Who's to say?"); 59158412Seric else 59258412Seric message("502 That's none of your business"); 59358082Seric break; 59458082Seric } 59558082Seric else if (!gothello && 59658092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 59758092Seric PrivacyFlags)) 59858082Seric { 59958151Seric message("503 I demand that you introduce yourself first"); 60058082Seric break; 60158082Seric } 60258092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6039339Seric break; 60455173Seric #ifdef LOG 60558020Seric if (LogLevel > 5) 60655173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 60755173Seric #endif 6085003Seric vrfyqueue = NULL; 6097762Seric QuickAbort = TRUE; 61058092Seric if (vrfy) 61158092Seric e->e_flags |= EF_VRFYONLY; 61262373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 61362373Seric *p++; 61462373Seric if (*p == '\0') 61562373Seric { 61662373Seric message("501 Argument required"); 61762373Seric Errors++; 61862373Seric } 61962373Seric else 62062373Seric { 62164284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 62262373Seric } 6237762Seric if (Errors != 0) 6249339Seric { 6259339Seric if (InChild) 6269339Seric finis(); 6277762Seric break; 6289339Seric } 62962373Seric if (vrfyqueue == NULL) 63062373Seric { 63162373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 63262373Seric } 6335003Seric while (vrfyqueue != NULL) 6345003Seric { 63563971Seric a = vrfyqueue; 63663971Seric while ((a = a->q_next) != NULL && 63763971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 63863971Seric continue; 6397685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 64058151Seric printvrfyaddr(vrfyqueue, a == NULL); 64163847Seric vrfyqueue = vrfyqueue->q_next; 6425003Seric } 6439339Seric if (InChild) 6449339Seric finis(); 6454549Seric break; 6464549Seric 6474549Seric case CMDHELP: /* help -- give user info */ 6484577Seric help(p); 6494549Seric break; 6504549Seric 6514549Seric case CMDNOOP: /* noop -- do nothing */ 65264122Seric message("250 OK"); 6534549Seric break; 6544549Seric 6554549Seric case CMDQUIT: /* quit -- leave mail */ 65658151Seric message("221 %s closing connection", MyHostName); 65761051Seric 65861051Seric /* avoid future 050 messages */ 65961051Seric Verbose = FALSE; 66061051Seric 6619339Seric if (InChild) 6629339Seric ExitStat = EX_QUIT; 6634549Seric finis(); 6644549Seric 6658544Seric case CMDVERB: /* set verbose mode */ 66659957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 66759957Seric { 66859957Seric /* this would give out the same info */ 66959957Seric message("502 Verbose unavailable"); 67059957Seric break; 67159957Seric } 6728544Seric Verbose = TRUE; 67358734Seric e->e_sendmode = SM_DELIVER; 67459957Seric message("250 Verbose mode"); 6758544Seric break; 6768544Seric 6779314Seric case CMDONEX: /* doing one transaction only */ 6789378Seric OneXact = TRUE; 67959957Seric message("250 Only one transaction"); 6809314Seric break; 6819314Seric 68236230Skarels # ifdef SMTPDEBUG 6839339Seric case CMDDBGQSHOW: /* show queues */ 6846907Seric printf("Send Queue="); 68555012Seric printaddr(e->e_sendqueue, TRUE); 6865003Seric break; 6877275Seric 6887275Seric case CMDDBGDEBUG: /* set debug mode */ 6897676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6907676Seric tTflag(p); 69158151Seric message("200 Debug set"); 6927275Seric break; 6937275Seric 69436230Skarels # else /* not SMTPDEBUG */ 69524945Seric 69636230Skarels case CMDDBGQSHOW: /* show queues */ 69736230Skarels case CMDDBGDEBUG: /* set debug mode */ 69836233Skarels # ifdef LOG 69958308Seric if (LogLevel > 0) 70036230Skarels syslog(LOG_NOTICE, 70158020Seric "\"%s\" command from %s (%s)", 70236230Skarels c->cmdname, RealHostName, 70358755Seric anynet_ntoa(&RealHostAddr)); 70436233Skarels # endif 70536230Skarels /* FALL THROUGH */ 70636230Skarels # endif /* SMTPDEBUG */ 70736230Skarels 7084549Seric case CMDERROR: /* unknown command */ 70958151Seric message("500 Command unrecognized"); 7104549Seric break; 7114549Seric 7124549Seric default: 71336230Skarels errno = 0; 71458151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7154549Seric break; 7164549Seric } 7174549Seric } 7184549Seric } 7194549Seric /* 7204549Seric ** SKIPWORD -- skip a fixed word. 7214549Seric ** 7224549Seric ** Parameters: 7234549Seric ** p -- place to start looking. 7244549Seric ** w -- word to skip. 7254549Seric ** 7264549Seric ** Returns: 7274549Seric ** p following w. 7284549Seric ** NULL on error. 7294549Seric ** 7304549Seric ** Side Effects: 7314549Seric ** clobbers the p data area. 7324549Seric */ 7334549Seric 7344549Seric static char * 7354549Seric skipword(p, w) 7364549Seric register char *p; 7374549Seric char *w; 7384549Seric { 7394549Seric register char *q; 7404549Seric 7414549Seric /* find beginning of word */ 74258050Seric while (isascii(*p) && isspace(*p)) 7434549Seric p++; 7444549Seric q = p; 7454549Seric 7464549Seric /* find end of word */ 74758050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7484549Seric p++; 74958050Seric while (isascii(*p) && isspace(*p)) 7504549Seric *p++ = '\0'; 7514549Seric if (*p != ':') 7524549Seric { 7534549Seric syntax: 75462373Seric message("501 Syntax error in parameters"); 7554549Seric Errors++; 7564549Seric return (NULL); 7574549Seric } 7584549Seric *p++ = '\0'; 75958050Seric while (isascii(*p) && isspace(*p)) 7604549Seric p++; 7614549Seric 76262373Seric if (*p == '\0') 76362373Seric goto syntax; 76462373Seric 7654549Seric /* see if the input word matches desired word */ 76633725Sbostic if (strcasecmp(q, w)) 7674549Seric goto syntax; 7684549Seric 7694549Seric return (p); 7704549Seric } 7714577Seric /* 77258151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 77358151Seric ** 77458151Seric ** Parameters: 77558151Seric ** a -- the address to print 77658151Seric ** last -- set if this is the last one. 77758151Seric ** 77858151Seric ** Returns: 77958151Seric ** none. 78058151Seric ** 78158151Seric ** Side Effects: 78258151Seric ** Prints the appropriate 250 codes. 78358151Seric */ 78458151Seric 78558151Seric printvrfyaddr(a, last) 78658151Seric register ADDRESS *a; 78758151Seric bool last; 78858151Seric { 78958151Seric char fmtbuf[20]; 79058151Seric 79158151Seric strcpy(fmtbuf, "250"); 79258151Seric fmtbuf[3] = last ? ' ' : '-'; 79358151Seric 79459746Seric if (a->q_fullname == NULL) 79559746Seric { 79659746Seric if (strchr(a->q_user, '@') == NULL) 79759746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 79859746Seric else 79959746Seric strcpy(&fmtbuf[4], "<%s>"); 80059746Seric message(fmtbuf, a->q_user, MyHostName); 80159746Seric } 80258151Seric else 80358151Seric { 80459746Seric if (strchr(a->q_user, '@') == NULL) 80559746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 80659746Seric else 80759746Seric strcpy(&fmtbuf[4], "%s <%s>"); 80859746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 80958151Seric } 81058151Seric } 81158151Seric /* 8124577Seric ** HELP -- implement the HELP command. 8134577Seric ** 8144577Seric ** Parameters: 8154577Seric ** topic -- the topic we want help for. 8164577Seric ** 8174577Seric ** Returns: 8184577Seric ** none. 8194577Seric ** 8204577Seric ** Side Effects: 8214577Seric ** outputs the help file to message output. 8224577Seric */ 8234577Seric 8244577Seric help(topic) 8254577Seric char *topic; 8264577Seric { 8274577Seric register FILE *hf; 8284577Seric int len; 8294577Seric char buf[MAXLINE]; 8304577Seric bool noinfo; 8314577Seric 8328269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8334577Seric { 8344577Seric /* no help */ 83511931Seric errno = 0; 83658151Seric message("502 HELP not implemented"); 8374577Seric return; 8384577Seric } 8394577Seric 84049669Seric if (topic == NULL || *topic == '\0') 84149669Seric topic = "smtp"; 84249669Seric else 84349669Seric makelower(topic); 84449669Seric 8454577Seric len = strlen(topic); 8464577Seric noinfo = TRUE; 8474577Seric 8484577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8494577Seric { 8504577Seric if (strncmp(buf, topic, len) == 0) 8514577Seric { 8524577Seric register char *p; 8534577Seric 85456795Seric p = strchr(buf, '\t'); 8554577Seric if (p == NULL) 8564577Seric p = buf; 8574577Seric else 8584577Seric p++; 8594577Seric fixcrlf(p, TRUE); 86058151Seric message("214-%s", p); 8614577Seric noinfo = FALSE; 8624577Seric } 8634577Seric } 8644577Seric 8654577Seric if (noinfo) 86658151Seric message("504 HELP topic unknown"); 8674577Seric else 86858151Seric message("214 End of HELP info"); 8694628Seric (void) fclose(hf); 8704577Seric } 8718544Seric /* 8729339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8739339Seric ** 8749339Seric ** Parameters: 8759339Seric ** label -- a string used in error messages 8769339Seric ** 8779339Seric ** Returns: 8789339Seric ** zero in the child 8799339Seric ** one in the parent 8809339Seric ** 8819339Seric ** Side Effects: 8829339Seric ** none. 8839339Seric */ 8848544Seric 88555012Seric runinchild(label, e) 8869339Seric char *label; 88755012Seric register ENVELOPE *e; 8889339Seric { 8899339Seric int childpid; 8909339Seric 89116158Seric if (!OneXact) 8929339Seric { 89316158Seric childpid = dofork(); 89416158Seric if (childpid < 0) 89516158Seric { 89616158Seric syserr("%s: cannot fork", label); 89716158Seric return (1); 89816158Seric } 89916158Seric if (childpid > 0) 90016158Seric { 90116158Seric auto int st; 9029339Seric 90316158Seric /* parent -- wait for child to complete */ 90461093Seric setproctitle("server %s child wait", CurHostName); 90516158Seric st = waitfor(childpid); 90616158Seric if (st == -1) 90716158Seric syserr("%s: lost child", label); 9089339Seric 90916158Seric /* if we exited on a QUIT command, complete the process */ 91016158Seric if (st == (EX_QUIT << 8)) 91116158Seric finis(); 9129339Seric 91316158Seric return (1); 91416158Seric } 91516158Seric else 91616158Seric { 91716158Seric /* child */ 91816158Seric InChild = TRUE; 91925050Seric QuickAbort = FALSE; 92055012Seric clearenvelope(e, FALSE); 92116158Seric } 9229339Seric } 92315256Seric 92416158Seric /* open alias database */ 92560537Seric initmaps(FALSE, e); 92616158Seric 92716158Seric return (0); 9289339Seric } 9299339Seric 93056795Seric # endif /* SMTP */ 931