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*67963Seric static char sccsid[] = "@(#)srvrsmtp.c 8.47 (Berkeley) 11/21/94 (with SMTP)"; 1433731Sbostic #else 15*67963Seric static char sccsid[] = "@(#)srvrsmtp.c 8.47 (Berkeley) 11/21/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"); 310*67963Seric 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 } 487*67963Seric else if (strcasecmp(kp, "omts") == 0) 488*67963Seric { 489*67963Seric if (vp == NULL) 490*67963Seric { 491*67963Seric usrerr("501 OMTS requires a value"); 492*67963Seric /* NOTREACHED */ 493*67963Seric } 494*67963Seric e->e_omts = newstr(vp); 495*67963Seric } 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; 54416886Seric a->q_flags |= QPRIMARY; 54555012Seric a = recipient(a, &e->e_sendqueue, e); 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 582*67963Seric rcpt_esmtp_args(a, kp, vp, e); 583*67963Seric 58467880Seric } 58512612Seric if (Errors != 0) 58612612Seric break; 58712612Seric 58812612Seric /* no errors during parsing, but might be a duplicate */ 58955012Seric e->e_to = p; 59012612Seric if (!bitset(QBADADDR, a->q_flags)) 59159747Seric { 59264718Seric message("250 Recipient ok%s", 59364718Seric bitset(QQUEUEUP, a->q_flags) ? 59464718Seric " (will queue)" : ""); 59559747Seric nrcpts++; 59659747Seric } 59712612Seric else 5984549Seric { 59912612Seric /* punt -- should keep message in ADDRESS.... */ 60058151Seric message("550 Addressee unknown"); 6014549Seric } 60255012Seric e->e_to = NULL; 6034549Seric break; 6044549Seric 6054549Seric case CMDDATA: /* data -- text of mail */ 60661093Seric SmtpPhase = "server DATA"; 60758109Seric if (!gotmail) 6084549Seric { 60958151Seric message("503 Need MAIL command"); 6104976Seric break; 6114549Seric } 61264718Seric else if (nrcpts <= 0) 6134549Seric { 61458151Seric message("503 Need RCPT (recipient)"); 6154976Seric break; 6164549Seric } 6174976Seric 61858929Seric /* check to see if we need to re-expand aliases */ 61963787Seric /* also reset QBADADDR on already-diagnosted addrs */ 62063787Seric doublequeue = FALSE; 62158929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 62258929Seric { 62358929Seric if (bitset(QVERIFIED, a->q_flags)) 62463787Seric { 62563787Seric /* need to re-expand aliases */ 62663787Seric doublequeue = TRUE; 62763787Seric } 62863787Seric if (bitset(QBADADDR, a->q_flags)) 62963787Seric { 63063787Seric /* make this "go away" */ 63163787Seric a->q_flags |= QDONTSEND; 63263787Seric a->q_flags &= ~QBADADDR; 63363787Seric } 63458929Seric } 63558929Seric 6364976Seric /* collect the text of the message */ 63724943Seric SmtpPhase = "collect"; 63867546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 63964766Seric if (Errors != 0) 64064766Seric goto abortmessage; 64167131Seric 64267131Seric /* from now on, we have to operate silently */ 64363965Seric HoldErrs = TRUE; 64467131Seric e->e_errormode = EM_MAIL; 6454976Seric 6468238Seric /* 6478238Seric ** Arrange to send to everyone. 6488238Seric ** If sending to multiple people, mail back 6498238Seric ** errors rather than reporting directly. 6508238Seric ** In any case, don't mail back errors for 6518238Seric ** anything that has happened up to 6528238Seric ** now (the other end will do this). 65310197Seric ** Truncate our transcript -- the mail has gotten 65410197Seric ** to us successfully, and if we have 65510197Seric ** to mail this back, it will be easier 65610197Seric ** on the reader. 6578238Seric ** Then send to everyone. 6588238Seric ** Finally give a reply code. If an error has 6598238Seric ** already been given, don't mail a 6608238Seric ** message back. 6619339Seric ** We goose error returns by clearing error bit. 6628238Seric */ 6638238Seric 66424943Seric SmtpPhase = "delivery"; 66555012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 66658714Seric id = e->e_id; 6674976Seric 66867131Seric if (doublequeue) 66959730Seric { 67067131Seric /* make sure it is in the queue */ 67167131Seric queueup(e, TRUE, FALSE); 67259730Seric } 6738238Seric else 67458919Seric { 67567131Seric /* send to all recipients */ 67667131Seric sendall(e, SM_DEFAULT); 67767131Seric } 67867131Seric e->e_to = NULL; 67959747Seric 68067131Seric /* issue success message */ 68167131Seric message("250 %s Message accepted for delivery", id); 68264296Seric 68367131Seric /* if we just queued, poke it */ 68467131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 68567131Seric { 68667131Seric extern pid_t dowork(); 68767131Seric 68867131Seric unlockqueue(e); 68967131Seric (void) dowork(id, TRUE, TRUE, e); 69058919Seric } 69158883Seric 69259747Seric abortmessage: 6939339Seric /* if in a child, pop back to our parent */ 6949339Seric if (InChild) 6959339Seric finis(); 69624943Seric 69724943Seric /* clean up a bit */ 69858109Seric gotmail = FALSE; 69955012Seric dropenvelope(e); 70058179Seric CurEnv = e = newenvelope(e, CurEnv); 70155012Seric e->e_flags = BlankEnvelope.e_flags; 7024549Seric break; 7034549Seric 7044549Seric case CMDRSET: /* rset -- reset state */ 70558151Seric message("250 Reset state"); 70664359Seric e->e_flags |= EF_CLRQUEUE; 7079339Seric if (InChild) 7089339Seric finis(); 70958109Seric 71058109Seric /* clean up a bit */ 71158109Seric gotmail = FALSE; 71258109Seric dropenvelope(e); 71358179Seric CurEnv = e = newenvelope(e, CurEnv); 7149339Seric break; 7154549Seric 7164549Seric case CMDVRFY: /* vrfy -- verify address */ 71758092Seric case CMDEXPN: /* expn -- expand address */ 71858092Seric vrfy = c->cmdcode == CMDVRFY; 71958092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 72058092Seric PrivacyFlags)) 72158082Seric { 72258412Seric if (vrfy) 72367160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 72458412Seric else 72565192Seric message("502 Sorry, we do not allow this operation"); 72665017Seric #ifdef LOG 72765017Seric if (LogLevel > 5) 72865017Seric syslog(LOG_INFO, "%s: %s [rejected]", 72965017Seric CurSmtpClient, inp); 73065017Seric #endif 73158082Seric break; 73258082Seric } 73358082Seric else if (!gothello && 73458092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 73558092Seric PrivacyFlags)) 73658082Seric { 73758151Seric message("503 I demand that you introduce yourself first"); 73858082Seric break; 73958082Seric } 74058092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7419339Seric break; 74255173Seric #ifdef LOG 74358020Seric if (LogLevel > 5) 74465017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 74555173Seric #endif 7465003Seric vrfyqueue = NULL; 7477762Seric QuickAbort = TRUE; 74858092Seric if (vrfy) 74958092Seric e->e_flags |= EF_VRFYONLY; 75062373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 75167615Seric p++; 75262373Seric if (*p == '\0') 75362373Seric { 75462373Seric message("501 Argument required"); 75562373Seric Errors++; 75662373Seric } 75762373Seric else 75862373Seric { 75964284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 76062373Seric } 7617762Seric if (Errors != 0) 7629339Seric { 7639339Seric if (InChild) 7649339Seric finis(); 7657762Seric break; 7669339Seric } 76762373Seric if (vrfyqueue == NULL) 76862373Seric { 76962373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 77062373Seric } 7715003Seric while (vrfyqueue != NULL) 7725003Seric { 77363971Seric a = vrfyqueue; 77463971Seric while ((a = a->q_next) != NULL && 77563971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 77663971Seric continue; 7777685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 77858151Seric printvrfyaddr(vrfyqueue, a == NULL); 77963847Seric vrfyqueue = vrfyqueue->q_next; 7805003Seric } 7819339Seric if (InChild) 7829339Seric finis(); 7834549Seric break; 7844549Seric 7854549Seric case CMDHELP: /* help -- give user info */ 7864577Seric help(p); 7874549Seric break; 7884549Seric 7894549Seric case CMDNOOP: /* noop -- do nothing */ 79064122Seric message("250 OK"); 7914549Seric break; 7924549Seric 7934549Seric case CMDQUIT: /* quit -- leave mail */ 79458151Seric message("221 %s closing connection", MyHostName); 79561051Seric 79666283Seric doquit: 79761051Seric /* avoid future 050 messages */ 79866017Seric disconnect(1, e); 79961051Seric 8009339Seric if (InChild) 8019339Seric ExitStat = EX_QUIT; 8024549Seric finis(); 8034549Seric 8048544Seric case CMDVERB: /* set verbose mode */ 80559957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 80659957Seric { 80759957Seric /* this would give out the same info */ 80859957Seric message("502 Verbose unavailable"); 80959957Seric break; 81059957Seric } 8118544Seric Verbose = TRUE; 81258734Seric e->e_sendmode = SM_DELIVER; 81359957Seric message("250 Verbose mode"); 8148544Seric break; 8158544Seric 8169314Seric case CMDONEX: /* doing one transaction only */ 8179378Seric OneXact = TRUE; 81859957Seric message("250 Only one transaction"); 8199314Seric break; 8209314Seric 82136230Skarels # ifdef SMTPDEBUG 8229339Seric case CMDDBGQSHOW: /* show queues */ 8236907Seric printf("Send Queue="); 82455012Seric printaddr(e->e_sendqueue, TRUE); 8255003Seric break; 8267275Seric 8277275Seric case CMDDBGDEBUG: /* set debug mode */ 8287676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 8297676Seric tTflag(p); 83058151Seric message("200 Debug set"); 8317275Seric break; 8327275Seric 83336230Skarels # else /* not SMTPDEBUG */ 83436230Skarels case CMDDBGQSHOW: /* show queues */ 83536230Skarels case CMDDBGDEBUG: /* set debug mode */ 83664685Seric # endif /* SMTPDEBUG */ 83764685Seric case CMDLOGBOGUS: /* bogus command */ 83836233Skarels # ifdef LOG 83958308Seric if (LogLevel > 0) 84064685Seric syslog(LOG_CRIT, 84158020Seric "\"%s\" command from %s (%s)", 84266005Seric c->cmdname, peerhostname, 84358755Seric anynet_ntoa(&RealHostAddr)); 84436233Skarels # endif 84536230Skarels /* FALL THROUGH */ 84636230Skarels 8474549Seric case CMDERROR: /* unknown command */ 84866283Seric if (++badcommands > MAXBADCOMMANDS) 84966283Seric { 85066283Seric message("421 %s Too many bad commands; closing connection", 85166283Seric MyHostName); 85266283Seric goto doquit; 85366283Seric } 85466283Seric 85558151Seric message("500 Command unrecognized"); 8564549Seric break; 8574549Seric 8584549Seric default: 85936230Skarels errno = 0; 86058151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8614549Seric break; 8624549Seric } 8634549Seric } 8644549Seric } 8654549Seric /* 8664549Seric ** SKIPWORD -- skip a fixed word. 8674549Seric ** 8684549Seric ** Parameters: 8694549Seric ** p -- place to start looking. 8704549Seric ** w -- word to skip. 8714549Seric ** 8724549Seric ** Returns: 8734549Seric ** p following w. 8744549Seric ** NULL on error. 8754549Seric ** 8764549Seric ** Side Effects: 8774549Seric ** clobbers the p data area. 8784549Seric */ 8794549Seric 8804549Seric static char * 8814549Seric skipword(p, w) 8824549Seric register char *p; 8834549Seric char *w; 8844549Seric { 8854549Seric register char *q; 88666005Seric char *firstp = p; 8874549Seric 8884549Seric /* find beginning of word */ 88958050Seric while (isascii(*p) && isspace(*p)) 8904549Seric p++; 8914549Seric q = p; 8924549Seric 8934549Seric /* find end of word */ 89458050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8954549Seric p++; 89658050Seric while (isascii(*p) && isspace(*p)) 8974549Seric *p++ = '\0'; 8984549Seric if (*p != ':') 8994549Seric { 9004549Seric syntax: 90166005Seric message("501 Syntax error in parameters scanning \"%s\"", 90266005Seric firstp); 9034549Seric Errors++; 9044549Seric return (NULL); 9054549Seric } 9064549Seric *p++ = '\0'; 90758050Seric while (isascii(*p) && isspace(*p)) 9084549Seric p++; 9094549Seric 91062373Seric if (*p == '\0') 91162373Seric goto syntax; 91262373Seric 9134549Seric /* see if the input word matches desired word */ 91433725Sbostic if (strcasecmp(q, w)) 9154549Seric goto syntax; 9164549Seric 9174549Seric return (p); 9184549Seric } 9194577Seric /* 920*67963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 921*67963Seric ** 922*67963Seric ** Parameters: 923*67963Seric ** a -- the address corresponding to the To: parameter. 924*67963Seric ** kp -- the parameter key. 925*67963Seric ** vp -- the value of that parameter. 926*67963Seric ** e -- the envelope. 927*67963Seric ** 928*67963Seric ** Returns: 929*67963Seric ** none. 930*67963Seric */ 931*67963Seric 932*67963Seric rcpt_esmtp_args(a, kp, vp, e) 933*67963Seric ADDRESS *a; 934*67963Seric char *kp; 935*67963Seric char *vp; 936*67963Seric ENVELOPE *e; 937*67963Seric { 938*67963Seric if (strcasecmp(kp, "notify") == 0) 939*67963Seric { 940*67963Seric char *p; 941*67963Seric 942*67963Seric if (vp == NULL) 943*67963Seric { 944*67963Seric usrerr("501 NOTIFY requires a value"); 945*67963Seric /* NOTREACHED */ 946*67963Seric } 947*67963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 948*67963Seric if (strcasecmp(vp, "never") == 0) 949*67963Seric return; 950*67963Seric for (p = vp; p != NULL; vp = p) 951*67963Seric { 952*67963Seric p = strchr(p, ','); 953*67963Seric if (p != NULL) 954*67963Seric *p++ = '\0'; 955*67963Seric if (strcasecmp(vp, "success") == 0) 956*67963Seric a->q_flags |= QPINGONSUCCESS; 957*67963Seric else if (strcasecmp(vp, "failure") == 0) 958*67963Seric a->q_flags |= QPINGONFAILURE; 959*67963Seric else if (strcasecmp(vp, "delay") == 0) 960*67963Seric a->q_flags |= QPINGONDELAY; 961*67963Seric else 962*67963Seric { 963*67963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 964*67963Seric vp); 965*67963Seric /* NOTREACHED */ 966*67963Seric } 967*67963Seric } 968*67963Seric } 969*67963Seric else if (strcasecmp(kp, "ret") == 0) 970*67963Seric { 971*67963Seric if (vp == NULL) 972*67963Seric { 973*67963Seric usrerr("501 RET requires a value"); 974*67963Seric /* NOTREACHED */ 975*67963Seric } 976*67963Seric a->q_flags |= QHAS_RET_PARAM; 977*67963Seric if (strcasecmp(vp, "hdrs") == 0) 978*67963Seric a->q_flags |= QRET_HDRS; 979*67963Seric else if (strcasecmp(vp, "full") != 0) 980*67963Seric { 981*67963Seric usrerr("501 Bad argument \"%s\" to RET", vp); 982*67963Seric /* NOTREACHED */ 983*67963Seric } 984*67963Seric } 985*67963Seric else if (strcasecmp(kp, "orcpt") == 0) 986*67963Seric { 987*67963Seric if (vp == NULL) 988*67963Seric { 989*67963Seric usrerr("501 ORCPT requires a value"); 990*67963Seric /* NOTREACHED */ 991*67963Seric } 992*67963Seric a->q_orcpt = newstr(vp); 993*67963Seric } 994*67963Seric else 995*67963Seric { 996*67963Seric usrerr("501 %s parameter unrecognized", kp); 997*67963Seric /* NOTREACHED */ 998*67963Seric } 999*67963Seric } 1000*67963Seric /* 100158151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 100258151Seric ** 100358151Seric ** Parameters: 100458151Seric ** a -- the address to print 100558151Seric ** last -- set if this is the last one. 100658151Seric ** 100758151Seric ** Returns: 100858151Seric ** none. 100958151Seric ** 101058151Seric ** Side Effects: 101158151Seric ** Prints the appropriate 250 codes. 101258151Seric */ 101358151Seric 101458151Seric printvrfyaddr(a, last) 101558151Seric register ADDRESS *a; 101658151Seric bool last; 101758151Seric { 101858151Seric char fmtbuf[20]; 101958151Seric 102058151Seric strcpy(fmtbuf, "250"); 102158151Seric fmtbuf[3] = last ? ' ' : '-'; 102258151Seric 102359746Seric if (a->q_fullname == NULL) 102459746Seric { 102559746Seric if (strchr(a->q_user, '@') == NULL) 102659746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 102759746Seric else 102859746Seric strcpy(&fmtbuf[4], "<%s>"); 102959746Seric message(fmtbuf, a->q_user, MyHostName); 103059746Seric } 103158151Seric else 103258151Seric { 103359746Seric if (strchr(a->q_user, '@') == NULL) 103459746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 103559746Seric else 103659746Seric strcpy(&fmtbuf[4], "%s <%s>"); 103759746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 103858151Seric } 103958151Seric } 104058151Seric /* 10414577Seric ** HELP -- implement the HELP command. 10424577Seric ** 10434577Seric ** Parameters: 10444577Seric ** topic -- the topic we want help for. 10454577Seric ** 10464577Seric ** Returns: 10474577Seric ** none. 10484577Seric ** 10494577Seric ** Side Effects: 10504577Seric ** outputs the help file to message output. 10514577Seric */ 10524577Seric 10534577Seric help(topic) 10544577Seric char *topic; 10554577Seric { 10564577Seric register FILE *hf; 10574577Seric int len; 10584577Seric char buf[MAXLINE]; 10594577Seric bool noinfo; 10604577Seric 10618269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 10624577Seric { 10634577Seric /* no help */ 106411931Seric errno = 0; 106558151Seric message("502 HELP not implemented"); 10664577Seric return; 10674577Seric } 10684577Seric 106949669Seric if (topic == NULL || *topic == '\0') 107049669Seric topic = "smtp"; 107149669Seric else 107249669Seric makelower(topic); 107349669Seric 10744577Seric len = strlen(topic); 10754577Seric noinfo = TRUE; 10764577Seric 10774577Seric while (fgets(buf, sizeof buf, hf) != NULL) 10784577Seric { 10794577Seric if (strncmp(buf, topic, len) == 0) 10804577Seric { 10814577Seric register char *p; 10824577Seric 108356795Seric p = strchr(buf, '\t'); 10844577Seric if (p == NULL) 10854577Seric p = buf; 10864577Seric else 10874577Seric p++; 10884577Seric fixcrlf(p, TRUE); 108958151Seric message("214-%s", p); 10904577Seric noinfo = FALSE; 10914577Seric } 10924577Seric } 10934577Seric 10944577Seric if (noinfo) 109558151Seric message("504 HELP topic unknown"); 10964577Seric else 109758151Seric message("214 End of HELP info"); 10984628Seric (void) fclose(hf); 10994577Seric } 11008544Seric /* 11019339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11029339Seric ** 11039339Seric ** Parameters: 11049339Seric ** label -- a string used in error messages 11059339Seric ** 11069339Seric ** Returns: 11079339Seric ** zero in the child 11089339Seric ** one in the parent 11099339Seric ** 11109339Seric ** Side Effects: 11119339Seric ** none. 11129339Seric */ 11138544Seric 111455012Seric runinchild(label, e) 11159339Seric char *label; 111655012Seric register ENVELOPE *e; 11179339Seric { 11189339Seric int childpid; 11199339Seric 112016158Seric if (!OneXact) 11219339Seric { 112216158Seric childpid = dofork(); 112316158Seric if (childpid < 0) 112416158Seric { 112516158Seric syserr("%s: cannot fork", label); 112616158Seric return (1); 112716158Seric } 112816158Seric if (childpid > 0) 112916158Seric { 113016158Seric auto int st; 11319339Seric 113216158Seric /* parent -- wait for child to complete */ 113361093Seric setproctitle("server %s child wait", CurHostName); 113416158Seric st = waitfor(childpid); 113516158Seric if (st == -1) 113616158Seric syserr("%s: lost child", label); 113764948Seric else if (!WIFEXITED(st)) 113864948Seric syserr("%s: died on signal %d", 113964948Seric label, st & 0177); 11409339Seric 114116158Seric /* if we exited on a QUIT command, complete the process */ 114266017Seric if (WEXITSTATUS(st) == EX_QUIT) 114366017Seric { 114466017Seric disconnect(1, e); 114516158Seric finis(); 114666017Seric } 11479339Seric 114816158Seric return (1); 114916158Seric } 115016158Seric else 115116158Seric { 115216158Seric /* child */ 115316158Seric InChild = TRUE; 115425050Seric QuickAbort = FALSE; 115555012Seric clearenvelope(e, FALSE); 115616158Seric } 11579339Seric } 115815256Seric 115916158Seric /* open alias database */ 116060537Seric initmaps(FALSE, e); 116116158Seric 116216158Seric return (0); 11639339Seric } 11649339Seric 116556795Seric # endif /* SMTP */ 1166