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*67417Seric static char sccsid[] = "@(#)srvrsmtp.c 8.40 (Berkeley) 06/17/94 (with SMTP)"; 1433731Sbostic #else 15*67417Seric static char sccsid[] = "@(#)srvrsmtp.c 8.40 (Berkeley) 06/17/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 } 26259016Seric sendinghost = newstr(p); 26360210Seric gothello = TRUE; 26460210Seric if (c->cmdcode != CMDEHLO) 26560239Seric { 26660239Seric /* print old message and be done with it */ 26760239Seric message("250 %s Hello %s, pleased to meet you", 26865017Seric MyHostName, CurSmtpClient); 26960210Seric break; 27060239Seric } 27160239Seric 27260239Seric /* print extended message and brag */ 27360239Seric message("250-%s Hello %s, pleased to meet you", 27466760Seric MyHostName, CurSmtpClient); 27558323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 27658323Seric message("250-EXPN"); 277*67417Seric #ifdef ADVERTISE_MIME 278*67417Seric message("250-8BITMIME"); 279*67417Seric #endif 28064359Seric if (MaxMessageSize > 0) 28164359Seric message("250-SIZE %ld", MaxMessageSize); 28259271Seric else 28359271Seric message("250-SIZE"); 28458323Seric message("250 HELP"); 2854976Seric break; 2864976Seric 2874549Seric case CMDMAIL: /* mail -- designate sender */ 28861093Seric SmtpPhase = "server MAIL"; 28924943Seric 2909314Seric /* check for validity of this command */ 29158789Seric if (!gothello) 29258082Seric { 29358957Seric /* set sending host to our known value */ 29459016Seric if (sendinghost == NULL) 29566005Seric sendinghost = peerhostname; 29658957Seric 29758789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 29858821Seric { 29958789Seric message("503 Polite people say HELO first"); 30058821Seric break; 30158821Seric } 30258082Seric } 30358109Seric if (gotmail) 3044558Seric { 30558151Seric message("503 Sender already specified"); 30663843Seric if (InChild) 30763843Seric finis(); 3084558Seric break; 3094558Seric } 3109339Seric if (InChild) 3119339Seric { 31236230Skarels errno = 0; 31358151Seric syserr("503 Nested MAIL command: MAIL %s", p); 31458069Seric finis(); 3159339Seric } 3169339Seric 3179339Seric /* fork a subprocess to process this command */ 31855012Seric if (runinchild("SMTP-MAIL", e) > 0) 3199339Seric break; 32063753Seric if (!gothello) 32163753Seric { 32263753Seric auth_warning(e, 32366005Seric "Host %s didn't use HELO protocol", 32466005Seric peerhostname); 32563753Seric } 32665947Seric #ifdef PICKY_HELO_CHECK 32766005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 32866005Seric (strcasecmp(peerhostname, "localhost") != 0 || 32965823Seric strcasecmp(sendinghost, MyHostName) != 0)) 33065823Seric { 33165823Seric auth_warning(e, "Host %s claimed to be %s", 33266005Seric peerhostname, sendinghost); 33365823Seric } 33465947Seric #endif 33565823Seric 33658323Seric if (protocol == NULL) 33758323Seric protocol = "SMTP"; 33858323Seric define('r', protocol, e); 33959016Seric define('s', sendinghost, e); 34055012Seric initsys(e); 34159747Seric nrcpts = 0; 34265089Seric e->e_flags |= EF_LOGSENDER; 34365058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3449339Seric 3459339Seric /* child -- go do the processing */ 3464549Seric p = skipword(p, "from"); 3474549Seric if (p == NULL) 3484549Seric break; 34957977Seric if (setjmp(TopFrame) > 0) 35058147Seric { 35158147Seric /* this failed -- undo work */ 35258147Seric if (InChild) 35359058Seric { 35459058Seric QuickAbort = FALSE; 35559058Seric SuprErrs = TRUE; 35663787Seric e->e_flags &= ~EF_FATALERRS; 35758147Seric finis(); 35859058Seric } 35957977Seric break; 36058147Seric } 36157977Seric QuickAbort = TRUE; 36258333Seric 36358333Seric /* must parse sender first */ 36458333Seric delimptr = NULL; 36558704Seric setsender(p, e, &delimptr, FALSE); 36658333Seric p = delimptr; 36758333Seric if (p != NULL && *p != '\0') 36858333Seric *p++ = '\0'; 36958333Seric 37066325Seric /* check for possible spoofing */ 37166325Seric if (RealUid != 0 && OpMode == MD_SMTP && 37266325Seric (e->e_from.q_mailer != LocalMailer && 37366325Seric strcmp(e->e_from.q_user, RealUserName) != 0)) 37466325Seric { 37566325Seric auth_warning(e, "%s owned process doing -bs", 37666325Seric RealUserName); 37766325Seric } 37866325Seric 37958333Seric /* now parse ESMTP arguments */ 38058333Seric msize = 0; 38166764Seric while (p != NULL && *p != '\0') 38258333Seric { 38358333Seric char *kp; 38466304Seric char *vp = NULL; 38558333Seric 38658333Seric /* locate the beginning of the keyword */ 38758333Seric while (isascii(*p) && isspace(*p)) 38858333Seric p++; 38958333Seric if (*p == '\0') 39058333Seric break; 39158333Seric kp = p; 39258333Seric 39358333Seric /* skip to the value portion */ 39458333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 39558333Seric p++; 39658333Seric if (*p == '=') 39758333Seric { 39858333Seric *p++ = '\0'; 39958333Seric vp = p; 40058333Seric 40158333Seric /* skip to the end of the value */ 40258333Seric while (*p != '\0' && *p != ' ' && 40358333Seric !(isascii(*p) && iscntrl(*p)) && 40458333Seric *p != '=') 40558333Seric p++; 40658333Seric } 40758333Seric 40858333Seric if (*p != '\0') 40958333Seric *p++ = '\0'; 41058333Seric 41158333Seric if (tTd(19, 1)) 41266764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 41358333Seric vp == NULL ? "<null>" : vp); 41458333Seric 41558333Seric if (strcasecmp(kp, "size") == 0) 41658333Seric { 41759093Seric if (vp == NULL) 41858333Seric { 41958333Seric usrerr("501 SIZE requires a value"); 42058333Seric /* NOTREACHED */ 42158333Seric } 42266772Seric # ifdef __STDC__ 42366772Seric msize = strtoul(vp, (char **) NULL, 10); 42466772Seric # else 42566772Seric msize = strtol(vp, (char **) NULL, 10); 42666772Seric # endif 42758333Seric } 42859093Seric else if (strcasecmp(kp, "body") == 0) 42959093Seric { 43059093Seric if (vp == NULL) 43159093Seric { 43259093Seric usrerr("501 BODY requires a value"); 43359093Seric /* NOTREACHED */ 43459093Seric } 435*67417Seric e->e_bodytype = newstr(vp); 43659093Seric if (strcasecmp(vp, "8bitmime") == 0) 43759093Seric { 43859709Seric SevenBit = FALSE; 43959093Seric } 44059093Seric else if (strcasecmp(vp, "7bit") == 0) 44159093Seric { 44259709Seric SevenBit = TRUE; 44359093Seric } 44459093Seric else 44559093Seric { 44659093Seric usrerr("501 Unknown BODY type %s", 44759093Seric vp); 448*67417Seric /* NOTREACHED */ 44959093Seric } 45059093Seric } 45158333Seric else 45258333Seric { 45358333Seric usrerr("501 %s parameter unrecognized", kp); 45458333Seric /* NOTREACHED */ 45558333Seric } 45658333Seric } 45759284Seric 45859284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 45959284Seric { 46059284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 46159284Seric MaxMessageSize); 46259284Seric /* NOTREACHED */ 46359284Seric } 46458333Seric 46558333Seric if (!enoughspace(msize)) 46658333Seric { 46758333Seric message("452 Insufficient disk space; try again later"); 46858333Seric break; 46958333Seric } 47058151Seric message("250 Sender ok"); 47158147Seric gotmail = TRUE; 4724549Seric break; 4734549Seric 4744976Seric case CMDRCPT: /* rcpt -- designate recipient */ 47558850Seric if (!gotmail) 47658850Seric { 47758850Seric usrerr("503 Need MAIL before RCPT"); 47858850Seric break; 47958850Seric } 48061093Seric SmtpPhase = "server RCPT"; 48112612Seric if (setjmp(TopFrame) > 0) 48214785Seric { 48355012Seric e->e_flags &= ~EF_FATALERRS; 48412612Seric break; 48514785Seric } 48612612Seric QuickAbort = TRUE; 48751951Seric LogUsrErrs = TRUE; 48858093Seric 48959699Seric if (e->e_sendmode != SM_DELIVER) 49059699Seric e->e_flags |= EF_VRFYONLY; 49158919Seric 4924549Seric p = skipword(p, "to"); 4934549Seric if (p == NULL) 4944549Seric break; 49564284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 49612612Seric if (a == NULL) 49712612Seric break; 49816886Seric a->q_flags |= QPRIMARY; 49955012Seric a = recipient(a, &e->e_sendqueue, e); 50012612Seric if (Errors != 0) 50112612Seric break; 50212612Seric 50312612Seric /* no errors during parsing, but might be a duplicate */ 50455012Seric e->e_to = p; 50512612Seric if (!bitset(QBADADDR, a->q_flags)) 50659747Seric { 50764718Seric message("250 Recipient ok%s", 50864718Seric bitset(QQUEUEUP, a->q_flags) ? 50964718Seric " (will queue)" : ""); 51059747Seric nrcpts++; 51159747Seric } 51212612Seric else 5134549Seric { 51412612Seric /* punt -- should keep message in ADDRESS.... */ 51558151Seric message("550 Addressee unknown"); 5164549Seric } 51755012Seric e->e_to = NULL; 5184549Seric break; 5194549Seric 5204549Seric case CMDDATA: /* data -- text of mail */ 52161093Seric SmtpPhase = "server DATA"; 52258109Seric if (!gotmail) 5234549Seric { 52458151Seric message("503 Need MAIL command"); 5254976Seric break; 5264549Seric } 52764718Seric else if (nrcpts <= 0) 5284549Seric { 52958151Seric message("503 Need RCPT (recipient)"); 5304976Seric break; 5314549Seric } 5324976Seric 53358929Seric /* check to see if we need to re-expand aliases */ 53463787Seric /* also reset QBADADDR on already-diagnosted addrs */ 53563787Seric doublequeue = FALSE; 53658929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 53758929Seric { 53858929Seric if (bitset(QVERIFIED, a->q_flags)) 53963787Seric { 54063787Seric /* need to re-expand aliases */ 54163787Seric doublequeue = TRUE; 54263787Seric } 54363787Seric if (bitset(QBADADDR, a->q_flags)) 54463787Seric { 54563787Seric /* make this "go away" */ 54663787Seric a->q_flags |= QDONTSEND; 54763787Seric a->q_flags &= ~QBADADDR; 54863787Seric } 54958929Seric } 55058929Seric 5514976Seric /* collect the text of the message */ 55224943Seric SmtpPhase = "collect"; 55364766Seric collect(TRUE, doublequeue, e); 55464766Seric if (Errors != 0) 55564766Seric goto abortmessage; 55667131Seric 55767131Seric /* from now on, we have to operate silently */ 55863965Seric HoldErrs = TRUE; 55967131Seric e->e_errormode = EM_MAIL; 5604976Seric 5618238Seric /* 5628238Seric ** Arrange to send to everyone. 5638238Seric ** If sending to multiple people, mail back 5648238Seric ** errors rather than reporting directly. 5658238Seric ** In any case, don't mail back errors for 5668238Seric ** anything that has happened up to 5678238Seric ** now (the other end will do this). 56810197Seric ** Truncate our transcript -- the mail has gotten 56910197Seric ** to us successfully, and if we have 57010197Seric ** to mail this back, it will be easier 57110197Seric ** on the reader. 5728238Seric ** Then send to everyone. 5738238Seric ** Finally give a reply code. If an error has 5748238Seric ** already been given, don't mail a 5758238Seric ** message back. 5769339Seric ** We goose error returns by clearing error bit. 5778238Seric */ 5788238Seric 57924943Seric SmtpPhase = "delivery"; 58055012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 58158714Seric id = e->e_id; 5824976Seric 58367131Seric if (doublequeue) 58459730Seric { 58567131Seric /* make sure it is in the queue */ 58667131Seric queueup(e, TRUE, FALSE); 58759730Seric } 5888238Seric else 58958919Seric { 59067131Seric /* send to all recipients */ 59167131Seric sendall(e, SM_DEFAULT); 59267131Seric } 59367131Seric e->e_to = NULL; 59459747Seric 59567131Seric /* issue success message */ 59667131Seric message("250 %s Message accepted for delivery", id); 59764296Seric 59867131Seric /* if we just queued, poke it */ 59967131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 60067131Seric { 60167131Seric extern pid_t dowork(); 60267131Seric 60367131Seric unlockqueue(e); 60467131Seric (void) dowork(id, TRUE, TRUE, e); 60558919Seric } 60658883Seric 60759747Seric abortmessage: 6089339Seric /* if in a child, pop back to our parent */ 6099339Seric if (InChild) 6109339Seric finis(); 61124943Seric 61224943Seric /* clean up a bit */ 61358109Seric gotmail = FALSE; 61455012Seric dropenvelope(e); 61558179Seric CurEnv = e = newenvelope(e, CurEnv); 61655012Seric e->e_flags = BlankEnvelope.e_flags; 6174549Seric break; 6184549Seric 6194549Seric case CMDRSET: /* rset -- reset state */ 62058151Seric message("250 Reset state"); 62164359Seric e->e_flags |= EF_CLRQUEUE; 6229339Seric if (InChild) 6239339Seric finis(); 62458109Seric 62558109Seric /* clean up a bit */ 62658109Seric gotmail = FALSE; 62758109Seric dropenvelope(e); 62858179Seric CurEnv = e = newenvelope(e, CurEnv); 6299339Seric break; 6304549Seric 6314549Seric case CMDVRFY: /* vrfy -- verify address */ 63258092Seric case CMDEXPN: /* expn -- expand address */ 63358092Seric vrfy = c->cmdcode == CMDVRFY; 63458092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 63558092Seric PrivacyFlags)) 63658082Seric { 63758412Seric if (vrfy) 63867160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 63958412Seric else 64065192Seric message("502 Sorry, we do not allow this operation"); 64165017Seric #ifdef LOG 64265017Seric if (LogLevel > 5) 64365017Seric syslog(LOG_INFO, "%s: %s [rejected]", 64465017Seric CurSmtpClient, inp); 64565017Seric #endif 64658082Seric break; 64758082Seric } 64858082Seric else if (!gothello && 64958092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 65058092Seric PrivacyFlags)) 65158082Seric { 65258151Seric message("503 I demand that you introduce yourself first"); 65358082Seric break; 65458082Seric } 65558092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6569339Seric break; 65755173Seric #ifdef LOG 65858020Seric if (LogLevel > 5) 65965017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 66055173Seric #endif 6615003Seric vrfyqueue = NULL; 6627762Seric QuickAbort = TRUE; 66358092Seric if (vrfy) 66458092Seric e->e_flags |= EF_VRFYONLY; 66562373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 66662373Seric *p++; 66762373Seric if (*p == '\0') 66862373Seric { 66962373Seric message("501 Argument required"); 67062373Seric Errors++; 67162373Seric } 67262373Seric else 67362373Seric { 67464284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 67562373Seric } 6767762Seric if (Errors != 0) 6779339Seric { 6789339Seric if (InChild) 6799339Seric finis(); 6807762Seric break; 6819339Seric } 68262373Seric if (vrfyqueue == NULL) 68362373Seric { 68462373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 68562373Seric } 6865003Seric while (vrfyqueue != NULL) 6875003Seric { 68863971Seric a = vrfyqueue; 68963971Seric while ((a = a->q_next) != NULL && 69063971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 69163971Seric continue; 6927685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 69358151Seric printvrfyaddr(vrfyqueue, a == NULL); 69463847Seric vrfyqueue = vrfyqueue->q_next; 6955003Seric } 6969339Seric if (InChild) 6979339Seric finis(); 6984549Seric break; 6994549Seric 7004549Seric case CMDHELP: /* help -- give user info */ 7014577Seric help(p); 7024549Seric break; 7034549Seric 7044549Seric case CMDNOOP: /* noop -- do nothing */ 70564122Seric message("250 OK"); 7064549Seric break; 7074549Seric 7084549Seric case CMDQUIT: /* quit -- leave mail */ 70958151Seric message("221 %s closing connection", MyHostName); 71061051Seric 71166283Seric doquit: 71261051Seric /* avoid future 050 messages */ 71366017Seric disconnect(1, e); 71461051Seric 7159339Seric if (InChild) 7169339Seric ExitStat = EX_QUIT; 7174549Seric finis(); 7184549Seric 7198544Seric case CMDVERB: /* set verbose mode */ 72059957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 72159957Seric { 72259957Seric /* this would give out the same info */ 72359957Seric message("502 Verbose unavailable"); 72459957Seric break; 72559957Seric } 7268544Seric Verbose = TRUE; 72758734Seric e->e_sendmode = SM_DELIVER; 72859957Seric message("250 Verbose mode"); 7298544Seric break; 7308544Seric 7319314Seric case CMDONEX: /* doing one transaction only */ 7329378Seric OneXact = TRUE; 73359957Seric message("250 Only one transaction"); 7349314Seric break; 7359314Seric 73636230Skarels # ifdef SMTPDEBUG 7379339Seric case CMDDBGQSHOW: /* show queues */ 7386907Seric printf("Send Queue="); 73955012Seric printaddr(e->e_sendqueue, TRUE); 7405003Seric break; 7417275Seric 7427275Seric case CMDDBGDEBUG: /* set debug mode */ 7437676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7447676Seric tTflag(p); 74558151Seric message("200 Debug set"); 7467275Seric break; 7477275Seric 74836230Skarels # else /* not SMTPDEBUG */ 74936230Skarels case CMDDBGQSHOW: /* show queues */ 75036230Skarels case CMDDBGDEBUG: /* set debug mode */ 75164685Seric # endif /* SMTPDEBUG */ 75264685Seric case CMDLOGBOGUS: /* bogus command */ 75336233Skarels # ifdef LOG 75458308Seric if (LogLevel > 0) 75564685Seric syslog(LOG_CRIT, 75658020Seric "\"%s\" command from %s (%s)", 75766005Seric c->cmdname, peerhostname, 75858755Seric anynet_ntoa(&RealHostAddr)); 75936233Skarels # endif 76036230Skarels /* FALL THROUGH */ 76136230Skarels 7624549Seric case CMDERROR: /* unknown command */ 76366283Seric if (++badcommands > MAXBADCOMMANDS) 76466283Seric { 76566283Seric message("421 %s Too many bad commands; closing connection", 76666283Seric MyHostName); 76766283Seric goto doquit; 76866283Seric } 76966283Seric 77058151Seric message("500 Command unrecognized"); 7714549Seric break; 7724549Seric 7734549Seric default: 77436230Skarels errno = 0; 77558151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7764549Seric break; 7774549Seric } 7784549Seric } 7794549Seric } 7804549Seric /* 7814549Seric ** SKIPWORD -- skip a fixed word. 7824549Seric ** 7834549Seric ** Parameters: 7844549Seric ** p -- place to start looking. 7854549Seric ** w -- word to skip. 7864549Seric ** 7874549Seric ** Returns: 7884549Seric ** p following w. 7894549Seric ** NULL on error. 7904549Seric ** 7914549Seric ** Side Effects: 7924549Seric ** clobbers the p data area. 7934549Seric */ 7944549Seric 7954549Seric static char * 7964549Seric skipword(p, w) 7974549Seric register char *p; 7984549Seric char *w; 7994549Seric { 8004549Seric register char *q; 80166005Seric char *firstp = p; 8024549Seric 8034549Seric /* find beginning of word */ 80458050Seric while (isascii(*p) && isspace(*p)) 8054549Seric p++; 8064549Seric q = p; 8074549Seric 8084549Seric /* find end of word */ 80958050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8104549Seric p++; 81158050Seric while (isascii(*p) && isspace(*p)) 8124549Seric *p++ = '\0'; 8134549Seric if (*p != ':') 8144549Seric { 8154549Seric syntax: 81666005Seric message("501 Syntax error in parameters scanning \"%s\"", 81766005Seric firstp); 8184549Seric Errors++; 8194549Seric return (NULL); 8204549Seric } 8214549Seric *p++ = '\0'; 82258050Seric while (isascii(*p) && isspace(*p)) 8234549Seric p++; 8244549Seric 82562373Seric if (*p == '\0') 82662373Seric goto syntax; 82762373Seric 8284549Seric /* see if the input word matches desired word */ 82933725Sbostic if (strcasecmp(q, w)) 8304549Seric goto syntax; 8314549Seric 8324549Seric return (p); 8334549Seric } 8344577Seric /* 83558151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 83658151Seric ** 83758151Seric ** Parameters: 83858151Seric ** a -- the address to print 83958151Seric ** last -- set if this is the last one. 84058151Seric ** 84158151Seric ** Returns: 84258151Seric ** none. 84358151Seric ** 84458151Seric ** Side Effects: 84558151Seric ** Prints the appropriate 250 codes. 84658151Seric */ 84758151Seric 84858151Seric printvrfyaddr(a, last) 84958151Seric register ADDRESS *a; 85058151Seric bool last; 85158151Seric { 85258151Seric char fmtbuf[20]; 85358151Seric 85458151Seric strcpy(fmtbuf, "250"); 85558151Seric fmtbuf[3] = last ? ' ' : '-'; 85658151Seric 85759746Seric if (a->q_fullname == NULL) 85859746Seric { 85959746Seric if (strchr(a->q_user, '@') == NULL) 86059746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 86159746Seric else 86259746Seric strcpy(&fmtbuf[4], "<%s>"); 86359746Seric message(fmtbuf, a->q_user, MyHostName); 86459746Seric } 86558151Seric else 86658151Seric { 86759746Seric if (strchr(a->q_user, '@') == NULL) 86859746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 86959746Seric else 87059746Seric strcpy(&fmtbuf[4], "%s <%s>"); 87159746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 87258151Seric } 87358151Seric } 87458151Seric /* 8754577Seric ** HELP -- implement the HELP command. 8764577Seric ** 8774577Seric ** Parameters: 8784577Seric ** topic -- the topic we want help for. 8794577Seric ** 8804577Seric ** Returns: 8814577Seric ** none. 8824577Seric ** 8834577Seric ** Side Effects: 8844577Seric ** outputs the help file to message output. 8854577Seric */ 8864577Seric 8874577Seric help(topic) 8884577Seric char *topic; 8894577Seric { 8904577Seric register FILE *hf; 8914577Seric int len; 8924577Seric char buf[MAXLINE]; 8934577Seric bool noinfo; 8944577Seric 8958269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8964577Seric { 8974577Seric /* no help */ 89811931Seric errno = 0; 89958151Seric message("502 HELP not implemented"); 9004577Seric return; 9014577Seric } 9024577Seric 90349669Seric if (topic == NULL || *topic == '\0') 90449669Seric topic = "smtp"; 90549669Seric else 90649669Seric makelower(topic); 90749669Seric 9084577Seric len = strlen(topic); 9094577Seric noinfo = TRUE; 9104577Seric 9114577Seric while (fgets(buf, sizeof buf, hf) != NULL) 9124577Seric { 9134577Seric if (strncmp(buf, topic, len) == 0) 9144577Seric { 9154577Seric register char *p; 9164577Seric 91756795Seric p = strchr(buf, '\t'); 9184577Seric if (p == NULL) 9194577Seric p = buf; 9204577Seric else 9214577Seric p++; 9224577Seric fixcrlf(p, TRUE); 92358151Seric message("214-%s", p); 9244577Seric noinfo = FALSE; 9254577Seric } 9264577Seric } 9274577Seric 9284577Seric if (noinfo) 92958151Seric message("504 HELP topic unknown"); 9304577Seric else 93158151Seric message("214 End of HELP info"); 9324628Seric (void) fclose(hf); 9334577Seric } 9348544Seric /* 9359339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 9369339Seric ** 9379339Seric ** Parameters: 9389339Seric ** label -- a string used in error messages 9399339Seric ** 9409339Seric ** Returns: 9419339Seric ** zero in the child 9429339Seric ** one in the parent 9439339Seric ** 9449339Seric ** Side Effects: 9459339Seric ** none. 9469339Seric */ 9478544Seric 94855012Seric runinchild(label, e) 9499339Seric char *label; 95055012Seric register ENVELOPE *e; 9519339Seric { 9529339Seric int childpid; 9539339Seric 95416158Seric if (!OneXact) 9559339Seric { 95616158Seric childpid = dofork(); 95716158Seric if (childpid < 0) 95816158Seric { 95916158Seric syserr("%s: cannot fork", label); 96016158Seric return (1); 96116158Seric } 96216158Seric if (childpid > 0) 96316158Seric { 96416158Seric auto int st; 9659339Seric 96616158Seric /* parent -- wait for child to complete */ 96761093Seric setproctitle("server %s child wait", CurHostName); 96816158Seric st = waitfor(childpid); 96916158Seric if (st == -1) 97016158Seric syserr("%s: lost child", label); 97164948Seric else if (!WIFEXITED(st)) 97264948Seric syserr("%s: died on signal %d", 97364948Seric label, st & 0177); 9749339Seric 97516158Seric /* if we exited on a QUIT command, complete the process */ 97666017Seric if (WEXITSTATUS(st) == EX_QUIT) 97766017Seric { 97866017Seric disconnect(1, e); 97916158Seric finis(); 98066017Seric } 9819339Seric 98216158Seric return (1); 98316158Seric } 98416158Seric else 98516158Seric { 98616158Seric /* child */ 98716158Seric InChild = TRUE; 98825050Seric QuickAbort = FALSE; 98955012Seric clearenvelope(e, FALSE); 99016158Seric } 9919339Seric } 99215256Seric 99316158Seric /* open alias database */ 99460537Seric initmaps(FALSE, e); 99516158Seric 99616158Seric return (0); 9979339Seric } 9989339Seric 99956795Seric # endif /* SMTP */ 1000