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*68267Seric static char sccsid[] = "@(#)srvrsmtp.c 8.54 (Berkeley) 02/10/95 (with SMTP)"; 1433731Sbostic #else 15*68267Seric static char sccsid[] = "@(#)srvrsmtp.c 8.54 (Berkeley) 02/10/95 (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 */ 11266772Seric 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 } 26267445Seric 26367445Seric /* check for valid domain name (re 1123 5.2.5) */ 26467445Seric if (*p == '\0') 26567445Seric { 26667445Seric message("501 %s requires domain address", 26767445Seric cmdbuf); 26867445Seric break; 26967445Seric } 27067445Seric else 27167445Seric { 27267445Seric register char *q; 27367445Seric 27467445Seric for (q = p; *q != '\0'; q++) 27567445Seric { 27667445Seric if (!isascii(*q)) 27767445Seric break; 27867445Seric if (isalnum(*q)) 27967445Seric continue; 28067445Seric if (strchr("[].-_#", *q) == NULL) 28167445Seric break; 28267445Seric } 28367445Seric if (*q != '\0') 28467445Seric { 28567445Seric message("501 Invalid domain name"); 28667445Seric break; 28767445Seric } 28867445Seric } 28967445Seric 29059016Seric sendinghost = newstr(p); 29160210Seric gothello = TRUE; 29260210Seric if (c->cmdcode != CMDEHLO) 29360239Seric { 29460239Seric /* print old message and be done with it */ 29560239Seric message("250 %s Hello %s, pleased to meet you", 29665017Seric MyHostName, CurSmtpClient); 29760210Seric break; 29860239Seric } 29960239Seric 30060239Seric /* print extended message and brag */ 30160239Seric message("250-%s Hello %s, pleased to meet you", 30266760Seric MyHostName, CurSmtpClient); 30358323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 30458323Seric message("250-EXPN"); 30567417Seric message("250-8BITMIME"); 30664359Seric if (MaxMessageSize > 0) 30764359Seric message("250-SIZE %ld", MaxMessageSize); 30859271Seric else 30959271Seric message("250-SIZE"); 31068028Seric #ifdef DSN 31168228Seric message("250-X-DSN-2 (Unpublished draft as of Sat, 04 Feb 1995)"); 31268028Seric #endif 31358323Seric message("250 HELP"); 3144976Seric break; 3154976Seric 3164549Seric case CMDMAIL: /* mail -- designate sender */ 31761093Seric SmtpPhase = "server MAIL"; 31824943Seric 3199314Seric /* check for validity of this command */ 32058789Seric if (!gothello) 32158082Seric { 32258957Seric /* set sending host to our known value */ 32359016Seric if (sendinghost == NULL) 32466005Seric sendinghost = peerhostname; 32558957Seric 32658789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 32758821Seric { 32858789Seric message("503 Polite people say HELO first"); 32958821Seric break; 33058821Seric } 33158082Seric } 33258109Seric if (gotmail) 3334558Seric { 33458151Seric message("503 Sender already specified"); 33563843Seric if (InChild) 33663843Seric finis(); 3374558Seric break; 3384558Seric } 3399339Seric if (InChild) 3409339Seric { 34136230Skarels errno = 0; 34258151Seric syserr("503 Nested MAIL command: MAIL %s", p); 34358069Seric finis(); 3449339Seric } 3459339Seric 3469339Seric /* fork a subprocess to process this command */ 34755012Seric if (runinchild("SMTP-MAIL", e) > 0) 3489339Seric break; 34963753Seric if (!gothello) 35063753Seric { 35163753Seric auth_warning(e, 35266005Seric "Host %s didn't use HELO protocol", 35366005Seric peerhostname); 35463753Seric } 35565947Seric #ifdef PICKY_HELO_CHECK 35666005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 35766005Seric (strcasecmp(peerhostname, "localhost") != 0 || 35865823Seric strcasecmp(sendinghost, MyHostName) != 0)) 35965823Seric { 36065823Seric auth_warning(e, "Host %s claimed to be %s", 36166005Seric peerhostname, sendinghost); 36265823Seric } 36365947Seric #endif 36465823Seric 36558323Seric if (protocol == NULL) 36658323Seric protocol = "SMTP"; 36758323Seric define('r', protocol, e); 36859016Seric define('s', sendinghost, e); 36955012Seric initsys(e); 37059747Seric nrcpts = 0; 37165089Seric e->e_flags |= EF_LOGSENDER; 37265058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3739339Seric 3749339Seric /* child -- go do the processing */ 3754549Seric p = skipword(p, "from"); 3764549Seric if (p == NULL) 3774549Seric break; 37857977Seric if (setjmp(TopFrame) > 0) 37958147Seric { 38058147Seric /* this failed -- undo work */ 38158147Seric if (InChild) 38259058Seric { 38359058Seric QuickAbort = FALSE; 38459058Seric SuprErrs = TRUE; 38563787Seric e->e_flags &= ~EF_FATALERRS; 38658147Seric finis(); 38759058Seric } 38857977Seric break; 38958147Seric } 39057977Seric QuickAbort = TRUE; 39158333Seric 39258333Seric /* must parse sender first */ 39358333Seric delimptr = NULL; 39458704Seric setsender(p, e, &delimptr, FALSE); 39558333Seric p = delimptr; 39658333Seric if (p != NULL && *p != '\0') 39758333Seric *p++ = '\0'; 39858333Seric 39966325Seric /* check for possible spoofing */ 40066325Seric if (RealUid != 0 && OpMode == MD_SMTP && 40167473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 40267473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 40366325Seric { 40466325Seric auth_warning(e, "%s owned process doing -bs", 40566325Seric RealUserName); 40666325Seric } 40766325Seric 40858333Seric /* now parse ESMTP arguments */ 40958333Seric msize = 0; 41066764Seric while (p != NULL && *p != '\0') 41158333Seric { 41258333Seric char *kp; 41366304Seric char *vp = NULL; 41458333Seric 41558333Seric /* locate the beginning of the keyword */ 41658333Seric while (isascii(*p) && isspace(*p)) 41758333Seric p++; 41858333Seric if (*p == '\0') 41958333Seric break; 42058333Seric kp = p; 42158333Seric 42258333Seric /* skip to the value portion */ 42358333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 42458333Seric p++; 42558333Seric if (*p == '=') 42658333Seric { 42758333Seric *p++ = '\0'; 42858333Seric vp = p; 42958333Seric 43058333Seric /* skip to the end of the value */ 43158333Seric while (*p != '\0' && *p != ' ' && 43258333Seric !(isascii(*p) && iscntrl(*p)) && 43358333Seric *p != '=') 43458333Seric p++; 43558333Seric } 43658333Seric 43758333Seric if (*p != '\0') 43858333Seric *p++ = '\0'; 43958333Seric 44058333Seric if (tTd(19, 1)) 44166764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 44258333Seric vp == NULL ? "<null>" : vp); 44358333Seric 44458333Seric if (strcasecmp(kp, "size") == 0) 44558333Seric { 44659093Seric if (vp == NULL) 44758333Seric { 44858333Seric usrerr("501 SIZE requires a value"); 44958333Seric /* NOTREACHED */ 45058333Seric } 45166772Seric # ifdef __STDC__ 45266772Seric msize = strtoul(vp, (char **) NULL, 10); 45366772Seric # else 45466772Seric msize = strtol(vp, (char **) NULL, 10); 45566772Seric # endif 45658333Seric } 45759093Seric else if (strcasecmp(kp, "body") == 0) 45859093Seric { 45959093Seric if (vp == NULL) 46059093Seric { 46159093Seric usrerr("501 BODY requires a value"); 46259093Seric /* NOTREACHED */ 46359093Seric } 46459093Seric if (strcasecmp(vp, "8bitmime") == 0) 46559093Seric { 46667546Seric SevenBitInput = FALSE; 46759093Seric } 46859093Seric else if (strcasecmp(vp, "7bit") == 0) 46959093Seric { 47067546Seric SevenBitInput = TRUE; 47159093Seric } 47259093Seric else 47359093Seric { 47459093Seric usrerr("501 Unknown BODY type %s", 47559093Seric vp); 47667417Seric /* NOTREACHED */ 47759093Seric } 478*68267Seric e->e_bodytype = newstr(vp); 47959093Seric } 48067880Seric else if (strcasecmp(kp, "envid") == 0) 48167880Seric { 48267880Seric if (vp == NULL) 48367880Seric { 48467880Seric usrerr("501 ENVID requires a value"); 48567880Seric /* NOTREACHED */ 48667880Seric } 48767880Seric e->e_envid = newstr(vp); 48867880Seric } 48958333Seric else 49058333Seric { 49158333Seric usrerr("501 %s parameter unrecognized", kp); 49258333Seric /* NOTREACHED */ 49358333Seric } 49458333Seric } 49559284Seric 49659284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 49759284Seric { 49859284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 49959284Seric MaxMessageSize); 50059284Seric /* NOTREACHED */ 50159284Seric } 50258333Seric 50358333Seric if (!enoughspace(msize)) 50458333Seric { 50558333Seric message("452 Insufficient disk space; try again later"); 50658333Seric break; 50758333Seric } 50858151Seric message("250 Sender ok"); 50958147Seric gotmail = TRUE; 5104549Seric break; 5114549Seric 5124976Seric case CMDRCPT: /* rcpt -- designate recipient */ 51358850Seric if (!gotmail) 51458850Seric { 51558850Seric usrerr("503 Need MAIL before RCPT"); 51658850Seric break; 51758850Seric } 51861093Seric SmtpPhase = "server RCPT"; 51912612Seric if (setjmp(TopFrame) > 0) 52014785Seric { 52155012Seric e->e_flags &= ~EF_FATALERRS; 52212612Seric break; 52314785Seric } 52412612Seric QuickAbort = TRUE; 52551951Seric LogUsrErrs = TRUE; 52658093Seric 52759699Seric if (e->e_sendmode != SM_DELIVER) 52859699Seric e->e_flags |= EF_VRFYONLY; 52958919Seric 5304549Seric p = skipword(p, "to"); 5314549Seric if (p == NULL) 5324549Seric break; 53367880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 53412612Seric if (a == NULL) 53512612Seric break; 53667880Seric p = delimptr; 53767880Seric 53867880Seric /* now parse ESMTP arguments */ 53967880Seric while (p != NULL && *p != '\0') 54067880Seric { 54167880Seric char *kp; 54267880Seric char *vp = NULL; 54367880Seric 54467880Seric /* locate the beginning of the keyword */ 54567880Seric while (isascii(*p) && isspace(*p)) 54667880Seric p++; 54767880Seric if (*p == '\0') 54867880Seric break; 54967880Seric kp = p; 55067880Seric 55167880Seric /* skip to the value portion */ 55267880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 55367880Seric p++; 55467880Seric if (*p == '=') 55567880Seric { 55667880Seric *p++ = '\0'; 55767880Seric vp = p; 55867880Seric 55967880Seric /* skip to the end of the value */ 56067880Seric while (*p != '\0' && *p != ' ' && 56167880Seric !(isascii(*p) && iscntrl(*p)) && 56267880Seric *p != '=') 56367880Seric p++; 56467880Seric } 56567880Seric 56667880Seric if (*p != '\0') 56767880Seric *p++ = '\0'; 56867880Seric 56967880Seric if (tTd(19, 1)) 57067880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 57167880Seric vp == NULL ? "<null>" : vp); 57267880Seric 57367963Seric rcpt_esmtp_args(a, kp, vp, e); 57467963Seric 57567880Seric } 57667980Seric 57767980Seric /* save in recipient list after ESMTP mods */ 57867980Seric a->q_flags |= QPRIMARY; 57967982Seric a = recipient(a, &e->e_sendqueue, 0, e); 58067980Seric 58112612Seric if (Errors != 0) 58212612Seric break; 58312612Seric 58412612Seric /* no errors during parsing, but might be a duplicate */ 58555012Seric e->e_to = p; 58612612Seric if (!bitset(QBADADDR, a->q_flags)) 58759747Seric { 58864718Seric message("250 Recipient ok%s", 58964718Seric bitset(QQUEUEUP, a->q_flags) ? 59064718Seric " (will queue)" : ""); 59159747Seric nrcpts++; 59259747Seric } 59312612Seric else 5944549Seric { 59512612Seric /* punt -- should keep message in ADDRESS.... */ 59658151Seric message("550 Addressee unknown"); 5974549Seric } 59855012Seric e->e_to = NULL; 5994549Seric break; 6004549Seric 6014549Seric case CMDDATA: /* data -- text of mail */ 60261093Seric SmtpPhase = "server DATA"; 60358109Seric if (!gotmail) 6044549Seric { 60558151Seric message("503 Need MAIL command"); 6064976Seric break; 6074549Seric } 60864718Seric else if (nrcpts <= 0) 6094549Seric { 61058151Seric message("503 Need RCPT (recipient)"); 6114976Seric break; 6124549Seric } 6134976Seric 61458929Seric /* check to see if we need to re-expand aliases */ 61563787Seric /* also reset QBADADDR on already-diagnosted addrs */ 61663787Seric doublequeue = FALSE; 61758929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 61858929Seric { 61958929Seric if (bitset(QVERIFIED, a->q_flags)) 62063787Seric { 62163787Seric /* need to re-expand aliases */ 62263787Seric doublequeue = TRUE; 62363787Seric } 62463787Seric if (bitset(QBADADDR, a->q_flags)) 62563787Seric { 62663787Seric /* make this "go away" */ 62763787Seric a->q_flags |= QDONTSEND; 62863787Seric a->q_flags &= ~QBADADDR; 62963787Seric } 63058929Seric } 63158929Seric 6324976Seric /* collect the text of the message */ 63324943Seric SmtpPhase = "collect"; 63467546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 63564766Seric if (Errors != 0) 63664766Seric goto abortmessage; 63767131Seric 63867131Seric /* from now on, we have to operate silently */ 63963965Seric HoldErrs = TRUE; 64067131Seric e->e_errormode = EM_MAIL; 6414976Seric 6428238Seric /* 6438238Seric ** Arrange to send to everyone. 6448238Seric ** If sending to multiple people, mail back 6458238Seric ** errors rather than reporting directly. 6468238Seric ** In any case, don't mail back errors for 6478238Seric ** anything that has happened up to 6488238Seric ** now (the other end will do this). 64910197Seric ** Truncate our transcript -- the mail has gotten 65010197Seric ** to us successfully, and if we have 65110197Seric ** to mail this back, it will be easier 65210197Seric ** on the reader. 6538238Seric ** Then send to everyone. 6548238Seric ** Finally give a reply code. If an error has 6558238Seric ** already been given, don't mail a 6568238Seric ** message back. 6579339Seric ** We goose error returns by clearing error bit. 6588238Seric */ 6598238Seric 66024943Seric SmtpPhase = "delivery"; 66155012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 66258714Seric id = e->e_id; 6634976Seric 66467131Seric if (doublequeue) 66559730Seric { 66667131Seric /* make sure it is in the queue */ 66767131Seric queueup(e, TRUE, FALSE); 66868035Seric if (e->e_sendmode == SM_QUEUE) 66968035Seric e->e_flags |= EF_KEEPQUEUE; 67059730Seric } 6718238Seric else 67258919Seric { 67367131Seric /* send to all recipients */ 67467131Seric sendall(e, SM_DEFAULT); 67567131Seric } 67667131Seric e->e_to = NULL; 67759747Seric 67867131Seric /* issue success message */ 67967131Seric message("250 %s Message accepted for delivery", id); 68064296Seric 68167131Seric /* if we just queued, poke it */ 68267131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 68367131Seric { 68467131Seric extern pid_t dowork(); 68567131Seric 68667131Seric unlockqueue(e); 68767131Seric (void) dowork(id, TRUE, TRUE, e); 68858919Seric } 68958883Seric 69059747Seric abortmessage: 6919339Seric /* if in a child, pop back to our parent */ 6929339Seric if (InChild) 6939339Seric finis(); 69424943Seric 69524943Seric /* clean up a bit */ 69658109Seric gotmail = FALSE; 69755012Seric dropenvelope(e); 69858179Seric CurEnv = e = newenvelope(e, CurEnv); 69955012Seric e->e_flags = BlankEnvelope.e_flags; 7004549Seric break; 7014549Seric 7024549Seric case CMDRSET: /* rset -- reset state */ 70358151Seric message("250 Reset state"); 70464359Seric e->e_flags |= EF_CLRQUEUE; 7059339Seric if (InChild) 7069339Seric finis(); 70758109Seric 70858109Seric /* clean up a bit */ 70958109Seric gotmail = FALSE; 71058109Seric dropenvelope(e); 71158179Seric CurEnv = e = newenvelope(e, CurEnv); 7129339Seric break; 7134549Seric 7144549Seric case CMDVRFY: /* vrfy -- verify address */ 71558092Seric case CMDEXPN: /* expn -- expand address */ 71658092Seric vrfy = c->cmdcode == CMDVRFY; 71758092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 71858092Seric PrivacyFlags)) 71958082Seric { 72058412Seric if (vrfy) 72167160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 72258412Seric else 72365192Seric message("502 Sorry, we do not allow this operation"); 72465017Seric #ifdef LOG 72565017Seric if (LogLevel > 5) 72665017Seric syslog(LOG_INFO, "%s: %s [rejected]", 72765017Seric CurSmtpClient, inp); 72865017Seric #endif 72958082Seric break; 73058082Seric } 73158082Seric else if (!gothello && 73258092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 73358092Seric PrivacyFlags)) 73458082Seric { 73558151Seric message("503 I demand that you introduce yourself first"); 73658082Seric break; 73758082Seric } 73858092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7399339Seric break; 74055173Seric #ifdef LOG 74158020Seric if (LogLevel > 5) 74265017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 74355173Seric #endif 7445003Seric vrfyqueue = NULL; 7457762Seric QuickAbort = TRUE; 74658092Seric if (vrfy) 74758092Seric e->e_flags |= EF_VRFYONLY; 74862373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 74967615Seric p++; 75062373Seric if (*p == '\0') 75162373Seric { 75262373Seric message("501 Argument required"); 75362373Seric Errors++; 75462373Seric } 75562373Seric else 75662373Seric { 75767990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 75862373Seric } 7597762Seric if (Errors != 0) 7609339Seric { 7619339Seric if (InChild) 7629339Seric finis(); 7637762Seric break; 7649339Seric } 76562373Seric if (vrfyqueue == NULL) 76662373Seric { 76762373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 76862373Seric } 7695003Seric while (vrfyqueue != NULL) 7705003Seric { 77163971Seric a = vrfyqueue; 77263971Seric while ((a = a->q_next) != NULL && 77363971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 77463971Seric continue; 7757685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 77658151Seric printvrfyaddr(vrfyqueue, a == NULL); 77763847Seric vrfyqueue = vrfyqueue->q_next; 7785003Seric } 7799339Seric if (InChild) 7809339Seric finis(); 7814549Seric break; 7824549Seric 7834549Seric case CMDHELP: /* help -- give user info */ 7844577Seric help(p); 7854549Seric break; 7864549Seric 7874549Seric case CMDNOOP: /* noop -- do nothing */ 78864122Seric message("250 OK"); 7894549Seric break; 7904549Seric 7914549Seric case CMDQUIT: /* quit -- leave mail */ 79258151Seric message("221 %s closing connection", MyHostName); 79361051Seric 79466283Seric doquit: 79561051Seric /* avoid future 050 messages */ 79666017Seric disconnect(1, e); 79761051Seric 7989339Seric if (InChild) 7999339Seric ExitStat = EX_QUIT; 8004549Seric finis(); 8014549Seric 8028544Seric case CMDVERB: /* set verbose mode */ 80359957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 80459957Seric { 80559957Seric /* this would give out the same info */ 80659957Seric message("502 Verbose unavailable"); 80759957Seric break; 80859957Seric } 8098544Seric Verbose = TRUE; 81058734Seric e->e_sendmode = SM_DELIVER; 81159957Seric message("250 Verbose mode"); 8128544Seric break; 8138544Seric 8149314Seric case CMDONEX: /* doing one transaction only */ 8159378Seric OneXact = TRUE; 81659957Seric message("250 Only one transaction"); 8179314Seric break; 8189314Seric 81936230Skarels # ifdef SMTPDEBUG 8209339Seric case CMDDBGQSHOW: /* show queues */ 8216907Seric printf("Send Queue="); 82255012Seric printaddr(e->e_sendqueue, TRUE); 8235003Seric break; 8247275Seric 8257275Seric case CMDDBGDEBUG: /* set debug mode */ 8267676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 8277676Seric tTflag(p); 82858151Seric message("200 Debug set"); 8297275Seric break; 8307275Seric 83136230Skarels # else /* not SMTPDEBUG */ 83236230Skarels case CMDDBGQSHOW: /* show queues */ 83336230Skarels case CMDDBGDEBUG: /* set debug mode */ 83464685Seric # endif /* SMTPDEBUG */ 83564685Seric case CMDLOGBOGUS: /* bogus command */ 83636233Skarels # ifdef LOG 83758308Seric if (LogLevel > 0) 83864685Seric syslog(LOG_CRIT, 83958020Seric "\"%s\" command from %s (%s)", 84066005Seric c->cmdname, peerhostname, 84158755Seric anynet_ntoa(&RealHostAddr)); 84236233Skarels # endif 84336230Skarels /* FALL THROUGH */ 84436230Skarels 8454549Seric case CMDERROR: /* unknown command */ 84666283Seric if (++badcommands > MAXBADCOMMANDS) 84766283Seric { 84866283Seric message("421 %s Too many bad commands; closing connection", 84966283Seric MyHostName); 85066283Seric goto doquit; 85166283Seric } 85266283Seric 85358151Seric message("500 Command unrecognized"); 8544549Seric break; 8554549Seric 8564549Seric default: 85736230Skarels errno = 0; 85858151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8594549Seric break; 8604549Seric } 8614549Seric } 8624549Seric } 8634549Seric /* 8644549Seric ** SKIPWORD -- skip a fixed word. 8654549Seric ** 8664549Seric ** Parameters: 8674549Seric ** p -- place to start looking. 8684549Seric ** w -- word to skip. 8694549Seric ** 8704549Seric ** Returns: 8714549Seric ** p following w. 8724549Seric ** NULL on error. 8734549Seric ** 8744549Seric ** Side Effects: 8754549Seric ** clobbers the p data area. 8764549Seric */ 8774549Seric 8784549Seric static char * 8794549Seric skipword(p, w) 8804549Seric register char *p; 8814549Seric char *w; 8824549Seric { 8834549Seric register char *q; 88466005Seric char *firstp = p; 8854549Seric 8864549Seric /* find beginning of word */ 88758050Seric while (isascii(*p) && isspace(*p)) 8884549Seric p++; 8894549Seric q = p; 8904549Seric 8914549Seric /* find end of word */ 89258050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8934549Seric p++; 89458050Seric while (isascii(*p) && isspace(*p)) 8954549Seric *p++ = '\0'; 8964549Seric if (*p != ':') 8974549Seric { 8984549Seric syntax: 89966005Seric message("501 Syntax error in parameters scanning \"%s\"", 90066005Seric firstp); 9014549Seric Errors++; 9024549Seric return (NULL); 9034549Seric } 9044549Seric *p++ = '\0'; 90558050Seric while (isascii(*p) && isspace(*p)) 9064549Seric p++; 9074549Seric 90862373Seric if (*p == '\0') 90962373Seric goto syntax; 91062373Seric 9114549Seric /* see if the input word matches desired word */ 91233725Sbostic if (strcasecmp(q, w)) 9134549Seric goto syntax; 9144549Seric 9154549Seric return (p); 9164549Seric } 9174577Seric /* 91867963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 91967963Seric ** 92067963Seric ** Parameters: 92167963Seric ** a -- the address corresponding to the To: parameter. 92267963Seric ** kp -- the parameter key. 92367963Seric ** vp -- the value of that parameter. 92467963Seric ** e -- the envelope. 92567963Seric ** 92667963Seric ** Returns: 92767963Seric ** none. 92867963Seric */ 92967963Seric 93067963Seric rcpt_esmtp_args(a, kp, vp, e) 93167963Seric ADDRESS *a; 93267963Seric char *kp; 93367963Seric char *vp; 93467963Seric ENVELOPE *e; 93567963Seric { 93667963Seric if (strcasecmp(kp, "notify") == 0) 93767963Seric { 93867963Seric char *p; 93967963Seric 94067963Seric if (vp == NULL) 94167963Seric { 94267963Seric usrerr("501 NOTIFY requires a value"); 94367963Seric /* NOTREACHED */ 94467963Seric } 94567963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 94667963Seric if (strcasecmp(vp, "never") == 0) 94767963Seric return; 94867963Seric for (p = vp; p != NULL; vp = p) 94967963Seric { 95067963Seric p = strchr(p, ','); 95167963Seric if (p != NULL) 95267963Seric *p++ = '\0'; 95367963Seric if (strcasecmp(vp, "success") == 0) 95467963Seric a->q_flags |= QPINGONSUCCESS; 95567963Seric else if (strcasecmp(vp, "failure") == 0) 95667963Seric a->q_flags |= QPINGONFAILURE; 95767963Seric else if (strcasecmp(vp, "delay") == 0) 95867963Seric a->q_flags |= QPINGONDELAY; 95967963Seric else 96067963Seric { 96167963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 96267963Seric vp); 96367963Seric /* NOTREACHED */ 96467963Seric } 96567963Seric } 96667963Seric } 96767963Seric else if (strcasecmp(kp, "ret") == 0) 96867963Seric { 96967963Seric if (vp == NULL) 97067963Seric { 97167963Seric usrerr("501 RET requires a value"); 97267963Seric /* NOTREACHED */ 97367963Seric } 97467963Seric a->q_flags |= QHAS_RET_PARAM; 97567963Seric if (strcasecmp(vp, "hdrs") == 0) 97667963Seric a->q_flags |= QRET_HDRS; 97767963Seric else if (strcasecmp(vp, "full") != 0) 97867963Seric { 97967963Seric usrerr("501 Bad argument \"%s\" to RET", vp); 98067963Seric /* NOTREACHED */ 98167963Seric } 98267963Seric } 98367963Seric else if (strcasecmp(kp, "orcpt") == 0) 98467963Seric { 98567963Seric if (vp == NULL) 98667963Seric { 98767963Seric usrerr("501 ORCPT requires a value"); 98867963Seric /* NOTREACHED */ 98967963Seric } 99067963Seric a->q_orcpt = newstr(vp); 99167963Seric } 99267963Seric else 99367963Seric { 99467963Seric usrerr("501 %s parameter unrecognized", kp); 99567963Seric /* NOTREACHED */ 99667963Seric } 99767963Seric } 99867963Seric /* 99958151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 100058151Seric ** 100158151Seric ** Parameters: 100258151Seric ** a -- the address to print 100358151Seric ** last -- set if this is the last one. 100458151Seric ** 100558151Seric ** Returns: 100658151Seric ** none. 100758151Seric ** 100858151Seric ** Side Effects: 100958151Seric ** Prints the appropriate 250 codes. 101058151Seric */ 101158151Seric 101258151Seric printvrfyaddr(a, last) 101358151Seric register ADDRESS *a; 101458151Seric bool last; 101558151Seric { 101658151Seric char fmtbuf[20]; 101758151Seric 101858151Seric strcpy(fmtbuf, "250"); 101958151Seric fmtbuf[3] = last ? ' ' : '-'; 102058151Seric 102159746Seric if (a->q_fullname == NULL) 102259746Seric { 102359746Seric if (strchr(a->q_user, '@') == NULL) 102459746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 102559746Seric else 102659746Seric strcpy(&fmtbuf[4], "<%s>"); 102759746Seric message(fmtbuf, a->q_user, MyHostName); 102859746Seric } 102958151Seric else 103058151Seric { 103159746Seric if (strchr(a->q_user, '@') == NULL) 103259746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 103359746Seric else 103459746Seric strcpy(&fmtbuf[4], "%s <%s>"); 103559746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 103658151Seric } 103758151Seric } 103858151Seric /* 10394577Seric ** HELP -- implement the HELP command. 10404577Seric ** 10414577Seric ** Parameters: 10424577Seric ** topic -- the topic we want help for. 10434577Seric ** 10444577Seric ** Returns: 10454577Seric ** none. 10464577Seric ** 10474577Seric ** Side Effects: 10484577Seric ** outputs the help file to message output. 10494577Seric */ 10504577Seric 10514577Seric help(topic) 10524577Seric char *topic; 10534577Seric { 10544577Seric register FILE *hf; 10554577Seric int len; 10564577Seric char buf[MAXLINE]; 10574577Seric bool noinfo; 10584577Seric 10598269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 10604577Seric { 10614577Seric /* no help */ 106211931Seric errno = 0; 106358151Seric message("502 HELP not implemented"); 10644577Seric return; 10654577Seric } 10664577Seric 106749669Seric if (topic == NULL || *topic == '\0') 106849669Seric topic = "smtp"; 106949669Seric else 107049669Seric makelower(topic); 107149669Seric 10724577Seric len = strlen(topic); 10734577Seric noinfo = TRUE; 10744577Seric 10754577Seric while (fgets(buf, sizeof buf, hf) != NULL) 10764577Seric { 10774577Seric if (strncmp(buf, topic, len) == 0) 10784577Seric { 10794577Seric register char *p; 10804577Seric 108156795Seric p = strchr(buf, '\t'); 10824577Seric if (p == NULL) 10834577Seric p = buf; 10844577Seric else 10854577Seric p++; 10864577Seric fixcrlf(p, TRUE); 108758151Seric message("214-%s", p); 10884577Seric noinfo = FALSE; 10894577Seric } 10904577Seric } 10914577Seric 10924577Seric if (noinfo) 109358151Seric message("504 HELP topic unknown"); 10944577Seric else 109558151Seric message("214 End of HELP info"); 10964628Seric (void) fclose(hf); 10974577Seric } 10988544Seric /* 10999339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11009339Seric ** 11019339Seric ** Parameters: 11029339Seric ** label -- a string used in error messages 11039339Seric ** 11049339Seric ** Returns: 11059339Seric ** zero in the child 11069339Seric ** one in the parent 11079339Seric ** 11089339Seric ** Side Effects: 11099339Seric ** none. 11109339Seric */ 11118544Seric 111255012Seric runinchild(label, e) 11139339Seric char *label; 111455012Seric register ENVELOPE *e; 11159339Seric { 11169339Seric int childpid; 11179339Seric 111816158Seric if (!OneXact) 11199339Seric { 112016158Seric childpid = dofork(); 112116158Seric if (childpid < 0) 112216158Seric { 112316158Seric syserr("%s: cannot fork", label); 112416158Seric return (1); 112516158Seric } 112616158Seric if (childpid > 0) 112716158Seric { 112816158Seric auto int st; 11299339Seric 113016158Seric /* parent -- wait for child to complete */ 113161093Seric setproctitle("server %s child wait", CurHostName); 113216158Seric st = waitfor(childpid); 113316158Seric if (st == -1) 113416158Seric syserr("%s: lost child", label); 113564948Seric else if (!WIFEXITED(st)) 113664948Seric syserr("%s: died on signal %d", 113764948Seric label, st & 0177); 11389339Seric 113916158Seric /* if we exited on a QUIT command, complete the process */ 114066017Seric if (WEXITSTATUS(st) == EX_QUIT) 114166017Seric { 114266017Seric disconnect(1, e); 114316158Seric finis(); 114466017Seric } 11459339Seric 114616158Seric return (1); 114716158Seric } 114816158Seric else 114916158Seric { 115016158Seric /* child */ 115116158Seric InChild = TRUE; 115225050Seric QuickAbort = FALSE; 115355012Seric clearenvelope(e, FALSE); 115416158Seric } 11559339Seric } 115615256Seric 115716158Seric /* open alias database */ 115860537Seric initmaps(FALSE, e); 115916158Seric 116016158Seric return (0); 11619339Seric } 11629339Seric 116356795Seric # endif /* SMTP */ 1164