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*67546Seric static char sccsid[] = "@(#)srvrsmtp.c 8.43 (Berkeley) 07/23/94 (with SMTP)"; 1433731Sbostic #else 15*67546Seric static char sccsid[] = "@(#)srvrsmtp.c 8.43 (Berkeley) 07/23/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 #ifdef ADVERTISE_MIME 30667417Seric message("250-8BITMIME"); 30767417Seric #endif 30864359Seric if (MaxMessageSize > 0) 30964359Seric message("250-SIZE %ld", MaxMessageSize); 31059271Seric else 31159271Seric message("250-SIZE"); 31258323Seric message("250 HELP"); 3134976Seric break; 3144976Seric 3154549Seric case CMDMAIL: /* mail -- designate sender */ 31661093Seric SmtpPhase = "server MAIL"; 31724943Seric 3189314Seric /* check for validity of this command */ 31958789Seric if (!gothello) 32058082Seric { 32158957Seric /* set sending host to our known value */ 32259016Seric if (sendinghost == NULL) 32366005Seric sendinghost = peerhostname; 32458957Seric 32558789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 32658821Seric { 32758789Seric message("503 Polite people say HELO first"); 32858821Seric break; 32958821Seric } 33058082Seric } 33158109Seric if (gotmail) 3324558Seric { 33358151Seric message("503 Sender already specified"); 33463843Seric if (InChild) 33563843Seric finis(); 3364558Seric break; 3374558Seric } 3389339Seric if (InChild) 3399339Seric { 34036230Skarels errno = 0; 34158151Seric syserr("503 Nested MAIL command: MAIL %s", p); 34258069Seric finis(); 3439339Seric } 3449339Seric 3459339Seric /* fork a subprocess to process this command */ 34655012Seric if (runinchild("SMTP-MAIL", e) > 0) 3479339Seric break; 34863753Seric if (!gothello) 34963753Seric { 35063753Seric auth_warning(e, 35166005Seric "Host %s didn't use HELO protocol", 35266005Seric peerhostname); 35363753Seric } 35465947Seric #ifdef PICKY_HELO_CHECK 35566005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 35666005Seric (strcasecmp(peerhostname, "localhost") != 0 || 35765823Seric strcasecmp(sendinghost, MyHostName) != 0)) 35865823Seric { 35965823Seric auth_warning(e, "Host %s claimed to be %s", 36066005Seric peerhostname, sendinghost); 36165823Seric } 36265947Seric #endif 36365823Seric 36458323Seric if (protocol == NULL) 36558323Seric protocol = "SMTP"; 36658323Seric define('r', protocol, e); 36759016Seric define('s', sendinghost, e); 36855012Seric initsys(e); 36959747Seric nrcpts = 0; 37065089Seric e->e_flags |= EF_LOGSENDER; 37165058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3729339Seric 3739339Seric /* child -- go do the processing */ 3744549Seric p = skipword(p, "from"); 3754549Seric if (p == NULL) 3764549Seric break; 37757977Seric if (setjmp(TopFrame) > 0) 37858147Seric { 37958147Seric /* this failed -- undo work */ 38058147Seric if (InChild) 38159058Seric { 38259058Seric QuickAbort = FALSE; 38359058Seric SuprErrs = TRUE; 38463787Seric e->e_flags &= ~EF_FATALERRS; 38558147Seric finis(); 38659058Seric } 38757977Seric break; 38858147Seric } 38957977Seric QuickAbort = TRUE; 39058333Seric 39158333Seric /* must parse sender first */ 39258333Seric delimptr = NULL; 39358704Seric setsender(p, e, &delimptr, FALSE); 39458333Seric p = delimptr; 39558333Seric if (p != NULL && *p != '\0') 39658333Seric *p++ = '\0'; 39758333Seric 39866325Seric /* check for possible spoofing */ 39966325Seric if (RealUid != 0 && OpMode == MD_SMTP && 40067473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 40167473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 40266325Seric { 40366325Seric auth_warning(e, "%s owned process doing -bs", 40466325Seric RealUserName); 40566325Seric } 40666325Seric 40758333Seric /* now parse ESMTP arguments */ 40858333Seric msize = 0; 40966764Seric while (p != NULL && *p != '\0') 41058333Seric { 41158333Seric char *kp; 41266304Seric char *vp = NULL; 41358333Seric 41458333Seric /* locate the beginning of the keyword */ 41558333Seric while (isascii(*p) && isspace(*p)) 41658333Seric p++; 41758333Seric if (*p == '\0') 41858333Seric break; 41958333Seric kp = p; 42058333Seric 42158333Seric /* skip to the value portion */ 42258333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 42358333Seric p++; 42458333Seric if (*p == '=') 42558333Seric { 42658333Seric *p++ = '\0'; 42758333Seric vp = p; 42858333Seric 42958333Seric /* skip to the end of the value */ 43058333Seric while (*p != '\0' && *p != ' ' && 43158333Seric !(isascii(*p) && iscntrl(*p)) && 43258333Seric *p != '=') 43358333Seric p++; 43458333Seric } 43558333Seric 43658333Seric if (*p != '\0') 43758333Seric *p++ = '\0'; 43858333Seric 43958333Seric if (tTd(19, 1)) 44066764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 44158333Seric vp == NULL ? "<null>" : vp); 44258333Seric 44358333Seric if (strcasecmp(kp, "size") == 0) 44458333Seric { 44559093Seric if (vp == NULL) 44658333Seric { 44758333Seric usrerr("501 SIZE requires a value"); 44858333Seric /* NOTREACHED */ 44958333Seric } 45066772Seric # ifdef __STDC__ 45166772Seric msize = strtoul(vp, (char **) NULL, 10); 45266772Seric # else 45366772Seric msize = strtol(vp, (char **) NULL, 10); 45466772Seric # endif 45558333Seric } 45659093Seric else if (strcasecmp(kp, "body") == 0) 45759093Seric { 45859093Seric if (vp == NULL) 45959093Seric { 46059093Seric usrerr("501 BODY requires a value"); 46159093Seric /* NOTREACHED */ 46259093Seric } 46367417Seric e->e_bodytype = newstr(vp); 46459093Seric if (strcasecmp(vp, "8bitmime") == 0) 46559093Seric { 466*67546Seric SevenBitInput = FALSE; 46759093Seric } 46859093Seric else if (strcasecmp(vp, "7bit") == 0) 46959093Seric { 470*67546Seric SevenBitInput = TRUE; 47159093Seric } 47259093Seric else 47359093Seric { 47459093Seric usrerr("501 Unknown BODY type %s", 47559093Seric vp); 47667417Seric /* NOTREACHED */ 47759093Seric } 47859093Seric } 47958333Seric else 48058333Seric { 48158333Seric usrerr("501 %s parameter unrecognized", kp); 48258333Seric /* NOTREACHED */ 48358333Seric } 48458333Seric } 48559284Seric 48659284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 48759284Seric { 48859284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 48959284Seric MaxMessageSize); 49059284Seric /* NOTREACHED */ 49159284Seric } 49258333Seric 49358333Seric if (!enoughspace(msize)) 49458333Seric { 49558333Seric message("452 Insufficient disk space; try again later"); 49658333Seric break; 49758333Seric } 49858151Seric message("250 Sender ok"); 49958147Seric gotmail = TRUE; 5004549Seric break; 5014549Seric 5024976Seric case CMDRCPT: /* rcpt -- designate recipient */ 50358850Seric if (!gotmail) 50458850Seric { 50558850Seric usrerr("503 Need MAIL before RCPT"); 50658850Seric break; 50758850Seric } 50861093Seric SmtpPhase = "server RCPT"; 50912612Seric if (setjmp(TopFrame) > 0) 51014785Seric { 51155012Seric e->e_flags &= ~EF_FATALERRS; 51212612Seric break; 51314785Seric } 51412612Seric QuickAbort = TRUE; 51551951Seric LogUsrErrs = TRUE; 51658093Seric 51759699Seric if (e->e_sendmode != SM_DELIVER) 51859699Seric e->e_flags |= EF_VRFYONLY; 51958919Seric 5204549Seric p = skipword(p, "to"); 5214549Seric if (p == NULL) 5224549Seric break; 52364284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 52412612Seric if (a == NULL) 52512612Seric break; 52616886Seric a->q_flags |= QPRIMARY; 52755012Seric a = recipient(a, &e->e_sendqueue, e); 52812612Seric if (Errors != 0) 52912612Seric break; 53012612Seric 53112612Seric /* no errors during parsing, but might be a duplicate */ 53255012Seric e->e_to = p; 53312612Seric if (!bitset(QBADADDR, a->q_flags)) 53459747Seric { 53564718Seric message("250 Recipient ok%s", 53664718Seric bitset(QQUEUEUP, a->q_flags) ? 53764718Seric " (will queue)" : ""); 53859747Seric nrcpts++; 53959747Seric } 54012612Seric else 5414549Seric { 54212612Seric /* punt -- should keep message in ADDRESS.... */ 54358151Seric message("550 Addressee unknown"); 5444549Seric } 54555012Seric e->e_to = NULL; 5464549Seric break; 5474549Seric 5484549Seric case CMDDATA: /* data -- text of mail */ 54961093Seric SmtpPhase = "server DATA"; 55058109Seric if (!gotmail) 5514549Seric { 55258151Seric message("503 Need MAIL command"); 5534976Seric break; 5544549Seric } 55564718Seric else if (nrcpts <= 0) 5564549Seric { 55758151Seric message("503 Need RCPT (recipient)"); 5584976Seric break; 5594549Seric } 5604976Seric 56158929Seric /* check to see if we need to re-expand aliases */ 56263787Seric /* also reset QBADADDR on already-diagnosted addrs */ 56363787Seric doublequeue = FALSE; 56458929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 56558929Seric { 56658929Seric if (bitset(QVERIFIED, a->q_flags)) 56763787Seric { 56863787Seric /* need to re-expand aliases */ 56963787Seric doublequeue = TRUE; 57063787Seric } 57163787Seric if (bitset(QBADADDR, a->q_flags)) 57263787Seric { 57363787Seric /* make this "go away" */ 57463787Seric a->q_flags |= QDONTSEND; 57563787Seric a->q_flags &= ~QBADADDR; 57663787Seric } 57758929Seric } 57858929Seric 5794976Seric /* collect the text of the message */ 58024943Seric SmtpPhase = "collect"; 581*67546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 58264766Seric if (Errors != 0) 58364766Seric goto abortmessage; 58467131Seric 58567131Seric /* from now on, we have to operate silently */ 58663965Seric HoldErrs = TRUE; 58767131Seric e->e_errormode = EM_MAIL; 5884976Seric 5898238Seric /* 5908238Seric ** Arrange to send to everyone. 5918238Seric ** If sending to multiple people, mail back 5928238Seric ** errors rather than reporting directly. 5938238Seric ** In any case, don't mail back errors for 5948238Seric ** anything that has happened up to 5958238Seric ** now (the other end will do this). 59610197Seric ** Truncate our transcript -- the mail has gotten 59710197Seric ** to us successfully, and if we have 59810197Seric ** to mail this back, it will be easier 59910197Seric ** on the reader. 6008238Seric ** Then send to everyone. 6018238Seric ** Finally give a reply code. If an error has 6028238Seric ** already been given, don't mail a 6038238Seric ** message back. 6049339Seric ** We goose error returns by clearing error bit. 6058238Seric */ 6068238Seric 60724943Seric SmtpPhase = "delivery"; 60855012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 60958714Seric id = e->e_id; 6104976Seric 61167131Seric if (doublequeue) 61259730Seric { 61367131Seric /* make sure it is in the queue */ 61467131Seric queueup(e, TRUE, FALSE); 61559730Seric } 6168238Seric else 61758919Seric { 61867131Seric /* send to all recipients */ 61967131Seric sendall(e, SM_DEFAULT); 62067131Seric } 62167131Seric e->e_to = NULL; 62259747Seric 62367131Seric /* issue success message */ 62467131Seric message("250 %s Message accepted for delivery", id); 62564296Seric 62667131Seric /* if we just queued, poke it */ 62767131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 62867131Seric { 62967131Seric extern pid_t dowork(); 63067131Seric 63167131Seric unlockqueue(e); 63267131Seric (void) dowork(id, TRUE, TRUE, e); 63358919Seric } 63458883Seric 63559747Seric abortmessage: 6369339Seric /* if in a child, pop back to our parent */ 6379339Seric if (InChild) 6389339Seric finis(); 63924943Seric 64024943Seric /* clean up a bit */ 64158109Seric gotmail = FALSE; 64255012Seric dropenvelope(e); 64358179Seric CurEnv = e = newenvelope(e, CurEnv); 64455012Seric e->e_flags = BlankEnvelope.e_flags; 6454549Seric break; 6464549Seric 6474549Seric case CMDRSET: /* rset -- reset state */ 64858151Seric message("250 Reset state"); 64964359Seric e->e_flags |= EF_CLRQUEUE; 6509339Seric if (InChild) 6519339Seric finis(); 65258109Seric 65358109Seric /* clean up a bit */ 65458109Seric gotmail = FALSE; 65558109Seric dropenvelope(e); 65658179Seric CurEnv = e = newenvelope(e, CurEnv); 6579339Seric break; 6584549Seric 6594549Seric case CMDVRFY: /* vrfy -- verify address */ 66058092Seric case CMDEXPN: /* expn -- expand address */ 66158092Seric vrfy = c->cmdcode == CMDVRFY; 66258092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 66358092Seric PrivacyFlags)) 66458082Seric { 66558412Seric if (vrfy) 66667160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 66758412Seric else 66865192Seric message("502 Sorry, we do not allow this operation"); 66965017Seric #ifdef LOG 67065017Seric if (LogLevel > 5) 67165017Seric syslog(LOG_INFO, "%s: %s [rejected]", 67265017Seric CurSmtpClient, inp); 67365017Seric #endif 67458082Seric break; 67558082Seric } 67658082Seric else if (!gothello && 67758092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 67858092Seric PrivacyFlags)) 67958082Seric { 68058151Seric message("503 I demand that you introduce yourself first"); 68158082Seric break; 68258082Seric } 68358092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6849339Seric break; 68555173Seric #ifdef LOG 68658020Seric if (LogLevel > 5) 68765017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 68855173Seric #endif 6895003Seric vrfyqueue = NULL; 6907762Seric QuickAbort = TRUE; 69158092Seric if (vrfy) 69258092Seric e->e_flags |= EF_VRFYONLY; 69362373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 69462373Seric *p++; 69562373Seric if (*p == '\0') 69662373Seric { 69762373Seric message("501 Argument required"); 69862373Seric Errors++; 69962373Seric } 70062373Seric else 70162373Seric { 70264284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 70362373Seric } 7047762Seric if (Errors != 0) 7059339Seric { 7069339Seric if (InChild) 7079339Seric finis(); 7087762Seric break; 7099339Seric } 71062373Seric if (vrfyqueue == NULL) 71162373Seric { 71262373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 71362373Seric } 7145003Seric while (vrfyqueue != NULL) 7155003Seric { 71663971Seric a = vrfyqueue; 71763971Seric while ((a = a->q_next) != NULL && 71863971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 71963971Seric continue; 7207685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 72158151Seric printvrfyaddr(vrfyqueue, a == NULL); 72263847Seric vrfyqueue = vrfyqueue->q_next; 7235003Seric } 7249339Seric if (InChild) 7259339Seric finis(); 7264549Seric break; 7274549Seric 7284549Seric case CMDHELP: /* help -- give user info */ 7294577Seric help(p); 7304549Seric break; 7314549Seric 7324549Seric case CMDNOOP: /* noop -- do nothing */ 73364122Seric message("250 OK"); 7344549Seric break; 7354549Seric 7364549Seric case CMDQUIT: /* quit -- leave mail */ 73758151Seric message("221 %s closing connection", MyHostName); 73861051Seric 73966283Seric doquit: 74061051Seric /* avoid future 050 messages */ 74166017Seric disconnect(1, e); 74261051Seric 7439339Seric if (InChild) 7449339Seric ExitStat = EX_QUIT; 7454549Seric finis(); 7464549Seric 7478544Seric case CMDVERB: /* set verbose mode */ 74859957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 74959957Seric { 75059957Seric /* this would give out the same info */ 75159957Seric message("502 Verbose unavailable"); 75259957Seric break; 75359957Seric } 7548544Seric Verbose = TRUE; 75558734Seric e->e_sendmode = SM_DELIVER; 75659957Seric message("250 Verbose mode"); 7578544Seric break; 7588544Seric 7599314Seric case CMDONEX: /* doing one transaction only */ 7609378Seric OneXact = TRUE; 76159957Seric message("250 Only one transaction"); 7629314Seric break; 7639314Seric 76436230Skarels # ifdef SMTPDEBUG 7659339Seric case CMDDBGQSHOW: /* show queues */ 7666907Seric printf("Send Queue="); 76755012Seric printaddr(e->e_sendqueue, TRUE); 7685003Seric break; 7697275Seric 7707275Seric case CMDDBGDEBUG: /* set debug mode */ 7717676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7727676Seric tTflag(p); 77358151Seric message("200 Debug set"); 7747275Seric break; 7757275Seric 77636230Skarels # else /* not SMTPDEBUG */ 77736230Skarels case CMDDBGQSHOW: /* show queues */ 77836230Skarels case CMDDBGDEBUG: /* set debug mode */ 77964685Seric # endif /* SMTPDEBUG */ 78064685Seric case CMDLOGBOGUS: /* bogus command */ 78136233Skarels # ifdef LOG 78258308Seric if (LogLevel > 0) 78364685Seric syslog(LOG_CRIT, 78458020Seric "\"%s\" command from %s (%s)", 78566005Seric c->cmdname, peerhostname, 78658755Seric anynet_ntoa(&RealHostAddr)); 78736233Skarels # endif 78836230Skarels /* FALL THROUGH */ 78936230Skarels 7904549Seric case CMDERROR: /* unknown command */ 79166283Seric if (++badcommands > MAXBADCOMMANDS) 79266283Seric { 79366283Seric message("421 %s Too many bad commands; closing connection", 79466283Seric MyHostName); 79566283Seric goto doquit; 79666283Seric } 79766283Seric 79858151Seric message("500 Command unrecognized"); 7994549Seric break; 8004549Seric 8014549Seric default: 80236230Skarels errno = 0; 80358151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8044549Seric break; 8054549Seric } 8064549Seric } 8074549Seric } 8084549Seric /* 8094549Seric ** SKIPWORD -- skip a fixed word. 8104549Seric ** 8114549Seric ** Parameters: 8124549Seric ** p -- place to start looking. 8134549Seric ** w -- word to skip. 8144549Seric ** 8154549Seric ** Returns: 8164549Seric ** p following w. 8174549Seric ** NULL on error. 8184549Seric ** 8194549Seric ** Side Effects: 8204549Seric ** clobbers the p data area. 8214549Seric */ 8224549Seric 8234549Seric static char * 8244549Seric skipword(p, w) 8254549Seric register char *p; 8264549Seric char *w; 8274549Seric { 8284549Seric register char *q; 82966005Seric char *firstp = p; 8304549Seric 8314549Seric /* find beginning of word */ 83258050Seric while (isascii(*p) && isspace(*p)) 8334549Seric p++; 8344549Seric q = p; 8354549Seric 8364549Seric /* find end of word */ 83758050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8384549Seric p++; 83958050Seric while (isascii(*p) && isspace(*p)) 8404549Seric *p++ = '\0'; 8414549Seric if (*p != ':') 8424549Seric { 8434549Seric syntax: 84466005Seric message("501 Syntax error in parameters scanning \"%s\"", 84566005Seric firstp); 8464549Seric Errors++; 8474549Seric return (NULL); 8484549Seric } 8494549Seric *p++ = '\0'; 85058050Seric while (isascii(*p) && isspace(*p)) 8514549Seric p++; 8524549Seric 85362373Seric if (*p == '\0') 85462373Seric goto syntax; 85562373Seric 8564549Seric /* see if the input word matches desired word */ 85733725Sbostic if (strcasecmp(q, w)) 8584549Seric goto syntax; 8594549Seric 8604549Seric return (p); 8614549Seric } 8624577Seric /* 86358151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 86458151Seric ** 86558151Seric ** Parameters: 86658151Seric ** a -- the address to print 86758151Seric ** last -- set if this is the last one. 86858151Seric ** 86958151Seric ** Returns: 87058151Seric ** none. 87158151Seric ** 87258151Seric ** Side Effects: 87358151Seric ** Prints the appropriate 250 codes. 87458151Seric */ 87558151Seric 87658151Seric printvrfyaddr(a, last) 87758151Seric register ADDRESS *a; 87858151Seric bool last; 87958151Seric { 88058151Seric char fmtbuf[20]; 88158151Seric 88258151Seric strcpy(fmtbuf, "250"); 88358151Seric fmtbuf[3] = last ? ' ' : '-'; 88458151Seric 88559746Seric if (a->q_fullname == NULL) 88659746Seric { 88759746Seric if (strchr(a->q_user, '@') == NULL) 88859746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 88959746Seric else 89059746Seric strcpy(&fmtbuf[4], "<%s>"); 89159746Seric message(fmtbuf, a->q_user, MyHostName); 89259746Seric } 89358151Seric else 89458151Seric { 89559746Seric if (strchr(a->q_user, '@') == NULL) 89659746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 89759746Seric else 89859746Seric strcpy(&fmtbuf[4], "%s <%s>"); 89959746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 90058151Seric } 90158151Seric } 90258151Seric /* 9034577Seric ** HELP -- implement the HELP command. 9044577Seric ** 9054577Seric ** Parameters: 9064577Seric ** topic -- the topic we want help for. 9074577Seric ** 9084577Seric ** Returns: 9094577Seric ** none. 9104577Seric ** 9114577Seric ** Side Effects: 9124577Seric ** outputs the help file to message output. 9134577Seric */ 9144577Seric 9154577Seric help(topic) 9164577Seric char *topic; 9174577Seric { 9184577Seric register FILE *hf; 9194577Seric int len; 9204577Seric char buf[MAXLINE]; 9214577Seric bool noinfo; 9224577Seric 9238269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 9244577Seric { 9254577Seric /* no help */ 92611931Seric errno = 0; 92758151Seric message("502 HELP not implemented"); 9284577Seric return; 9294577Seric } 9304577Seric 93149669Seric if (topic == NULL || *topic == '\0') 93249669Seric topic = "smtp"; 93349669Seric else 93449669Seric makelower(topic); 93549669Seric 9364577Seric len = strlen(topic); 9374577Seric noinfo = TRUE; 9384577Seric 9394577Seric while (fgets(buf, sizeof buf, hf) != NULL) 9404577Seric { 9414577Seric if (strncmp(buf, topic, len) == 0) 9424577Seric { 9434577Seric register char *p; 9444577Seric 94556795Seric p = strchr(buf, '\t'); 9464577Seric if (p == NULL) 9474577Seric p = buf; 9484577Seric else 9494577Seric p++; 9504577Seric fixcrlf(p, TRUE); 95158151Seric message("214-%s", p); 9524577Seric noinfo = FALSE; 9534577Seric } 9544577Seric } 9554577Seric 9564577Seric if (noinfo) 95758151Seric message("504 HELP topic unknown"); 9584577Seric else 95958151Seric message("214 End of HELP info"); 9604628Seric (void) fclose(hf); 9614577Seric } 9628544Seric /* 9639339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 9649339Seric ** 9659339Seric ** Parameters: 9669339Seric ** label -- a string used in error messages 9679339Seric ** 9689339Seric ** Returns: 9699339Seric ** zero in the child 9709339Seric ** one in the parent 9719339Seric ** 9729339Seric ** Side Effects: 9739339Seric ** none. 9749339Seric */ 9758544Seric 97655012Seric runinchild(label, e) 9779339Seric char *label; 97855012Seric register ENVELOPE *e; 9799339Seric { 9809339Seric int childpid; 9819339Seric 98216158Seric if (!OneXact) 9839339Seric { 98416158Seric childpid = dofork(); 98516158Seric if (childpid < 0) 98616158Seric { 98716158Seric syserr("%s: cannot fork", label); 98816158Seric return (1); 98916158Seric } 99016158Seric if (childpid > 0) 99116158Seric { 99216158Seric auto int st; 9939339Seric 99416158Seric /* parent -- wait for child to complete */ 99561093Seric setproctitle("server %s child wait", CurHostName); 99616158Seric st = waitfor(childpid); 99716158Seric if (st == -1) 99816158Seric syserr("%s: lost child", label); 99964948Seric else if (!WIFEXITED(st)) 100064948Seric syserr("%s: died on signal %d", 100164948Seric label, st & 0177); 10029339Seric 100316158Seric /* if we exited on a QUIT command, complete the process */ 100466017Seric if (WEXITSTATUS(st) == EX_QUIT) 100566017Seric { 100666017Seric disconnect(1, e); 100716158Seric finis(); 100866017Seric } 10099339Seric 101016158Seric return (1); 101116158Seric } 101216158Seric else 101316158Seric { 101416158Seric /* child */ 101516158Seric InChild = TRUE; 101625050Seric QuickAbort = FALSE; 101755012Seric clearenvelope(e, FALSE); 101816158Seric } 10199339Seric } 102015256Seric 102116158Seric /* open alias database */ 102260537Seric initmaps(FALSE, e); 102316158Seric 102416158Seric return (0); 10259339Seric } 10269339Seric 102756795Seric # endif /* SMTP */ 1028