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*66772Seric static char sccsid[] = "@(#)srvrsmtp.c 8.37 (Berkeley) 04/13/94 (with SMTP)"; 1433731Sbostic #else 15*66772Seric static char sccsid[] = "@(#)srvrsmtp.c 8.37 (Berkeley) 04/13/94 (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(); 9466325Seric extern char RealUserName[]; 9563937Seric 9666325Seric 9766283Seric #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 9866283Seric 9955012Seric smtp(e) 10055012Seric register ENVELOPE *e; 1014549Seric { 1024549Seric register char *p; 1038544Seric register struct cmd *c; 1044549Seric char *cmd; 1055003Seric auto ADDRESS *vrfyqueue; 10612612Seric ADDRESS *a; 10758109Seric bool gotmail; /* mail command received */ 10858092Seric bool gothello; /* helo command received */ 10958092Seric bool vrfy; /* set if this is a vrfy command */ 11058323Seric char *protocol; /* sending protocol */ 11159016Seric char *sendinghost; /* sending hostname */ 112*66772Seric unsigned long msize; /* approximate maximum message size */ 11366005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11458333Seric auto char *delimptr; 11558714Seric char *id; 11659747Seric int nrcpts; /* number of RCPT commands */ 11763787Seric bool doublequeue; 11866283Seric int badcommands = 0; /* count of bad commands */ 1198544Seric char inp[MAXLINE]; 12057232Seric char cmdbuf[MAXLINE]; 1217124Seric extern char Version[]; 12224943Seric extern ENVELOPE BlankEnvelope; 1234549Seric 12459066Seric if (fileno(OutChannel) != fileno(stdout)) 1257363Seric { 1267363Seric /* arrange for debugging output to go to remote host */ 12759066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1287363Seric } 12955012Seric settime(e); 13066005Seric peerhostname = RealHostName; 13166005Seric if (peerhostname == NULL) 13266005Seric peerhostname = "localhost"; 13366005Seric CurHostName = peerhostname; 13465017Seric CurSmtpClient = macvalue('_', e); 13565017Seric if (CurSmtpClient == NULL) 13666003Seric CurSmtpClient = CurHostName; 13765017Seric 13865017Seric setproctitle("server %s startup", CurSmtpClient); 13958050Seric expand("\201e", inp, &inp[sizeof inp], e); 14064496Seric if (BrokenSmtpPeers) 14164496Seric { 14266762Seric p = strchr(inp, '\n'); 14366762Seric if (p != NULL) 14466762Seric *p = '\0'; 14564496Seric message("220 %s", inp); 14664496Seric } 14764496Seric else 14864496Seric { 14966745Seric char *q = inp; 15066745Seric 15166745Seric while (q != NULL) 15266745Seric { 15366762Seric p = strchr(q, '\n'); 15466762Seric if (p != NULL) 15566762Seric *p++ = '\0'; 15666745Seric message("220-%s", q); 15766745Seric q = p; 15866745Seric } 15964496Seric message("220 ESMTP spoken here"); 16064496Seric } 16158330Seric protocol = NULL; 16259016Seric sendinghost = macvalue('s', e); 16358082Seric gothello = FALSE; 16458330Seric gotmail = FALSE; 1654549Seric for (;;) 1664549Seric { 16712612Seric /* arrange for backout */ 16865751Seric if (setjmp(TopFrame) > 0) 16959058Seric { 17065751Seric /* if() nesting is necessary for Cray UNICOS */ 17165751Seric if (InChild) 17265751Seric { 17365751Seric QuickAbort = FALSE; 17465751Seric SuprErrs = TRUE; 17565751Seric finis(); 17665751Seric } 17759058Seric } 17812612Seric QuickAbort = FALSE; 17912612Seric HoldErrs = FALSE; 18051951Seric LogUsrErrs = FALSE; 18163843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 18212612Seric 1837356Seric /* setup for the read */ 18455012Seric e->e_to = NULL; 1854577Seric Errors = 0; 1867275Seric (void) fflush(stdout); 1877356Seric 1887356Seric /* read the input line */ 18961093Seric SmtpPhase = "server cmd read"; 19061093Seric setproctitle("server %s cmd read", CurHostName); 19161093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 19261093Seric SmtpPhase); 1937356Seric 1947685Seric /* handle errors */ 1957356Seric if (p == NULL) 1967356Seric { 1974549Seric /* end of file, just die */ 19866017Seric disconnect(1, e); 19958151Seric message("421 %s Lost input channel from %s", 20065017Seric MyHostName, CurSmtpClient); 20155464Seric #ifdef LOG 20263843Seric if (LogLevel > (gotmail ? 1 : 19)) 20355464Seric syslog(LOG_NOTICE, "lost input channel from %s", 20465017Seric CurSmtpClient); 20555464Seric #endif 20658069Seric if (InChild) 20758069Seric ExitStat = EX_QUIT; 2084549Seric finis(); 2094549Seric } 2104549Seric 2114549Seric /* clean up end of line */ 2124558Seric fixcrlf(inp, TRUE); 2134549Seric 2144713Seric /* echo command to transcript */ 21555012Seric if (e->e_xfp != NULL) 21655012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2174713Seric 21859060Seric if (e->e_id == NULL) 21965058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 22059060Seric else 22165058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 22259060Seric 2234549Seric /* break off command */ 22458050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2254549Seric continue; 22657232Seric cmd = cmdbuf; 22758050Seric while (*p != '\0' && 22858050Seric !(isascii(*p) && isspace(*p)) && 22958050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 23024981Seric *cmd++ = *p++; 23124981Seric *cmd = '\0'; 2324549Seric 23325691Seric /* throw away leading whitespace */ 23458050Seric while (isascii(*p) && isspace(*p)) 23525691Seric p++; 23625691Seric 2374549Seric /* decode command */ 2384549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2394549Seric { 24033725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2414549Seric break; 2424549Seric } 2434549Seric 24451954Seric /* reset errors */ 24551954Seric errno = 0; 24651954Seric 2474549Seric /* process command */ 2484549Seric switch (c->cmdcode) 2494549Seric { 2504976Seric case CMDHELO: /* hello -- introduce yourself */ 25158323Seric case CMDEHLO: /* extended hello */ 25258323Seric if (c->cmdcode == CMDEHLO) 25358323Seric { 25458323Seric protocol = "ESMTP"; 25561093Seric SmtpPhase = "server EHLO"; 25658323Seric } 25758323Seric else 25858323Seric { 25958323Seric protocol = "SMTP"; 26061093Seric SmtpPhase = "server HELO"; 26158323Seric } 26259016Seric sendinghost = newstr(p); 26360210Seric gothello = TRUE; 26460210Seric if (c->cmdcode != CMDEHLO) 26560239Seric { 26660239Seric /* print old message and be done with it */ 26760239Seric message("250 %s Hello %s, pleased to meet you", 26865017Seric MyHostName, CurSmtpClient); 26960210Seric break; 27060239Seric } 27160239Seric 27260239Seric /* print extended message and brag */ 27360239Seric message("250-%s Hello %s, pleased to meet you", 27466760Seric MyHostName, CurSmtpClient); 27558323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 27658323Seric message("250-EXPN"); 27764359Seric if (MaxMessageSize > 0) 27864359Seric message("250-SIZE %ld", MaxMessageSize); 27959271Seric else 28059271Seric message("250-SIZE"); 28158323Seric message("250 HELP"); 2824976Seric break; 2834976Seric 2844549Seric case CMDMAIL: /* mail -- designate sender */ 28561093Seric SmtpPhase = "server MAIL"; 28624943Seric 2879314Seric /* check for validity of this command */ 28858789Seric if (!gothello) 28958082Seric { 29058957Seric /* set sending host to our known value */ 29159016Seric if (sendinghost == NULL) 29266005Seric sendinghost = peerhostname; 29358957Seric 29458789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 29558821Seric { 29658789Seric message("503 Polite people say HELO first"); 29758821Seric break; 29858821Seric } 29958082Seric } 30058109Seric if (gotmail) 3014558Seric { 30258151Seric message("503 Sender already specified"); 30363843Seric if (InChild) 30463843Seric finis(); 3054558Seric break; 3064558Seric } 3079339Seric if (InChild) 3089339Seric { 30936230Skarels errno = 0; 31058151Seric syserr("503 Nested MAIL command: MAIL %s", p); 31158069Seric finis(); 3129339Seric } 3139339Seric 3149339Seric /* fork a subprocess to process this command */ 31555012Seric if (runinchild("SMTP-MAIL", e) > 0) 3169339Seric break; 31763753Seric if (!gothello) 31863753Seric { 31963753Seric auth_warning(e, 32066005Seric "Host %s didn't use HELO protocol", 32166005Seric peerhostname); 32263753Seric } 32365947Seric #ifdef PICKY_HELO_CHECK 32466005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 32566005Seric (strcasecmp(peerhostname, "localhost") != 0 || 32665823Seric strcasecmp(sendinghost, MyHostName) != 0)) 32765823Seric { 32865823Seric auth_warning(e, "Host %s claimed to be %s", 32966005Seric peerhostname, sendinghost); 33065823Seric } 33165947Seric #endif 33265823Seric 33358323Seric if (protocol == NULL) 33458323Seric protocol = "SMTP"; 33558323Seric define('r', protocol, e); 33659016Seric define('s', sendinghost, e); 33755012Seric initsys(e); 33859747Seric nrcpts = 0; 33965089Seric e->e_flags |= EF_LOGSENDER; 34065058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3419339Seric 3429339Seric /* child -- go do the processing */ 3434549Seric p = skipword(p, "from"); 3444549Seric if (p == NULL) 3454549Seric break; 34657977Seric if (setjmp(TopFrame) > 0) 34758147Seric { 34858147Seric /* this failed -- undo work */ 34958147Seric if (InChild) 35059058Seric { 35159058Seric QuickAbort = FALSE; 35259058Seric SuprErrs = TRUE; 35363787Seric e->e_flags &= ~EF_FATALERRS; 35458147Seric finis(); 35559058Seric } 35657977Seric break; 35758147Seric } 35857977Seric QuickAbort = TRUE; 35958333Seric 36058333Seric /* must parse sender first */ 36158333Seric delimptr = NULL; 36258704Seric setsender(p, e, &delimptr, FALSE); 36358333Seric p = delimptr; 36458333Seric if (p != NULL && *p != '\0') 36558333Seric *p++ = '\0'; 36658333Seric 36766325Seric /* check for possible spoofing */ 36866325Seric if (RealUid != 0 && OpMode == MD_SMTP && 36966325Seric (e->e_from.q_mailer != LocalMailer && 37066325Seric strcmp(e->e_from.q_user, RealUserName) != 0)) 37166325Seric { 37266325Seric auth_warning(e, "%s owned process doing -bs", 37366325Seric RealUserName); 37466325Seric } 37566325Seric 37658333Seric /* now parse ESMTP arguments */ 37758333Seric msize = 0; 37866764Seric while (p != NULL && *p != '\0') 37958333Seric { 38058333Seric char *kp; 38166304Seric char *vp = NULL; 38258333Seric 38358333Seric /* locate the beginning of the keyword */ 38458333Seric while (isascii(*p) && isspace(*p)) 38558333Seric p++; 38658333Seric if (*p == '\0') 38758333Seric break; 38858333Seric kp = p; 38958333Seric 39058333Seric /* skip to the value portion */ 39158333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 39258333Seric p++; 39358333Seric if (*p == '=') 39458333Seric { 39558333Seric *p++ = '\0'; 39658333Seric vp = p; 39758333Seric 39858333Seric /* skip to the end of the value */ 39958333Seric while (*p != '\0' && *p != ' ' && 40058333Seric !(isascii(*p) && iscntrl(*p)) && 40158333Seric *p != '=') 40258333Seric p++; 40358333Seric } 40458333Seric 40558333Seric if (*p != '\0') 40658333Seric *p++ = '\0'; 40758333Seric 40858333Seric if (tTd(19, 1)) 40966764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 41058333Seric vp == NULL ? "<null>" : vp); 41158333Seric 41258333Seric if (strcasecmp(kp, "size") == 0) 41358333Seric { 41459093Seric if (vp == NULL) 41558333Seric { 41658333Seric usrerr("501 SIZE requires a value"); 41758333Seric /* NOTREACHED */ 41858333Seric } 419*66772Seric # ifdef __STDC__ 420*66772Seric msize = strtoul(vp, (char **) NULL, 10); 421*66772Seric # else 422*66772Seric msize = strtol(vp, (char **) NULL, 10); 423*66772Seric # endif 42458333Seric } 42559093Seric else if (strcasecmp(kp, "body") == 0) 42659093Seric { 42759093Seric if (vp == NULL) 42859093Seric { 42959093Seric usrerr("501 BODY requires a value"); 43059093Seric /* NOTREACHED */ 43159093Seric } 43259093Seric # ifdef MIME 43359093Seric if (strcasecmp(vp, "8bitmime") == 0) 43459093Seric { 43559093Seric e->e_bodytype = "8BITMIME"; 43659709Seric SevenBit = FALSE; 43759093Seric } 43859093Seric else if (strcasecmp(vp, "7bit") == 0) 43959093Seric { 44059093Seric e->e_bodytype = "7BIT"; 44159709Seric SevenBit = TRUE; 44259093Seric } 44359093Seric else 44459093Seric { 44559093Seric usrerr("501 Unknown BODY type %s", 44659093Seric vp); 44759093Seric } 44859093Seric # endif 44959093Seric } 45058333Seric else 45158333Seric { 45258333Seric usrerr("501 %s parameter unrecognized", kp); 45358333Seric /* NOTREACHED */ 45458333Seric } 45558333Seric } 45659284Seric 45759284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 45859284Seric { 45959284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 46059284Seric MaxMessageSize); 46159284Seric /* NOTREACHED */ 46259284Seric } 46358333Seric 46458333Seric if (!enoughspace(msize)) 46558333Seric { 46658333Seric message("452 Insufficient disk space; try again later"); 46758333Seric break; 46858333Seric } 46958151Seric message("250 Sender ok"); 47058147Seric gotmail = TRUE; 4714549Seric break; 4724549Seric 4734976Seric case CMDRCPT: /* rcpt -- designate recipient */ 47458850Seric if (!gotmail) 47558850Seric { 47658850Seric usrerr("503 Need MAIL before RCPT"); 47758850Seric break; 47858850Seric } 47961093Seric SmtpPhase = "server RCPT"; 48012612Seric if (setjmp(TopFrame) > 0) 48114785Seric { 48255012Seric e->e_flags &= ~EF_FATALERRS; 48312612Seric break; 48414785Seric } 48512612Seric QuickAbort = TRUE; 48651951Seric LogUsrErrs = TRUE; 48758093Seric 48859699Seric if (e->e_sendmode != SM_DELIVER) 48959699Seric e->e_flags |= EF_VRFYONLY; 49058919Seric 4914549Seric p = skipword(p, "to"); 4924549Seric if (p == NULL) 4934549Seric break; 49464284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 49512612Seric if (a == NULL) 49612612Seric break; 49716886Seric a->q_flags |= QPRIMARY; 49855012Seric a = recipient(a, &e->e_sendqueue, e); 49912612Seric if (Errors != 0) 50012612Seric break; 50112612Seric 50212612Seric /* no errors during parsing, but might be a duplicate */ 50355012Seric e->e_to = p; 50412612Seric if (!bitset(QBADADDR, a->q_flags)) 50559747Seric { 50664718Seric message("250 Recipient ok%s", 50764718Seric bitset(QQUEUEUP, a->q_flags) ? 50864718Seric " (will queue)" : ""); 50959747Seric nrcpts++; 51059747Seric } 51112612Seric else 5124549Seric { 51312612Seric /* punt -- should keep message in ADDRESS.... */ 51458151Seric message("550 Addressee unknown"); 5154549Seric } 51655012Seric e->e_to = NULL; 5174549Seric break; 5184549Seric 5194549Seric case CMDDATA: /* data -- text of mail */ 52061093Seric SmtpPhase = "server DATA"; 52158109Seric if (!gotmail) 5224549Seric { 52358151Seric message("503 Need MAIL command"); 5244976Seric break; 5254549Seric } 52664718Seric else if (nrcpts <= 0) 5274549Seric { 52858151Seric message("503 Need RCPT (recipient)"); 5294976Seric break; 5304549Seric } 5314976Seric 53258929Seric /* check to see if we need to re-expand aliases */ 53363787Seric /* also reset QBADADDR on already-diagnosted addrs */ 53463787Seric doublequeue = FALSE; 53558929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 53658929Seric { 53758929Seric if (bitset(QVERIFIED, a->q_flags)) 53863787Seric { 53963787Seric /* need to re-expand aliases */ 54063787Seric doublequeue = TRUE; 54163787Seric } 54263787Seric if (bitset(QBADADDR, a->q_flags)) 54363787Seric { 54463787Seric /* make this "go away" */ 54563787Seric a->q_flags |= QDONTSEND; 54663787Seric a->q_flags &= ~QBADADDR; 54763787Seric } 54858929Seric } 54958929Seric 5504976Seric /* collect the text of the message */ 55124943Seric SmtpPhase = "collect"; 55264766Seric collect(TRUE, doublequeue, e); 55364766Seric if (Errors != 0) 55464766Seric goto abortmessage; 55563965Seric HoldErrs = TRUE; 5564976Seric 5578238Seric /* 5588238Seric ** Arrange to send to everyone. 5598238Seric ** If sending to multiple people, mail back 5608238Seric ** errors rather than reporting directly. 5618238Seric ** In any case, don't mail back errors for 5628238Seric ** anything that has happened up to 5638238Seric ** now (the other end will do this). 56410197Seric ** Truncate our transcript -- the mail has gotten 56510197Seric ** to us successfully, and if we have 56610197Seric ** to mail this back, it will be easier 56710197Seric ** on the reader. 5688238Seric ** Then send to everyone. 5698238Seric ** Finally give a reply code. If an error has 5708238Seric ** already been given, don't mail a 5718238Seric ** message back. 5729339Seric ** We goose error returns by clearing error bit. 5738238Seric */ 5748238Seric 57524943Seric SmtpPhase = "delivery"; 57663787Seric if (nrcpts != 1 && !doublequeue) 5779378Seric { 5789378Seric HoldErrs = TRUE; 57958734Seric e->e_errormode = EM_MAIL; 5809378Seric } 58155012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 58258714Seric id = e->e_id; 5834976Seric 5844976Seric /* send to all recipients */ 58563787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 58655012Seric e->e_to = NULL; 5874976Seric 5888238Seric /* issue success if appropriate and reset */ 5898238Seric if (Errors == 0 || HoldErrs) 59058855Seric message("250 %s Message accepted for delivery", id); 59159747Seric 59259747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 59359730Seric { 59459730Seric /* avoid sending back an extra message */ 59559730Seric e->e_flags &= ~EF_FATALERRS; 59659747Seric e->e_flags |= EF_CLRQUEUE; 59759730Seric } 5988238Seric else 59958919Seric { 60059747Seric /* from now on, we have to operate silently */ 60159747Seric HoldErrs = TRUE; 60259747Seric e->e_errormode = EM_MAIL; 60359747Seric 60459730Seric /* if we just queued, poke it */ 60563787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 60659730Seric { 60764296Seric extern pid_t dowork(); 60864296Seric 60959730Seric unlockqueue(e); 61064296Seric (void) dowork(id, TRUE, TRUE, e); 61159730Seric } 61258919Seric } 61358883Seric 61459747Seric abortmessage: 6159339Seric /* if in a child, pop back to our parent */ 6169339Seric if (InChild) 6179339Seric finis(); 61824943Seric 61924943Seric /* clean up a bit */ 62058109Seric gotmail = FALSE; 62155012Seric dropenvelope(e); 62258179Seric CurEnv = e = newenvelope(e, CurEnv); 62355012Seric e->e_flags = BlankEnvelope.e_flags; 6244549Seric break; 6254549Seric 6264549Seric case CMDRSET: /* rset -- reset state */ 62758151Seric message("250 Reset state"); 62864359Seric e->e_flags |= EF_CLRQUEUE; 6299339Seric if (InChild) 6309339Seric finis(); 63158109Seric 63258109Seric /* clean up a bit */ 63358109Seric gotmail = FALSE; 63458109Seric dropenvelope(e); 63558179Seric CurEnv = e = newenvelope(e, CurEnv); 6369339Seric break; 6374549Seric 6384549Seric case CMDVRFY: /* vrfy -- verify address */ 63958092Seric case CMDEXPN: /* expn -- expand address */ 64058092Seric vrfy = c->cmdcode == CMDVRFY; 64158092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 64258092Seric PrivacyFlags)) 64358082Seric { 64458412Seric if (vrfy) 64558412Seric message("252 Who's to say?"); 64658412Seric else 64765192Seric message("502 Sorry, we do not allow this operation"); 64865017Seric #ifdef LOG 64965017Seric if (LogLevel > 5) 65065017Seric syslog(LOG_INFO, "%s: %s [rejected]", 65165017Seric CurSmtpClient, inp); 65265017Seric #endif 65358082Seric break; 65458082Seric } 65558082Seric else if (!gothello && 65658092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 65758092Seric PrivacyFlags)) 65858082Seric { 65958151Seric message("503 I demand that you introduce yourself first"); 66058082Seric break; 66158082Seric } 66258092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6639339Seric break; 66455173Seric #ifdef LOG 66558020Seric if (LogLevel > 5) 66665017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 66755173Seric #endif 6685003Seric vrfyqueue = NULL; 6697762Seric QuickAbort = TRUE; 67058092Seric if (vrfy) 67158092Seric e->e_flags |= EF_VRFYONLY; 67262373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 67362373Seric *p++; 67462373Seric if (*p == '\0') 67562373Seric { 67662373Seric message("501 Argument required"); 67762373Seric Errors++; 67862373Seric } 67962373Seric else 68062373Seric { 68164284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 68262373Seric } 6837762Seric if (Errors != 0) 6849339Seric { 6859339Seric if (InChild) 6869339Seric finis(); 6877762Seric break; 6889339Seric } 68962373Seric if (vrfyqueue == NULL) 69062373Seric { 69162373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 69262373Seric } 6935003Seric while (vrfyqueue != NULL) 6945003Seric { 69563971Seric a = vrfyqueue; 69663971Seric while ((a = a->q_next) != NULL && 69763971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 69863971Seric continue; 6997685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 70058151Seric printvrfyaddr(vrfyqueue, a == NULL); 70163847Seric vrfyqueue = vrfyqueue->q_next; 7025003Seric } 7039339Seric if (InChild) 7049339Seric finis(); 7054549Seric break; 7064549Seric 7074549Seric case CMDHELP: /* help -- give user info */ 7084577Seric help(p); 7094549Seric break; 7104549Seric 7114549Seric case CMDNOOP: /* noop -- do nothing */ 71264122Seric message("250 OK"); 7134549Seric break; 7144549Seric 7154549Seric case CMDQUIT: /* quit -- leave mail */ 71658151Seric message("221 %s closing connection", MyHostName); 71761051Seric 71866283Seric doquit: 71961051Seric /* avoid future 050 messages */ 72066017Seric disconnect(1, e); 72161051Seric 7229339Seric if (InChild) 7239339Seric ExitStat = EX_QUIT; 7244549Seric finis(); 7254549Seric 7268544Seric case CMDVERB: /* set verbose mode */ 72759957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 72859957Seric { 72959957Seric /* this would give out the same info */ 73059957Seric message("502 Verbose unavailable"); 73159957Seric break; 73259957Seric } 7338544Seric Verbose = TRUE; 73458734Seric e->e_sendmode = SM_DELIVER; 73559957Seric message("250 Verbose mode"); 7368544Seric break; 7378544Seric 7389314Seric case CMDONEX: /* doing one transaction only */ 7399378Seric OneXact = TRUE; 74059957Seric message("250 Only one transaction"); 7419314Seric break; 7429314Seric 74336230Skarels # ifdef SMTPDEBUG 7449339Seric case CMDDBGQSHOW: /* show queues */ 7456907Seric printf("Send Queue="); 74655012Seric printaddr(e->e_sendqueue, TRUE); 7475003Seric break; 7487275Seric 7497275Seric case CMDDBGDEBUG: /* set debug mode */ 7507676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7517676Seric tTflag(p); 75258151Seric message("200 Debug set"); 7537275Seric break; 7547275Seric 75536230Skarels # else /* not SMTPDEBUG */ 75636230Skarels case CMDDBGQSHOW: /* show queues */ 75736230Skarels case CMDDBGDEBUG: /* set debug mode */ 75864685Seric # endif /* SMTPDEBUG */ 75964685Seric case CMDLOGBOGUS: /* bogus command */ 76036233Skarels # ifdef LOG 76158308Seric if (LogLevel > 0) 76264685Seric syslog(LOG_CRIT, 76358020Seric "\"%s\" command from %s (%s)", 76466005Seric c->cmdname, peerhostname, 76558755Seric anynet_ntoa(&RealHostAddr)); 76636233Skarels # endif 76736230Skarels /* FALL THROUGH */ 76836230Skarels 7694549Seric case CMDERROR: /* unknown command */ 77066283Seric if (++badcommands > MAXBADCOMMANDS) 77166283Seric { 77266283Seric message("421 %s Too many bad commands; closing connection", 77366283Seric MyHostName); 77466283Seric goto doquit; 77566283Seric } 77666283Seric 77758151Seric message("500 Command unrecognized"); 7784549Seric break; 7794549Seric 7804549Seric default: 78136230Skarels errno = 0; 78258151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7834549Seric break; 7844549Seric } 7854549Seric } 7864549Seric } 7874549Seric /* 7884549Seric ** SKIPWORD -- skip a fixed word. 7894549Seric ** 7904549Seric ** Parameters: 7914549Seric ** p -- place to start looking. 7924549Seric ** w -- word to skip. 7934549Seric ** 7944549Seric ** Returns: 7954549Seric ** p following w. 7964549Seric ** NULL on error. 7974549Seric ** 7984549Seric ** Side Effects: 7994549Seric ** clobbers the p data area. 8004549Seric */ 8014549Seric 8024549Seric static char * 8034549Seric skipword(p, w) 8044549Seric register char *p; 8054549Seric char *w; 8064549Seric { 8074549Seric register char *q; 80866005Seric char *firstp = p; 8094549Seric 8104549Seric /* find beginning of word */ 81158050Seric while (isascii(*p) && isspace(*p)) 8124549Seric p++; 8134549Seric q = p; 8144549Seric 8154549Seric /* find end of word */ 81658050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8174549Seric p++; 81858050Seric while (isascii(*p) && isspace(*p)) 8194549Seric *p++ = '\0'; 8204549Seric if (*p != ':') 8214549Seric { 8224549Seric syntax: 82366005Seric message("501 Syntax error in parameters scanning \"%s\"", 82466005Seric firstp); 8254549Seric Errors++; 8264549Seric return (NULL); 8274549Seric } 8284549Seric *p++ = '\0'; 82958050Seric while (isascii(*p) && isspace(*p)) 8304549Seric p++; 8314549Seric 83262373Seric if (*p == '\0') 83362373Seric goto syntax; 83462373Seric 8354549Seric /* see if the input word matches desired word */ 83633725Sbostic if (strcasecmp(q, w)) 8374549Seric goto syntax; 8384549Seric 8394549Seric return (p); 8404549Seric } 8414577Seric /* 84258151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 84358151Seric ** 84458151Seric ** Parameters: 84558151Seric ** a -- the address to print 84658151Seric ** last -- set if this is the last one. 84758151Seric ** 84858151Seric ** Returns: 84958151Seric ** none. 85058151Seric ** 85158151Seric ** Side Effects: 85258151Seric ** Prints the appropriate 250 codes. 85358151Seric */ 85458151Seric 85558151Seric printvrfyaddr(a, last) 85658151Seric register ADDRESS *a; 85758151Seric bool last; 85858151Seric { 85958151Seric char fmtbuf[20]; 86058151Seric 86158151Seric strcpy(fmtbuf, "250"); 86258151Seric fmtbuf[3] = last ? ' ' : '-'; 86358151Seric 86459746Seric if (a->q_fullname == NULL) 86559746Seric { 86659746Seric if (strchr(a->q_user, '@') == NULL) 86759746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 86859746Seric else 86959746Seric strcpy(&fmtbuf[4], "<%s>"); 87059746Seric message(fmtbuf, a->q_user, MyHostName); 87159746Seric } 87258151Seric else 87358151Seric { 87459746Seric if (strchr(a->q_user, '@') == NULL) 87559746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 87659746Seric else 87759746Seric strcpy(&fmtbuf[4], "%s <%s>"); 87859746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 87958151Seric } 88058151Seric } 88158151Seric /* 8824577Seric ** HELP -- implement the HELP command. 8834577Seric ** 8844577Seric ** Parameters: 8854577Seric ** topic -- the topic we want help for. 8864577Seric ** 8874577Seric ** Returns: 8884577Seric ** none. 8894577Seric ** 8904577Seric ** Side Effects: 8914577Seric ** outputs the help file to message output. 8924577Seric */ 8934577Seric 8944577Seric help(topic) 8954577Seric char *topic; 8964577Seric { 8974577Seric register FILE *hf; 8984577Seric int len; 8994577Seric char buf[MAXLINE]; 9004577Seric bool noinfo; 9014577Seric 9028269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 9034577Seric { 9044577Seric /* no help */ 90511931Seric errno = 0; 90658151Seric message("502 HELP not implemented"); 9074577Seric return; 9084577Seric } 9094577Seric 91049669Seric if (topic == NULL || *topic == '\0') 91149669Seric topic = "smtp"; 91249669Seric else 91349669Seric makelower(topic); 91449669Seric 9154577Seric len = strlen(topic); 9164577Seric noinfo = TRUE; 9174577Seric 9184577Seric while (fgets(buf, sizeof buf, hf) != NULL) 9194577Seric { 9204577Seric if (strncmp(buf, topic, len) == 0) 9214577Seric { 9224577Seric register char *p; 9234577Seric 92456795Seric p = strchr(buf, '\t'); 9254577Seric if (p == NULL) 9264577Seric p = buf; 9274577Seric else 9284577Seric p++; 9294577Seric fixcrlf(p, TRUE); 93058151Seric message("214-%s", p); 9314577Seric noinfo = FALSE; 9324577Seric } 9334577Seric } 9344577Seric 9354577Seric if (noinfo) 93658151Seric message("504 HELP topic unknown"); 9374577Seric else 93858151Seric message("214 End of HELP info"); 9394628Seric (void) fclose(hf); 9404577Seric } 9418544Seric /* 9429339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 9439339Seric ** 9449339Seric ** Parameters: 9459339Seric ** label -- a string used in error messages 9469339Seric ** 9479339Seric ** Returns: 9489339Seric ** zero in the child 9499339Seric ** one in the parent 9509339Seric ** 9519339Seric ** Side Effects: 9529339Seric ** none. 9539339Seric */ 9548544Seric 95555012Seric runinchild(label, e) 9569339Seric char *label; 95755012Seric register ENVELOPE *e; 9589339Seric { 9599339Seric int childpid; 9609339Seric 96116158Seric if (!OneXact) 9629339Seric { 96316158Seric childpid = dofork(); 96416158Seric if (childpid < 0) 96516158Seric { 96616158Seric syserr("%s: cannot fork", label); 96716158Seric return (1); 96816158Seric } 96916158Seric if (childpid > 0) 97016158Seric { 97116158Seric auto int st; 9729339Seric 97316158Seric /* parent -- wait for child to complete */ 97461093Seric setproctitle("server %s child wait", CurHostName); 97516158Seric st = waitfor(childpid); 97616158Seric if (st == -1) 97716158Seric syserr("%s: lost child", label); 97864948Seric else if (!WIFEXITED(st)) 97964948Seric syserr("%s: died on signal %d", 98064948Seric label, st & 0177); 9819339Seric 98216158Seric /* if we exited on a QUIT command, complete the process */ 98366017Seric if (WEXITSTATUS(st) == EX_QUIT) 98466017Seric { 98566017Seric disconnect(1, e); 98616158Seric finis(); 98766017Seric } 9889339Seric 98916158Seric return (1); 99016158Seric } 99116158Seric else 99216158Seric { 99316158Seric /* child */ 99416158Seric InChild = TRUE; 99525050Seric QuickAbort = FALSE; 99655012Seric clearenvelope(e, FALSE); 99716158Seric } 9989339Seric } 99915256Seric 100016158Seric /* open alias database */ 100160537Seric initmaps(FALSE, e); 100216158Seric 100316158Seric return (0); 10049339Seric } 10059339Seric 100656795Seric # endif /* SMTP */ 1007