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*63787Seric static char sccsid[] = "@(#)srvrsmtp.c 8.3 (Berkeley) 07/13/93 (with SMTP)"; 1433731Sbostic #else 15*63787Seric static char sccsid[] = "@(#)srvrsmtp.c 8.3 (Berkeley) 07/13/93 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 2011728Seric # include <signal.h> 214549Seric 2233731Sbostic # ifdef SMTP 234556Seric 244549Seric /* 254549Seric ** SMTP -- run the SMTP protocol. 264549Seric ** 274549Seric ** Parameters: 284549Seric ** none. 294549Seric ** 304549Seric ** Returns: 314549Seric ** never. 324549Seric ** 334549Seric ** Side Effects: 344549Seric ** Reads commands from the input channel and processes 354549Seric ** them. 364549Seric */ 374549Seric 384549Seric struct cmd 394549Seric { 404549Seric char *cmdname; /* command name */ 414549Seric int cmdcode; /* internal code, see below */ 424549Seric }; 434549Seric 444549Seric /* values for cmdcode */ 454549Seric # define CMDERROR 0 /* bad command */ 464549Seric # define CMDMAIL 1 /* mail -- designate sender */ 474976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 484549Seric # define CMDDATA 3 /* data -- send message text */ 499339Seric # define CMDRSET 4 /* rset -- reset state */ 509339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5158092Seric # define CMDEXPN 6 /* expn -- expand address */ 529339Seric # define CMDNOOP 7 /* noop -- do nothing */ 539339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 549339Seric # define CMDHELO 9 /* helo -- be polite */ 5558092Seric # define CMDHELP 10 /* help -- give usage info */ 5658323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5758092Seric /* non-standard commands */ 5858092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5958092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 6036230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6158092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6258092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 634549Seric 644549Seric static struct cmd CmdTab[] = 654549Seric { 664549Seric "mail", CMDMAIL, 674976Seric "rcpt", CMDRCPT, 684549Seric "data", CMDDATA, 694549Seric "rset", CMDRSET, 704549Seric "vrfy", CMDVRFY, 7158092Seric "expn", CMDEXPN, 724549Seric "help", CMDHELP, 734549Seric "noop", CMDNOOP, 744549Seric "quit", CMDQUIT, 754976Seric "helo", CMDHELO, 7658323Seric "ehlo", CMDEHLO, 778544Seric "verb", CMDVERB, 789314Seric "onex", CMDONEX, 7936230Skarels /* 8036230Skarels * remaining commands are here only 8136230Skarels * to trap and log attempts to use them 8236230Skarels */ 839339Seric "showq", CMDDBGQSHOW, 848544Seric "debug", CMDDBGDEBUG, 854549Seric NULL, CMDERROR, 864549Seric }; 874549Seric 889339Seric bool InChild = FALSE; /* true if running in a subprocess */ 899378Seric bool OneXact = FALSE; /* one xaction only this run */ 9011146Seric 919339Seric #define EX_QUIT 22 /* special code for QUIT command */ 928544Seric 9355012Seric smtp(e) 9455012Seric register ENVELOPE *e; 954549Seric { 964549Seric register char *p; 978544Seric register struct cmd *c; 984549Seric char *cmd; 9946928Sbostic static char *skipword(); 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 */ 111*63787Seric 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; 14458092Seric e->e_flags &= ~EF_VRFYONLY; 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 16458020Seric if (LogLevel > 1) 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); 22558308Seric if (strcasecmp(p, RealHostName) != 0) 22611146Seric { 22758789Seric auth_warning(e, "Host %s claimed to be %s", 22858789Seric RealHostName, p); 22911146Seric } 23058957Seric p = macvalue('_', e); 23158957Seric if (p == NULL) 23259016Seric p = RealHostName; 23358323Seric 23460210Seric gothello = TRUE; 23560210Seric if (c->cmdcode != CMDEHLO) 23660239Seric { 23760239Seric /* print old message and be done with it */ 23860239Seric message("250 %s Hello %s, pleased to meet you", 23960239Seric MyHostName, p); 24060210Seric break; 24160239Seric } 24260239Seric 24360239Seric /* print extended message and brag */ 24460239Seric message("250-%s Hello %s, pleased to meet you", 24560239Seric MyHostName, p); 24658323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 24758323Seric message("250-EXPN"); 24859271Seric if (MaxMessageSize > 0) 24959271Seric message("250-SIZE %ld", MaxMessageSize); 25059271Seric else 25159271Seric message("250-SIZE"); 25258323Seric message("250 HELP"); 2534976Seric break; 2544976Seric 2554549Seric case CMDMAIL: /* mail -- designate sender */ 25661093Seric SmtpPhase = "server MAIL"; 25724943Seric 2589314Seric /* check for validity of this command */ 25958789Seric if (!gothello) 26058082Seric { 26158957Seric /* set sending host to our known value */ 26259016Seric if (sendinghost == NULL) 26359016Seric sendinghost = RealHostName; 26458957Seric 26558789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 26658821Seric { 26758789Seric message("503 Polite people say HELO first"); 26858821Seric break; 26958821Seric } 27058082Seric } 27158109Seric if (gotmail) 2724558Seric { 27358151Seric message("503 Sender already specified"); 2744558Seric break; 2754558Seric } 2769339Seric if (InChild) 2779339Seric { 27836230Skarels errno = 0; 27958151Seric syserr("503 Nested MAIL command: MAIL %s", p); 28058069Seric finis(); 2819339Seric } 2829339Seric 2839339Seric /* fork a subprocess to process this command */ 28455012Seric if (runinchild("SMTP-MAIL", e) > 0) 2859339Seric break; 28663753Seric if (!gothello) 28763753Seric { 28863753Seric auth_warning(e, 28963753Seric "Host %s didn't use HELO protocol", 29063753Seric RealHostName); 29163753Seric } 29258323Seric if (protocol == NULL) 29358323Seric protocol = "SMTP"; 29458323Seric define('r', protocol, e); 29559016Seric define('s', sendinghost, e); 29655012Seric initsys(e); 29759747Seric nrcpts = 0; 29857389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2999339Seric 3009339Seric /* child -- go do the processing */ 3014549Seric p = skipword(p, "from"); 3024549Seric if (p == NULL) 3034549Seric break; 30457977Seric if (setjmp(TopFrame) > 0) 30558147Seric { 30658147Seric /* this failed -- undo work */ 30758147Seric if (InChild) 30859058Seric { 30959058Seric QuickAbort = FALSE; 31059058Seric SuprErrs = TRUE; 311*63787Seric e->e_flags &= ~EF_FATALERRS; 31258147Seric finis(); 31359058Seric } 31457977Seric break; 31558147Seric } 31657977Seric QuickAbort = TRUE; 31758333Seric 31858333Seric /* must parse sender first */ 31958333Seric delimptr = NULL; 32058704Seric setsender(p, e, &delimptr, FALSE); 32158333Seric p = delimptr; 32258333Seric if (p != NULL && *p != '\0') 32358333Seric *p++ = '\0'; 32458333Seric 32558333Seric /* now parse ESMTP arguments */ 32658333Seric msize = 0; 32758333Seric for (; p != NULL && *p != '\0'; p++) 32858333Seric { 32958333Seric char *kp; 33058333Seric char *vp; 33158333Seric 33258333Seric /* locate the beginning of the keyword */ 33358333Seric while (isascii(*p) && isspace(*p)) 33458333Seric p++; 33558333Seric if (*p == '\0') 33658333Seric break; 33758333Seric kp = p; 33858333Seric 33958333Seric /* skip to the value portion */ 34058333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 34158333Seric p++; 34258333Seric if (*p == '=') 34358333Seric { 34458333Seric *p++ = '\0'; 34558333Seric vp = p; 34658333Seric 34758333Seric /* skip to the end of the value */ 34858333Seric while (*p != '\0' && *p != ' ' && 34958333Seric !(isascii(*p) && iscntrl(*p)) && 35058333Seric *p != '=') 35158333Seric p++; 35258333Seric } 35358333Seric 35458333Seric if (*p != '\0') 35558333Seric *p++ = '\0'; 35658333Seric 35758333Seric if (tTd(19, 1)) 35858333Seric printf("MAIL: got arg %s=%s\n", kp, 35958333Seric vp == NULL ? "<null>" : vp); 36058333Seric 36158333Seric if (strcasecmp(kp, "size") == 0) 36258333Seric { 36359093Seric if (vp == NULL) 36458333Seric { 36558333Seric usrerr("501 SIZE requires a value"); 36658333Seric /* NOTREACHED */ 36758333Seric } 36858333Seric msize = atol(vp); 36958333Seric } 37059093Seric else if (strcasecmp(kp, "body") == 0) 37159093Seric { 37259093Seric if (vp == NULL) 37359093Seric { 37459093Seric usrerr("501 BODY requires a value"); 37559093Seric /* NOTREACHED */ 37659093Seric } 37759093Seric # ifdef MIME 37859093Seric if (strcasecmp(vp, "8bitmime") == 0) 37959093Seric { 38059093Seric e->e_bodytype = "8BITMIME"; 38159709Seric SevenBit = FALSE; 38259093Seric } 38359093Seric else if (strcasecmp(vp, "7bit") == 0) 38459093Seric { 38559093Seric e->e_bodytype = "7BIT"; 38659709Seric SevenBit = TRUE; 38759093Seric } 38859093Seric else 38959093Seric { 39059093Seric usrerr("501 Unknown BODY type %s", 39159093Seric vp); 39259093Seric } 39359093Seric # endif 39459093Seric } 39558333Seric else 39658333Seric { 39758333Seric usrerr("501 %s parameter unrecognized", kp); 39858333Seric /* NOTREACHED */ 39958333Seric } 40058333Seric } 40159284Seric 40259284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 40359284Seric { 40459284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 40559284Seric MaxMessageSize); 40659284Seric /* NOTREACHED */ 40759284Seric } 40858333Seric 40958333Seric if (!enoughspace(msize)) 41058333Seric { 41158333Seric message("452 Insufficient disk space; try again later"); 41258333Seric break; 41358333Seric } 41458151Seric message("250 Sender ok"); 41558147Seric gotmail = TRUE; 4164549Seric break; 4174549Seric 4184976Seric case CMDRCPT: /* rcpt -- designate recipient */ 41958850Seric if (!gotmail) 42058850Seric { 42158850Seric usrerr("503 Need MAIL before RCPT"); 42258850Seric break; 42358850Seric } 42461093Seric SmtpPhase = "server RCPT"; 42512612Seric if (setjmp(TopFrame) > 0) 42614785Seric { 42755012Seric e->e_flags &= ~EF_FATALERRS; 42812612Seric break; 42914785Seric } 43012612Seric QuickAbort = TRUE; 43151951Seric LogUsrErrs = TRUE; 43258093Seric 43359699Seric if (e->e_sendmode != SM_DELIVER) 43459699Seric e->e_flags |= EF_VRFYONLY; 43558919Seric 4364549Seric p = skipword(p, "to"); 4374549Seric if (p == NULL) 4384549Seric break; 43958333Seric a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); 44012612Seric if (a == NULL) 44112612Seric break; 44216886Seric a->q_flags |= QPRIMARY; 44355012Seric a = recipient(a, &e->e_sendqueue, e); 44412612Seric if (Errors != 0) 44512612Seric break; 44612612Seric 44712612Seric /* no errors during parsing, but might be a duplicate */ 44855012Seric e->e_to = p; 44912612Seric if (!bitset(QBADADDR, a->q_flags)) 45059747Seric { 45158151Seric message("250 Recipient ok"); 45259747Seric nrcpts++; 45359747Seric } 45412612Seric else 4554549Seric { 45612612Seric /* punt -- should keep message in ADDRESS.... */ 45758151Seric message("550 Addressee unknown"); 4584549Seric } 45955012Seric e->e_to = NULL; 4604549Seric break; 4614549Seric 4624549Seric case CMDDATA: /* data -- text of mail */ 46361093Seric SmtpPhase = "server DATA"; 46458109Seric if (!gotmail) 4654549Seric { 46658151Seric message("503 Need MAIL command"); 4674976Seric break; 4684549Seric } 46955012Seric else if (e->e_nrcpts <= 0) 4704549Seric { 47158151Seric message("503 Need RCPT (recipient)"); 4724976Seric break; 4734549Seric } 4744976Seric 47558929Seric /* check to see if we need to re-expand aliases */ 476*63787Seric /* also reset QBADADDR on already-diagnosted addrs */ 477*63787Seric doublequeue = FALSE; 47858929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 47958929Seric { 48058929Seric if (bitset(QVERIFIED, a->q_flags)) 481*63787Seric { 482*63787Seric /* need to re-expand aliases */ 483*63787Seric doublequeue = TRUE; 484*63787Seric } 485*63787Seric if (bitset(QBADADDR, a->q_flags)) 486*63787Seric { 487*63787Seric /* make this "go away" */ 488*63787Seric a->q_flags |= QDONTSEND; 489*63787Seric a->q_flags &= ~QBADADDR; 490*63787Seric } 49158929Seric } 49258929Seric 4934976Seric /* collect the text of the message */ 49424943Seric SmtpPhase = "collect"; 495*63787Seric collect(TRUE, doublequeue, e); 49659730Seric e->e_flags &= ~EF_FATALERRS; 4974976Seric if (Errors != 0) 49859747Seric goto abortmessage; 4994976Seric 5008238Seric /* 5018238Seric ** Arrange to send to everyone. 5028238Seric ** If sending to multiple people, mail back 5038238Seric ** errors rather than reporting directly. 5048238Seric ** In any case, don't mail back errors for 5058238Seric ** anything that has happened up to 5068238Seric ** now (the other end will do this). 50710197Seric ** Truncate our transcript -- the mail has gotten 50810197Seric ** to us successfully, and if we have 50910197Seric ** to mail this back, it will be easier 51010197Seric ** on the reader. 5118238Seric ** Then send to everyone. 5128238Seric ** Finally give a reply code. If an error has 5138238Seric ** already been given, don't mail a 5148238Seric ** message back. 5159339Seric ** We goose error returns by clearing error bit. 5168238Seric */ 5178238Seric 51824943Seric SmtpPhase = "delivery"; 519*63787Seric if (nrcpts != 1 && !doublequeue) 5209378Seric { 5219378Seric HoldErrs = TRUE; 52258734Seric e->e_errormode = EM_MAIL; 5239378Seric } 52455012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 52558714Seric id = e->e_id; 5264976Seric 5274976Seric /* send to all recipients */ 528*63787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 52955012Seric e->e_to = NULL; 5304976Seric 53123516Seric /* save statistics */ 53255012Seric markstats(e, (ADDRESS *) NULL); 53323516Seric 5348238Seric /* issue success if appropriate and reset */ 5358238Seric if (Errors == 0 || HoldErrs) 53658855Seric message("250 %s Message accepted for delivery", id); 53759747Seric 53859747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 53959730Seric { 54059730Seric /* avoid sending back an extra message */ 54159730Seric e->e_flags &= ~EF_FATALERRS; 54259747Seric e->e_flags |= EF_CLRQUEUE; 54359730Seric } 5448238Seric else 54558919Seric { 54659747Seric /* from now on, we have to operate silently */ 54759747Seric HoldErrs = TRUE; 54859747Seric e->e_errormode = EM_MAIL; 54959747Seric 55059730Seric /* if we just queued, poke it */ 551*63787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 55259730Seric { 55359730Seric unlockqueue(e); 55459730Seric dowork(id, TRUE, TRUE, e); 55559730Seric e->e_id = NULL; 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"); 5739339Seric if (InChild) 5749339Seric finis(); 57558109Seric 57658109Seric /* clean up a bit */ 57758109Seric gotmail = FALSE; 57858109Seric dropenvelope(e); 57958179Seric CurEnv = e = newenvelope(e, CurEnv); 5809339Seric break; 5814549Seric 5824549Seric case CMDVRFY: /* vrfy -- verify address */ 58358092Seric case CMDEXPN: /* expn -- expand address */ 58458092Seric vrfy = c->cmdcode == CMDVRFY; 58558092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 58658092Seric PrivacyFlags)) 58758082Seric { 58858412Seric if (vrfy) 58958412Seric message("252 Who's to say?"); 59058412Seric else 59158412Seric message("502 That's none of your business"); 59258082Seric break; 59358082Seric } 59458082Seric else if (!gothello && 59558092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 59658092Seric PrivacyFlags)) 59758082Seric { 59858151Seric message("503 I demand that you introduce yourself first"); 59958082Seric break; 60058082Seric } 60158092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6029339Seric break; 60355173Seric #ifdef LOG 60458020Seric if (LogLevel > 5) 60555173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 60655173Seric #endif 6075003Seric vrfyqueue = NULL; 6087762Seric QuickAbort = TRUE; 60958092Seric if (vrfy) 61058092Seric e->e_flags |= EF_VRFYONLY; 61162373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 61262373Seric *p++; 61362373Seric if (*p == '\0') 61462373Seric { 61562373Seric message("501 Argument required"); 61662373Seric Errors++; 61762373Seric } 61862373Seric else 61962373Seric { 62062373Seric (void) sendtolist(p, (ADDRESS *) NULL, 62162373Seric &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 { 6355003Seric register ADDRESS *a = vrfyqueue->q_next; 6365003Seric 6377685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 6385003Seric a = a->q_next; 6395003Seric 6407685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 64158151Seric printvrfyaddr(vrfyqueue, a == NULL); 6425003Seric else if (a == NULL) 64358151Seric message("554 Self destructive alias loop"); 6445003Seric vrfyqueue = a; 6455003Seric } 6469339Seric if (InChild) 6479339Seric finis(); 6484549Seric break; 6494549Seric 6504549Seric case CMDHELP: /* help -- give user info */ 6514577Seric help(p); 6524549Seric break; 6534549Seric 6544549Seric case CMDNOOP: /* noop -- do nothing */ 65558151Seric message("200 OK"); 6564549Seric break; 6574549Seric 6584549Seric case CMDQUIT: /* quit -- leave mail */ 65958151Seric message("221 %s closing connection", MyHostName); 66061051Seric 66161051Seric /* avoid future 050 messages */ 66261051Seric Verbose = FALSE; 66361051Seric 6649339Seric if (InChild) 6659339Seric ExitStat = EX_QUIT; 6664549Seric finis(); 6674549Seric 6688544Seric case CMDVERB: /* set verbose mode */ 66959957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 67059957Seric { 67159957Seric /* this would give out the same info */ 67259957Seric message("502 Verbose unavailable"); 67359957Seric break; 67459957Seric } 6758544Seric Verbose = TRUE; 67658734Seric e->e_sendmode = SM_DELIVER; 67759957Seric message("250 Verbose mode"); 6788544Seric break; 6798544Seric 6809314Seric case CMDONEX: /* doing one transaction only */ 6819378Seric OneXact = TRUE; 68259957Seric message("250 Only one transaction"); 6839314Seric break; 6849314Seric 68536230Skarels # ifdef SMTPDEBUG 6869339Seric case CMDDBGQSHOW: /* show queues */ 6876907Seric printf("Send Queue="); 68855012Seric printaddr(e->e_sendqueue, TRUE); 6895003Seric break; 6907275Seric 6917275Seric case CMDDBGDEBUG: /* set debug mode */ 6927676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6937676Seric tTflag(p); 69458151Seric message("200 Debug set"); 6957275Seric break; 6967275Seric 69736230Skarels # else /* not SMTPDEBUG */ 69824945Seric 69936230Skarels case CMDDBGQSHOW: /* show queues */ 70036230Skarels case CMDDBGDEBUG: /* set debug mode */ 70136233Skarels # ifdef LOG 70258308Seric if (LogLevel > 0) 70336230Skarels syslog(LOG_NOTICE, 70458020Seric "\"%s\" command from %s (%s)", 70536230Skarels c->cmdname, RealHostName, 70658755Seric anynet_ntoa(&RealHostAddr)); 70736233Skarels # endif 70836230Skarels /* FALL THROUGH */ 70936230Skarels # endif /* SMTPDEBUG */ 71036230Skarels 7114549Seric case CMDERROR: /* unknown command */ 71258151Seric message("500 Command unrecognized"); 7134549Seric break; 7144549Seric 7154549Seric default: 71636230Skarels errno = 0; 71758151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7184549Seric break; 7194549Seric } 7204549Seric } 7214549Seric } 7224549Seric /* 7234549Seric ** SKIPWORD -- skip a fixed word. 7244549Seric ** 7254549Seric ** Parameters: 7264549Seric ** p -- place to start looking. 7274549Seric ** w -- word to skip. 7284549Seric ** 7294549Seric ** Returns: 7304549Seric ** p following w. 7314549Seric ** NULL on error. 7324549Seric ** 7334549Seric ** Side Effects: 7344549Seric ** clobbers the p data area. 7354549Seric */ 7364549Seric 7374549Seric static char * 7384549Seric skipword(p, w) 7394549Seric register char *p; 7404549Seric char *w; 7414549Seric { 7424549Seric register char *q; 7434549Seric 7444549Seric /* find beginning of word */ 74558050Seric while (isascii(*p) && isspace(*p)) 7464549Seric p++; 7474549Seric q = p; 7484549Seric 7494549Seric /* find end of word */ 75058050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7514549Seric p++; 75258050Seric while (isascii(*p) && isspace(*p)) 7534549Seric *p++ = '\0'; 7544549Seric if (*p != ':') 7554549Seric { 7564549Seric syntax: 75762373Seric message("501 Syntax error in parameters"); 7584549Seric Errors++; 7594549Seric return (NULL); 7604549Seric } 7614549Seric *p++ = '\0'; 76258050Seric while (isascii(*p) && isspace(*p)) 7634549Seric p++; 7644549Seric 76562373Seric if (*p == '\0') 76662373Seric goto syntax; 76762373Seric 7684549Seric /* see if the input word matches desired word */ 76933725Sbostic if (strcasecmp(q, w)) 7704549Seric goto syntax; 7714549Seric 7724549Seric return (p); 7734549Seric } 7744577Seric /* 77558151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 77658151Seric ** 77758151Seric ** Parameters: 77858151Seric ** a -- the address to print 77958151Seric ** last -- set if this is the last one. 78058151Seric ** 78158151Seric ** Returns: 78258151Seric ** none. 78358151Seric ** 78458151Seric ** Side Effects: 78558151Seric ** Prints the appropriate 250 codes. 78658151Seric */ 78758151Seric 78858151Seric printvrfyaddr(a, last) 78958151Seric register ADDRESS *a; 79058151Seric bool last; 79158151Seric { 79258151Seric char fmtbuf[20]; 79358151Seric 79458151Seric strcpy(fmtbuf, "250"); 79558151Seric fmtbuf[3] = last ? ' ' : '-'; 79658151Seric 79759746Seric if (a->q_fullname == NULL) 79859746Seric { 79959746Seric if (strchr(a->q_user, '@') == NULL) 80059746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 80159746Seric else 80259746Seric strcpy(&fmtbuf[4], "<%s>"); 80359746Seric message(fmtbuf, a->q_user, MyHostName); 80459746Seric } 80558151Seric else 80658151Seric { 80759746Seric if (strchr(a->q_user, '@') == NULL) 80859746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 80959746Seric else 81059746Seric strcpy(&fmtbuf[4], "%s <%s>"); 81159746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 81258151Seric } 81358151Seric } 81458151Seric /* 8154577Seric ** HELP -- implement the HELP command. 8164577Seric ** 8174577Seric ** Parameters: 8184577Seric ** topic -- the topic we want help for. 8194577Seric ** 8204577Seric ** Returns: 8214577Seric ** none. 8224577Seric ** 8234577Seric ** Side Effects: 8244577Seric ** outputs the help file to message output. 8254577Seric */ 8264577Seric 8274577Seric help(topic) 8284577Seric char *topic; 8294577Seric { 8304577Seric register FILE *hf; 8314577Seric int len; 8324577Seric char buf[MAXLINE]; 8334577Seric bool noinfo; 8344577Seric 8358269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8364577Seric { 8374577Seric /* no help */ 83811931Seric errno = 0; 83958151Seric message("502 HELP not implemented"); 8404577Seric return; 8414577Seric } 8424577Seric 84349669Seric if (topic == NULL || *topic == '\0') 84449669Seric topic = "smtp"; 84549669Seric else 84649669Seric makelower(topic); 84749669Seric 8484577Seric len = strlen(topic); 8494577Seric noinfo = TRUE; 8504577Seric 8514577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8524577Seric { 8534577Seric if (strncmp(buf, topic, len) == 0) 8544577Seric { 8554577Seric register char *p; 8564577Seric 85756795Seric p = strchr(buf, '\t'); 8584577Seric if (p == NULL) 8594577Seric p = buf; 8604577Seric else 8614577Seric p++; 8624577Seric fixcrlf(p, TRUE); 86358151Seric message("214-%s", p); 8644577Seric noinfo = FALSE; 8654577Seric } 8664577Seric } 8674577Seric 8684577Seric if (noinfo) 86958151Seric message("504 HELP topic unknown"); 8704577Seric else 87158151Seric message("214 End of HELP info"); 8724628Seric (void) fclose(hf); 8734577Seric } 8748544Seric /* 8759339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8769339Seric ** 8779339Seric ** Parameters: 8789339Seric ** label -- a string used in error messages 8799339Seric ** 8809339Seric ** Returns: 8819339Seric ** zero in the child 8829339Seric ** one in the parent 8839339Seric ** 8849339Seric ** Side Effects: 8859339Seric ** none. 8869339Seric */ 8878544Seric 88855012Seric runinchild(label, e) 8899339Seric char *label; 89055012Seric register ENVELOPE *e; 8919339Seric { 8929339Seric int childpid; 8939339Seric 89416158Seric if (!OneXact) 8959339Seric { 89616158Seric childpid = dofork(); 89716158Seric if (childpid < 0) 89816158Seric { 89916158Seric syserr("%s: cannot fork", label); 90016158Seric return (1); 90116158Seric } 90216158Seric if (childpid > 0) 90316158Seric { 90416158Seric auto int st; 9059339Seric 90616158Seric /* parent -- wait for child to complete */ 90761093Seric setproctitle("server %s child wait", CurHostName); 90816158Seric st = waitfor(childpid); 90916158Seric if (st == -1) 91016158Seric syserr("%s: lost child", label); 9119339Seric 91216158Seric /* if we exited on a QUIT command, complete the process */ 91316158Seric if (st == (EX_QUIT << 8)) 91416158Seric finis(); 9159339Seric 91616158Seric return (1); 91716158Seric } 91816158Seric else 91916158Seric { 92016158Seric /* child */ 92116158Seric InChild = TRUE; 92225050Seric QuickAbort = FALSE; 92355012Seric clearenvelope(e, FALSE); 92416158Seric } 9259339Seric } 92615256Seric 92716158Seric /* open alias database */ 92860537Seric initmaps(FALSE, e); 92916158Seric 93016158Seric return (0); 9319339Seric } 9329339Seric 93356795Seric # endif /* SMTP */ 934