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*67990Seric static char sccsid[] = "@(#)srvrsmtp.c 8.50 (Berkeley) 11/25/94 (with SMTP)"; 1433731Sbostic #else 15*67990Seric static char sccsid[] = "@(#)srvrsmtp.c 8.50 (Berkeley) 11/25/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"); 31067963Seric message("250-X-DSN-1"); 31158323Seric message("250 HELP"); 3124976Seric break; 3134976Seric 3144549Seric case CMDMAIL: /* mail -- designate sender */ 31561093Seric SmtpPhase = "server MAIL"; 31624943Seric 3179314Seric /* check for validity of this command */ 31858789Seric if (!gothello) 31958082Seric { 32058957Seric /* set sending host to our known value */ 32159016Seric if (sendinghost == NULL) 32266005Seric sendinghost = peerhostname; 32358957Seric 32458789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 32558821Seric { 32658789Seric message("503 Polite people say HELO first"); 32758821Seric break; 32858821Seric } 32958082Seric } 33058109Seric if (gotmail) 3314558Seric { 33258151Seric message("503 Sender already specified"); 33363843Seric if (InChild) 33463843Seric finis(); 3354558Seric break; 3364558Seric } 3379339Seric if (InChild) 3389339Seric { 33936230Skarels errno = 0; 34058151Seric syserr("503 Nested MAIL command: MAIL %s", p); 34158069Seric finis(); 3429339Seric } 3439339Seric 3449339Seric /* fork a subprocess to process this command */ 34555012Seric if (runinchild("SMTP-MAIL", e) > 0) 3469339Seric break; 34763753Seric if (!gothello) 34863753Seric { 34963753Seric auth_warning(e, 35066005Seric "Host %s didn't use HELO protocol", 35166005Seric peerhostname); 35263753Seric } 35365947Seric #ifdef PICKY_HELO_CHECK 35466005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 35566005Seric (strcasecmp(peerhostname, "localhost") != 0 || 35665823Seric strcasecmp(sendinghost, MyHostName) != 0)) 35765823Seric { 35865823Seric auth_warning(e, "Host %s claimed to be %s", 35966005Seric peerhostname, sendinghost); 36065823Seric } 36165947Seric #endif 36265823Seric 36358323Seric if (protocol == NULL) 36458323Seric protocol = "SMTP"; 36558323Seric define('r', protocol, e); 36659016Seric define('s', sendinghost, e); 36755012Seric initsys(e); 36859747Seric nrcpts = 0; 36965089Seric e->e_flags |= EF_LOGSENDER; 37065058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3719339Seric 3729339Seric /* child -- go do the processing */ 3734549Seric p = skipword(p, "from"); 3744549Seric if (p == NULL) 3754549Seric break; 37657977Seric if (setjmp(TopFrame) > 0) 37758147Seric { 37858147Seric /* this failed -- undo work */ 37958147Seric if (InChild) 38059058Seric { 38159058Seric QuickAbort = FALSE; 38259058Seric SuprErrs = TRUE; 38363787Seric e->e_flags &= ~EF_FATALERRS; 38458147Seric finis(); 38559058Seric } 38657977Seric break; 38758147Seric } 38857977Seric QuickAbort = TRUE; 38958333Seric 39058333Seric /* must parse sender first */ 39158333Seric delimptr = NULL; 39258704Seric setsender(p, e, &delimptr, FALSE); 39358333Seric p = delimptr; 39458333Seric if (p != NULL && *p != '\0') 39558333Seric *p++ = '\0'; 39658333Seric 39766325Seric /* check for possible spoofing */ 39866325Seric if (RealUid != 0 && OpMode == MD_SMTP && 39967473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 40067473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 40166325Seric { 40266325Seric auth_warning(e, "%s owned process doing -bs", 40366325Seric RealUserName); 40466325Seric } 40566325Seric 40658333Seric /* now parse ESMTP arguments */ 40758333Seric msize = 0; 40866764Seric while (p != NULL && *p != '\0') 40958333Seric { 41058333Seric char *kp; 41166304Seric char *vp = NULL; 41258333Seric 41358333Seric /* locate the beginning of the keyword */ 41458333Seric while (isascii(*p) && isspace(*p)) 41558333Seric p++; 41658333Seric if (*p == '\0') 41758333Seric break; 41858333Seric kp = p; 41958333Seric 42058333Seric /* skip to the value portion */ 42158333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 42258333Seric p++; 42358333Seric if (*p == '=') 42458333Seric { 42558333Seric *p++ = '\0'; 42658333Seric vp = p; 42758333Seric 42858333Seric /* skip to the end of the value */ 42958333Seric while (*p != '\0' && *p != ' ' && 43058333Seric !(isascii(*p) && iscntrl(*p)) && 43158333Seric *p != '=') 43258333Seric p++; 43358333Seric } 43458333Seric 43558333Seric if (*p != '\0') 43658333Seric *p++ = '\0'; 43758333Seric 43858333Seric if (tTd(19, 1)) 43966764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 44058333Seric vp == NULL ? "<null>" : vp); 44158333Seric 44258333Seric if (strcasecmp(kp, "size") == 0) 44358333Seric { 44459093Seric if (vp == NULL) 44558333Seric { 44658333Seric usrerr("501 SIZE requires a value"); 44758333Seric /* NOTREACHED */ 44858333Seric } 44966772Seric # ifdef __STDC__ 45066772Seric msize = strtoul(vp, (char **) NULL, 10); 45166772Seric # else 45266772Seric msize = strtol(vp, (char **) NULL, 10); 45366772Seric # endif 45458333Seric } 45559093Seric else if (strcasecmp(kp, "body") == 0) 45659093Seric { 45759093Seric if (vp == NULL) 45859093Seric { 45959093Seric usrerr("501 BODY requires a value"); 46059093Seric /* NOTREACHED */ 46159093Seric } 46267417Seric e->e_bodytype = newstr(vp); 46359093Seric if (strcasecmp(vp, "8bitmime") == 0) 46459093Seric { 46567546Seric SevenBitInput = FALSE; 46659093Seric } 46759093Seric else if (strcasecmp(vp, "7bit") == 0) 46859093Seric { 46967546Seric SevenBitInput = TRUE; 47059093Seric } 47159093Seric else 47259093Seric { 47359093Seric usrerr("501 Unknown BODY type %s", 47459093Seric vp); 47567417Seric /* NOTREACHED */ 47659093Seric } 47759093Seric } 47867880Seric else if (strcasecmp(kp, "envid") == 0) 47967880Seric { 48067880Seric if (vp == NULL) 48167880Seric { 48267880Seric usrerr("501 ENVID requires a value"); 48367880Seric /* NOTREACHED */ 48467880Seric } 48567880Seric e->e_envid = newstr(vp); 48667880Seric } 48767963Seric else if (strcasecmp(kp, "omts") == 0) 48867963Seric { 48967963Seric if (vp == NULL) 49067963Seric { 49167963Seric usrerr("501 OMTS requires a value"); 49267963Seric /* NOTREACHED */ 49367963Seric } 49467963Seric e->e_omts = newstr(vp); 49567963Seric } 49658333Seric else 49758333Seric { 49858333Seric usrerr("501 %s parameter unrecognized", kp); 49958333Seric /* NOTREACHED */ 50058333Seric } 50158333Seric } 50259284Seric 50359284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 50459284Seric { 50559284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 50659284Seric MaxMessageSize); 50759284Seric /* NOTREACHED */ 50859284Seric } 50958333Seric 51058333Seric if (!enoughspace(msize)) 51158333Seric { 51258333Seric message("452 Insufficient disk space; try again later"); 51358333Seric break; 51458333Seric } 51558151Seric message("250 Sender ok"); 51658147Seric gotmail = TRUE; 5174549Seric break; 5184549Seric 5194976Seric case CMDRCPT: /* rcpt -- designate recipient */ 52058850Seric if (!gotmail) 52158850Seric { 52258850Seric usrerr("503 Need MAIL before RCPT"); 52358850Seric break; 52458850Seric } 52561093Seric SmtpPhase = "server RCPT"; 52612612Seric if (setjmp(TopFrame) > 0) 52714785Seric { 52855012Seric e->e_flags &= ~EF_FATALERRS; 52912612Seric break; 53014785Seric } 53112612Seric QuickAbort = TRUE; 53251951Seric LogUsrErrs = TRUE; 53358093Seric 53459699Seric if (e->e_sendmode != SM_DELIVER) 53559699Seric e->e_flags |= EF_VRFYONLY; 53658919Seric 5374549Seric p = skipword(p, "to"); 5384549Seric if (p == NULL) 5394549Seric break; 54067880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 54112612Seric if (a == NULL) 54212612Seric break; 54367880Seric p = delimptr; 54467880Seric 54567880Seric /* now parse ESMTP arguments */ 54667880Seric while (p != NULL && *p != '\0') 54767880Seric { 54867880Seric char *kp; 54967880Seric char *vp = NULL; 55067880Seric 55167880Seric /* locate the beginning of the keyword */ 55267880Seric while (isascii(*p) && isspace(*p)) 55367880Seric p++; 55467880Seric if (*p == '\0') 55567880Seric break; 55667880Seric kp = p; 55767880Seric 55867880Seric /* skip to the value portion */ 55967880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 56067880Seric p++; 56167880Seric if (*p == '=') 56267880Seric { 56367880Seric *p++ = '\0'; 56467880Seric vp = p; 56567880Seric 56667880Seric /* skip to the end of the value */ 56767880Seric while (*p != '\0' && *p != ' ' && 56867880Seric !(isascii(*p) && iscntrl(*p)) && 56967880Seric *p != '=') 57067880Seric p++; 57167880Seric } 57267880Seric 57367880Seric if (*p != '\0') 57467880Seric *p++ = '\0'; 57567880Seric 57667880Seric if (tTd(19, 1)) 57767880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 57867880Seric vp == NULL ? "<null>" : vp); 57967880Seric 58067963Seric rcpt_esmtp_args(a, kp, vp, e); 58167963Seric 58267880Seric } 58367980Seric 58467980Seric /* save in recipient list after ESMTP mods */ 58567980Seric a->q_flags |= QPRIMARY; 58667982Seric a = recipient(a, &e->e_sendqueue, 0, e); 58767980Seric 58812612Seric if (Errors != 0) 58912612Seric break; 59012612Seric 59112612Seric /* no errors during parsing, but might be a duplicate */ 59255012Seric e->e_to = p; 59312612Seric if (!bitset(QBADADDR, a->q_flags)) 59459747Seric { 59564718Seric message("250 Recipient ok%s", 59664718Seric bitset(QQUEUEUP, a->q_flags) ? 59764718Seric " (will queue)" : ""); 59859747Seric nrcpts++; 59959747Seric } 60012612Seric else 6014549Seric { 60212612Seric /* punt -- should keep message in ADDRESS.... */ 60358151Seric message("550 Addressee unknown"); 6044549Seric } 60555012Seric e->e_to = NULL; 6064549Seric break; 6074549Seric 6084549Seric case CMDDATA: /* data -- text of mail */ 60961093Seric SmtpPhase = "server DATA"; 61058109Seric if (!gotmail) 6114549Seric { 61258151Seric message("503 Need MAIL command"); 6134976Seric break; 6144549Seric } 61564718Seric else if (nrcpts <= 0) 6164549Seric { 61758151Seric message("503 Need RCPT (recipient)"); 6184976Seric break; 6194549Seric } 6204976Seric 62158929Seric /* check to see if we need to re-expand aliases */ 62263787Seric /* also reset QBADADDR on already-diagnosted addrs */ 62363787Seric doublequeue = FALSE; 62458929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 62558929Seric { 62658929Seric if (bitset(QVERIFIED, a->q_flags)) 62763787Seric { 62863787Seric /* need to re-expand aliases */ 62963787Seric doublequeue = TRUE; 63063787Seric } 63163787Seric if (bitset(QBADADDR, a->q_flags)) 63263787Seric { 63363787Seric /* make this "go away" */ 63463787Seric a->q_flags |= QDONTSEND; 63563787Seric a->q_flags &= ~QBADADDR; 63663787Seric } 63758929Seric } 63858929Seric 6394976Seric /* collect the text of the message */ 64024943Seric SmtpPhase = "collect"; 64167546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 64264766Seric if (Errors != 0) 64364766Seric goto abortmessage; 64467131Seric 64567131Seric /* from now on, we have to operate silently */ 64663965Seric HoldErrs = TRUE; 64767131Seric e->e_errormode = EM_MAIL; 6484976Seric 6498238Seric /* 6508238Seric ** Arrange to send to everyone. 6518238Seric ** If sending to multiple people, mail back 6528238Seric ** errors rather than reporting directly. 6538238Seric ** In any case, don't mail back errors for 6548238Seric ** anything that has happened up to 6558238Seric ** now (the other end will do this). 65610197Seric ** Truncate our transcript -- the mail has gotten 65710197Seric ** to us successfully, and if we have 65810197Seric ** to mail this back, it will be easier 65910197Seric ** on the reader. 6608238Seric ** Then send to everyone. 6618238Seric ** Finally give a reply code. If an error has 6628238Seric ** already been given, don't mail a 6638238Seric ** message back. 6649339Seric ** We goose error returns by clearing error bit. 6658238Seric */ 6668238Seric 66724943Seric SmtpPhase = "delivery"; 66855012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 66958714Seric id = e->e_id; 6704976Seric 67167131Seric if (doublequeue) 67259730Seric { 67367131Seric /* make sure it is in the queue */ 67467131Seric queueup(e, TRUE, FALSE); 67559730Seric } 6768238Seric else 67758919Seric { 67867131Seric /* send to all recipients */ 67967131Seric sendall(e, SM_DEFAULT); 68067131Seric } 68167131Seric e->e_to = NULL; 68259747Seric 68367131Seric /* issue success message */ 68467131Seric message("250 %s Message accepted for delivery", id); 68564296Seric 68667131Seric /* if we just queued, poke it */ 68767131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 68867131Seric { 68967131Seric extern pid_t dowork(); 69067131Seric 69167131Seric unlockqueue(e); 69267131Seric (void) dowork(id, TRUE, TRUE, e); 69358919Seric } 69458883Seric 69559747Seric abortmessage: 6969339Seric /* if in a child, pop back to our parent */ 6979339Seric if (InChild) 6989339Seric finis(); 69924943Seric 70024943Seric /* clean up a bit */ 70158109Seric gotmail = FALSE; 70255012Seric dropenvelope(e); 70358179Seric CurEnv = e = newenvelope(e, CurEnv); 70455012Seric e->e_flags = BlankEnvelope.e_flags; 7054549Seric break; 7064549Seric 7074549Seric case CMDRSET: /* rset -- reset state */ 70858151Seric message("250 Reset state"); 70964359Seric e->e_flags |= EF_CLRQUEUE; 7109339Seric if (InChild) 7119339Seric finis(); 71258109Seric 71358109Seric /* clean up a bit */ 71458109Seric gotmail = FALSE; 71558109Seric dropenvelope(e); 71658179Seric CurEnv = e = newenvelope(e, CurEnv); 7179339Seric break; 7184549Seric 7194549Seric case CMDVRFY: /* vrfy -- verify address */ 72058092Seric case CMDEXPN: /* expn -- expand address */ 72158092Seric vrfy = c->cmdcode == CMDVRFY; 72258092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 72358092Seric PrivacyFlags)) 72458082Seric { 72558412Seric if (vrfy) 72667160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 72758412Seric else 72865192Seric message("502 Sorry, we do not allow this operation"); 72965017Seric #ifdef LOG 73065017Seric if (LogLevel > 5) 73165017Seric syslog(LOG_INFO, "%s: %s [rejected]", 73265017Seric CurSmtpClient, inp); 73365017Seric #endif 73458082Seric break; 73558082Seric } 73658082Seric else if (!gothello && 73758092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 73858092Seric PrivacyFlags)) 73958082Seric { 74058151Seric message("503 I demand that you introduce yourself first"); 74158082Seric break; 74258082Seric } 74358092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7449339Seric break; 74555173Seric #ifdef LOG 74658020Seric if (LogLevel > 5) 74765017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 74855173Seric #endif 7495003Seric vrfyqueue = NULL; 7507762Seric QuickAbort = TRUE; 75158092Seric if (vrfy) 75258092Seric e->e_flags |= EF_VRFYONLY; 75362373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 75467615Seric p++; 75562373Seric if (*p == '\0') 75662373Seric { 75762373Seric message("501 Argument required"); 75862373Seric Errors++; 75962373Seric } 76062373Seric else 76162373Seric { 762*67990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 76362373Seric } 7647762Seric if (Errors != 0) 7659339Seric { 7669339Seric if (InChild) 7679339Seric finis(); 7687762Seric break; 7699339Seric } 77062373Seric if (vrfyqueue == NULL) 77162373Seric { 77262373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 77362373Seric } 7745003Seric while (vrfyqueue != NULL) 7755003Seric { 77663971Seric a = vrfyqueue; 77763971Seric while ((a = a->q_next) != NULL && 77863971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 77963971Seric continue; 7807685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 78158151Seric printvrfyaddr(vrfyqueue, a == NULL); 78263847Seric vrfyqueue = vrfyqueue->q_next; 7835003Seric } 7849339Seric if (InChild) 7859339Seric finis(); 7864549Seric break; 7874549Seric 7884549Seric case CMDHELP: /* help -- give user info */ 7894577Seric help(p); 7904549Seric break; 7914549Seric 7924549Seric case CMDNOOP: /* noop -- do nothing */ 79364122Seric message("250 OK"); 7944549Seric break; 7954549Seric 7964549Seric case CMDQUIT: /* quit -- leave mail */ 79758151Seric message("221 %s closing connection", MyHostName); 79861051Seric 79966283Seric doquit: 80061051Seric /* avoid future 050 messages */ 80166017Seric disconnect(1, e); 80261051Seric 8039339Seric if (InChild) 8049339Seric ExitStat = EX_QUIT; 8054549Seric finis(); 8064549Seric 8078544Seric case CMDVERB: /* set verbose mode */ 80859957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 80959957Seric { 81059957Seric /* this would give out the same info */ 81159957Seric message("502 Verbose unavailable"); 81259957Seric break; 81359957Seric } 8148544Seric Verbose = TRUE; 81558734Seric e->e_sendmode = SM_DELIVER; 81659957Seric message("250 Verbose mode"); 8178544Seric break; 8188544Seric 8199314Seric case CMDONEX: /* doing one transaction only */ 8209378Seric OneXact = TRUE; 82159957Seric message("250 Only one transaction"); 8229314Seric break; 8239314Seric 82436230Skarels # ifdef SMTPDEBUG 8259339Seric case CMDDBGQSHOW: /* show queues */ 8266907Seric printf("Send Queue="); 82755012Seric printaddr(e->e_sendqueue, TRUE); 8285003Seric break; 8297275Seric 8307275Seric case CMDDBGDEBUG: /* set debug mode */ 8317676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 8327676Seric tTflag(p); 83358151Seric message("200 Debug set"); 8347275Seric break; 8357275Seric 83636230Skarels # else /* not SMTPDEBUG */ 83736230Skarels case CMDDBGQSHOW: /* show queues */ 83836230Skarels case CMDDBGDEBUG: /* set debug mode */ 83964685Seric # endif /* SMTPDEBUG */ 84064685Seric case CMDLOGBOGUS: /* bogus command */ 84136233Skarels # ifdef LOG 84258308Seric if (LogLevel > 0) 84364685Seric syslog(LOG_CRIT, 84458020Seric "\"%s\" command from %s (%s)", 84566005Seric c->cmdname, peerhostname, 84658755Seric anynet_ntoa(&RealHostAddr)); 84736233Skarels # endif 84836230Skarels /* FALL THROUGH */ 84936230Skarels 8504549Seric case CMDERROR: /* unknown command */ 85166283Seric if (++badcommands > MAXBADCOMMANDS) 85266283Seric { 85366283Seric message("421 %s Too many bad commands; closing connection", 85466283Seric MyHostName); 85566283Seric goto doquit; 85666283Seric } 85766283Seric 85858151Seric message("500 Command unrecognized"); 8594549Seric break; 8604549Seric 8614549Seric default: 86236230Skarels errno = 0; 86358151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8644549Seric break; 8654549Seric } 8664549Seric } 8674549Seric } 8684549Seric /* 8694549Seric ** SKIPWORD -- skip a fixed word. 8704549Seric ** 8714549Seric ** Parameters: 8724549Seric ** p -- place to start looking. 8734549Seric ** w -- word to skip. 8744549Seric ** 8754549Seric ** Returns: 8764549Seric ** p following w. 8774549Seric ** NULL on error. 8784549Seric ** 8794549Seric ** Side Effects: 8804549Seric ** clobbers the p data area. 8814549Seric */ 8824549Seric 8834549Seric static char * 8844549Seric skipword(p, w) 8854549Seric register char *p; 8864549Seric char *w; 8874549Seric { 8884549Seric register char *q; 88966005Seric char *firstp = p; 8904549Seric 8914549Seric /* find beginning of word */ 89258050Seric while (isascii(*p) && isspace(*p)) 8934549Seric p++; 8944549Seric q = p; 8954549Seric 8964549Seric /* find end of word */ 89758050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8984549Seric p++; 89958050Seric while (isascii(*p) && isspace(*p)) 9004549Seric *p++ = '\0'; 9014549Seric if (*p != ':') 9024549Seric { 9034549Seric syntax: 90466005Seric message("501 Syntax error in parameters scanning \"%s\"", 90566005Seric firstp); 9064549Seric Errors++; 9074549Seric return (NULL); 9084549Seric } 9094549Seric *p++ = '\0'; 91058050Seric while (isascii(*p) && isspace(*p)) 9114549Seric p++; 9124549Seric 91362373Seric if (*p == '\0') 91462373Seric goto syntax; 91562373Seric 9164549Seric /* see if the input word matches desired word */ 91733725Sbostic if (strcasecmp(q, w)) 9184549Seric goto syntax; 9194549Seric 9204549Seric return (p); 9214549Seric } 9224577Seric /* 92367963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 92467963Seric ** 92567963Seric ** Parameters: 92667963Seric ** a -- the address corresponding to the To: parameter. 92767963Seric ** kp -- the parameter key. 92867963Seric ** vp -- the value of that parameter. 92967963Seric ** e -- the envelope. 93067963Seric ** 93167963Seric ** Returns: 93267963Seric ** none. 93367963Seric */ 93467963Seric 93567963Seric rcpt_esmtp_args(a, kp, vp, e) 93667963Seric ADDRESS *a; 93767963Seric char *kp; 93867963Seric char *vp; 93967963Seric ENVELOPE *e; 94067963Seric { 94167963Seric if (strcasecmp(kp, "notify") == 0) 94267963Seric { 94367963Seric char *p; 94467963Seric 94567963Seric if (vp == NULL) 94667963Seric { 94767963Seric usrerr("501 NOTIFY requires a value"); 94867963Seric /* NOTREACHED */ 94967963Seric } 95067963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 95167963Seric if (strcasecmp(vp, "never") == 0) 95267963Seric return; 95367963Seric for (p = vp; p != NULL; vp = p) 95467963Seric { 95567963Seric p = strchr(p, ','); 95667963Seric if (p != NULL) 95767963Seric *p++ = '\0'; 95867963Seric if (strcasecmp(vp, "success") == 0) 95967963Seric a->q_flags |= QPINGONSUCCESS; 96067963Seric else if (strcasecmp(vp, "failure") == 0) 96167963Seric a->q_flags |= QPINGONFAILURE; 96267963Seric else if (strcasecmp(vp, "delay") == 0) 96367963Seric a->q_flags |= QPINGONDELAY; 96467963Seric else 96567963Seric { 96667963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 96767963Seric vp); 96867963Seric /* NOTREACHED */ 96967963Seric } 97067963Seric } 97167963Seric } 97267963Seric else if (strcasecmp(kp, "ret") == 0) 97367963Seric { 97467963Seric if (vp == NULL) 97567963Seric { 97667963Seric usrerr("501 RET requires a value"); 97767963Seric /* NOTREACHED */ 97867963Seric } 97967963Seric a->q_flags |= QHAS_RET_PARAM; 98067963Seric if (strcasecmp(vp, "hdrs") == 0) 98167963Seric a->q_flags |= QRET_HDRS; 98267963Seric else if (strcasecmp(vp, "full") != 0) 98367963Seric { 98467963Seric usrerr("501 Bad argument \"%s\" to RET", vp); 98567963Seric /* NOTREACHED */ 98667963Seric } 98767963Seric } 98867963Seric else if (strcasecmp(kp, "orcpt") == 0) 98967963Seric { 99067963Seric if (vp == NULL) 99167963Seric { 99267963Seric usrerr("501 ORCPT requires a value"); 99367963Seric /* NOTREACHED */ 99467963Seric } 99567963Seric a->q_orcpt = newstr(vp); 99667963Seric } 99767963Seric else 99867963Seric { 99967963Seric usrerr("501 %s parameter unrecognized", kp); 100067963Seric /* NOTREACHED */ 100167963Seric } 100267963Seric } 100367963Seric /* 100458151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 100558151Seric ** 100658151Seric ** Parameters: 100758151Seric ** a -- the address to print 100858151Seric ** last -- set if this is the last one. 100958151Seric ** 101058151Seric ** Returns: 101158151Seric ** none. 101258151Seric ** 101358151Seric ** Side Effects: 101458151Seric ** Prints the appropriate 250 codes. 101558151Seric */ 101658151Seric 101758151Seric printvrfyaddr(a, last) 101858151Seric register ADDRESS *a; 101958151Seric bool last; 102058151Seric { 102158151Seric char fmtbuf[20]; 102258151Seric 102358151Seric strcpy(fmtbuf, "250"); 102458151Seric fmtbuf[3] = last ? ' ' : '-'; 102558151Seric 102659746Seric if (a->q_fullname == NULL) 102759746Seric { 102859746Seric if (strchr(a->q_user, '@') == NULL) 102959746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 103059746Seric else 103159746Seric strcpy(&fmtbuf[4], "<%s>"); 103259746Seric message(fmtbuf, a->q_user, MyHostName); 103359746Seric } 103458151Seric else 103558151Seric { 103659746Seric if (strchr(a->q_user, '@') == NULL) 103759746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 103859746Seric else 103959746Seric strcpy(&fmtbuf[4], "%s <%s>"); 104059746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 104158151Seric } 104258151Seric } 104358151Seric /* 10444577Seric ** HELP -- implement the HELP command. 10454577Seric ** 10464577Seric ** Parameters: 10474577Seric ** topic -- the topic we want help for. 10484577Seric ** 10494577Seric ** Returns: 10504577Seric ** none. 10514577Seric ** 10524577Seric ** Side Effects: 10534577Seric ** outputs the help file to message output. 10544577Seric */ 10554577Seric 10564577Seric help(topic) 10574577Seric char *topic; 10584577Seric { 10594577Seric register FILE *hf; 10604577Seric int len; 10614577Seric char buf[MAXLINE]; 10624577Seric bool noinfo; 10634577Seric 10648269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 10654577Seric { 10664577Seric /* no help */ 106711931Seric errno = 0; 106858151Seric message("502 HELP not implemented"); 10694577Seric return; 10704577Seric } 10714577Seric 107249669Seric if (topic == NULL || *topic == '\0') 107349669Seric topic = "smtp"; 107449669Seric else 107549669Seric makelower(topic); 107649669Seric 10774577Seric len = strlen(topic); 10784577Seric noinfo = TRUE; 10794577Seric 10804577Seric while (fgets(buf, sizeof buf, hf) != NULL) 10814577Seric { 10824577Seric if (strncmp(buf, topic, len) == 0) 10834577Seric { 10844577Seric register char *p; 10854577Seric 108656795Seric p = strchr(buf, '\t'); 10874577Seric if (p == NULL) 10884577Seric p = buf; 10894577Seric else 10904577Seric p++; 10914577Seric fixcrlf(p, TRUE); 109258151Seric message("214-%s", p); 10934577Seric noinfo = FALSE; 10944577Seric } 10954577Seric } 10964577Seric 10974577Seric if (noinfo) 109858151Seric message("504 HELP topic unknown"); 10994577Seric else 110058151Seric message("214 End of HELP info"); 11014628Seric (void) fclose(hf); 11024577Seric } 11038544Seric /* 11049339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11059339Seric ** 11069339Seric ** Parameters: 11079339Seric ** label -- a string used in error messages 11089339Seric ** 11099339Seric ** Returns: 11109339Seric ** zero in the child 11119339Seric ** one in the parent 11129339Seric ** 11139339Seric ** Side Effects: 11149339Seric ** none. 11159339Seric */ 11168544Seric 111755012Seric runinchild(label, e) 11189339Seric char *label; 111955012Seric register ENVELOPE *e; 11209339Seric { 11219339Seric int childpid; 11229339Seric 112316158Seric if (!OneXact) 11249339Seric { 112516158Seric childpid = dofork(); 112616158Seric if (childpid < 0) 112716158Seric { 112816158Seric syserr("%s: cannot fork", label); 112916158Seric return (1); 113016158Seric } 113116158Seric if (childpid > 0) 113216158Seric { 113316158Seric auto int st; 11349339Seric 113516158Seric /* parent -- wait for child to complete */ 113661093Seric setproctitle("server %s child wait", CurHostName); 113716158Seric st = waitfor(childpid); 113816158Seric if (st == -1) 113916158Seric syserr("%s: lost child", label); 114064948Seric else if (!WIFEXITED(st)) 114164948Seric syserr("%s: died on signal %d", 114264948Seric label, st & 0177); 11439339Seric 114416158Seric /* if we exited on a QUIT command, complete the process */ 114566017Seric if (WEXITSTATUS(st) == EX_QUIT) 114666017Seric { 114766017Seric disconnect(1, e); 114816158Seric finis(); 114966017Seric } 11509339Seric 115116158Seric return (1); 115216158Seric } 115316158Seric else 115416158Seric { 115516158Seric /* child */ 115616158Seric InChild = TRUE; 115725050Seric QuickAbort = FALSE; 115855012Seric clearenvelope(e, FALSE); 115916158Seric } 11609339Seric } 116115256Seric 116216158Seric /* open alias database */ 116360537Seric initmaps(FALSE, e); 116416158Seric 116516158Seric return (0); 11669339Seric } 11679339Seric 116856795Seric # endif /* SMTP */ 1169