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*65058Seric static char sccsid[] = "@(#)srvrsmtp.c 8.21 (Berkeley) 12/10/93 (with SMTP)"; 1433731Sbostic #else 15*65058Seric static char sccsid[] = "@(#)srvrsmtp.c 8.21 (Berkeley) 12/10/93 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 204549Seric 2133731Sbostic # ifdef SMTP 224556Seric 234549Seric /* 244549Seric ** SMTP -- run the SMTP protocol. 254549Seric ** 264549Seric ** Parameters: 274549Seric ** none. 284549Seric ** 294549Seric ** Returns: 304549Seric ** never. 314549Seric ** 324549Seric ** Side Effects: 334549Seric ** Reads commands from the input channel and processes 344549Seric ** them. 354549Seric */ 364549Seric 374549Seric struct cmd 384549Seric { 394549Seric char *cmdname; /* command name */ 404549Seric int cmdcode; /* internal code, see below */ 414549Seric }; 424549Seric 434549Seric /* values for cmdcode */ 444549Seric # define CMDERROR 0 /* bad command */ 454549Seric # define CMDMAIL 1 /* mail -- designate sender */ 464976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 474549Seric # define CMDDATA 3 /* data -- send message text */ 489339Seric # define CMDRSET 4 /* rset -- reset state */ 499339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5058092Seric # define CMDEXPN 6 /* expn -- expand address */ 519339Seric # define CMDNOOP 7 /* noop -- do nothing */ 529339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 539339Seric # define CMDHELO 9 /* helo -- be polite */ 5458092Seric # define CMDHELP 10 /* help -- give usage info */ 5558323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5658092Seric /* non-standard commands */ 5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 5964685Seric /* use this to catch and log "door handle" attempts on your system */ 6064685Seric # define CMDLOGBOGUS 23 /* bogus command that should be logged */ 6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6258092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6358092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 644549Seric 654549Seric static struct cmd CmdTab[] = 664549Seric { 674549Seric "mail", CMDMAIL, 684976Seric "rcpt", CMDRCPT, 694549Seric "data", CMDDATA, 704549Seric "rset", CMDRSET, 714549Seric "vrfy", CMDVRFY, 7258092Seric "expn", CMDEXPN, 734549Seric "help", CMDHELP, 744549Seric "noop", CMDNOOP, 754549Seric "quit", CMDQUIT, 764976Seric "helo", CMDHELO, 7758323Seric "ehlo", CMDEHLO, 788544Seric "verb", CMDVERB, 799314Seric "onex", CMDONEX, 8036230Skarels /* 8136230Skarels * remaining commands are here only 8236230Skarels * to trap and log attempts to use them 8336230Skarels */ 849339Seric "showq", CMDDBGQSHOW, 858544Seric "debug", CMDDBGDEBUG, 8664685Seric "wiz", CMDLOGBOGUS, 874549Seric NULL, CMDERROR, 884549Seric }; 894549Seric 909378Seric bool OneXact = FALSE; /* one xaction only this run */ 9165017Seric char *CurSmtpClient; /* who's at the other end of channel */ 9211146Seric 9363937Seric static char *skipword(); 9463937Seric 9555012Seric smtp(e) 9655012Seric register ENVELOPE *e; 974549Seric { 984549Seric register char *p; 998544Seric register struct cmd *c; 1004549Seric char *cmd; 1015003Seric auto ADDRESS *vrfyqueue; 10212612Seric ADDRESS *a; 10358109Seric bool gotmail; /* mail command received */ 10458092Seric bool gothello; /* helo command received */ 10558092Seric bool vrfy; /* set if this is a vrfy command */ 10658323Seric char *protocol; /* sending protocol */ 10759016Seric char *sendinghost; /* sending hostname */ 10858333Seric long msize; /* approximate maximum message size */ 10958333Seric auto char *delimptr; 11058714Seric char *id; 11159747Seric int nrcpts; /* number of RCPT commands */ 11263787Seric bool doublequeue; 1138544Seric char inp[MAXLINE]; 11457232Seric char cmdbuf[MAXLINE]; 1157124Seric extern char Version[]; 11624943Seric extern ENVELOPE BlankEnvelope; 1174549Seric 11859066Seric if (fileno(OutChannel) != fileno(stdout)) 1197363Seric { 1207363Seric /* arrange for debugging output to go to remote host */ 12159066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1227363Seric } 12355012Seric settime(e); 12457642Seric CurHostName = RealHostName; 12565017Seric CurSmtpClient = macvalue('_', e); 12665017Seric if (CurSmtpClient == NULL) 12765017Seric CurSmtpClient = RealHostName; 12865017Seric 12965017Seric setproctitle("server %s startup", CurSmtpClient); 13058050Seric expand("\201e", inp, &inp[sizeof inp], e); 13164496Seric if (BrokenSmtpPeers) 13264496Seric { 13364496Seric message("220 %s", inp); 13464496Seric } 13564496Seric else 13664496Seric { 13764496Seric message("220-%s", inp); 13864496Seric message("220 ESMTP spoken here"); 13964496Seric } 14058330Seric protocol = NULL; 14159016Seric sendinghost = macvalue('s', e); 14258082Seric gothello = FALSE; 14358330Seric gotmail = FALSE; 1444549Seric for (;;) 1454549Seric { 14612612Seric /* arrange for backout */ 14712612Seric if (setjmp(TopFrame) > 0 && InChild) 14859058Seric { 14959058Seric QuickAbort = FALSE; 15059058Seric SuprErrs = TRUE; 15112612Seric finis(); 15259058Seric } 15312612Seric QuickAbort = FALSE; 15412612Seric HoldErrs = FALSE; 15551951Seric LogUsrErrs = FALSE; 15663843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 15712612Seric 1587356Seric /* setup for the read */ 15955012Seric e->e_to = NULL; 1604577Seric Errors = 0; 1617275Seric (void) fflush(stdout); 1627356Seric 1637356Seric /* read the input line */ 16461093Seric SmtpPhase = "server cmd read"; 16561093Seric setproctitle("server %s cmd read", CurHostName); 16661093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 16761093Seric SmtpPhase); 1687356Seric 1697685Seric /* handle errors */ 1707356Seric if (p == NULL) 1717356Seric { 1724549Seric /* end of file, just die */ 17358151Seric message("421 %s Lost input channel from %s", 17465017Seric MyHostName, CurSmtpClient); 17555464Seric #ifdef LOG 17663843Seric if (LogLevel > (gotmail ? 1 : 19)) 17755464Seric syslog(LOG_NOTICE, "lost input channel from %s", 17865017Seric CurSmtpClient); 17955464Seric #endif 18058069Seric if (InChild) 18158069Seric ExitStat = EX_QUIT; 1824549Seric finis(); 1834549Seric } 1844549Seric 1854549Seric /* clean up end of line */ 1864558Seric fixcrlf(inp, TRUE); 1874549Seric 1884713Seric /* echo command to transcript */ 18955012Seric if (e->e_xfp != NULL) 19055012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1914713Seric 19259060Seric if (e->e_id == NULL) 193*65058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 19459060Seric else 195*65058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 19659060Seric 1974549Seric /* break off command */ 19858050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1994549Seric continue; 20057232Seric cmd = cmdbuf; 20158050Seric while (*p != '\0' && 20258050Seric !(isascii(*p) && isspace(*p)) && 20358050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 20424981Seric *cmd++ = *p++; 20524981Seric *cmd = '\0'; 2064549Seric 20725691Seric /* throw away leading whitespace */ 20858050Seric while (isascii(*p) && isspace(*p)) 20925691Seric p++; 21025691Seric 2114549Seric /* decode command */ 2124549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2134549Seric { 21433725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2154549Seric break; 2164549Seric } 2174549Seric 21851954Seric /* reset errors */ 21951954Seric errno = 0; 22051954Seric 2214549Seric /* process command */ 2224549Seric switch (c->cmdcode) 2234549Seric { 2244976Seric case CMDHELO: /* hello -- introduce yourself */ 22558323Seric case CMDEHLO: /* extended hello */ 22658323Seric if (c->cmdcode == CMDEHLO) 22758323Seric { 22858323Seric protocol = "ESMTP"; 22961093Seric SmtpPhase = "server EHLO"; 23058323Seric } 23158323Seric else 23258323Seric { 23358323Seric protocol = "SMTP"; 23461093Seric SmtpPhase = "server HELO"; 23558323Seric } 23659016Seric sendinghost = newstr(p); 23764284Seric if (strcasecmp(p, RealHostName) != 0 && 23864284Seric (strcasecmp(RealHostName, "localhost") != 0 || 23964284Seric strcasecmp(p, MyHostName) != 0)) 24011146Seric { 24158789Seric auth_warning(e, "Host %s claimed to be %s", 24258789Seric RealHostName, p); 24311146Seric } 24458323Seric 24560210Seric gothello = TRUE; 24660210Seric if (c->cmdcode != CMDEHLO) 24760239Seric { 24860239Seric /* print old message and be done with it */ 24960239Seric message("250 %s Hello %s, pleased to meet you", 25065017Seric MyHostName, CurSmtpClient); 25160210Seric break; 25260239Seric } 25360239Seric 25460239Seric /* print extended message and brag */ 25560239Seric message("250-%s Hello %s, pleased to meet you", 25660239Seric MyHostName, p); 25758323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 25858323Seric message("250-EXPN"); 25964359Seric if (MaxMessageSize > 0) 26064359Seric message("250-SIZE %ld", MaxMessageSize); 26159271Seric else 26259271Seric message("250-SIZE"); 26358323Seric message("250 HELP"); 2644976Seric break; 2654976Seric 2664549Seric case CMDMAIL: /* mail -- designate sender */ 26761093Seric SmtpPhase = "server MAIL"; 26824943Seric 2699314Seric /* check for validity of this command */ 27058789Seric if (!gothello) 27158082Seric { 27258957Seric /* set sending host to our known value */ 27359016Seric if (sendinghost == NULL) 27459016Seric sendinghost = RealHostName; 27558957Seric 27658789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 27758821Seric { 27858789Seric message("503 Polite people say HELO first"); 27958821Seric break; 28058821Seric } 28158082Seric } 28258109Seric if (gotmail) 2834558Seric { 28458151Seric message("503 Sender already specified"); 28563843Seric if (InChild) 28663843Seric finis(); 2874558Seric break; 2884558Seric } 2899339Seric if (InChild) 2909339Seric { 29136230Skarels errno = 0; 29258151Seric syserr("503 Nested MAIL command: MAIL %s", p); 29358069Seric finis(); 2949339Seric } 2959339Seric 2969339Seric /* fork a subprocess to process this command */ 29755012Seric if (runinchild("SMTP-MAIL", e) > 0) 2989339Seric break; 29963753Seric if (!gothello) 30063753Seric { 30163753Seric auth_warning(e, 30263753Seric "Host %s didn't use HELO protocol", 30363753Seric RealHostName); 30463753Seric } 30558323Seric if (protocol == NULL) 30658323Seric protocol = "SMTP"; 30758323Seric define('r', protocol, e); 30859016Seric define('s', sendinghost, e); 30955012Seric initsys(e); 31059747Seric nrcpts = 0; 311*65058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3129339Seric 3139339Seric /* child -- go do the processing */ 3144549Seric p = skipword(p, "from"); 3154549Seric if (p == NULL) 3164549Seric break; 31757977Seric if (setjmp(TopFrame) > 0) 31858147Seric { 31958147Seric /* this failed -- undo work */ 32058147Seric if (InChild) 32159058Seric { 32259058Seric QuickAbort = FALSE; 32359058Seric SuprErrs = TRUE; 32463787Seric e->e_flags &= ~EF_FATALERRS; 32558147Seric finis(); 32659058Seric } 32757977Seric break; 32858147Seric } 32957977Seric QuickAbort = TRUE; 33058333Seric 33158333Seric /* must parse sender first */ 33258333Seric delimptr = NULL; 33358704Seric setsender(p, e, &delimptr, FALSE); 33458333Seric p = delimptr; 33558333Seric if (p != NULL && *p != '\0') 33658333Seric *p++ = '\0'; 33758333Seric 33858333Seric /* now parse ESMTP arguments */ 33958333Seric msize = 0; 34058333Seric for (; p != NULL && *p != '\0'; p++) 34158333Seric { 34258333Seric char *kp; 34358333Seric char *vp; 34458333Seric 34558333Seric /* locate the beginning of the keyword */ 34658333Seric while (isascii(*p) && isspace(*p)) 34758333Seric p++; 34858333Seric if (*p == '\0') 34958333Seric break; 35058333Seric kp = p; 35158333Seric 35258333Seric /* skip to the value portion */ 35358333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 35458333Seric p++; 35558333Seric if (*p == '=') 35658333Seric { 35758333Seric *p++ = '\0'; 35858333Seric vp = p; 35958333Seric 36058333Seric /* skip to the end of the value */ 36158333Seric while (*p != '\0' && *p != ' ' && 36258333Seric !(isascii(*p) && iscntrl(*p)) && 36358333Seric *p != '=') 36458333Seric p++; 36558333Seric } 36658333Seric 36758333Seric if (*p != '\0') 36858333Seric *p++ = '\0'; 36958333Seric 37058333Seric if (tTd(19, 1)) 37158333Seric printf("MAIL: got arg %s=%s\n", kp, 37258333Seric vp == NULL ? "<null>" : vp); 37358333Seric 37458333Seric if (strcasecmp(kp, "size") == 0) 37558333Seric { 37659093Seric if (vp == NULL) 37758333Seric { 37858333Seric usrerr("501 SIZE requires a value"); 37958333Seric /* NOTREACHED */ 38058333Seric } 38158333Seric msize = atol(vp); 38258333Seric } 38359093Seric else if (strcasecmp(kp, "body") == 0) 38459093Seric { 38559093Seric if (vp == NULL) 38659093Seric { 38759093Seric usrerr("501 BODY requires a value"); 38859093Seric /* NOTREACHED */ 38959093Seric } 39059093Seric # ifdef MIME 39159093Seric if (strcasecmp(vp, "8bitmime") == 0) 39259093Seric { 39359093Seric e->e_bodytype = "8BITMIME"; 39459709Seric SevenBit = FALSE; 39559093Seric } 39659093Seric else if (strcasecmp(vp, "7bit") == 0) 39759093Seric { 39859093Seric e->e_bodytype = "7BIT"; 39959709Seric SevenBit = TRUE; 40059093Seric } 40159093Seric else 40259093Seric { 40359093Seric usrerr("501 Unknown BODY type %s", 40459093Seric vp); 40559093Seric } 40659093Seric # endif 40759093Seric } 40858333Seric else 40958333Seric { 41058333Seric usrerr("501 %s parameter unrecognized", kp); 41158333Seric /* NOTREACHED */ 41258333Seric } 41358333Seric } 41459284Seric 41559284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 41659284Seric { 41759284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 41859284Seric MaxMessageSize); 41959284Seric /* NOTREACHED */ 42059284Seric } 42158333Seric 42258333Seric if (!enoughspace(msize)) 42358333Seric { 42458333Seric message("452 Insufficient disk space; try again later"); 42558333Seric break; 42658333Seric } 42758151Seric message("250 Sender ok"); 42858147Seric gotmail = TRUE; 4294549Seric break; 4304549Seric 4314976Seric case CMDRCPT: /* rcpt -- designate recipient */ 43258850Seric if (!gotmail) 43358850Seric { 43458850Seric usrerr("503 Need MAIL before RCPT"); 43558850Seric break; 43658850Seric } 43761093Seric SmtpPhase = "server RCPT"; 43812612Seric if (setjmp(TopFrame) > 0) 43914785Seric { 44055012Seric e->e_flags &= ~EF_FATALERRS; 44112612Seric break; 44214785Seric } 44312612Seric QuickAbort = TRUE; 44451951Seric LogUsrErrs = TRUE; 44558093Seric 44659699Seric if (e->e_sendmode != SM_DELIVER) 44759699Seric e->e_flags |= EF_VRFYONLY; 44858919Seric 4494549Seric p = skipword(p, "to"); 4504549Seric if (p == NULL) 4514549Seric break; 45264284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 45312612Seric if (a == NULL) 45412612Seric break; 45516886Seric a->q_flags |= QPRIMARY; 45655012Seric a = recipient(a, &e->e_sendqueue, e); 45712612Seric if (Errors != 0) 45812612Seric break; 45912612Seric 46012612Seric /* no errors during parsing, but might be a duplicate */ 46155012Seric e->e_to = p; 46212612Seric if (!bitset(QBADADDR, a->q_flags)) 46359747Seric { 46464718Seric message("250 Recipient ok%s", 46564718Seric bitset(QQUEUEUP, a->q_flags) ? 46664718Seric " (will queue)" : ""); 46759747Seric nrcpts++; 46859747Seric } 46912612Seric else 4704549Seric { 47112612Seric /* punt -- should keep message in ADDRESS.... */ 47258151Seric message("550 Addressee unknown"); 4734549Seric } 47455012Seric e->e_to = NULL; 4754549Seric break; 4764549Seric 4774549Seric case CMDDATA: /* data -- text of mail */ 47861093Seric SmtpPhase = "server DATA"; 47958109Seric if (!gotmail) 4804549Seric { 48158151Seric message("503 Need MAIL command"); 4824976Seric break; 4834549Seric } 48464718Seric else if (nrcpts <= 0) 4854549Seric { 48658151Seric message("503 Need RCPT (recipient)"); 4874976Seric break; 4884549Seric } 4894976Seric 49058929Seric /* check to see if we need to re-expand aliases */ 49163787Seric /* also reset QBADADDR on already-diagnosted addrs */ 49263787Seric doublequeue = FALSE; 49358929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 49458929Seric { 49558929Seric if (bitset(QVERIFIED, a->q_flags)) 49663787Seric { 49763787Seric /* need to re-expand aliases */ 49863787Seric doublequeue = TRUE; 49963787Seric } 50063787Seric if (bitset(QBADADDR, a->q_flags)) 50163787Seric { 50263787Seric /* make this "go away" */ 50363787Seric a->q_flags |= QDONTSEND; 50463787Seric a->q_flags &= ~QBADADDR; 50563787Seric } 50658929Seric } 50758929Seric 5084976Seric /* collect the text of the message */ 50924943Seric SmtpPhase = "collect"; 51064766Seric collect(TRUE, doublequeue, e); 51164766Seric if (Errors != 0) 51264766Seric goto abortmessage; 51363965Seric HoldErrs = TRUE; 5144976Seric 5158238Seric /* 5168238Seric ** Arrange to send to everyone. 5178238Seric ** If sending to multiple people, mail back 5188238Seric ** errors rather than reporting directly. 5198238Seric ** In any case, don't mail back errors for 5208238Seric ** anything that has happened up to 5218238Seric ** now (the other end will do this). 52210197Seric ** Truncate our transcript -- the mail has gotten 52310197Seric ** to us successfully, and if we have 52410197Seric ** to mail this back, it will be easier 52510197Seric ** on the reader. 5268238Seric ** Then send to everyone. 5278238Seric ** Finally give a reply code. If an error has 5288238Seric ** already been given, don't mail a 5298238Seric ** message back. 5309339Seric ** We goose error returns by clearing error bit. 5318238Seric */ 5328238Seric 53324943Seric SmtpPhase = "delivery"; 53463787Seric if (nrcpts != 1 && !doublequeue) 5359378Seric { 5369378Seric HoldErrs = TRUE; 53758734Seric e->e_errormode = EM_MAIL; 5389378Seric } 53955012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 54058714Seric id = e->e_id; 5414976Seric 5424976Seric /* send to all recipients */ 54363787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 54455012Seric e->e_to = NULL; 5454976Seric 5468238Seric /* issue success if appropriate and reset */ 5478238Seric if (Errors == 0 || HoldErrs) 54858855Seric message("250 %s Message accepted for delivery", id); 54959747Seric 55059747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 55159730Seric { 55259730Seric /* avoid sending back an extra message */ 55359730Seric e->e_flags &= ~EF_FATALERRS; 55459747Seric e->e_flags |= EF_CLRQUEUE; 55559730Seric } 5568238Seric else 55758919Seric { 55859747Seric /* from now on, we have to operate silently */ 55959747Seric HoldErrs = TRUE; 56059747Seric e->e_errormode = EM_MAIL; 56159747Seric 56259730Seric /* if we just queued, poke it */ 56363787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 56459730Seric { 56564296Seric extern pid_t dowork(); 56664296Seric 56759730Seric unlockqueue(e); 56864296Seric (void) dowork(id, TRUE, TRUE, e); 56959730Seric } 57058919Seric } 57158883Seric 57259747Seric abortmessage: 5739339Seric /* if in a child, pop back to our parent */ 5749339Seric if (InChild) 5759339Seric finis(); 57624943Seric 57724943Seric /* clean up a bit */ 57858109Seric gotmail = FALSE; 57955012Seric dropenvelope(e); 58058179Seric CurEnv = e = newenvelope(e, CurEnv); 58155012Seric e->e_flags = BlankEnvelope.e_flags; 5824549Seric break; 5834549Seric 5844549Seric case CMDRSET: /* rset -- reset state */ 58558151Seric message("250 Reset state"); 58664359Seric e->e_flags |= EF_CLRQUEUE; 5879339Seric if (InChild) 5889339Seric finis(); 58958109Seric 59058109Seric /* clean up a bit */ 59158109Seric gotmail = FALSE; 59258109Seric dropenvelope(e); 59358179Seric CurEnv = e = newenvelope(e, CurEnv); 5949339Seric break; 5954549Seric 5964549Seric case CMDVRFY: /* vrfy -- verify address */ 59758092Seric case CMDEXPN: /* expn -- expand address */ 59858092Seric vrfy = c->cmdcode == CMDVRFY; 59958092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 60058092Seric PrivacyFlags)) 60158082Seric { 60258412Seric if (vrfy) 60358412Seric message("252 Who's to say?"); 60458412Seric else 60558412Seric message("502 That's none of your business"); 60665017Seric #ifdef LOG 60765017Seric if (LogLevel > 5) 60865017Seric syslog(LOG_INFO, "%s: %s [rejected]", 60965017Seric CurSmtpClient, inp); 61065017Seric #endif 61158082Seric break; 61258082Seric } 61358082Seric else if (!gothello && 61458092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 61558092Seric PrivacyFlags)) 61658082Seric { 61758151Seric message("503 I demand that you introduce yourself first"); 61858082Seric break; 61958082Seric } 62058092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6219339Seric break; 62255173Seric #ifdef LOG 62358020Seric if (LogLevel > 5) 62465017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 62555173Seric #endif 6265003Seric vrfyqueue = NULL; 6277762Seric QuickAbort = TRUE; 62858092Seric if (vrfy) 62958092Seric e->e_flags |= EF_VRFYONLY; 63062373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 63162373Seric *p++; 63262373Seric if (*p == '\0') 63362373Seric { 63462373Seric message("501 Argument required"); 63562373Seric Errors++; 63662373Seric } 63762373Seric else 63862373Seric { 63964284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 64062373Seric } 6417762Seric if (Errors != 0) 6429339Seric { 6439339Seric if (InChild) 6449339Seric finis(); 6457762Seric break; 6469339Seric } 64762373Seric if (vrfyqueue == NULL) 64862373Seric { 64962373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 65062373Seric } 6515003Seric while (vrfyqueue != NULL) 6525003Seric { 65363971Seric a = vrfyqueue; 65463971Seric while ((a = a->q_next) != NULL && 65563971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 65663971Seric continue; 6577685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 65858151Seric printvrfyaddr(vrfyqueue, a == NULL); 65963847Seric vrfyqueue = vrfyqueue->q_next; 6605003Seric } 6619339Seric if (InChild) 6629339Seric finis(); 6634549Seric break; 6644549Seric 6654549Seric case CMDHELP: /* help -- give user info */ 6664577Seric help(p); 6674549Seric break; 6684549Seric 6694549Seric case CMDNOOP: /* noop -- do nothing */ 67064122Seric message("250 OK"); 6714549Seric break; 6724549Seric 6734549Seric case CMDQUIT: /* quit -- leave mail */ 67458151Seric message("221 %s closing connection", MyHostName); 67561051Seric 67661051Seric /* avoid future 050 messages */ 67761051Seric Verbose = FALSE; 67861051Seric 6799339Seric if (InChild) 6809339Seric ExitStat = EX_QUIT; 6814549Seric finis(); 6824549Seric 6838544Seric case CMDVERB: /* set verbose mode */ 68459957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 68559957Seric { 68659957Seric /* this would give out the same info */ 68759957Seric message("502 Verbose unavailable"); 68859957Seric break; 68959957Seric } 6908544Seric Verbose = TRUE; 69158734Seric e->e_sendmode = SM_DELIVER; 69259957Seric message("250 Verbose mode"); 6938544Seric break; 6948544Seric 6959314Seric case CMDONEX: /* doing one transaction only */ 6969378Seric OneXact = TRUE; 69759957Seric message("250 Only one transaction"); 6989314Seric break; 6999314Seric 70036230Skarels # ifdef SMTPDEBUG 7019339Seric case CMDDBGQSHOW: /* show queues */ 7026907Seric printf("Send Queue="); 70355012Seric printaddr(e->e_sendqueue, TRUE); 7045003Seric break; 7057275Seric 7067275Seric case CMDDBGDEBUG: /* set debug mode */ 7077676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7087676Seric tTflag(p); 70958151Seric message("200 Debug set"); 7107275Seric break; 7117275Seric 71236230Skarels # else /* not SMTPDEBUG */ 71336230Skarels case CMDDBGQSHOW: /* show queues */ 71436230Skarels case CMDDBGDEBUG: /* set debug mode */ 71564685Seric # endif /* SMTPDEBUG */ 71664685Seric case CMDLOGBOGUS: /* bogus command */ 71736233Skarels # ifdef LOG 71858308Seric if (LogLevel > 0) 71964685Seric syslog(LOG_CRIT, 72058020Seric "\"%s\" command from %s (%s)", 72136230Skarels c->cmdname, RealHostName, 72258755Seric anynet_ntoa(&RealHostAddr)); 72336233Skarels # endif 72436230Skarels /* FALL THROUGH */ 72536230Skarels 7264549Seric case CMDERROR: /* unknown command */ 72758151Seric message("500 Command unrecognized"); 7284549Seric break; 7294549Seric 7304549Seric default: 73136230Skarels errno = 0; 73258151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7334549Seric break; 7344549Seric } 7354549Seric } 7364549Seric } 7374549Seric /* 7384549Seric ** SKIPWORD -- skip a fixed word. 7394549Seric ** 7404549Seric ** Parameters: 7414549Seric ** p -- place to start looking. 7424549Seric ** w -- word to skip. 7434549Seric ** 7444549Seric ** Returns: 7454549Seric ** p following w. 7464549Seric ** NULL on error. 7474549Seric ** 7484549Seric ** Side Effects: 7494549Seric ** clobbers the p data area. 7504549Seric */ 7514549Seric 7524549Seric static char * 7534549Seric skipword(p, w) 7544549Seric register char *p; 7554549Seric char *w; 7564549Seric { 7574549Seric register char *q; 7584549Seric 7594549Seric /* find beginning of word */ 76058050Seric while (isascii(*p) && isspace(*p)) 7614549Seric p++; 7624549Seric q = p; 7634549Seric 7644549Seric /* find end of word */ 76558050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7664549Seric p++; 76758050Seric while (isascii(*p) && isspace(*p)) 7684549Seric *p++ = '\0'; 7694549Seric if (*p != ':') 7704549Seric { 7714549Seric syntax: 77262373Seric message("501 Syntax error in parameters"); 7734549Seric Errors++; 7744549Seric return (NULL); 7754549Seric } 7764549Seric *p++ = '\0'; 77758050Seric while (isascii(*p) && isspace(*p)) 7784549Seric p++; 7794549Seric 78062373Seric if (*p == '\0') 78162373Seric goto syntax; 78262373Seric 7834549Seric /* see if the input word matches desired word */ 78433725Sbostic if (strcasecmp(q, w)) 7854549Seric goto syntax; 7864549Seric 7874549Seric return (p); 7884549Seric } 7894577Seric /* 79058151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 79158151Seric ** 79258151Seric ** Parameters: 79358151Seric ** a -- the address to print 79458151Seric ** last -- set if this is the last one. 79558151Seric ** 79658151Seric ** Returns: 79758151Seric ** none. 79858151Seric ** 79958151Seric ** Side Effects: 80058151Seric ** Prints the appropriate 250 codes. 80158151Seric */ 80258151Seric 80358151Seric printvrfyaddr(a, last) 80458151Seric register ADDRESS *a; 80558151Seric bool last; 80658151Seric { 80758151Seric char fmtbuf[20]; 80858151Seric 80958151Seric strcpy(fmtbuf, "250"); 81058151Seric fmtbuf[3] = last ? ' ' : '-'; 81158151Seric 81259746Seric if (a->q_fullname == NULL) 81359746Seric { 81459746Seric if (strchr(a->q_user, '@') == NULL) 81559746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 81659746Seric else 81759746Seric strcpy(&fmtbuf[4], "<%s>"); 81859746Seric message(fmtbuf, a->q_user, MyHostName); 81959746Seric } 82058151Seric else 82158151Seric { 82259746Seric if (strchr(a->q_user, '@') == NULL) 82359746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 82459746Seric else 82559746Seric strcpy(&fmtbuf[4], "%s <%s>"); 82659746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 82758151Seric } 82858151Seric } 82958151Seric /* 8304577Seric ** HELP -- implement the HELP command. 8314577Seric ** 8324577Seric ** Parameters: 8334577Seric ** topic -- the topic we want help for. 8344577Seric ** 8354577Seric ** Returns: 8364577Seric ** none. 8374577Seric ** 8384577Seric ** Side Effects: 8394577Seric ** outputs the help file to message output. 8404577Seric */ 8414577Seric 8424577Seric help(topic) 8434577Seric char *topic; 8444577Seric { 8454577Seric register FILE *hf; 8464577Seric int len; 8474577Seric char buf[MAXLINE]; 8484577Seric bool noinfo; 8494577Seric 8508269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8514577Seric { 8524577Seric /* no help */ 85311931Seric errno = 0; 85458151Seric message("502 HELP not implemented"); 8554577Seric return; 8564577Seric } 8574577Seric 85849669Seric if (topic == NULL || *topic == '\0') 85949669Seric topic = "smtp"; 86049669Seric else 86149669Seric makelower(topic); 86249669Seric 8634577Seric len = strlen(topic); 8644577Seric noinfo = TRUE; 8654577Seric 8664577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8674577Seric { 8684577Seric if (strncmp(buf, topic, len) == 0) 8694577Seric { 8704577Seric register char *p; 8714577Seric 87256795Seric p = strchr(buf, '\t'); 8734577Seric if (p == NULL) 8744577Seric p = buf; 8754577Seric else 8764577Seric p++; 8774577Seric fixcrlf(p, TRUE); 87858151Seric message("214-%s", p); 8794577Seric noinfo = FALSE; 8804577Seric } 8814577Seric } 8824577Seric 8834577Seric if (noinfo) 88458151Seric message("504 HELP topic unknown"); 8854577Seric else 88658151Seric message("214 End of HELP info"); 8874628Seric (void) fclose(hf); 8884577Seric } 8898544Seric /* 8909339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8919339Seric ** 8929339Seric ** Parameters: 8939339Seric ** label -- a string used in error messages 8949339Seric ** 8959339Seric ** Returns: 8969339Seric ** zero in the child 8979339Seric ** one in the parent 8989339Seric ** 8999339Seric ** Side Effects: 9009339Seric ** none. 9019339Seric */ 9028544Seric 90355012Seric runinchild(label, e) 9049339Seric char *label; 90555012Seric register ENVELOPE *e; 9069339Seric { 9079339Seric int childpid; 9089339Seric 90916158Seric if (!OneXact) 9109339Seric { 91116158Seric childpid = dofork(); 91216158Seric if (childpid < 0) 91316158Seric { 91416158Seric syserr("%s: cannot fork", label); 91516158Seric return (1); 91616158Seric } 91716158Seric if (childpid > 0) 91816158Seric { 91916158Seric auto int st; 9209339Seric 92116158Seric /* parent -- wait for child to complete */ 92261093Seric setproctitle("server %s child wait", CurHostName); 92316158Seric st = waitfor(childpid); 92416158Seric if (st == -1) 92516158Seric syserr("%s: lost child", label); 92664948Seric else if (!WIFEXITED(st)) 92764948Seric syserr("%s: died on signal %d", 92864948Seric label, st & 0177); 9299339Seric 93016158Seric /* if we exited on a QUIT command, complete the process */ 93116158Seric if (st == (EX_QUIT << 8)) 93216158Seric finis(); 9339339Seric 93416158Seric return (1); 93516158Seric } 93616158Seric else 93716158Seric { 93816158Seric /* child */ 93916158Seric InChild = TRUE; 94025050Seric QuickAbort = FALSE; 94155012Seric clearenvelope(e, FALSE); 94216158Seric } 9439339Seric } 94415256Seric 94516158Seric /* open alias database */ 94660537Seric initmaps(FALSE, e); 94716158Seric 94816158Seric return (0); 9499339Seric } 9509339Seric 95156795Seric # endif /* SMTP */ 952