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*63843Seric static char sccsid[] = "@(#)srvrsmtp.c 8.4 (Berkeley) 07/16/93 (with SMTP)"; 1433731Sbostic #else 15*63843Seric static char sccsid[] = "@(#)srvrsmtp.c 8.4 (Berkeley) 07/16/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 */ 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; 144*63843Seric 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 164*63843Seric 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); 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"); 274*63843Seric if (InChild) 275*63843Seric finis(); 2764558Seric break; 2774558Seric } 2789339Seric if (InChild) 2799339Seric { 28036230Skarels errno = 0; 28158151Seric syserr("503 Nested MAIL command: MAIL %s", p); 28258069Seric finis(); 2839339Seric } 2849339Seric 2859339Seric /* fork a subprocess to process this command */ 28655012Seric if (runinchild("SMTP-MAIL", e) > 0) 2879339Seric break; 28863753Seric if (!gothello) 28963753Seric { 29063753Seric auth_warning(e, 29163753Seric "Host %s didn't use HELO protocol", 29263753Seric RealHostName); 29363753Seric } 29458323Seric if (protocol == NULL) 29558323Seric protocol = "SMTP"; 29658323Seric define('r', protocol, e); 29759016Seric define('s', sendinghost, e); 29855012Seric initsys(e); 29959747Seric nrcpts = 0; 30057389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 3019339Seric 3029339Seric /* child -- go do the processing */ 3034549Seric p = skipword(p, "from"); 3044549Seric if (p == NULL) 3054549Seric break; 30657977Seric if (setjmp(TopFrame) > 0) 30758147Seric { 30858147Seric /* this failed -- undo work */ 30958147Seric if (InChild) 31059058Seric { 31159058Seric QuickAbort = FALSE; 31259058Seric SuprErrs = TRUE; 31363787Seric e->e_flags &= ~EF_FATALERRS; 31458147Seric finis(); 31559058Seric } 31657977Seric break; 31758147Seric } 31857977Seric QuickAbort = TRUE; 31958333Seric 32058333Seric /* must parse sender first */ 32158333Seric delimptr = NULL; 32258704Seric setsender(p, e, &delimptr, FALSE); 32358333Seric p = delimptr; 32458333Seric if (p != NULL && *p != '\0') 32558333Seric *p++ = '\0'; 32658333Seric 32758333Seric /* now parse ESMTP arguments */ 32858333Seric msize = 0; 32958333Seric for (; p != NULL && *p != '\0'; p++) 33058333Seric { 33158333Seric char *kp; 33258333Seric char *vp; 33358333Seric 33458333Seric /* locate the beginning of the keyword */ 33558333Seric while (isascii(*p) && isspace(*p)) 33658333Seric p++; 33758333Seric if (*p == '\0') 33858333Seric break; 33958333Seric kp = p; 34058333Seric 34158333Seric /* skip to the value portion */ 34258333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 34358333Seric p++; 34458333Seric if (*p == '=') 34558333Seric { 34658333Seric *p++ = '\0'; 34758333Seric vp = p; 34858333Seric 34958333Seric /* skip to the end of the value */ 35058333Seric while (*p != '\0' && *p != ' ' && 35158333Seric !(isascii(*p) && iscntrl(*p)) && 35258333Seric *p != '=') 35358333Seric p++; 35458333Seric } 35558333Seric 35658333Seric if (*p != '\0') 35758333Seric *p++ = '\0'; 35858333Seric 35958333Seric if (tTd(19, 1)) 36058333Seric printf("MAIL: got arg %s=%s\n", kp, 36158333Seric vp == NULL ? "<null>" : vp); 36258333Seric 36358333Seric if (strcasecmp(kp, "size") == 0) 36458333Seric { 36559093Seric if (vp == NULL) 36658333Seric { 36758333Seric usrerr("501 SIZE requires a value"); 36858333Seric /* NOTREACHED */ 36958333Seric } 37058333Seric msize = atol(vp); 37158333Seric } 37259093Seric else if (strcasecmp(kp, "body") == 0) 37359093Seric { 37459093Seric if (vp == NULL) 37559093Seric { 37659093Seric usrerr("501 BODY requires a value"); 37759093Seric /* NOTREACHED */ 37859093Seric } 37959093Seric # ifdef MIME 38059093Seric if (strcasecmp(vp, "8bitmime") == 0) 38159093Seric { 38259093Seric e->e_bodytype = "8BITMIME"; 38359709Seric SevenBit = FALSE; 38459093Seric } 38559093Seric else if (strcasecmp(vp, "7bit") == 0) 38659093Seric { 38759093Seric e->e_bodytype = "7BIT"; 38859709Seric SevenBit = TRUE; 38959093Seric } 39059093Seric else 39159093Seric { 39259093Seric usrerr("501 Unknown BODY type %s", 39359093Seric vp); 39459093Seric } 39559093Seric # endif 39659093Seric } 39758333Seric else 39858333Seric { 39958333Seric usrerr("501 %s parameter unrecognized", kp); 40058333Seric /* NOTREACHED */ 40158333Seric } 40258333Seric } 40359284Seric 40459284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 40559284Seric { 40659284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 40759284Seric MaxMessageSize); 40859284Seric /* NOTREACHED */ 40959284Seric } 41058333Seric 41158333Seric if (!enoughspace(msize)) 41258333Seric { 41358333Seric message("452 Insufficient disk space; try again later"); 41458333Seric break; 41558333Seric } 41658151Seric message("250 Sender ok"); 41758147Seric gotmail = TRUE; 4184549Seric break; 4194549Seric 4204976Seric case CMDRCPT: /* rcpt -- designate recipient */ 42158850Seric if (!gotmail) 42258850Seric { 42358850Seric usrerr("503 Need MAIL before RCPT"); 42458850Seric break; 42558850Seric } 42661093Seric SmtpPhase = "server RCPT"; 42712612Seric if (setjmp(TopFrame) > 0) 42814785Seric { 42955012Seric e->e_flags &= ~EF_FATALERRS; 43012612Seric break; 43114785Seric } 43212612Seric QuickAbort = TRUE; 43351951Seric LogUsrErrs = TRUE; 43458093Seric 43559699Seric if (e->e_sendmode != SM_DELIVER) 43659699Seric e->e_flags |= EF_VRFYONLY; 43758919Seric 4384549Seric p = skipword(p, "to"); 4394549Seric if (p == NULL) 4404549Seric break; 44158333Seric a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); 44212612Seric if (a == NULL) 44312612Seric break; 44416886Seric a->q_flags |= QPRIMARY; 44555012Seric a = recipient(a, &e->e_sendqueue, e); 44612612Seric if (Errors != 0) 44712612Seric break; 44812612Seric 44912612Seric /* no errors during parsing, but might be a duplicate */ 45055012Seric e->e_to = p; 45112612Seric if (!bitset(QBADADDR, a->q_flags)) 45259747Seric { 45358151Seric message("250 Recipient ok"); 45459747Seric nrcpts++; 45559747Seric } 45612612Seric else 4574549Seric { 45812612Seric /* punt -- should keep message in ADDRESS.... */ 45958151Seric message("550 Addressee unknown"); 4604549Seric } 46155012Seric e->e_to = NULL; 4624549Seric break; 4634549Seric 4644549Seric case CMDDATA: /* data -- text of mail */ 46561093Seric SmtpPhase = "server DATA"; 46658109Seric if (!gotmail) 4674549Seric { 46858151Seric message("503 Need MAIL command"); 4694976Seric break; 4704549Seric } 47155012Seric else if (e->e_nrcpts <= 0) 4724549Seric { 47358151Seric message("503 Need RCPT (recipient)"); 4744976Seric break; 4754549Seric } 4764976Seric 47758929Seric /* check to see if we need to re-expand aliases */ 47863787Seric /* also reset QBADADDR on already-diagnosted addrs */ 47963787Seric doublequeue = FALSE; 48058929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 48158929Seric { 48258929Seric if (bitset(QVERIFIED, a->q_flags)) 48363787Seric { 48463787Seric /* need to re-expand aliases */ 48563787Seric doublequeue = TRUE; 48663787Seric } 48763787Seric if (bitset(QBADADDR, a->q_flags)) 48863787Seric { 48963787Seric /* make this "go away" */ 49063787Seric a->q_flags |= QDONTSEND; 49163787Seric a->q_flags &= ~QBADADDR; 49263787Seric } 49358929Seric } 49458929Seric 4954976Seric /* collect the text of the message */ 49624943Seric SmtpPhase = "collect"; 497*63843Seric SuprErrs = TRUE; 49863787Seric collect(TRUE, doublequeue, e); 4994976Seric if (Errors != 0) 50059747Seric goto abortmessage; 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; 532*63843Seric SuprErrs = FALSE; 5334976Seric 53423516Seric /* save statistics */ 53555012Seric markstats(e, (ADDRESS *) NULL); 53623516Seric 5378238Seric /* issue success if appropriate and reset */ 5388238Seric if (Errors == 0 || HoldErrs) 53958855Seric message("250 %s Message accepted for delivery", id); 54059747Seric 54159747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 54259730Seric { 54359730Seric /* avoid sending back an extra message */ 54459730Seric e->e_flags &= ~EF_FATALERRS; 54559747Seric e->e_flags |= EF_CLRQUEUE; 54659730Seric } 5478238Seric else 54858919Seric { 54959747Seric /* from now on, we have to operate silently */ 55059747Seric HoldErrs = TRUE; 55159747Seric e->e_errormode = EM_MAIL; 55259747Seric 55359730Seric /* if we just queued, poke it */ 55463787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 55559730Seric { 55659730Seric unlockqueue(e); 55759730Seric dowork(id, TRUE, TRUE, e); 55859730Seric e->e_id = NULL; 55959730Seric } 56058919Seric } 56158883Seric 56259747Seric abortmessage: 5639339Seric /* if in a child, pop back to our parent */ 5649339Seric if (InChild) 5659339Seric finis(); 56624943Seric 56724943Seric /* clean up a bit */ 56858109Seric gotmail = FALSE; 56955012Seric dropenvelope(e); 57058179Seric CurEnv = e = newenvelope(e, CurEnv); 57155012Seric e->e_flags = BlankEnvelope.e_flags; 5724549Seric break; 5734549Seric 5744549Seric case CMDRSET: /* rset -- reset state */ 57558151Seric message("250 Reset state"); 5769339Seric if (InChild) 5779339Seric finis(); 57858109Seric 57958109Seric /* clean up a bit */ 58058109Seric gotmail = FALSE; 58158109Seric dropenvelope(e); 58258179Seric CurEnv = e = newenvelope(e, CurEnv); 5839339Seric break; 5844549Seric 5854549Seric case CMDVRFY: /* vrfy -- verify address */ 58658092Seric case CMDEXPN: /* expn -- expand address */ 58758092Seric vrfy = c->cmdcode == CMDVRFY; 58858092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 58958092Seric PrivacyFlags)) 59058082Seric { 59158412Seric if (vrfy) 59258412Seric message("252 Who's to say?"); 59358412Seric else 59458412Seric message("502 That's none of your business"); 59558082Seric break; 59658082Seric } 59758082Seric else if (!gothello && 59858092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 59958092Seric PrivacyFlags)) 60058082Seric { 60158151Seric message("503 I demand that you introduce yourself first"); 60258082Seric break; 60358082Seric } 60458092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6059339Seric break; 60655173Seric #ifdef LOG 60758020Seric if (LogLevel > 5) 60855173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 60955173Seric #endif 6105003Seric vrfyqueue = NULL; 6117762Seric QuickAbort = TRUE; 61258092Seric if (vrfy) 61358092Seric e->e_flags |= EF_VRFYONLY; 61462373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 61562373Seric *p++; 61662373Seric if (*p == '\0') 61762373Seric { 61862373Seric message("501 Argument required"); 61962373Seric Errors++; 62062373Seric } 62162373Seric else 62262373Seric { 62362373Seric (void) sendtolist(p, (ADDRESS *) NULL, 62462373Seric &vrfyqueue, e); 62562373Seric } 6267762Seric if (Errors != 0) 6279339Seric { 6289339Seric if (InChild) 6299339Seric finis(); 6307762Seric break; 6319339Seric } 63262373Seric if (vrfyqueue == NULL) 63362373Seric { 63462373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 63562373Seric } 6365003Seric while (vrfyqueue != NULL) 6375003Seric { 6385003Seric register ADDRESS *a = vrfyqueue->q_next; 6395003Seric 6407685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 6415003Seric a = a->q_next; 6425003Seric 6437685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 64458151Seric printvrfyaddr(vrfyqueue, a == NULL); 6455003Seric else if (a == NULL) 64658151Seric message("554 Self destructive alias loop"); 6475003Seric vrfyqueue = a; 6485003Seric } 6499339Seric if (InChild) 6509339Seric finis(); 6514549Seric break; 6524549Seric 6534549Seric case CMDHELP: /* help -- give user info */ 6544577Seric help(p); 6554549Seric break; 6564549Seric 6574549Seric case CMDNOOP: /* noop -- do nothing */ 65858151Seric message("200 OK"); 6594549Seric break; 6604549Seric 6614549Seric case CMDQUIT: /* quit -- leave mail */ 66258151Seric message("221 %s closing connection", MyHostName); 66361051Seric 66461051Seric /* avoid future 050 messages */ 66561051Seric Verbose = FALSE; 66661051Seric 6679339Seric if (InChild) 6689339Seric ExitStat = EX_QUIT; 6694549Seric finis(); 6704549Seric 6718544Seric case CMDVERB: /* set verbose mode */ 67259957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 67359957Seric { 67459957Seric /* this would give out the same info */ 67559957Seric message("502 Verbose unavailable"); 67659957Seric break; 67759957Seric } 6788544Seric Verbose = TRUE; 67958734Seric e->e_sendmode = SM_DELIVER; 68059957Seric message("250 Verbose mode"); 6818544Seric break; 6828544Seric 6839314Seric case CMDONEX: /* doing one transaction only */ 6849378Seric OneXact = TRUE; 68559957Seric message("250 Only one transaction"); 6869314Seric break; 6879314Seric 68836230Skarels # ifdef SMTPDEBUG 6899339Seric case CMDDBGQSHOW: /* show queues */ 6906907Seric printf("Send Queue="); 69155012Seric printaddr(e->e_sendqueue, TRUE); 6925003Seric break; 6937275Seric 6947275Seric case CMDDBGDEBUG: /* set debug mode */ 6957676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6967676Seric tTflag(p); 69758151Seric message("200 Debug set"); 6987275Seric break; 6997275Seric 70036230Skarels # else /* not SMTPDEBUG */ 70124945Seric 70236230Skarels case CMDDBGQSHOW: /* show queues */ 70336230Skarels case CMDDBGDEBUG: /* set debug mode */ 70436233Skarels # ifdef LOG 70558308Seric if (LogLevel > 0) 70636230Skarels syslog(LOG_NOTICE, 70758020Seric "\"%s\" command from %s (%s)", 70836230Skarels c->cmdname, RealHostName, 70958755Seric anynet_ntoa(&RealHostAddr)); 71036233Skarels # endif 71136230Skarels /* FALL THROUGH */ 71236230Skarels # endif /* SMTPDEBUG */ 71336230Skarels 7144549Seric case CMDERROR: /* unknown command */ 71558151Seric message("500 Command unrecognized"); 7164549Seric break; 7174549Seric 7184549Seric default: 71936230Skarels errno = 0; 72058151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7214549Seric break; 7224549Seric } 7234549Seric } 7244549Seric } 7254549Seric /* 7264549Seric ** SKIPWORD -- skip a fixed word. 7274549Seric ** 7284549Seric ** Parameters: 7294549Seric ** p -- place to start looking. 7304549Seric ** w -- word to skip. 7314549Seric ** 7324549Seric ** Returns: 7334549Seric ** p following w. 7344549Seric ** NULL on error. 7354549Seric ** 7364549Seric ** Side Effects: 7374549Seric ** clobbers the p data area. 7384549Seric */ 7394549Seric 7404549Seric static char * 7414549Seric skipword(p, w) 7424549Seric register char *p; 7434549Seric char *w; 7444549Seric { 7454549Seric register char *q; 7464549Seric 7474549Seric /* find beginning of word */ 74858050Seric while (isascii(*p) && isspace(*p)) 7494549Seric p++; 7504549Seric q = p; 7514549Seric 7524549Seric /* find end of word */ 75358050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7544549Seric p++; 75558050Seric while (isascii(*p) && isspace(*p)) 7564549Seric *p++ = '\0'; 7574549Seric if (*p != ':') 7584549Seric { 7594549Seric syntax: 76062373Seric message("501 Syntax error in parameters"); 7614549Seric Errors++; 7624549Seric return (NULL); 7634549Seric } 7644549Seric *p++ = '\0'; 76558050Seric while (isascii(*p) && isspace(*p)) 7664549Seric p++; 7674549Seric 76862373Seric if (*p == '\0') 76962373Seric goto syntax; 77062373Seric 7714549Seric /* see if the input word matches desired word */ 77233725Sbostic if (strcasecmp(q, w)) 7734549Seric goto syntax; 7744549Seric 7754549Seric return (p); 7764549Seric } 7774577Seric /* 77858151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 77958151Seric ** 78058151Seric ** Parameters: 78158151Seric ** a -- the address to print 78258151Seric ** last -- set if this is the last one. 78358151Seric ** 78458151Seric ** Returns: 78558151Seric ** none. 78658151Seric ** 78758151Seric ** Side Effects: 78858151Seric ** Prints the appropriate 250 codes. 78958151Seric */ 79058151Seric 79158151Seric printvrfyaddr(a, last) 79258151Seric register ADDRESS *a; 79358151Seric bool last; 79458151Seric { 79558151Seric char fmtbuf[20]; 79658151Seric 79758151Seric strcpy(fmtbuf, "250"); 79858151Seric fmtbuf[3] = last ? ' ' : '-'; 79958151Seric 80059746Seric if (a->q_fullname == NULL) 80159746Seric { 80259746Seric if (strchr(a->q_user, '@') == NULL) 80359746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 80459746Seric else 80559746Seric strcpy(&fmtbuf[4], "<%s>"); 80659746Seric message(fmtbuf, a->q_user, MyHostName); 80759746Seric } 80858151Seric else 80958151Seric { 81059746Seric if (strchr(a->q_user, '@') == NULL) 81159746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 81259746Seric else 81359746Seric strcpy(&fmtbuf[4], "%s <%s>"); 81459746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 81558151Seric } 81658151Seric } 81758151Seric /* 8184577Seric ** HELP -- implement the HELP command. 8194577Seric ** 8204577Seric ** Parameters: 8214577Seric ** topic -- the topic we want help for. 8224577Seric ** 8234577Seric ** Returns: 8244577Seric ** none. 8254577Seric ** 8264577Seric ** Side Effects: 8274577Seric ** outputs the help file to message output. 8284577Seric */ 8294577Seric 8304577Seric help(topic) 8314577Seric char *topic; 8324577Seric { 8334577Seric register FILE *hf; 8344577Seric int len; 8354577Seric char buf[MAXLINE]; 8364577Seric bool noinfo; 8374577Seric 8388269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8394577Seric { 8404577Seric /* no help */ 84111931Seric errno = 0; 84258151Seric message("502 HELP not implemented"); 8434577Seric return; 8444577Seric } 8454577Seric 84649669Seric if (topic == NULL || *topic == '\0') 84749669Seric topic = "smtp"; 84849669Seric else 84949669Seric makelower(topic); 85049669Seric 8514577Seric len = strlen(topic); 8524577Seric noinfo = TRUE; 8534577Seric 8544577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8554577Seric { 8564577Seric if (strncmp(buf, topic, len) == 0) 8574577Seric { 8584577Seric register char *p; 8594577Seric 86056795Seric p = strchr(buf, '\t'); 8614577Seric if (p == NULL) 8624577Seric p = buf; 8634577Seric else 8644577Seric p++; 8654577Seric fixcrlf(p, TRUE); 86658151Seric message("214-%s", p); 8674577Seric noinfo = FALSE; 8684577Seric } 8694577Seric } 8704577Seric 8714577Seric if (noinfo) 87258151Seric message("504 HELP topic unknown"); 8734577Seric else 87458151Seric message("214 End of HELP info"); 8754628Seric (void) fclose(hf); 8764577Seric } 8778544Seric /* 8789339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8799339Seric ** 8809339Seric ** Parameters: 8819339Seric ** label -- a string used in error messages 8829339Seric ** 8839339Seric ** Returns: 8849339Seric ** zero in the child 8859339Seric ** one in the parent 8869339Seric ** 8879339Seric ** Side Effects: 8889339Seric ** none. 8899339Seric */ 8908544Seric 89155012Seric runinchild(label, e) 8929339Seric char *label; 89355012Seric register ENVELOPE *e; 8949339Seric { 8959339Seric int childpid; 8969339Seric 89716158Seric if (!OneXact) 8989339Seric { 89916158Seric childpid = dofork(); 90016158Seric if (childpid < 0) 90116158Seric { 90216158Seric syserr("%s: cannot fork", label); 90316158Seric return (1); 90416158Seric } 90516158Seric if (childpid > 0) 90616158Seric { 90716158Seric auto int st; 9089339Seric 90916158Seric /* parent -- wait for child to complete */ 91061093Seric setproctitle("server %s child wait", CurHostName); 91116158Seric st = waitfor(childpid); 91216158Seric if (st == -1) 91316158Seric syserr("%s: lost child", label); 9149339Seric 91516158Seric /* if we exited on a QUIT command, complete the process */ 91616158Seric if (st == (EX_QUIT << 8)) 91716158Seric finis(); 9189339Seric 91916158Seric return (1); 92016158Seric } 92116158Seric else 92216158Seric { 92316158Seric /* child */ 92416158Seric InChild = TRUE; 92525050Seric QuickAbort = FALSE; 92655012Seric clearenvelope(e, FALSE); 92716158Seric } 9289339Seric } 92915256Seric 93016158Seric /* open alias database */ 93160537Seric initmaps(FALSE, e); 93216158Seric 93316158Seric return (0); 9349339Seric } 9359339Seric 93656795Seric # endif /* SMTP */ 937