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*68035Seric static char sccsid[] = "@(#)srvrsmtp.c 8.52 (Berkeley) 12/05/94 (with SMTP)"; 1433731Sbostic #else 15*68035Seric static char sccsid[] = "@(#)srvrsmtp.c 8.52 (Berkeley) 12/05/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 */ 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 31167963Seric message("250-X-DSN-1"); 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 } 46467417Seric e->e_bodytype = newstr(vp); 46559093Seric if (strcasecmp(vp, "8bitmime") == 0) 46659093Seric { 46767546Seric SevenBitInput = FALSE; 46859093Seric } 46959093Seric else if (strcasecmp(vp, "7bit") == 0) 47059093Seric { 47167546Seric SevenBitInput = TRUE; 47259093Seric } 47359093Seric else 47459093Seric { 47559093Seric usrerr("501 Unknown BODY type %s", 47659093Seric vp); 47767417Seric /* NOTREACHED */ 47859093Seric } 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 } 48967963Seric else if (strcasecmp(kp, "omts") == 0) 49067963Seric { 49167963Seric if (vp == NULL) 49267963Seric { 49367963Seric usrerr("501 OMTS requires a value"); 49467963Seric /* NOTREACHED */ 49567963Seric } 49667963Seric e->e_omts = newstr(vp); 49767963Seric } 49858333Seric else 49958333Seric { 50058333Seric usrerr("501 %s parameter unrecognized", kp); 50158333Seric /* NOTREACHED */ 50258333Seric } 50358333Seric } 50459284Seric 50559284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 50659284Seric { 50759284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 50859284Seric MaxMessageSize); 50959284Seric /* NOTREACHED */ 51059284Seric } 51158333Seric 51258333Seric if (!enoughspace(msize)) 51358333Seric { 51458333Seric message("452 Insufficient disk space; try again later"); 51558333Seric break; 51658333Seric } 51758151Seric message("250 Sender ok"); 51858147Seric gotmail = TRUE; 5194549Seric break; 5204549Seric 5214976Seric case CMDRCPT: /* rcpt -- designate recipient */ 52258850Seric if (!gotmail) 52358850Seric { 52458850Seric usrerr("503 Need MAIL before RCPT"); 52558850Seric break; 52658850Seric } 52761093Seric SmtpPhase = "server RCPT"; 52812612Seric if (setjmp(TopFrame) > 0) 52914785Seric { 53055012Seric e->e_flags &= ~EF_FATALERRS; 53112612Seric break; 53214785Seric } 53312612Seric QuickAbort = TRUE; 53451951Seric LogUsrErrs = TRUE; 53558093Seric 53659699Seric if (e->e_sendmode != SM_DELIVER) 53759699Seric e->e_flags |= EF_VRFYONLY; 53858919Seric 5394549Seric p = skipword(p, "to"); 5404549Seric if (p == NULL) 5414549Seric break; 54267880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 54312612Seric if (a == NULL) 54412612Seric break; 54567880Seric p = delimptr; 54667880Seric 54767880Seric /* now parse ESMTP arguments */ 54867880Seric while (p != NULL && *p != '\0') 54967880Seric { 55067880Seric char *kp; 55167880Seric char *vp = NULL; 55267880Seric 55367880Seric /* locate the beginning of the keyword */ 55467880Seric while (isascii(*p) && isspace(*p)) 55567880Seric p++; 55667880Seric if (*p == '\0') 55767880Seric break; 55867880Seric kp = p; 55967880Seric 56067880Seric /* skip to the value portion */ 56167880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 56267880Seric p++; 56367880Seric if (*p == '=') 56467880Seric { 56567880Seric *p++ = '\0'; 56667880Seric vp = p; 56767880Seric 56867880Seric /* skip to the end of the value */ 56967880Seric while (*p != '\0' && *p != ' ' && 57067880Seric !(isascii(*p) && iscntrl(*p)) && 57167880Seric *p != '=') 57267880Seric p++; 57367880Seric } 57467880Seric 57567880Seric if (*p != '\0') 57667880Seric *p++ = '\0'; 57767880Seric 57867880Seric if (tTd(19, 1)) 57967880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 58067880Seric vp == NULL ? "<null>" : vp); 58167880Seric 58267963Seric rcpt_esmtp_args(a, kp, vp, e); 58367963Seric 58467880Seric } 58567980Seric 58667980Seric /* save in recipient list after ESMTP mods */ 58767980Seric a->q_flags |= QPRIMARY; 58867982Seric a = recipient(a, &e->e_sendqueue, 0, e); 58967980Seric 59012612Seric if (Errors != 0) 59112612Seric break; 59212612Seric 59312612Seric /* no errors during parsing, but might be a duplicate */ 59455012Seric e->e_to = p; 59512612Seric if (!bitset(QBADADDR, a->q_flags)) 59659747Seric { 59764718Seric message("250 Recipient ok%s", 59864718Seric bitset(QQUEUEUP, a->q_flags) ? 59964718Seric " (will queue)" : ""); 60059747Seric nrcpts++; 60159747Seric } 60212612Seric else 6034549Seric { 60412612Seric /* punt -- should keep message in ADDRESS.... */ 60558151Seric message("550 Addressee unknown"); 6064549Seric } 60755012Seric e->e_to = NULL; 6084549Seric break; 6094549Seric 6104549Seric case CMDDATA: /* data -- text of mail */ 61161093Seric SmtpPhase = "server DATA"; 61258109Seric if (!gotmail) 6134549Seric { 61458151Seric message("503 Need MAIL command"); 6154976Seric break; 6164549Seric } 61764718Seric else if (nrcpts <= 0) 6184549Seric { 61958151Seric message("503 Need RCPT (recipient)"); 6204976Seric break; 6214549Seric } 6224976Seric 62358929Seric /* check to see if we need to re-expand aliases */ 62463787Seric /* also reset QBADADDR on already-diagnosted addrs */ 62563787Seric doublequeue = FALSE; 62658929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 62758929Seric { 62858929Seric if (bitset(QVERIFIED, a->q_flags)) 62963787Seric { 63063787Seric /* need to re-expand aliases */ 63163787Seric doublequeue = TRUE; 63263787Seric } 63363787Seric if (bitset(QBADADDR, a->q_flags)) 63463787Seric { 63563787Seric /* make this "go away" */ 63663787Seric a->q_flags |= QDONTSEND; 63763787Seric a->q_flags &= ~QBADADDR; 63863787Seric } 63958929Seric } 64058929Seric 6414976Seric /* collect the text of the message */ 64224943Seric SmtpPhase = "collect"; 64367546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 64464766Seric if (Errors != 0) 64564766Seric goto abortmessage; 64667131Seric 64767131Seric /* from now on, we have to operate silently */ 64863965Seric HoldErrs = TRUE; 64967131Seric e->e_errormode = EM_MAIL; 6504976Seric 6518238Seric /* 6528238Seric ** Arrange to send to everyone. 6538238Seric ** If sending to multiple people, mail back 6548238Seric ** errors rather than reporting directly. 6558238Seric ** In any case, don't mail back errors for 6568238Seric ** anything that has happened up to 6578238Seric ** now (the other end will do this). 65810197Seric ** Truncate our transcript -- the mail has gotten 65910197Seric ** to us successfully, and if we have 66010197Seric ** to mail this back, it will be easier 66110197Seric ** on the reader. 6628238Seric ** Then send to everyone. 6638238Seric ** Finally give a reply code. If an error has 6648238Seric ** already been given, don't mail a 6658238Seric ** message back. 6669339Seric ** We goose error returns by clearing error bit. 6678238Seric */ 6688238Seric 66924943Seric SmtpPhase = "delivery"; 67055012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 67158714Seric id = e->e_id; 6724976Seric 67367131Seric if (doublequeue) 67459730Seric { 67567131Seric /* make sure it is in the queue */ 67667131Seric queueup(e, TRUE, FALSE); 677*68035Seric if (e->e_sendmode == SM_QUEUE) 678*68035Seric e->e_flags |= EF_KEEPQUEUE; 67959730Seric } 6808238Seric else 68158919Seric { 68267131Seric /* send to all recipients */ 68367131Seric sendall(e, SM_DEFAULT); 68467131Seric } 68567131Seric e->e_to = NULL; 68659747Seric 68767131Seric /* issue success message */ 68867131Seric message("250 %s Message accepted for delivery", id); 68964296Seric 69067131Seric /* if we just queued, poke it */ 69167131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 69267131Seric { 69367131Seric extern pid_t dowork(); 69467131Seric 69567131Seric unlockqueue(e); 69667131Seric (void) dowork(id, TRUE, TRUE, e); 69758919Seric } 69858883Seric 69959747Seric abortmessage: 7009339Seric /* if in a child, pop back to our parent */ 7019339Seric if (InChild) 7029339Seric finis(); 70324943Seric 70424943Seric /* clean up a bit */ 70558109Seric gotmail = FALSE; 70655012Seric dropenvelope(e); 70758179Seric CurEnv = e = newenvelope(e, CurEnv); 70855012Seric e->e_flags = BlankEnvelope.e_flags; 7094549Seric break; 7104549Seric 7114549Seric case CMDRSET: /* rset -- reset state */ 71258151Seric message("250 Reset state"); 71364359Seric e->e_flags |= EF_CLRQUEUE; 7149339Seric if (InChild) 7159339Seric finis(); 71658109Seric 71758109Seric /* clean up a bit */ 71858109Seric gotmail = FALSE; 71958109Seric dropenvelope(e); 72058179Seric CurEnv = e = newenvelope(e, CurEnv); 7219339Seric break; 7224549Seric 7234549Seric case CMDVRFY: /* vrfy -- verify address */ 72458092Seric case CMDEXPN: /* expn -- expand address */ 72558092Seric vrfy = c->cmdcode == CMDVRFY; 72658092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 72758092Seric PrivacyFlags)) 72858082Seric { 72958412Seric if (vrfy) 73067160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 73158412Seric else 73265192Seric message("502 Sorry, we do not allow this operation"); 73365017Seric #ifdef LOG 73465017Seric if (LogLevel > 5) 73565017Seric syslog(LOG_INFO, "%s: %s [rejected]", 73665017Seric CurSmtpClient, inp); 73765017Seric #endif 73858082Seric break; 73958082Seric } 74058082Seric else if (!gothello && 74158092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 74258092Seric PrivacyFlags)) 74358082Seric { 74458151Seric message("503 I demand that you introduce yourself first"); 74558082Seric break; 74658082Seric } 74758092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7489339Seric break; 74955173Seric #ifdef LOG 75058020Seric if (LogLevel > 5) 75165017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 75255173Seric #endif 7535003Seric vrfyqueue = NULL; 7547762Seric QuickAbort = TRUE; 75558092Seric if (vrfy) 75658092Seric e->e_flags |= EF_VRFYONLY; 75762373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 75867615Seric p++; 75962373Seric if (*p == '\0') 76062373Seric { 76162373Seric message("501 Argument required"); 76262373Seric Errors++; 76362373Seric } 76462373Seric else 76562373Seric { 76667990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 76762373Seric } 7687762Seric if (Errors != 0) 7699339Seric { 7709339Seric if (InChild) 7719339Seric finis(); 7727762Seric break; 7739339Seric } 77462373Seric if (vrfyqueue == NULL) 77562373Seric { 77662373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 77762373Seric } 7785003Seric while (vrfyqueue != NULL) 7795003Seric { 78063971Seric a = vrfyqueue; 78163971Seric while ((a = a->q_next) != NULL && 78263971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 78363971Seric continue; 7847685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 78558151Seric printvrfyaddr(vrfyqueue, a == NULL); 78663847Seric vrfyqueue = vrfyqueue->q_next; 7875003Seric } 7889339Seric if (InChild) 7899339Seric finis(); 7904549Seric break; 7914549Seric 7924549Seric case CMDHELP: /* help -- give user info */ 7934577Seric help(p); 7944549Seric break; 7954549Seric 7964549Seric case CMDNOOP: /* noop -- do nothing */ 79764122Seric message("250 OK"); 7984549Seric break; 7994549Seric 8004549Seric case CMDQUIT: /* quit -- leave mail */ 80158151Seric message("221 %s closing connection", MyHostName); 80261051Seric 80366283Seric doquit: 80461051Seric /* avoid future 050 messages */ 80566017Seric disconnect(1, e); 80661051Seric 8079339Seric if (InChild) 8089339Seric ExitStat = EX_QUIT; 8094549Seric finis(); 8104549Seric 8118544Seric case CMDVERB: /* set verbose mode */ 81259957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 81359957Seric { 81459957Seric /* this would give out the same info */ 81559957Seric message("502 Verbose unavailable"); 81659957Seric break; 81759957Seric } 8188544Seric Verbose = TRUE; 81958734Seric e->e_sendmode = SM_DELIVER; 82059957Seric message("250 Verbose mode"); 8218544Seric break; 8228544Seric 8239314Seric case CMDONEX: /* doing one transaction only */ 8249378Seric OneXact = TRUE; 82559957Seric message("250 Only one transaction"); 8269314Seric break; 8279314Seric 82836230Skarels # ifdef SMTPDEBUG 8299339Seric case CMDDBGQSHOW: /* show queues */ 8306907Seric printf("Send Queue="); 83155012Seric printaddr(e->e_sendqueue, TRUE); 8325003Seric break; 8337275Seric 8347275Seric case CMDDBGDEBUG: /* set debug mode */ 8357676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 8367676Seric tTflag(p); 83758151Seric message("200 Debug set"); 8387275Seric break; 8397275Seric 84036230Skarels # else /* not SMTPDEBUG */ 84136230Skarels case CMDDBGQSHOW: /* show queues */ 84236230Skarels case CMDDBGDEBUG: /* set debug mode */ 84364685Seric # endif /* SMTPDEBUG */ 84464685Seric case CMDLOGBOGUS: /* bogus command */ 84536233Skarels # ifdef LOG 84658308Seric if (LogLevel > 0) 84764685Seric syslog(LOG_CRIT, 84858020Seric "\"%s\" command from %s (%s)", 84966005Seric c->cmdname, peerhostname, 85058755Seric anynet_ntoa(&RealHostAddr)); 85136233Skarels # endif 85236230Skarels /* FALL THROUGH */ 85336230Skarels 8544549Seric case CMDERROR: /* unknown command */ 85566283Seric if (++badcommands > MAXBADCOMMANDS) 85666283Seric { 85766283Seric message("421 %s Too many bad commands; closing connection", 85866283Seric MyHostName); 85966283Seric goto doquit; 86066283Seric } 86166283Seric 86258151Seric message("500 Command unrecognized"); 8634549Seric break; 8644549Seric 8654549Seric default: 86636230Skarels errno = 0; 86758151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8684549Seric break; 8694549Seric } 8704549Seric } 8714549Seric } 8724549Seric /* 8734549Seric ** SKIPWORD -- skip a fixed word. 8744549Seric ** 8754549Seric ** Parameters: 8764549Seric ** p -- place to start looking. 8774549Seric ** w -- word to skip. 8784549Seric ** 8794549Seric ** Returns: 8804549Seric ** p following w. 8814549Seric ** NULL on error. 8824549Seric ** 8834549Seric ** Side Effects: 8844549Seric ** clobbers the p data area. 8854549Seric */ 8864549Seric 8874549Seric static char * 8884549Seric skipword(p, w) 8894549Seric register char *p; 8904549Seric char *w; 8914549Seric { 8924549Seric register char *q; 89366005Seric char *firstp = p; 8944549Seric 8954549Seric /* find beginning of word */ 89658050Seric while (isascii(*p) && isspace(*p)) 8974549Seric p++; 8984549Seric q = p; 8994549Seric 9004549Seric /* find end of word */ 90158050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 9024549Seric p++; 90358050Seric while (isascii(*p) && isspace(*p)) 9044549Seric *p++ = '\0'; 9054549Seric if (*p != ':') 9064549Seric { 9074549Seric syntax: 90866005Seric message("501 Syntax error in parameters scanning \"%s\"", 90966005Seric firstp); 9104549Seric Errors++; 9114549Seric return (NULL); 9124549Seric } 9134549Seric *p++ = '\0'; 91458050Seric while (isascii(*p) && isspace(*p)) 9154549Seric p++; 9164549Seric 91762373Seric if (*p == '\0') 91862373Seric goto syntax; 91962373Seric 9204549Seric /* see if the input word matches desired word */ 92133725Sbostic if (strcasecmp(q, w)) 9224549Seric goto syntax; 9234549Seric 9244549Seric return (p); 9254549Seric } 9264577Seric /* 92767963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 92867963Seric ** 92967963Seric ** Parameters: 93067963Seric ** a -- the address corresponding to the To: parameter. 93167963Seric ** kp -- the parameter key. 93267963Seric ** vp -- the value of that parameter. 93367963Seric ** e -- the envelope. 93467963Seric ** 93567963Seric ** Returns: 93667963Seric ** none. 93767963Seric */ 93867963Seric 93967963Seric rcpt_esmtp_args(a, kp, vp, e) 94067963Seric ADDRESS *a; 94167963Seric char *kp; 94267963Seric char *vp; 94367963Seric ENVELOPE *e; 94467963Seric { 94567963Seric if (strcasecmp(kp, "notify") == 0) 94667963Seric { 94767963Seric char *p; 94867963Seric 94967963Seric if (vp == NULL) 95067963Seric { 95167963Seric usrerr("501 NOTIFY requires a value"); 95267963Seric /* NOTREACHED */ 95367963Seric } 95467963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 95567963Seric if (strcasecmp(vp, "never") == 0) 95667963Seric return; 95767963Seric for (p = vp; p != NULL; vp = p) 95867963Seric { 95967963Seric p = strchr(p, ','); 96067963Seric if (p != NULL) 96167963Seric *p++ = '\0'; 96267963Seric if (strcasecmp(vp, "success") == 0) 96367963Seric a->q_flags |= QPINGONSUCCESS; 96467963Seric else if (strcasecmp(vp, "failure") == 0) 96567963Seric a->q_flags |= QPINGONFAILURE; 96667963Seric else if (strcasecmp(vp, "delay") == 0) 96767963Seric a->q_flags |= QPINGONDELAY; 96867963Seric else 96967963Seric { 97067963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 97167963Seric vp); 97267963Seric /* NOTREACHED */ 97367963Seric } 97467963Seric } 97567963Seric } 97667963Seric else if (strcasecmp(kp, "ret") == 0) 97767963Seric { 97867963Seric if (vp == NULL) 97967963Seric { 98067963Seric usrerr("501 RET requires a value"); 98167963Seric /* NOTREACHED */ 98267963Seric } 98367963Seric a->q_flags |= QHAS_RET_PARAM; 98467963Seric if (strcasecmp(vp, "hdrs") == 0) 98567963Seric a->q_flags |= QRET_HDRS; 98667963Seric else if (strcasecmp(vp, "full") != 0) 98767963Seric { 98867963Seric usrerr("501 Bad argument \"%s\" to RET", vp); 98967963Seric /* NOTREACHED */ 99067963Seric } 99167963Seric } 99267963Seric else if (strcasecmp(kp, "orcpt") == 0) 99367963Seric { 99467963Seric if (vp == NULL) 99567963Seric { 99667963Seric usrerr("501 ORCPT requires a value"); 99767963Seric /* NOTREACHED */ 99867963Seric } 99967963Seric a->q_orcpt = newstr(vp); 100067963Seric } 100167963Seric else 100267963Seric { 100367963Seric usrerr("501 %s parameter unrecognized", kp); 100467963Seric /* NOTREACHED */ 100567963Seric } 100667963Seric } 100767963Seric /* 100858151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 100958151Seric ** 101058151Seric ** Parameters: 101158151Seric ** a -- the address to print 101258151Seric ** last -- set if this is the last one. 101358151Seric ** 101458151Seric ** Returns: 101558151Seric ** none. 101658151Seric ** 101758151Seric ** Side Effects: 101858151Seric ** Prints the appropriate 250 codes. 101958151Seric */ 102058151Seric 102158151Seric printvrfyaddr(a, last) 102258151Seric register ADDRESS *a; 102358151Seric bool last; 102458151Seric { 102558151Seric char fmtbuf[20]; 102658151Seric 102758151Seric strcpy(fmtbuf, "250"); 102858151Seric fmtbuf[3] = last ? ' ' : '-'; 102958151Seric 103059746Seric if (a->q_fullname == NULL) 103159746Seric { 103259746Seric if (strchr(a->q_user, '@') == NULL) 103359746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 103459746Seric else 103559746Seric strcpy(&fmtbuf[4], "<%s>"); 103659746Seric message(fmtbuf, a->q_user, MyHostName); 103759746Seric } 103858151Seric else 103958151Seric { 104059746Seric if (strchr(a->q_user, '@') == NULL) 104159746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 104259746Seric else 104359746Seric strcpy(&fmtbuf[4], "%s <%s>"); 104459746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 104558151Seric } 104658151Seric } 104758151Seric /* 10484577Seric ** HELP -- implement the HELP command. 10494577Seric ** 10504577Seric ** Parameters: 10514577Seric ** topic -- the topic we want help for. 10524577Seric ** 10534577Seric ** Returns: 10544577Seric ** none. 10554577Seric ** 10564577Seric ** Side Effects: 10574577Seric ** outputs the help file to message output. 10584577Seric */ 10594577Seric 10604577Seric help(topic) 10614577Seric char *topic; 10624577Seric { 10634577Seric register FILE *hf; 10644577Seric int len; 10654577Seric char buf[MAXLINE]; 10664577Seric bool noinfo; 10674577Seric 10688269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 10694577Seric { 10704577Seric /* no help */ 107111931Seric errno = 0; 107258151Seric message("502 HELP not implemented"); 10734577Seric return; 10744577Seric } 10754577Seric 107649669Seric if (topic == NULL || *topic == '\0') 107749669Seric topic = "smtp"; 107849669Seric else 107949669Seric makelower(topic); 108049669Seric 10814577Seric len = strlen(topic); 10824577Seric noinfo = TRUE; 10834577Seric 10844577Seric while (fgets(buf, sizeof buf, hf) != NULL) 10854577Seric { 10864577Seric if (strncmp(buf, topic, len) == 0) 10874577Seric { 10884577Seric register char *p; 10894577Seric 109056795Seric p = strchr(buf, '\t'); 10914577Seric if (p == NULL) 10924577Seric p = buf; 10934577Seric else 10944577Seric p++; 10954577Seric fixcrlf(p, TRUE); 109658151Seric message("214-%s", p); 10974577Seric noinfo = FALSE; 10984577Seric } 10994577Seric } 11004577Seric 11014577Seric if (noinfo) 110258151Seric message("504 HELP topic unknown"); 11034577Seric else 110458151Seric message("214 End of HELP info"); 11054628Seric (void) fclose(hf); 11064577Seric } 11078544Seric /* 11089339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11099339Seric ** 11109339Seric ** Parameters: 11119339Seric ** label -- a string used in error messages 11129339Seric ** 11139339Seric ** Returns: 11149339Seric ** zero in the child 11159339Seric ** one in the parent 11169339Seric ** 11179339Seric ** Side Effects: 11189339Seric ** none. 11199339Seric */ 11208544Seric 112155012Seric runinchild(label, e) 11229339Seric char *label; 112355012Seric register ENVELOPE *e; 11249339Seric { 11259339Seric int childpid; 11269339Seric 112716158Seric if (!OneXact) 11289339Seric { 112916158Seric childpid = dofork(); 113016158Seric if (childpid < 0) 113116158Seric { 113216158Seric syserr("%s: cannot fork", label); 113316158Seric return (1); 113416158Seric } 113516158Seric if (childpid > 0) 113616158Seric { 113716158Seric auto int st; 11389339Seric 113916158Seric /* parent -- wait for child to complete */ 114061093Seric setproctitle("server %s child wait", CurHostName); 114116158Seric st = waitfor(childpid); 114216158Seric if (st == -1) 114316158Seric syserr("%s: lost child", label); 114464948Seric else if (!WIFEXITED(st)) 114564948Seric syserr("%s: died on signal %d", 114664948Seric label, st & 0177); 11479339Seric 114816158Seric /* if we exited on a QUIT command, complete the process */ 114966017Seric if (WEXITSTATUS(st) == EX_QUIT) 115066017Seric { 115166017Seric disconnect(1, e); 115216158Seric finis(); 115366017Seric } 11549339Seric 115516158Seric return (1); 115616158Seric } 115716158Seric else 115816158Seric { 115916158Seric /* child */ 116016158Seric InChild = TRUE; 116125050Seric QuickAbort = FALSE; 116255012Seric clearenvelope(e, FALSE); 116316158Seric } 11649339Seric } 116515256Seric 116616158Seric /* open alias database */ 116760537Seric initmaps(FALSE, e); 116816158Seric 116916158Seric return (0); 11709339Seric } 11719339Seric 117256795Seric # endif /* SMTP */ 1173