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*68769Seric static char sccsid[] = "@(#)srvrsmtp.c 8.66 (Berkeley) 04/10/95 (with SMTP)"; 1433731Sbostic #else 15*68769Seric static char sccsid[] = "@(#)srvrsmtp.c 8.66 (Berkeley) 04/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 */ 11266005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11358333Seric auto char *delimptr; 11458714Seric char *id; 11568433Seric int nrcpts = 0; /* number of RCPT commands */ 11663787Seric bool doublequeue; 11766283Seric int badcommands = 0; /* count of bad commands */ 1188544Seric char inp[MAXLINE]; 11957232Seric char cmdbuf[MAXLINE]; 12024943Seric extern ENVELOPE BlankEnvelope; 1214549Seric 12259066Seric if (fileno(OutChannel) != fileno(stdout)) 1237363Seric { 1247363Seric /* arrange for debugging output to go to remote host */ 12559066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1267363Seric } 12755012Seric settime(e); 12866005Seric peerhostname = RealHostName; 12966005Seric if (peerhostname == NULL) 13066005Seric peerhostname = "localhost"; 13166005Seric CurHostName = peerhostname; 13265017Seric CurSmtpClient = macvalue('_', e); 13365017Seric if (CurSmtpClient == NULL) 13466003Seric CurSmtpClient = CurHostName; 13565017Seric 13665017Seric setproctitle("server %s startup", CurSmtpClient); 13768529Seric expand("\201e", inp, sizeof inp, e); 138*68769Seric 139*68769Seric /* output the first line, inserting "ESMTP" as second word */ 140*68769Seric p = strchr(inp, '\n'); 141*68769Seric if (p != NULL) 142*68769Seric *p++ = '\0'; 143*68769Seric id = strchr(inp, ' '); 144*68769Seric if (id == NULL) 145*68769Seric id = &inp[strlen(inp)]; 146*68769Seric cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; 147*68769Seric message(cmd, id - inp, inp, id); 148*68769Seric 149*68769Seric /* output remaining lines */ 150*68769Seric while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) 15164496Seric { 152*68769Seric *p++ = '\0'; 153*68769Seric message("220-%s", id); 15464496Seric } 155*68769Seric if (id != NULL) 156*68769Seric message("220 %s", id); 15766745Seric 15858330Seric protocol = NULL; 15959016Seric sendinghost = macvalue('s', e); 16058082Seric gothello = FALSE; 16158330Seric gotmail = FALSE; 1624549Seric for (;;) 1634549Seric { 16412612Seric /* arrange for backout */ 16565751Seric if (setjmp(TopFrame) > 0) 16659058Seric { 16765751Seric /* if() nesting is necessary for Cray UNICOS */ 16865751Seric if (InChild) 16965751Seric { 17065751Seric QuickAbort = FALSE; 17165751Seric SuprErrs = TRUE; 17265751Seric finis(); 17365751Seric } 17459058Seric } 17512612Seric QuickAbort = FALSE; 17612612Seric HoldErrs = FALSE; 17751951Seric LogUsrErrs = FALSE; 17863843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 17912612Seric 1807356Seric /* setup for the read */ 18155012Seric e->e_to = NULL; 1824577Seric Errors = 0; 1837275Seric (void) fflush(stdout); 1847356Seric 1857356Seric /* read the input line */ 18661093Seric SmtpPhase = "server cmd read"; 18761093Seric setproctitle("server %s cmd read", CurHostName); 18861093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 18961093Seric SmtpPhase); 1907356Seric 1917685Seric /* handle errors */ 1927356Seric if (p == NULL) 1937356Seric { 1944549Seric /* end of file, just die */ 19566017Seric disconnect(1, e); 19658151Seric message("421 %s Lost input channel from %s", 19765017Seric MyHostName, CurSmtpClient); 19855464Seric #ifdef LOG 19963843Seric if (LogLevel > (gotmail ? 1 : 19)) 20055464Seric syslog(LOG_NOTICE, "lost input channel from %s", 20165017Seric CurSmtpClient); 20255464Seric #endif 20358069Seric if (InChild) 20458069Seric ExitStat = EX_QUIT; 2054549Seric finis(); 2064549Seric } 2074549Seric 2084549Seric /* clean up end of line */ 2094558Seric fixcrlf(inp, TRUE); 2104549Seric 2114713Seric /* echo command to transcript */ 21255012Seric if (e->e_xfp != NULL) 21355012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2144713Seric 21559060Seric if (e->e_id == NULL) 21665058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 21759060Seric else 21865058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 21959060Seric 2204549Seric /* break off command */ 22158050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2224549Seric continue; 22357232Seric cmd = cmdbuf; 22458050Seric while (*p != '\0' && 22558050Seric !(isascii(*p) && isspace(*p)) && 22658050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 22724981Seric *cmd++ = *p++; 22824981Seric *cmd = '\0'; 2294549Seric 23025691Seric /* throw away leading whitespace */ 23158050Seric while (isascii(*p) && isspace(*p)) 23225691Seric p++; 23325691Seric 2344549Seric /* decode command */ 2354549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2364549Seric { 23733725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2384549Seric break; 2394549Seric } 2404549Seric 24151954Seric /* reset errors */ 24251954Seric errno = 0; 24351954Seric 2444549Seric /* process command */ 2454549Seric switch (c->cmdcode) 2464549Seric { 2474976Seric case CMDHELO: /* hello -- introduce yourself */ 24858323Seric case CMDEHLO: /* extended hello */ 24958323Seric if (c->cmdcode == CMDEHLO) 25058323Seric { 25158323Seric protocol = "ESMTP"; 25261093Seric SmtpPhase = "server EHLO"; 25358323Seric } 25458323Seric else 25558323Seric { 25658323Seric protocol = "SMTP"; 25761093Seric SmtpPhase = "server HELO"; 25858323Seric } 25967445Seric 26067445Seric /* check for valid domain name (re 1123 5.2.5) */ 26167445Seric if (*p == '\0') 26267445Seric { 26367445Seric message("501 %s requires domain address", 26467445Seric cmdbuf); 26567445Seric break; 26667445Seric } 26767445Seric else 26867445Seric { 26967445Seric register char *q; 27067445Seric 27167445Seric for (q = p; *q != '\0'; q++) 27267445Seric { 27367445Seric if (!isascii(*q)) 27467445Seric break; 27567445Seric if (isalnum(*q)) 27667445Seric continue; 27767445Seric if (strchr("[].-_#", *q) == NULL) 27867445Seric break; 27967445Seric } 28067445Seric if (*q != '\0') 28167445Seric { 28267445Seric message("501 Invalid domain name"); 28367445Seric break; 28467445Seric } 28567445Seric } 28667445Seric 28759016Seric sendinghost = newstr(p); 28860210Seric gothello = TRUE; 28960210Seric if (c->cmdcode != CMDEHLO) 29060239Seric { 29160239Seric /* print old message and be done with it */ 29260239Seric message("250 %s Hello %s, pleased to meet you", 29365017Seric MyHostName, CurSmtpClient); 29460210Seric break; 29560239Seric } 29660239Seric 29760239Seric /* print extended message and brag */ 29860239Seric message("250-%s Hello %s, pleased to meet you", 29966760Seric MyHostName, CurSmtpClient); 30058323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 30158323Seric message("250-EXPN"); 30267417Seric message("250-8BITMIME"); 30364359Seric if (MaxMessageSize > 0) 30464359Seric message("250-SIZE %ld", MaxMessageSize); 30559271Seric else 30659271Seric message("250-SIZE"); 30768028Seric #ifdef DSN 30868606Seric message("250-X-DSN-03 (Unpublished draft of 12 Mar 1995)"); 30968028Seric #endif 31058323Seric message("250 HELP"); 3114976Seric break; 3124976Seric 3134549Seric case CMDMAIL: /* mail -- designate sender */ 31461093Seric SmtpPhase = "server MAIL"; 31524943Seric 3169314Seric /* check for validity of this command */ 31758789Seric if (!gothello) 31858082Seric { 31958957Seric /* set sending host to our known value */ 32059016Seric if (sendinghost == NULL) 32166005Seric sendinghost = peerhostname; 32258957Seric 32358789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 32458821Seric { 32558789Seric message("503 Polite people say HELO first"); 32658821Seric break; 32758821Seric } 32858082Seric } 32958109Seric if (gotmail) 3304558Seric { 33158151Seric message("503 Sender already specified"); 33263843Seric if (InChild) 33363843Seric finis(); 3344558Seric break; 3354558Seric } 3369339Seric if (InChild) 3379339Seric { 33836230Skarels errno = 0; 33958151Seric syserr("503 Nested MAIL command: MAIL %s", p); 34058069Seric finis(); 3419339Seric } 3429339Seric 3439339Seric /* fork a subprocess to process this command */ 34455012Seric if (runinchild("SMTP-MAIL", e) > 0) 3459339Seric break; 34663753Seric if (!gothello) 34763753Seric { 34863753Seric auth_warning(e, 34966005Seric "Host %s didn't use HELO protocol", 35066005Seric peerhostname); 35163753Seric } 35265947Seric #ifdef PICKY_HELO_CHECK 35366005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 35466005Seric (strcasecmp(peerhostname, "localhost") != 0 || 35565823Seric strcasecmp(sendinghost, MyHostName) != 0)) 35665823Seric { 35765823Seric auth_warning(e, "Host %s claimed to be %s", 35866005Seric peerhostname, sendinghost); 35965823Seric } 36065947Seric #endif 36165823Seric 36258323Seric if (protocol == NULL) 36358323Seric protocol = "SMTP"; 36458323Seric define('r', protocol, e); 36559016Seric define('s', sendinghost, e); 36655012Seric initsys(e); 36759747Seric nrcpts = 0; 36868582Seric e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; 36965058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3709339Seric 3719339Seric /* child -- go do the processing */ 3724549Seric p = skipword(p, "from"); 3734549Seric if (p == NULL) 3744549Seric break; 37557977Seric if (setjmp(TopFrame) > 0) 37658147Seric { 37758147Seric /* this failed -- undo work */ 37858147Seric if (InChild) 37959058Seric { 38059058Seric QuickAbort = FALSE; 38159058Seric SuprErrs = TRUE; 38263787Seric e->e_flags &= ~EF_FATALERRS; 38358147Seric finis(); 38459058Seric } 38557977Seric break; 38658147Seric } 38757977Seric QuickAbort = TRUE; 38858333Seric 38958333Seric /* must parse sender first */ 39058333Seric delimptr = NULL; 39158704Seric setsender(p, e, &delimptr, FALSE); 39258333Seric p = delimptr; 39358333Seric if (p != NULL && *p != '\0') 39458333Seric *p++ = '\0'; 39558333Seric 39666325Seric /* check for possible spoofing */ 39766325Seric if (RealUid != 0 && OpMode == MD_SMTP && 39867473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 39967473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 40066325Seric { 40166325Seric auth_warning(e, "%s owned process doing -bs", 40266325Seric RealUserName); 40366325Seric } 40466325Seric 40558333Seric /* now parse ESMTP arguments */ 40668560Seric e->e_msgsize = 0; 40766764Seric while (p != NULL && *p != '\0') 40858333Seric { 40958333Seric char *kp; 41066304Seric char *vp = NULL; 41158333Seric 41258333Seric /* locate the beginning of the keyword */ 41358333Seric while (isascii(*p) && isspace(*p)) 41458333Seric p++; 41558333Seric if (*p == '\0') 41658333Seric break; 41758333Seric kp = p; 41858333Seric 41958333Seric /* skip to the value portion */ 42058333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 42158333Seric p++; 42258333Seric if (*p == '=') 42358333Seric { 42458333Seric *p++ = '\0'; 42558333Seric vp = p; 42658333Seric 42758333Seric /* skip to the end of the value */ 42858333Seric while (*p != '\0' && *p != ' ' && 42958333Seric !(isascii(*p) && iscntrl(*p)) && 43058333Seric *p != '=') 43158333Seric p++; 43258333Seric } 43358333Seric 43458333Seric if (*p != '\0') 43558333Seric *p++ = '\0'; 43658333Seric 43758333Seric if (tTd(19, 1)) 43866764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 43958333Seric vp == NULL ? "<null>" : vp); 44058333Seric 44168559Seric mail_esmtp_args(kp, vp, e); 44258333Seric } 44359284Seric 44468560Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 44559284Seric { 44659284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 44759284Seric MaxMessageSize); 44859284Seric /* NOTREACHED */ 44959284Seric } 45058333Seric 45168560Seric if (!enoughspace(e->e_msgsize)) 45258333Seric { 45358333Seric message("452 Insufficient disk space; try again later"); 45458333Seric break; 45558333Seric } 45658151Seric message("250 Sender ok"); 45758147Seric gotmail = TRUE; 4584549Seric break; 4594549Seric 4604976Seric case CMDRCPT: /* rcpt -- designate recipient */ 46158850Seric if (!gotmail) 46258850Seric { 46358850Seric usrerr("503 Need MAIL before RCPT"); 46458850Seric break; 46558850Seric } 46661093Seric SmtpPhase = "server RCPT"; 46712612Seric if (setjmp(TopFrame) > 0) 46814785Seric { 46955012Seric e->e_flags &= ~EF_FATALERRS; 47012612Seric break; 47114785Seric } 47212612Seric QuickAbort = TRUE; 47351951Seric LogUsrErrs = TRUE; 47458093Seric 47559699Seric if (e->e_sendmode != SM_DELIVER) 47659699Seric e->e_flags |= EF_VRFYONLY; 47758919Seric 4784549Seric p = skipword(p, "to"); 4794549Seric if (p == NULL) 4804549Seric break; 48167880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 48212612Seric if (a == NULL) 48312612Seric break; 48467880Seric p = delimptr; 48567880Seric 48667880Seric /* now parse ESMTP arguments */ 48767880Seric while (p != NULL && *p != '\0') 48867880Seric { 48967880Seric char *kp; 49067880Seric char *vp = NULL; 49167880Seric 49267880Seric /* locate the beginning of the keyword */ 49367880Seric while (isascii(*p) && isspace(*p)) 49467880Seric p++; 49567880Seric if (*p == '\0') 49667880Seric break; 49767880Seric kp = p; 49867880Seric 49967880Seric /* skip to the value portion */ 50067880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 50167880Seric p++; 50267880Seric if (*p == '=') 50367880Seric { 50467880Seric *p++ = '\0'; 50567880Seric vp = p; 50667880Seric 50767880Seric /* skip to the end of the value */ 50867880Seric while (*p != '\0' && *p != ' ' && 50967880Seric !(isascii(*p) && iscntrl(*p)) && 51067880Seric *p != '=') 51167880Seric p++; 51267880Seric } 51367880Seric 51467880Seric if (*p != '\0') 51567880Seric *p++ = '\0'; 51667880Seric 51767880Seric if (tTd(19, 1)) 51867880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 51967880Seric vp == NULL ? "<null>" : vp); 52067880Seric 52167963Seric rcpt_esmtp_args(a, kp, vp, e); 52267963Seric 52367880Seric } 52467980Seric 52567980Seric /* save in recipient list after ESMTP mods */ 52667982Seric a = recipient(a, &e->e_sendqueue, 0, e); 52767980Seric 52812612Seric if (Errors != 0) 52912612Seric break; 53012612Seric 53112612Seric /* no errors during parsing, but might be a duplicate */ 53255012Seric e->e_to = p; 53312612Seric if (!bitset(QBADADDR, a->q_flags)) 53459747Seric { 53564718Seric message("250 Recipient ok%s", 53664718Seric bitset(QQUEUEUP, a->q_flags) ? 53764718Seric " (will queue)" : ""); 53859747Seric nrcpts++; 53959747Seric } 54012612Seric else 5414549Seric { 54212612Seric /* punt -- should keep message in ADDRESS.... */ 54358151Seric message("550 Addressee unknown"); 5444549Seric } 54555012Seric e->e_to = NULL; 5464549Seric break; 5474549Seric 5484549Seric case CMDDATA: /* data -- text of mail */ 54961093Seric SmtpPhase = "server DATA"; 55058109Seric if (!gotmail) 5514549Seric { 55258151Seric message("503 Need MAIL command"); 5534976Seric break; 5544549Seric } 55564718Seric else if (nrcpts <= 0) 5564549Seric { 55758151Seric message("503 Need RCPT (recipient)"); 5584976Seric break; 5594549Seric } 5604976Seric 56158929Seric /* check to see if we need to re-expand aliases */ 56263787Seric /* also reset QBADADDR on already-diagnosted addrs */ 56363787Seric doublequeue = FALSE; 56458929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 56558929Seric { 56658929Seric if (bitset(QVERIFIED, a->q_flags)) 56763787Seric { 56863787Seric /* need to re-expand aliases */ 56963787Seric doublequeue = TRUE; 57063787Seric } 57163787Seric if (bitset(QBADADDR, a->q_flags)) 57263787Seric { 57363787Seric /* make this "go away" */ 57463787Seric a->q_flags |= QDONTSEND; 57563787Seric a->q_flags &= ~QBADADDR; 57663787Seric } 57758929Seric } 57858929Seric 5794976Seric /* collect the text of the message */ 58024943Seric SmtpPhase = "collect"; 58168692Seric buffer_errors(); 58267546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 58368692Seric flush_errors(TRUE); 58464766Seric if (Errors != 0) 58564766Seric goto abortmessage; 58667131Seric 58768582Seric /* make sure we actually do delivery */ 58868582Seric e->e_flags &= ~EF_CLRQUEUE; 58968582Seric 59067131Seric /* from now on, we have to operate silently */ 59168692Seric buffer_errors(); 59267131Seric e->e_errormode = EM_MAIL; 5934976Seric 5948238Seric /* 5958238Seric ** Arrange to send to everyone. 5968238Seric ** If sending to multiple people, mail back 5978238Seric ** errors rather than reporting directly. 5988238Seric ** In any case, don't mail back errors for 5998238Seric ** anything that has happened up to 6008238Seric ** now (the other end will do this). 60110197Seric ** Truncate our transcript -- the mail has gotten 60210197Seric ** to us successfully, and if we have 60310197Seric ** to mail this back, it will be easier 60410197Seric ** on the reader. 6058238Seric ** Then send to everyone. 6068238Seric ** Finally give a reply code. If an error has 6078238Seric ** already been given, don't mail a 6088238Seric ** message back. 6099339Seric ** We goose error returns by clearing error bit. 6108238Seric */ 6118238Seric 61224943Seric SmtpPhase = "delivery"; 61355012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 61458714Seric id = e->e_id; 6154976Seric 61667131Seric if (doublequeue) 61759730Seric { 61867131Seric /* make sure it is in the queue */ 61967131Seric queueup(e, TRUE, FALSE); 62068035Seric if (e->e_sendmode == SM_QUEUE) 62168035Seric e->e_flags |= EF_KEEPQUEUE; 62259730Seric } 6238238Seric else 62458919Seric { 62567131Seric /* send to all recipients */ 62667131Seric sendall(e, SM_DEFAULT); 62767131Seric } 62867131Seric e->e_to = NULL; 62959747Seric 63067131Seric /* issue success message */ 63167131Seric message("250 %s Message accepted for delivery", id); 63264296Seric 63367131Seric /* if we just queued, poke it */ 63467131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 63567131Seric { 63667131Seric extern pid_t dowork(); 63767131Seric 63867131Seric unlockqueue(e); 63967131Seric (void) dowork(id, TRUE, TRUE, e); 64058919Seric } 64158883Seric 64259747Seric abortmessage: 6439339Seric /* if in a child, pop back to our parent */ 6449339Seric if (InChild) 6459339Seric finis(); 64624943Seric 64724943Seric /* clean up a bit */ 64858109Seric gotmail = FALSE; 64955012Seric dropenvelope(e); 65058179Seric CurEnv = e = newenvelope(e, CurEnv); 65155012Seric e->e_flags = BlankEnvelope.e_flags; 6524549Seric break; 6534549Seric 6544549Seric case CMDRSET: /* rset -- reset state */ 65558151Seric message("250 Reset state"); 65668603Seric 65768603Seric /* arrange to ignore any current send list */ 65868603Seric e->e_sendqueue = NULL; 65964359Seric e->e_flags |= EF_CLRQUEUE; 6609339Seric if (InChild) 6619339Seric finis(); 66258109Seric 66358109Seric /* clean up a bit */ 66458109Seric gotmail = FALSE; 66558109Seric dropenvelope(e); 66658179Seric CurEnv = e = newenvelope(e, CurEnv); 6679339Seric break; 6684549Seric 6694549Seric case CMDVRFY: /* vrfy -- verify address */ 67058092Seric case CMDEXPN: /* expn -- expand address */ 67158092Seric vrfy = c->cmdcode == CMDVRFY; 67258092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 67358092Seric PrivacyFlags)) 67458082Seric { 67558412Seric if (vrfy) 67667160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 67758412Seric else 67865192Seric message("502 Sorry, we do not allow this operation"); 67965017Seric #ifdef LOG 68065017Seric if (LogLevel > 5) 68165017Seric syslog(LOG_INFO, "%s: %s [rejected]", 68265017Seric CurSmtpClient, inp); 68365017Seric #endif 68458082Seric break; 68558082Seric } 68658082Seric else if (!gothello && 68758092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 68858092Seric PrivacyFlags)) 68958082Seric { 69058151Seric message("503 I demand that you introduce yourself first"); 69158082Seric break; 69258082Seric } 69358092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6949339Seric break; 69555173Seric #ifdef LOG 69658020Seric if (LogLevel > 5) 69765017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 69855173Seric #endif 6995003Seric vrfyqueue = NULL; 7007762Seric QuickAbort = TRUE; 70158092Seric if (vrfy) 70258092Seric e->e_flags |= EF_VRFYONLY; 70362373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 70467615Seric p++; 70562373Seric if (*p == '\0') 70662373Seric { 70762373Seric message("501 Argument required"); 70862373Seric Errors++; 70962373Seric } 71062373Seric else 71162373Seric { 71267990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 71362373Seric } 7147762Seric if (Errors != 0) 7159339Seric { 7169339Seric if (InChild) 7179339Seric finis(); 7187762Seric break; 7199339Seric } 72062373Seric if (vrfyqueue == NULL) 72162373Seric { 72262373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 72362373Seric } 7245003Seric while (vrfyqueue != NULL) 7255003Seric { 72663971Seric a = vrfyqueue; 72763971Seric while ((a = a->q_next) != NULL && 72863971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 72963971Seric continue; 7307685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 73158151Seric printvrfyaddr(vrfyqueue, a == NULL); 73263847Seric vrfyqueue = vrfyqueue->q_next; 7335003Seric } 7349339Seric if (InChild) 7359339Seric finis(); 7364549Seric break; 7374549Seric 7384549Seric case CMDHELP: /* help -- give user info */ 7394577Seric help(p); 7404549Seric break; 7414549Seric 7424549Seric case CMDNOOP: /* noop -- do nothing */ 74364122Seric message("250 OK"); 7444549Seric break; 7454549Seric 7464549Seric case CMDQUIT: /* quit -- leave mail */ 74758151Seric message("221 %s closing connection", MyHostName); 74861051Seric 74966283Seric doquit: 75068603Seric /* arrange to ignore any current send list */ 75168603Seric e->e_sendqueue = NULL; 75268603Seric 75361051Seric /* avoid future 050 messages */ 75466017Seric disconnect(1, e); 75561051Seric 7569339Seric if (InChild) 7579339Seric ExitStat = EX_QUIT; 7584549Seric finis(); 7594549Seric 7608544Seric case CMDVERB: /* set verbose mode */ 76159957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 76259957Seric { 76359957Seric /* this would give out the same info */ 76459957Seric message("502 Verbose unavailable"); 76559957Seric break; 76659957Seric } 7678544Seric Verbose = TRUE; 76858734Seric e->e_sendmode = SM_DELIVER; 76959957Seric message("250 Verbose mode"); 7708544Seric break; 7718544Seric 7729314Seric case CMDONEX: /* doing one transaction only */ 7739378Seric OneXact = TRUE; 77459957Seric message("250 Only one transaction"); 7759314Seric break; 7769314Seric 77736230Skarels # ifdef SMTPDEBUG 7789339Seric case CMDDBGQSHOW: /* show queues */ 7796907Seric printf("Send Queue="); 78055012Seric printaddr(e->e_sendqueue, TRUE); 7815003Seric break; 7827275Seric 7837275Seric case CMDDBGDEBUG: /* set debug mode */ 7847676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7857676Seric tTflag(p); 78658151Seric message("200 Debug set"); 7877275Seric break; 7887275Seric 78936230Skarels # else /* not SMTPDEBUG */ 79036230Skarels case CMDDBGQSHOW: /* show queues */ 79136230Skarels case CMDDBGDEBUG: /* set debug mode */ 79264685Seric # endif /* SMTPDEBUG */ 79364685Seric case CMDLOGBOGUS: /* bogus command */ 79436233Skarels # ifdef LOG 79558308Seric if (LogLevel > 0) 79664685Seric syslog(LOG_CRIT, 79758020Seric "\"%s\" command from %s (%s)", 79866005Seric c->cmdname, peerhostname, 79958755Seric anynet_ntoa(&RealHostAddr)); 80036233Skarels # endif 80136230Skarels /* FALL THROUGH */ 80236230Skarels 8034549Seric case CMDERROR: /* unknown command */ 80466283Seric if (++badcommands > MAXBADCOMMANDS) 80566283Seric { 80666283Seric message("421 %s Too many bad commands; closing connection", 80766283Seric MyHostName); 80866283Seric goto doquit; 80966283Seric } 81066283Seric 81158151Seric message("500 Command unrecognized"); 8124549Seric break; 8134549Seric 8144549Seric default: 81536230Skarels errno = 0; 81658151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8174549Seric break; 8184549Seric } 8194549Seric } 8204549Seric } 8214549Seric /* 8224549Seric ** SKIPWORD -- skip a fixed word. 8234549Seric ** 8244549Seric ** Parameters: 8254549Seric ** p -- place to start looking. 8264549Seric ** w -- word to skip. 8274549Seric ** 8284549Seric ** Returns: 8294549Seric ** p following w. 8304549Seric ** NULL on error. 8314549Seric ** 8324549Seric ** Side Effects: 8334549Seric ** clobbers the p data area. 8344549Seric */ 8354549Seric 8364549Seric static char * 8374549Seric skipword(p, w) 8384549Seric register char *p; 8394549Seric char *w; 8404549Seric { 8414549Seric register char *q; 84266005Seric char *firstp = p; 8434549Seric 8444549Seric /* find beginning of word */ 84558050Seric while (isascii(*p) && isspace(*p)) 8464549Seric p++; 8474549Seric q = p; 8484549Seric 8494549Seric /* find end of word */ 85058050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8514549Seric p++; 85258050Seric while (isascii(*p) && isspace(*p)) 8534549Seric *p++ = '\0'; 8544549Seric if (*p != ':') 8554549Seric { 8564549Seric syntax: 85766005Seric message("501 Syntax error in parameters scanning \"%s\"", 85866005Seric firstp); 8594549Seric Errors++; 8604549Seric return (NULL); 8614549Seric } 8624549Seric *p++ = '\0'; 86358050Seric while (isascii(*p) && isspace(*p)) 8644549Seric p++; 8654549Seric 86662373Seric if (*p == '\0') 86762373Seric goto syntax; 86862373Seric 8694549Seric /* see if the input word matches desired word */ 87033725Sbostic if (strcasecmp(q, w)) 8714549Seric goto syntax; 8724549Seric 8734549Seric return (p); 8744549Seric } 8754577Seric /* 87668559Seric ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 87768559Seric ** 87868559Seric ** Parameters: 87968559Seric ** kp -- the parameter key. 88068559Seric ** vp -- the value of that parameter. 88168559Seric ** e -- the envelope. 88268559Seric ** 88368559Seric ** Returns: 88468559Seric ** none. 88568559Seric */ 88668559Seric 88768559Seric mail_esmtp_args(kp, vp, e) 88868559Seric char *kp; 88968559Seric char *vp; 89068559Seric ENVELOPE *e; 89168559Seric { 89268559Seric if (strcasecmp(kp, "size") == 0) 89368559Seric { 89468559Seric if (vp == NULL) 89568559Seric { 89668559Seric usrerr("501 SIZE requires a value"); 89768559Seric /* NOTREACHED */ 89868559Seric } 89968559Seric # ifdef __STDC__ 90068560Seric e->e_msgsize = strtoul(vp, (char **) NULL, 10); 90168559Seric # else 90268560Seric e->e_msgsize = strtol(vp, (char **) NULL, 10); 90368559Seric # endif 90468559Seric } 90568559Seric else if (strcasecmp(kp, "body") == 0) 90668559Seric { 90768559Seric if (vp == NULL) 90868559Seric { 90968559Seric usrerr("501 BODY requires a value"); 91068559Seric /* NOTREACHED */ 91168559Seric } 91268559Seric if (strcasecmp(vp, "8bitmime") == 0) 91368559Seric { 91468559Seric SevenBitInput = FALSE; 91568559Seric } 91668559Seric else if (strcasecmp(vp, "7bit") == 0) 91768559Seric { 91868559Seric SevenBitInput = TRUE; 91968559Seric } 92068559Seric else 92168559Seric { 92268559Seric usrerr("501 Unknown BODY type %s", 92368559Seric vp); 92468559Seric /* NOTREACHED */ 92568559Seric } 92668559Seric e->e_bodytype = newstr(vp); 92768559Seric } 92868559Seric else if (strcasecmp(kp, "envid") == 0) 92968559Seric { 93068559Seric if (vp == NULL) 93168559Seric { 93268559Seric usrerr("501 ENVID requires a value"); 93368559Seric /* NOTREACHED */ 93468559Seric } 93568583Seric if (!xtextok(vp)) 93668583Seric { 93768583Seric usrerr("501 Syntax error in ENVID parameter value"); 93868583Seric /* NOTREACHED */ 93968583Seric } 94068583Seric if (e->e_envid != NULL) 94168583Seric { 94268583Seric usrerr("501 Duplicate ENVID parameter"); 94368583Seric /* NOTREACHED */ 94468583Seric } 94568559Seric e->e_envid = newstr(vp); 94668559Seric } 94768559Seric else if (strcasecmp(kp, "ret") == 0) 94868559Seric { 94968559Seric if (vp == NULL) 95068559Seric { 95168559Seric usrerr("501 RET requires a value"); 95268559Seric /* NOTREACHED */ 95368559Seric } 95468583Seric if (bitset(EF_RET_PARAM, e->e_flags)) 95568583Seric { 95668583Seric usrerr("501 Duplicate RET parameter"); 95768583Seric /* NOTREACHED */ 95868583Seric } 95968559Seric e->e_flags |= EF_RET_PARAM; 96068559Seric if (strcasecmp(vp, "hdrs") == 0) 96168559Seric e->e_flags |= EF_NO_BODY_RETN; 96268559Seric else if (strcasecmp(vp, "full") != 0) 96368559Seric { 96468559Seric usrerr("501 Bad argument \"%s\" to RET", vp); 96568559Seric /* NOTREACHED */ 96668559Seric } 96768559Seric } 96868559Seric else 96968559Seric { 97068559Seric usrerr("501 %s parameter unrecognized", kp); 97168559Seric /* NOTREACHED */ 97268559Seric } 97368559Seric } 97468559Seric /* 97567963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 97667963Seric ** 97767963Seric ** Parameters: 97867963Seric ** a -- the address corresponding to the To: parameter. 97967963Seric ** kp -- the parameter key. 98067963Seric ** vp -- the value of that parameter. 98167963Seric ** e -- the envelope. 98267963Seric ** 98367963Seric ** Returns: 98467963Seric ** none. 98567963Seric */ 98667963Seric 98767963Seric rcpt_esmtp_args(a, kp, vp, e) 98867963Seric ADDRESS *a; 98967963Seric char *kp; 99067963Seric char *vp; 99167963Seric ENVELOPE *e; 99267963Seric { 99367963Seric if (strcasecmp(kp, "notify") == 0) 99467963Seric { 99567963Seric char *p; 99667963Seric 99767963Seric if (vp == NULL) 99867963Seric { 99967963Seric usrerr("501 NOTIFY requires a value"); 100067963Seric /* NOTREACHED */ 100167963Seric } 100267963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 100368595Seric a->q_flags |= QHASNOTIFY; 100467963Seric if (strcasecmp(vp, "never") == 0) 100567963Seric return; 100667963Seric for (p = vp; p != NULL; vp = p) 100767963Seric { 100867963Seric p = strchr(p, ','); 100967963Seric if (p != NULL) 101067963Seric *p++ = '\0'; 101167963Seric if (strcasecmp(vp, "success") == 0) 101267963Seric a->q_flags |= QPINGONSUCCESS; 101367963Seric else if (strcasecmp(vp, "failure") == 0) 101467963Seric a->q_flags |= QPINGONFAILURE; 101567963Seric else if (strcasecmp(vp, "delay") == 0) 101667963Seric a->q_flags |= QPINGONDELAY; 101767963Seric else 101867963Seric { 101967963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 102067963Seric vp); 102167963Seric /* NOTREACHED */ 102267963Seric } 102367963Seric } 102467963Seric } 102567963Seric else if (strcasecmp(kp, "orcpt") == 0) 102667963Seric { 102767963Seric if (vp == NULL) 102867963Seric { 102967963Seric usrerr("501 ORCPT requires a value"); 103067963Seric /* NOTREACHED */ 103167963Seric } 103268583Seric if (!xtextok(vp)) 103368583Seric { 103468583Seric usrerr("501 Syntax error in ORCPT parameter value"); 103568583Seric /* NOTREACHED */ 103668583Seric } 103768583Seric if (a->q_orcpt != NULL) 103868583Seric { 103968583Seric usrerr("501 Duplicate ORCPT parameter"); 104068583Seric /* NOTREACHED */ 104168583Seric } 104267963Seric a->q_orcpt = newstr(vp); 104367963Seric } 104467963Seric else 104567963Seric { 104667963Seric usrerr("501 %s parameter unrecognized", kp); 104767963Seric /* NOTREACHED */ 104867963Seric } 104967963Seric } 105067963Seric /* 105158151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 105258151Seric ** 105358151Seric ** Parameters: 105458151Seric ** a -- the address to print 105558151Seric ** last -- set if this is the last one. 105658151Seric ** 105758151Seric ** Returns: 105858151Seric ** none. 105958151Seric ** 106058151Seric ** Side Effects: 106158151Seric ** Prints the appropriate 250 codes. 106258151Seric */ 106358151Seric 106458151Seric printvrfyaddr(a, last) 106558151Seric register ADDRESS *a; 106658151Seric bool last; 106758151Seric { 106858151Seric char fmtbuf[20]; 106958151Seric 107058151Seric strcpy(fmtbuf, "250"); 107158151Seric fmtbuf[3] = last ? ' ' : '-'; 107258151Seric 107359746Seric if (a->q_fullname == NULL) 107459746Seric { 107559746Seric if (strchr(a->q_user, '@') == NULL) 107659746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 107759746Seric else 107859746Seric strcpy(&fmtbuf[4], "<%s>"); 107959746Seric message(fmtbuf, a->q_user, MyHostName); 108059746Seric } 108158151Seric else 108258151Seric { 108359746Seric if (strchr(a->q_user, '@') == NULL) 108459746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 108559746Seric else 108659746Seric strcpy(&fmtbuf[4], "%s <%s>"); 108759746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 108858151Seric } 108958151Seric } 109058151Seric /* 10914577Seric ** HELP -- implement the HELP command. 10924577Seric ** 10934577Seric ** Parameters: 10944577Seric ** topic -- the topic we want help for. 10954577Seric ** 10964577Seric ** Returns: 10974577Seric ** none. 10984577Seric ** 10994577Seric ** Side Effects: 11004577Seric ** outputs the help file to message output. 11014577Seric */ 11024577Seric 11034577Seric help(topic) 11044577Seric char *topic; 11054577Seric { 11064577Seric register FILE *hf; 11074577Seric int len; 110868751Seric bool noinfo; 11094577Seric char buf[MAXLINE]; 111068751Seric extern char Version[]; 11114577Seric 111268751Seric 11138269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 11144577Seric { 11154577Seric /* no help */ 111611931Seric errno = 0; 111768751Seric message("502 Sendmail %s -- HELP not implemented", Version); 11184577Seric return; 11194577Seric } 11204577Seric 112149669Seric if (topic == NULL || *topic == '\0') 112268751Seric { 112349669Seric topic = "smtp"; 112468751Seric message("214-This is Sendmail version %s", Version); 112568751Seric noinfo = FALSE; 112668751Seric } 112749669Seric else 112868751Seric { 112949669Seric makelower(topic); 113068751Seric noinfo = TRUE; 113168751Seric } 113249669Seric 11334577Seric len = strlen(topic); 11344577Seric 11354577Seric while (fgets(buf, sizeof buf, hf) != NULL) 11364577Seric { 11374577Seric if (strncmp(buf, topic, len) == 0) 11384577Seric { 11394577Seric register char *p; 11404577Seric 114156795Seric p = strchr(buf, '\t'); 11424577Seric if (p == NULL) 11434577Seric p = buf; 11444577Seric else 11454577Seric p++; 11464577Seric fixcrlf(p, TRUE); 114758151Seric message("214-%s", p); 11484577Seric noinfo = FALSE; 11494577Seric } 11504577Seric } 11514577Seric 11524577Seric if (noinfo) 115358151Seric message("504 HELP topic unknown"); 11544577Seric else 115558151Seric message("214 End of HELP info"); 11564628Seric (void) fclose(hf); 11574577Seric } 11588544Seric /* 11599339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11609339Seric ** 11619339Seric ** Parameters: 11629339Seric ** label -- a string used in error messages 11639339Seric ** 11649339Seric ** Returns: 11659339Seric ** zero in the child 11669339Seric ** one in the parent 11679339Seric ** 11689339Seric ** Side Effects: 11699339Seric ** none. 11709339Seric */ 11718544Seric 117255012Seric runinchild(label, e) 11739339Seric char *label; 117455012Seric register ENVELOPE *e; 11759339Seric { 11769339Seric int childpid; 11779339Seric 117816158Seric if (!OneXact) 11799339Seric { 118016158Seric childpid = dofork(); 118116158Seric if (childpid < 0) 118216158Seric { 118316158Seric syserr("%s: cannot fork", label); 118416158Seric return (1); 118516158Seric } 118616158Seric if (childpid > 0) 118716158Seric { 118816158Seric auto int st; 11899339Seric 119016158Seric /* parent -- wait for child to complete */ 119161093Seric setproctitle("server %s child wait", CurHostName); 119216158Seric st = waitfor(childpid); 119316158Seric if (st == -1) 119416158Seric syserr("%s: lost child", label); 119564948Seric else if (!WIFEXITED(st)) 119664948Seric syserr("%s: died on signal %d", 119764948Seric label, st & 0177); 11989339Seric 119916158Seric /* if we exited on a QUIT command, complete the process */ 120066017Seric if (WEXITSTATUS(st) == EX_QUIT) 120166017Seric { 120266017Seric disconnect(1, e); 120316158Seric finis(); 120466017Seric } 12059339Seric 120616158Seric return (1); 120716158Seric } 120816158Seric else 120916158Seric { 121016158Seric /* child */ 121116158Seric InChild = TRUE; 121225050Seric QuickAbort = FALSE; 121355012Seric clearenvelope(e, FALSE); 121416158Seric } 12159339Seric } 121615256Seric 121716158Seric /* open alias database */ 121860537Seric initmaps(FALSE, e); 121916158Seric 122016158Seric return (0); 12219339Seric } 12229339Seric 122356795Seric # endif /* SMTP */ 1224