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*63937Seric static char sccsid[] = "@(#)srvrsmtp.c 8.6 (Berkeley) 07/19/93 (with SMTP)"; 1433731Sbostic #else 15*63937Seric static char sccsid[] = "@(#)srvrsmtp.c 8.6 (Berkeley) 07/19/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 93*63937Seric static char *skipword(); 94*63937Seric 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; 12561093Seric setproctitle("server %s startup", CurHostName); 12658050Seric expand("\201e", inp, &inp[sizeof inp], e); 12760210Seric message("220-%s", inp); 12860210Seric message("220 ESMTP spoken here"); 12958330Seric protocol = NULL; 13059016Seric sendinghost = macvalue('s', e); 13158082Seric gothello = FALSE; 13258330Seric gotmail = FALSE; 1334549Seric for (;;) 1344549Seric { 13512612Seric /* arrange for backout */ 13612612Seric if (setjmp(TopFrame) > 0 && InChild) 13759058Seric { 13859058Seric QuickAbort = FALSE; 13959058Seric SuprErrs = TRUE; 14012612Seric finis(); 14159058Seric } 14212612Seric QuickAbort = FALSE; 14312612Seric HoldErrs = FALSE; 14451951Seric LogUsrErrs = FALSE; 14563843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 14612612Seric 1477356Seric /* setup for the read */ 14855012Seric e->e_to = NULL; 1494577Seric Errors = 0; 1507275Seric (void) fflush(stdout); 1517356Seric 1527356Seric /* read the input line */ 15361093Seric SmtpPhase = "server cmd read"; 15461093Seric setproctitle("server %s cmd read", CurHostName); 15561093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 15661093Seric SmtpPhase); 1577356Seric 1587685Seric /* handle errors */ 1597356Seric if (p == NULL) 1607356Seric { 1614549Seric /* end of file, just die */ 16258151Seric message("421 %s Lost input channel from %s", 16325050Seric MyHostName, CurHostName); 16455464Seric #ifdef LOG 16563843Seric if (LogLevel > (gotmail ? 1 : 19)) 16655464Seric syslog(LOG_NOTICE, "lost input channel from %s", 16755464Seric CurHostName); 16855464Seric #endif 16958069Seric if (InChild) 17058069Seric ExitStat = EX_QUIT; 1714549Seric finis(); 1724549Seric } 1734549Seric 1744549Seric /* clean up end of line */ 1754558Seric fixcrlf(inp, TRUE); 1764549Seric 1774713Seric /* echo command to transcript */ 17855012Seric if (e->e_xfp != NULL) 17955012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1804713Seric 18159060Seric if (e->e_id == NULL) 18259060Seric setproctitle("%s: %s", CurHostName, inp); 18359060Seric else 18459060Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 18559060Seric 1864549Seric /* break off command */ 18758050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1884549Seric continue; 18957232Seric cmd = cmdbuf; 19058050Seric while (*p != '\0' && 19158050Seric !(isascii(*p) && isspace(*p)) && 19258050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 19324981Seric *cmd++ = *p++; 19424981Seric *cmd = '\0'; 1954549Seric 19625691Seric /* throw away leading whitespace */ 19758050Seric while (isascii(*p) && isspace(*p)) 19825691Seric p++; 19925691Seric 2004549Seric /* decode command */ 2014549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2024549Seric { 20333725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2044549Seric break; 2054549Seric } 2064549Seric 20751954Seric /* reset errors */ 20851954Seric errno = 0; 20951954Seric 2104549Seric /* process command */ 2114549Seric switch (c->cmdcode) 2124549Seric { 2134976Seric case CMDHELO: /* hello -- introduce yourself */ 21458323Seric case CMDEHLO: /* extended hello */ 21558323Seric if (c->cmdcode == CMDEHLO) 21658323Seric { 21758323Seric protocol = "ESMTP"; 21861093Seric SmtpPhase = "server EHLO"; 21958323Seric } 22058323Seric else 22158323Seric { 22258323Seric protocol = "SMTP"; 22361093Seric SmtpPhase = "server HELO"; 22458323Seric } 22559016Seric sendinghost = newstr(p); 22658308Seric if (strcasecmp(p, RealHostName) != 0) 22711146Seric { 22858789Seric auth_warning(e, "Host %s claimed to be %s", 22958789Seric RealHostName, p); 23011146Seric } 23158957Seric p = macvalue('_', e); 23258957Seric if (p == NULL) 23359016Seric p = RealHostName; 23458323Seric 23560210Seric gothello = TRUE; 23660210Seric if (c->cmdcode != CMDEHLO) 23760239Seric { 23860239Seric /* print old message and be done with it */ 23960239Seric message("250 %s Hello %s, pleased to meet you", 24060239Seric MyHostName, p); 24160210Seric break; 24260239Seric } 24360239Seric 24460239Seric /* print extended message and brag */ 24560239Seric message("250-%s Hello %s, pleased to meet you", 24660239Seric MyHostName, p); 24758323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 24858323Seric message("250-EXPN"); 24959271Seric if (MaxMessageSize > 0) 25059271Seric message("250-SIZE %ld", MaxMessageSize); 25159271Seric else 25259271Seric message("250-SIZE"); 25358323Seric message("250 HELP"); 2544976Seric break; 2554976Seric 2564549Seric case CMDMAIL: /* mail -- designate sender */ 25761093Seric SmtpPhase = "server MAIL"; 25824943Seric 2599314Seric /* check for validity of this command */ 26058789Seric if (!gothello) 26158082Seric { 26258957Seric /* set sending host to our known value */ 26359016Seric if (sendinghost == NULL) 26459016Seric sendinghost = RealHostName; 26558957Seric 26658789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 26758821Seric { 26858789Seric message("503 Polite people say HELO first"); 26958821Seric break; 27058821Seric } 27158082Seric } 27258109Seric if (gotmail) 2734558Seric { 27458151Seric message("503 Sender already specified"); 27563843Seric if (InChild) 27663843Seric finis(); 2774558Seric break; 2784558Seric } 2799339Seric if (InChild) 2809339Seric { 28136230Skarels errno = 0; 28258151Seric syserr("503 Nested MAIL command: MAIL %s", p); 28358069Seric finis(); 2849339Seric } 2859339Seric 2869339Seric /* fork a subprocess to process this command */ 28755012Seric if (runinchild("SMTP-MAIL", e) > 0) 2889339Seric break; 28963753Seric if (!gothello) 29063753Seric { 29163753Seric auth_warning(e, 29263753Seric "Host %s didn't use HELO protocol", 29363753Seric RealHostName); 29463753Seric } 29558323Seric if (protocol == NULL) 29658323Seric protocol = "SMTP"; 29758323Seric define('r', protocol, e); 29859016Seric define('s', sendinghost, e); 29955012Seric initsys(e); 30059747Seric nrcpts = 0; 30157389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 3029339Seric 3039339Seric /* child -- go do the processing */ 3044549Seric p = skipword(p, "from"); 3054549Seric if (p == NULL) 3064549Seric break; 30757977Seric if (setjmp(TopFrame) > 0) 30858147Seric { 30958147Seric /* this failed -- undo work */ 31058147Seric if (InChild) 31159058Seric { 31259058Seric QuickAbort = FALSE; 31359058Seric SuprErrs = TRUE; 31463787Seric e->e_flags &= ~EF_FATALERRS; 31558147Seric finis(); 31659058Seric } 31757977Seric break; 31858147Seric } 31957977Seric QuickAbort = TRUE; 32058333Seric 32158333Seric /* must parse sender first */ 32258333Seric delimptr = NULL; 32358704Seric setsender(p, e, &delimptr, FALSE); 32458333Seric p = delimptr; 32558333Seric if (p != NULL && *p != '\0') 32658333Seric *p++ = '\0'; 32758333Seric 32858333Seric /* now parse ESMTP arguments */ 32958333Seric msize = 0; 33058333Seric for (; p != NULL && *p != '\0'; p++) 33158333Seric { 33258333Seric char *kp; 33358333Seric char *vp; 33458333Seric 33558333Seric /* locate the beginning of the keyword */ 33658333Seric while (isascii(*p) && isspace(*p)) 33758333Seric p++; 33858333Seric if (*p == '\0') 33958333Seric break; 34058333Seric kp = p; 34158333Seric 34258333Seric /* skip to the value portion */ 34358333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 34458333Seric p++; 34558333Seric if (*p == '=') 34658333Seric { 34758333Seric *p++ = '\0'; 34858333Seric vp = p; 34958333Seric 35058333Seric /* skip to the end of the value */ 35158333Seric while (*p != '\0' && *p != ' ' && 35258333Seric !(isascii(*p) && iscntrl(*p)) && 35358333Seric *p != '=') 35458333Seric p++; 35558333Seric } 35658333Seric 35758333Seric if (*p != '\0') 35858333Seric *p++ = '\0'; 35958333Seric 36058333Seric if (tTd(19, 1)) 36158333Seric printf("MAIL: got arg %s=%s\n", kp, 36258333Seric vp == NULL ? "<null>" : vp); 36358333Seric 36458333Seric if (strcasecmp(kp, "size") == 0) 36558333Seric { 36659093Seric if (vp == NULL) 36758333Seric { 36858333Seric usrerr("501 SIZE requires a value"); 36958333Seric /* NOTREACHED */ 37058333Seric } 37158333Seric msize = atol(vp); 37258333Seric } 37359093Seric else if (strcasecmp(kp, "body") == 0) 37459093Seric { 37559093Seric if (vp == NULL) 37659093Seric { 37759093Seric usrerr("501 BODY requires a value"); 37859093Seric /* NOTREACHED */ 37959093Seric } 38059093Seric # ifdef MIME 38159093Seric if (strcasecmp(vp, "8bitmime") == 0) 38259093Seric { 38359093Seric e->e_bodytype = "8BITMIME"; 38459709Seric SevenBit = FALSE; 38559093Seric } 38659093Seric else if (strcasecmp(vp, "7bit") == 0) 38759093Seric { 38859093Seric e->e_bodytype = "7BIT"; 38959709Seric SevenBit = TRUE; 39059093Seric } 39159093Seric else 39259093Seric { 39359093Seric usrerr("501 Unknown BODY type %s", 39459093Seric vp); 39559093Seric } 39659093Seric # endif 39759093Seric } 39858333Seric else 39958333Seric { 40058333Seric usrerr("501 %s parameter unrecognized", kp); 40158333Seric /* NOTREACHED */ 40258333Seric } 40358333Seric } 40459284Seric 40559284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 40659284Seric { 40759284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 40859284Seric MaxMessageSize); 40959284Seric /* NOTREACHED */ 41059284Seric } 41158333Seric 41258333Seric if (!enoughspace(msize)) 41358333Seric { 41458333Seric message("452 Insufficient disk space; try again later"); 41558333Seric break; 41658333Seric } 41758151Seric message("250 Sender ok"); 41858147Seric gotmail = TRUE; 4194549Seric break; 4204549Seric 4214976Seric case CMDRCPT: /* rcpt -- designate recipient */ 42258850Seric if (!gotmail) 42358850Seric { 42458850Seric usrerr("503 Need MAIL before RCPT"); 42558850Seric break; 42658850Seric } 42761093Seric SmtpPhase = "server RCPT"; 42812612Seric if (setjmp(TopFrame) > 0) 42914785Seric { 43055012Seric e->e_flags &= ~EF_FATALERRS; 43112612Seric break; 43214785Seric } 43312612Seric QuickAbort = TRUE; 43451951Seric LogUsrErrs = TRUE; 43558093Seric 43659699Seric if (e->e_sendmode != SM_DELIVER) 43759699Seric e->e_flags |= EF_VRFYONLY; 43858919Seric 4394549Seric p = skipword(p, "to"); 4404549Seric if (p == NULL) 4414549Seric break; 44258333Seric a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); 44312612Seric if (a == NULL) 44412612Seric break; 44516886Seric a->q_flags |= QPRIMARY; 44655012Seric a = recipient(a, &e->e_sendqueue, e); 44712612Seric if (Errors != 0) 44812612Seric break; 44912612Seric 45012612Seric /* no errors during parsing, but might be a duplicate */ 45155012Seric e->e_to = p; 45212612Seric if (!bitset(QBADADDR, a->q_flags)) 45359747Seric { 45458151Seric message("250 Recipient ok"); 45559747Seric nrcpts++; 45659747Seric } 45712612Seric else 4584549Seric { 45912612Seric /* punt -- should keep message in ADDRESS.... */ 46058151Seric message("550 Addressee unknown"); 4614549Seric } 46255012Seric e->e_to = NULL; 4634549Seric break; 4644549Seric 4654549Seric case CMDDATA: /* data -- text of mail */ 46661093Seric SmtpPhase = "server DATA"; 46758109Seric if (!gotmail) 4684549Seric { 46958151Seric message("503 Need MAIL command"); 4704976Seric break; 4714549Seric } 47255012Seric else if (e->e_nrcpts <= 0) 4734549Seric { 47458151Seric message("503 Need RCPT (recipient)"); 4754976Seric break; 4764549Seric } 4774976Seric 47858929Seric /* check to see if we need to re-expand aliases */ 47963787Seric /* also reset QBADADDR on already-diagnosted addrs */ 48063787Seric doublequeue = FALSE; 48158929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 48258929Seric { 48358929Seric if (bitset(QVERIFIED, a->q_flags)) 48463787Seric { 48563787Seric /* need to re-expand aliases */ 48663787Seric doublequeue = TRUE; 48763787Seric } 48863787Seric if (bitset(QBADADDR, a->q_flags)) 48963787Seric { 49063787Seric /* make this "go away" */ 49163787Seric a->q_flags |= QDONTSEND; 49263787Seric a->q_flags &= ~QBADADDR; 49363787Seric } 49458929Seric } 49558929Seric 4964976Seric /* collect the text of the message */ 49724943Seric SmtpPhase = "collect"; 49863843Seric SuprErrs = TRUE; 49963787Seric collect(TRUE, doublequeue, e); 5004976Seric if (Errors != 0) 50159747Seric goto abortmessage; 5024976Seric 5038238Seric /* 5048238Seric ** Arrange to send to everyone. 5058238Seric ** If sending to multiple people, mail back 5068238Seric ** errors rather than reporting directly. 5078238Seric ** In any case, don't mail back errors for 5088238Seric ** anything that has happened up to 5098238Seric ** now (the other end will do this). 51010197Seric ** Truncate our transcript -- the mail has gotten 51110197Seric ** to us successfully, and if we have 51210197Seric ** to mail this back, it will be easier 51310197Seric ** on the reader. 5148238Seric ** Then send to everyone. 5158238Seric ** Finally give a reply code. If an error has 5168238Seric ** already been given, don't mail a 5178238Seric ** message back. 5189339Seric ** We goose error returns by clearing error bit. 5198238Seric */ 5208238Seric 52124943Seric SmtpPhase = "delivery"; 52263787Seric if (nrcpts != 1 && !doublequeue) 5239378Seric { 5249378Seric HoldErrs = TRUE; 52558734Seric e->e_errormode = EM_MAIL; 5269378Seric } 52755012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 52858714Seric id = e->e_id; 5294976Seric 5304976Seric /* send to all recipients */ 53163787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 53255012Seric e->e_to = NULL; 53363843Seric SuprErrs = FALSE; 5344976Seric 53523516Seric /* save statistics */ 53655012Seric markstats(e, (ADDRESS *) NULL); 53723516Seric 5388238Seric /* issue success if appropriate and reset */ 5398238Seric if (Errors == 0 || HoldErrs) 54058855Seric message("250 %s Message accepted for delivery", id); 54159747Seric 54259747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 54359730Seric { 54459730Seric /* avoid sending back an extra message */ 54559730Seric e->e_flags &= ~EF_FATALERRS; 54659747Seric e->e_flags |= EF_CLRQUEUE; 54759730Seric } 5488238Seric else 54958919Seric { 55059747Seric /* from now on, we have to operate silently */ 55159747Seric HoldErrs = TRUE; 55259747Seric e->e_errormode = EM_MAIL; 55359747Seric 55459730Seric /* if we just queued, poke it */ 55563787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 55659730Seric { 55759730Seric unlockqueue(e); 55859730Seric dowork(id, TRUE, TRUE, e); 55959730Seric e->e_id = NULL; 56059730Seric } 56158919Seric } 56258883Seric 56359747Seric abortmessage: 5649339Seric /* if in a child, pop back to our parent */ 5659339Seric if (InChild) 5669339Seric finis(); 56724943Seric 56824943Seric /* clean up a bit */ 56958109Seric gotmail = FALSE; 57055012Seric dropenvelope(e); 57158179Seric CurEnv = e = newenvelope(e, CurEnv); 57255012Seric e->e_flags = BlankEnvelope.e_flags; 5734549Seric break; 5744549Seric 5754549Seric case CMDRSET: /* rset -- reset state */ 57658151Seric message("250 Reset state"); 5779339Seric if (InChild) 5789339Seric finis(); 57958109Seric 58058109Seric /* clean up a bit */ 58158109Seric gotmail = FALSE; 58258109Seric dropenvelope(e); 58358179Seric CurEnv = e = newenvelope(e, CurEnv); 5849339Seric break; 5854549Seric 5864549Seric case CMDVRFY: /* vrfy -- verify address */ 58758092Seric case CMDEXPN: /* expn -- expand address */ 58858092Seric vrfy = c->cmdcode == CMDVRFY; 58958092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 59058092Seric PrivacyFlags)) 59158082Seric { 59258412Seric if (vrfy) 59358412Seric message("252 Who's to say?"); 59458412Seric else 59558412Seric message("502 That's none of your business"); 59658082Seric break; 59758082Seric } 59858082Seric else if (!gothello && 59958092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 60058092Seric PrivacyFlags)) 60158082Seric { 60258151Seric message("503 I demand that you introduce yourself first"); 60358082Seric break; 60458082Seric } 60558092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6069339Seric break; 60755173Seric #ifdef LOG 60858020Seric if (LogLevel > 5) 60955173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 61055173Seric #endif 6115003Seric vrfyqueue = NULL; 6127762Seric QuickAbort = TRUE; 61358092Seric if (vrfy) 61458092Seric e->e_flags |= EF_VRFYONLY; 61562373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 61662373Seric *p++; 61762373Seric if (*p == '\0') 61862373Seric { 61962373Seric message("501 Argument required"); 62062373Seric Errors++; 62162373Seric } 62262373Seric else 62362373Seric { 62462373Seric (void) sendtolist(p, (ADDRESS *) NULL, 62562373Seric &vrfyqueue, e); 62662373Seric } 6277762Seric if (Errors != 0) 6289339Seric { 6299339Seric if (InChild) 6309339Seric finis(); 6317762Seric break; 6329339Seric } 63362373Seric if (vrfyqueue == NULL) 63462373Seric { 63562373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 63662373Seric } 6375003Seric while (vrfyqueue != NULL) 6385003Seric { 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 */ 65258151Seric message("200 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