122712Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 3*62532Sbostic * Copyright (c) 1988, 1993 4*62532Sbostic * 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*62532Sbostic static char sccsid[] = "@(#)srvrsmtp.c 8.1 (Berkeley) 06/07/93 (with SMTP)"; 1433731Sbostic #else 15*62532Sbostic static char sccsid[] = "@(#)srvrsmtp.c 8.1 (Berkeley) 06/07/93 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 2011728Seric # include <signal.h> 214549Seric 2233731Sbostic # ifdef SMTP 234556Seric 244549Seric /* 254549Seric ** SMTP -- run the SMTP protocol. 264549Seric ** 274549Seric ** Parameters: 284549Seric ** none. 294549Seric ** 304549Seric ** Returns: 314549Seric ** never. 324549Seric ** 334549Seric ** Side Effects: 344549Seric ** Reads commands from the input channel and processes 354549Seric ** them. 364549Seric */ 374549Seric 384549Seric struct cmd 394549Seric { 404549Seric char *cmdname; /* command name */ 414549Seric int cmdcode; /* internal code, see below */ 424549Seric }; 434549Seric 444549Seric /* values for cmdcode */ 454549Seric # define CMDERROR 0 /* bad command */ 464549Seric # define CMDMAIL 1 /* mail -- designate sender */ 474976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 484549Seric # define CMDDATA 3 /* data -- send message text */ 499339Seric # define CMDRSET 4 /* rset -- reset state */ 509339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5158092Seric # define CMDEXPN 6 /* expn -- expand address */ 529339Seric # define CMDNOOP 7 /* noop -- do nothing */ 539339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 549339Seric # define CMDHELO 9 /* helo -- be polite */ 5558092Seric # define CMDHELP 10 /* help -- give usage info */ 5658323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5758092Seric /* non-standard commands */ 5858092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5958092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 6036230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6158092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6258092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 634549Seric 644549Seric static struct cmd CmdTab[] = 654549Seric { 664549Seric "mail", CMDMAIL, 674976Seric "rcpt", CMDRCPT, 684549Seric "data", CMDDATA, 694549Seric "rset", CMDRSET, 704549Seric "vrfy", CMDVRFY, 7158092Seric "expn", CMDEXPN, 724549Seric "help", CMDHELP, 734549Seric "noop", CMDNOOP, 744549Seric "quit", CMDQUIT, 754976Seric "helo", CMDHELO, 7658323Seric "ehlo", CMDEHLO, 778544Seric "verb", CMDVERB, 789314Seric "onex", CMDONEX, 7936230Skarels /* 8036230Skarels * remaining commands are here only 8136230Skarels * to trap and log attempts to use them 8236230Skarels */ 839339Seric "showq", CMDDBGQSHOW, 848544Seric "debug", CMDDBGDEBUG, 854549Seric NULL, CMDERROR, 864549Seric }; 874549Seric 889339Seric bool InChild = FALSE; /* true if running in a subprocess */ 899378Seric bool OneXact = FALSE; /* one xaction only this run */ 9011146Seric 919339Seric #define EX_QUIT 22 /* special code for QUIT command */ 928544Seric 9355012Seric smtp(e) 9455012Seric register ENVELOPE *e; 954549Seric { 964549Seric register char *p; 978544Seric register struct cmd *c; 984549Seric char *cmd; 9946928Sbostic static char *skipword(); 1005003Seric auto ADDRESS *vrfyqueue; 10112612Seric ADDRESS *a; 10258109Seric bool gotmail; /* mail command received */ 10358092Seric bool gothello; /* helo command received */ 10458092Seric bool vrfy; /* set if this is a vrfy command */ 10558323Seric char *protocol; /* sending protocol */ 10659016Seric char *sendinghost; /* sending hostname */ 10758333Seric long msize; /* approximate maximum message size */ 10858333Seric auto char *delimptr; 10958714Seric char *id; 11059747Seric int nrcpts; /* number of RCPT commands */ 1118544Seric char inp[MAXLINE]; 11257232Seric char cmdbuf[MAXLINE]; 1137124Seric extern char Version[]; 11424943Seric extern ENVELOPE BlankEnvelope; 1154549Seric 11659066Seric if (fileno(OutChannel) != fileno(stdout)) 1177363Seric { 1187363Seric /* arrange for debugging output to go to remote host */ 11959066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1207363Seric } 12155012Seric settime(e); 12257642Seric CurHostName = RealHostName; 12361093Seric setproctitle("server %s startup", CurHostName); 12458050Seric expand("\201e", inp, &inp[sizeof inp], e); 12560210Seric message("220-%s", inp); 12660210Seric message("220 ESMTP spoken here"); 12758330Seric protocol = NULL; 12859016Seric sendinghost = macvalue('s', e); 12958082Seric gothello = FALSE; 13058330Seric gotmail = FALSE; 1314549Seric for (;;) 1324549Seric { 13312612Seric /* arrange for backout */ 13412612Seric if (setjmp(TopFrame) > 0 && InChild) 13559058Seric { 13659058Seric QuickAbort = FALSE; 13759058Seric SuprErrs = TRUE; 13812612Seric finis(); 13959058Seric } 14012612Seric QuickAbort = FALSE; 14112612Seric HoldErrs = FALSE; 14251951Seric LogUsrErrs = FALSE; 14358092Seric e->e_flags &= ~EF_VRFYONLY; 14412612Seric 1457356Seric /* setup for the read */ 14655012Seric e->e_to = NULL; 1474577Seric Errors = 0; 1487275Seric (void) fflush(stdout); 1497356Seric 1507356Seric /* read the input line */ 15161093Seric SmtpPhase = "server cmd read"; 15261093Seric setproctitle("server %s cmd read", CurHostName); 15361093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 15461093Seric SmtpPhase); 1557356Seric 1567685Seric /* handle errors */ 1577356Seric if (p == NULL) 1587356Seric { 1594549Seric /* end of file, just die */ 16058151Seric message("421 %s Lost input channel from %s", 16125050Seric MyHostName, CurHostName); 16255464Seric #ifdef LOG 16358020Seric if (LogLevel > 1) 16455464Seric syslog(LOG_NOTICE, "lost input channel from %s", 16555464Seric CurHostName); 16655464Seric #endif 16758069Seric if (InChild) 16858069Seric ExitStat = EX_QUIT; 1694549Seric finis(); 1704549Seric } 1714549Seric 1724549Seric /* clean up end of line */ 1734558Seric fixcrlf(inp, TRUE); 1744549Seric 1754713Seric /* echo command to transcript */ 17655012Seric if (e->e_xfp != NULL) 17755012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1784713Seric 17959060Seric if (e->e_id == NULL) 18059060Seric setproctitle("%s: %s", CurHostName, inp); 18159060Seric else 18259060Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 18359060Seric 1844549Seric /* break off command */ 18558050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1864549Seric continue; 18757232Seric cmd = cmdbuf; 18858050Seric while (*p != '\0' && 18958050Seric !(isascii(*p) && isspace(*p)) && 19058050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 19124981Seric *cmd++ = *p++; 19224981Seric *cmd = '\0'; 1934549Seric 19425691Seric /* throw away leading whitespace */ 19558050Seric while (isascii(*p) && isspace(*p)) 19625691Seric p++; 19725691Seric 1984549Seric /* decode command */ 1994549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2004549Seric { 20133725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2024549Seric break; 2034549Seric } 2044549Seric 20551954Seric /* reset errors */ 20651954Seric errno = 0; 20751954Seric 2084549Seric /* process command */ 2094549Seric switch (c->cmdcode) 2104549Seric { 2114976Seric case CMDHELO: /* hello -- introduce yourself */ 21258323Seric case CMDEHLO: /* extended hello */ 21358323Seric if (c->cmdcode == CMDEHLO) 21458323Seric { 21558323Seric protocol = "ESMTP"; 21661093Seric SmtpPhase = "server EHLO"; 21758323Seric } 21858323Seric else 21958323Seric { 22058323Seric protocol = "SMTP"; 22161093Seric SmtpPhase = "server HELO"; 22258323Seric } 22359016Seric sendinghost = newstr(p); 22458308Seric if (strcasecmp(p, RealHostName) != 0) 22511146Seric { 22658789Seric auth_warning(e, "Host %s claimed to be %s", 22758789Seric RealHostName, p); 22811146Seric } 22958957Seric p = macvalue('_', e); 23058957Seric if (p == NULL) 23159016Seric p = RealHostName; 23258323Seric 23360210Seric gothello = TRUE; 23460210Seric if (c->cmdcode != CMDEHLO) 23560239Seric { 23660239Seric /* print old message and be done with it */ 23760239Seric message("250 %s Hello %s, pleased to meet you", 23860239Seric MyHostName, p); 23960210Seric break; 24060239Seric } 24160239Seric 24260239Seric /* print extended message and brag */ 24360239Seric message("250-%s Hello %s, pleased to meet you", 24460239Seric MyHostName, p); 24558323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 24658323Seric message("250-EXPN"); 24759271Seric if (MaxMessageSize > 0) 24859271Seric message("250-SIZE %ld", MaxMessageSize); 24959271Seric else 25059271Seric message("250-SIZE"); 25158323Seric message("250 HELP"); 2524976Seric break; 2534976Seric 2544549Seric case CMDMAIL: /* mail -- designate sender */ 25561093Seric SmtpPhase = "server MAIL"; 25624943Seric 2579314Seric /* check for validity of this command */ 25858789Seric if (!gothello) 25958082Seric { 26058957Seric /* set sending host to our known value */ 26159016Seric if (sendinghost == NULL) 26259016Seric sendinghost = RealHostName; 26358957Seric 26458789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 26558821Seric { 26658789Seric message("503 Polite people say HELO first"); 26758821Seric break; 26858821Seric } 26958789Seric else 27058821Seric { 27158789Seric auth_warning(e, 27258789Seric "Host %s didn't use HELO protocol", 27358789Seric RealHostName); 27458821Seric } 27558082Seric } 27658109Seric if (gotmail) 2774558Seric { 27858151Seric message("503 Sender already specified"); 2794558Seric break; 2804558Seric } 2819339Seric if (InChild) 2829339Seric { 28336230Skarels errno = 0; 28458151Seric syserr("503 Nested MAIL command: MAIL %s", p); 28558069Seric finis(); 2869339Seric } 2879339Seric 2889339Seric /* fork a subprocess to process this command */ 28955012Seric if (runinchild("SMTP-MAIL", e) > 0) 2909339Seric break; 29158323Seric if (protocol == NULL) 29258323Seric protocol = "SMTP"; 29358323Seric define('r', protocol, e); 29459016Seric define('s', sendinghost, e); 29555012Seric initsys(e); 29659747Seric nrcpts = 0; 29757389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2989339Seric 2999339Seric /* child -- go do the processing */ 3004549Seric p = skipword(p, "from"); 3014549Seric if (p == NULL) 3024549Seric break; 30357977Seric if (setjmp(TopFrame) > 0) 30458147Seric { 30558147Seric /* this failed -- undo work */ 30658147Seric if (InChild) 30759058Seric { 30859058Seric QuickAbort = FALSE; 30959058Seric SuprErrs = TRUE; 31058147Seric finis(); 31159058Seric } 31257977Seric break; 31358147Seric } 31457977Seric QuickAbort = TRUE; 31558333Seric 31658333Seric /* must parse sender first */ 31758333Seric delimptr = NULL; 31858704Seric setsender(p, e, &delimptr, FALSE); 31958333Seric p = delimptr; 32058333Seric if (p != NULL && *p != '\0') 32158333Seric *p++ = '\0'; 32258333Seric 32358333Seric /* now parse ESMTP arguments */ 32458333Seric msize = 0; 32558333Seric for (; p != NULL && *p != '\0'; p++) 32658333Seric { 32758333Seric char *kp; 32858333Seric char *vp; 32958333Seric 33058333Seric /* locate the beginning of the keyword */ 33158333Seric while (isascii(*p) && isspace(*p)) 33258333Seric p++; 33358333Seric if (*p == '\0') 33458333Seric break; 33558333Seric kp = p; 33658333Seric 33758333Seric /* skip to the value portion */ 33858333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 33958333Seric p++; 34058333Seric if (*p == '=') 34158333Seric { 34258333Seric *p++ = '\0'; 34358333Seric vp = p; 34458333Seric 34558333Seric /* skip to the end of the value */ 34658333Seric while (*p != '\0' && *p != ' ' && 34758333Seric !(isascii(*p) && iscntrl(*p)) && 34858333Seric *p != '=') 34958333Seric p++; 35058333Seric } 35158333Seric 35258333Seric if (*p != '\0') 35358333Seric *p++ = '\0'; 35458333Seric 35558333Seric if (tTd(19, 1)) 35658333Seric printf("MAIL: got arg %s=%s\n", kp, 35758333Seric vp == NULL ? "<null>" : vp); 35858333Seric 35958333Seric if (strcasecmp(kp, "size") == 0) 36058333Seric { 36159093Seric if (vp == NULL) 36258333Seric { 36358333Seric usrerr("501 SIZE requires a value"); 36458333Seric /* NOTREACHED */ 36558333Seric } 36658333Seric msize = atol(vp); 36758333Seric } 36859093Seric else if (strcasecmp(kp, "body") == 0) 36959093Seric { 37059093Seric if (vp == NULL) 37159093Seric { 37259093Seric usrerr("501 BODY requires a value"); 37359093Seric /* NOTREACHED */ 37459093Seric } 37559093Seric # ifdef MIME 37659093Seric if (strcasecmp(vp, "8bitmime") == 0) 37759093Seric { 37859093Seric e->e_bodytype = "8BITMIME"; 37959709Seric SevenBit = FALSE; 38059093Seric } 38159093Seric else if (strcasecmp(vp, "7bit") == 0) 38259093Seric { 38359093Seric e->e_bodytype = "7BIT"; 38459709Seric SevenBit = TRUE; 38559093Seric } 38659093Seric else 38759093Seric { 38859093Seric usrerr("501 Unknown BODY type %s", 38959093Seric vp); 39059093Seric } 39159093Seric # endif 39259093Seric } 39358333Seric else 39458333Seric { 39558333Seric usrerr("501 %s parameter unrecognized", kp); 39658333Seric /* NOTREACHED */ 39758333Seric } 39858333Seric } 39959284Seric 40059284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 40159284Seric { 40259284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 40359284Seric MaxMessageSize); 40459284Seric /* NOTREACHED */ 40559284Seric } 40658333Seric 40758333Seric if (!enoughspace(msize)) 40858333Seric { 40958333Seric message("452 Insufficient disk space; try again later"); 41058333Seric break; 41158333Seric } 41258151Seric message("250 Sender ok"); 41358147Seric gotmail = TRUE; 4144549Seric break; 4154549Seric 4164976Seric case CMDRCPT: /* rcpt -- designate recipient */ 41758850Seric if (!gotmail) 41858850Seric { 41958850Seric usrerr("503 Need MAIL before RCPT"); 42058850Seric break; 42158850Seric } 42261093Seric SmtpPhase = "server RCPT"; 42312612Seric if (setjmp(TopFrame) > 0) 42414785Seric { 42555012Seric e->e_flags &= ~EF_FATALERRS; 42612612Seric break; 42714785Seric } 42812612Seric QuickAbort = TRUE; 42951951Seric LogUsrErrs = TRUE; 43058093Seric 43159699Seric if (e->e_sendmode != SM_DELIVER) 43259699Seric e->e_flags |= EF_VRFYONLY; 43358919Seric 4344549Seric p = skipword(p, "to"); 4354549Seric if (p == NULL) 4364549Seric break; 43758333Seric a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); 43812612Seric if (a == NULL) 43912612Seric break; 44016886Seric a->q_flags |= QPRIMARY; 44155012Seric a = recipient(a, &e->e_sendqueue, e); 44212612Seric if (Errors != 0) 44312612Seric break; 44412612Seric 44512612Seric /* no errors during parsing, but might be a duplicate */ 44655012Seric e->e_to = p; 44712612Seric if (!bitset(QBADADDR, a->q_flags)) 44859747Seric { 44958151Seric message("250 Recipient ok"); 45059747Seric nrcpts++; 45159747Seric } 45212612Seric else 4534549Seric { 45412612Seric /* punt -- should keep message in ADDRESS.... */ 45558151Seric message("550 Addressee unknown"); 4564549Seric } 45755012Seric e->e_to = NULL; 4584549Seric break; 4594549Seric 4604549Seric case CMDDATA: /* data -- text of mail */ 46161093Seric SmtpPhase = "server DATA"; 46258109Seric if (!gotmail) 4634549Seric { 46458151Seric message("503 Need MAIL command"); 4654976Seric break; 4664549Seric } 46755012Seric else if (e->e_nrcpts <= 0) 4684549Seric { 46958151Seric message("503 Need RCPT (recipient)"); 4704976Seric break; 4714549Seric } 4724976Seric 47358929Seric /* check to see if we need to re-expand aliases */ 47458929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 47558929Seric { 47658929Seric if (bitset(QVERIFIED, a->q_flags)) 47758929Seric break; 47858929Seric } 47958929Seric 4804976Seric /* collect the text of the message */ 48124943Seric SmtpPhase = "collect"; 48258929Seric collect(TRUE, a != NULL, e); 48359730Seric e->e_flags &= ~EF_FATALERRS; 4844976Seric if (Errors != 0) 48559747Seric goto abortmessage; 4864976Seric 4878238Seric /* 4888238Seric ** Arrange to send to everyone. 4898238Seric ** If sending to multiple people, mail back 4908238Seric ** errors rather than reporting directly. 4918238Seric ** In any case, don't mail back errors for 4928238Seric ** anything that has happened up to 4938238Seric ** now (the other end will do this). 49410197Seric ** Truncate our transcript -- the mail has gotten 49510197Seric ** to us successfully, and if we have 49610197Seric ** to mail this back, it will be easier 49710197Seric ** on the reader. 4988238Seric ** Then send to everyone. 4998238Seric ** Finally give a reply code. If an error has 5008238Seric ** already been given, don't mail a 5018238Seric ** message back. 5029339Seric ** We goose error returns by clearing error bit. 5038238Seric */ 5048238Seric 50524943Seric SmtpPhase = "delivery"; 50659747Seric if (nrcpts != 1 && a == NULL) 5079378Seric { 5089378Seric HoldErrs = TRUE; 50958734Seric e->e_errormode = EM_MAIL; 5109378Seric } 51155012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 51258714Seric id = e->e_id; 5134976Seric 5144976Seric /* send to all recipients */ 51558919Seric sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE); 51655012Seric e->e_to = NULL; 5174976Seric 51823516Seric /* save statistics */ 51955012Seric markstats(e, (ADDRESS *) NULL); 52023516Seric 5218238Seric /* issue success if appropriate and reset */ 5228238Seric if (Errors == 0 || HoldErrs) 52358855Seric message("250 %s Message accepted for delivery", id); 52459747Seric 52559747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 52659730Seric { 52759730Seric /* avoid sending back an extra message */ 52859730Seric e->e_flags &= ~EF_FATALERRS; 52959747Seric e->e_flags |= EF_CLRQUEUE; 53059730Seric } 5318238Seric else 53258919Seric { 53359747Seric /* from now on, we have to operate silently */ 53459747Seric HoldErrs = TRUE; 53559747Seric e->e_errormode = EM_MAIL; 53659747Seric 53759730Seric /* if we just queued, poke it */ 53859730Seric if (a != NULL && e->e_sendmode != SM_QUEUE) 53959730Seric { 54059730Seric unlockqueue(e); 54159730Seric dowork(id, TRUE, TRUE, e); 54259730Seric e->e_id = NULL; 54359730Seric } 54458919Seric } 54558883Seric 54659747Seric abortmessage: 5479339Seric /* if in a child, pop back to our parent */ 5489339Seric if (InChild) 5499339Seric finis(); 55024943Seric 55124943Seric /* clean up a bit */ 55258109Seric gotmail = FALSE; 55355012Seric dropenvelope(e); 55458179Seric CurEnv = e = newenvelope(e, CurEnv); 55555012Seric e->e_flags = BlankEnvelope.e_flags; 5564549Seric break; 5574549Seric 5584549Seric case CMDRSET: /* rset -- reset state */ 55958151Seric message("250 Reset state"); 5609339Seric if (InChild) 5619339Seric finis(); 56258109Seric 56358109Seric /* clean up a bit */ 56458109Seric gotmail = FALSE; 56558109Seric dropenvelope(e); 56658179Seric CurEnv = e = newenvelope(e, CurEnv); 5679339Seric break; 5684549Seric 5694549Seric case CMDVRFY: /* vrfy -- verify address */ 57058092Seric case CMDEXPN: /* expn -- expand address */ 57158092Seric vrfy = c->cmdcode == CMDVRFY; 57258092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 57358092Seric PrivacyFlags)) 57458082Seric { 57558412Seric if (vrfy) 57658412Seric message("252 Who's to say?"); 57758412Seric else 57858412Seric message("502 That's none of your business"); 57958082Seric break; 58058082Seric } 58158082Seric else if (!gothello && 58258092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 58358092Seric PrivacyFlags)) 58458082Seric { 58558151Seric message("503 I demand that you introduce yourself first"); 58658082Seric break; 58758082Seric } 58858092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 5899339Seric break; 59055173Seric #ifdef LOG 59158020Seric if (LogLevel > 5) 59255173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 59355173Seric #endif 5945003Seric vrfyqueue = NULL; 5957762Seric QuickAbort = TRUE; 59658092Seric if (vrfy) 59758092Seric e->e_flags |= EF_VRFYONLY; 59862373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 59962373Seric *p++; 60062373Seric if (*p == '\0') 60162373Seric { 60262373Seric message("501 Argument required"); 60362373Seric Errors++; 60462373Seric } 60562373Seric else 60662373Seric { 60762373Seric (void) sendtolist(p, (ADDRESS *) NULL, 60862373Seric &vrfyqueue, e); 60962373Seric } 6107762Seric if (Errors != 0) 6119339Seric { 6129339Seric if (InChild) 6139339Seric finis(); 6147762Seric break; 6159339Seric } 61662373Seric if (vrfyqueue == NULL) 61762373Seric { 61862373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 61962373Seric } 6205003Seric while (vrfyqueue != NULL) 6215003Seric { 6225003Seric register ADDRESS *a = vrfyqueue->q_next; 6235003Seric 6247685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 6255003Seric a = a->q_next; 6265003Seric 6277685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 62858151Seric printvrfyaddr(vrfyqueue, a == NULL); 6295003Seric else if (a == NULL) 63058151Seric message("554 Self destructive alias loop"); 6315003Seric vrfyqueue = a; 6325003Seric } 6339339Seric if (InChild) 6349339Seric finis(); 6354549Seric break; 6364549Seric 6374549Seric case CMDHELP: /* help -- give user info */ 6384577Seric help(p); 6394549Seric break; 6404549Seric 6414549Seric case CMDNOOP: /* noop -- do nothing */ 64258151Seric message("200 OK"); 6434549Seric break; 6444549Seric 6454549Seric case CMDQUIT: /* quit -- leave mail */ 64658151Seric message("221 %s closing connection", MyHostName); 64761051Seric 64861051Seric /* avoid future 050 messages */ 64961051Seric Verbose = FALSE; 65061051Seric 6519339Seric if (InChild) 6529339Seric ExitStat = EX_QUIT; 6534549Seric finis(); 6544549Seric 6558544Seric case CMDVERB: /* set verbose mode */ 65659957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 65759957Seric { 65859957Seric /* this would give out the same info */ 65959957Seric message("502 Verbose unavailable"); 66059957Seric break; 66159957Seric } 6628544Seric Verbose = TRUE; 66358734Seric e->e_sendmode = SM_DELIVER; 66459957Seric message("250 Verbose mode"); 6658544Seric break; 6668544Seric 6679314Seric case CMDONEX: /* doing one transaction only */ 6689378Seric OneXact = TRUE; 66959957Seric message("250 Only one transaction"); 6709314Seric break; 6719314Seric 67236230Skarels # ifdef SMTPDEBUG 6739339Seric case CMDDBGQSHOW: /* show queues */ 6746907Seric printf("Send Queue="); 67555012Seric printaddr(e->e_sendqueue, TRUE); 6765003Seric break; 6777275Seric 6787275Seric case CMDDBGDEBUG: /* set debug mode */ 6797676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6807676Seric tTflag(p); 68158151Seric message("200 Debug set"); 6827275Seric break; 6837275Seric 68436230Skarels # else /* not SMTPDEBUG */ 68524945Seric 68636230Skarels case CMDDBGQSHOW: /* show queues */ 68736230Skarels case CMDDBGDEBUG: /* set debug mode */ 68836233Skarels # ifdef LOG 68958308Seric if (LogLevel > 0) 69036230Skarels syslog(LOG_NOTICE, 69158020Seric "\"%s\" command from %s (%s)", 69236230Skarels c->cmdname, RealHostName, 69358755Seric anynet_ntoa(&RealHostAddr)); 69436233Skarels # endif 69536230Skarels /* FALL THROUGH */ 69636230Skarels # endif /* SMTPDEBUG */ 69736230Skarels 6984549Seric case CMDERROR: /* unknown command */ 69958151Seric message("500 Command unrecognized"); 7004549Seric break; 7014549Seric 7024549Seric default: 70336230Skarels errno = 0; 70458151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7054549Seric break; 7064549Seric } 7074549Seric } 7084549Seric } 7094549Seric /* 7104549Seric ** SKIPWORD -- skip a fixed word. 7114549Seric ** 7124549Seric ** Parameters: 7134549Seric ** p -- place to start looking. 7144549Seric ** w -- word to skip. 7154549Seric ** 7164549Seric ** Returns: 7174549Seric ** p following w. 7184549Seric ** NULL on error. 7194549Seric ** 7204549Seric ** Side Effects: 7214549Seric ** clobbers the p data area. 7224549Seric */ 7234549Seric 7244549Seric static char * 7254549Seric skipword(p, w) 7264549Seric register char *p; 7274549Seric char *w; 7284549Seric { 7294549Seric register char *q; 7304549Seric 7314549Seric /* find beginning of word */ 73258050Seric while (isascii(*p) && isspace(*p)) 7334549Seric p++; 7344549Seric q = p; 7354549Seric 7364549Seric /* find end of word */ 73758050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7384549Seric p++; 73958050Seric while (isascii(*p) && isspace(*p)) 7404549Seric *p++ = '\0'; 7414549Seric if (*p != ':') 7424549Seric { 7434549Seric syntax: 74462373Seric message("501 Syntax error in parameters"); 7454549Seric Errors++; 7464549Seric return (NULL); 7474549Seric } 7484549Seric *p++ = '\0'; 74958050Seric while (isascii(*p) && isspace(*p)) 7504549Seric p++; 7514549Seric 75262373Seric if (*p == '\0') 75362373Seric goto syntax; 75462373Seric 7554549Seric /* see if the input word matches desired word */ 75633725Sbostic if (strcasecmp(q, w)) 7574549Seric goto syntax; 7584549Seric 7594549Seric return (p); 7604549Seric } 7614577Seric /* 76258151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 76358151Seric ** 76458151Seric ** Parameters: 76558151Seric ** a -- the address to print 76658151Seric ** last -- set if this is the last one. 76758151Seric ** 76858151Seric ** Returns: 76958151Seric ** none. 77058151Seric ** 77158151Seric ** Side Effects: 77258151Seric ** Prints the appropriate 250 codes. 77358151Seric */ 77458151Seric 77558151Seric printvrfyaddr(a, last) 77658151Seric register ADDRESS *a; 77758151Seric bool last; 77858151Seric { 77958151Seric char fmtbuf[20]; 78058151Seric 78158151Seric strcpy(fmtbuf, "250"); 78258151Seric fmtbuf[3] = last ? ' ' : '-'; 78358151Seric 78459746Seric if (a->q_fullname == NULL) 78559746Seric { 78659746Seric if (strchr(a->q_user, '@') == NULL) 78759746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 78859746Seric else 78959746Seric strcpy(&fmtbuf[4], "<%s>"); 79059746Seric message(fmtbuf, a->q_user, MyHostName); 79159746Seric } 79258151Seric else 79358151Seric { 79459746Seric if (strchr(a->q_user, '@') == NULL) 79559746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 79659746Seric else 79759746Seric strcpy(&fmtbuf[4], "%s <%s>"); 79859746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 79958151Seric } 80058151Seric } 80158151Seric /* 8024577Seric ** HELP -- implement the HELP command. 8034577Seric ** 8044577Seric ** Parameters: 8054577Seric ** topic -- the topic we want help for. 8064577Seric ** 8074577Seric ** Returns: 8084577Seric ** none. 8094577Seric ** 8104577Seric ** Side Effects: 8114577Seric ** outputs the help file to message output. 8124577Seric */ 8134577Seric 8144577Seric help(topic) 8154577Seric char *topic; 8164577Seric { 8174577Seric register FILE *hf; 8184577Seric int len; 8194577Seric char buf[MAXLINE]; 8204577Seric bool noinfo; 8214577Seric 8228269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8234577Seric { 8244577Seric /* no help */ 82511931Seric errno = 0; 82658151Seric message("502 HELP not implemented"); 8274577Seric return; 8284577Seric } 8294577Seric 83049669Seric if (topic == NULL || *topic == '\0') 83149669Seric topic = "smtp"; 83249669Seric else 83349669Seric makelower(topic); 83449669Seric 8354577Seric len = strlen(topic); 8364577Seric noinfo = TRUE; 8374577Seric 8384577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8394577Seric { 8404577Seric if (strncmp(buf, topic, len) == 0) 8414577Seric { 8424577Seric register char *p; 8434577Seric 84456795Seric p = strchr(buf, '\t'); 8454577Seric if (p == NULL) 8464577Seric p = buf; 8474577Seric else 8484577Seric p++; 8494577Seric fixcrlf(p, TRUE); 85058151Seric message("214-%s", p); 8514577Seric noinfo = FALSE; 8524577Seric } 8534577Seric } 8544577Seric 8554577Seric if (noinfo) 85658151Seric message("504 HELP topic unknown"); 8574577Seric else 85858151Seric message("214 End of HELP info"); 8594628Seric (void) fclose(hf); 8604577Seric } 8618544Seric /* 8629339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8639339Seric ** 8649339Seric ** Parameters: 8659339Seric ** label -- a string used in error messages 8669339Seric ** 8679339Seric ** Returns: 8689339Seric ** zero in the child 8699339Seric ** one in the parent 8709339Seric ** 8719339Seric ** Side Effects: 8729339Seric ** none. 8739339Seric */ 8748544Seric 87555012Seric runinchild(label, e) 8769339Seric char *label; 87755012Seric register ENVELOPE *e; 8789339Seric { 8799339Seric int childpid; 8809339Seric 88116158Seric if (!OneXact) 8829339Seric { 88316158Seric childpid = dofork(); 88416158Seric if (childpid < 0) 88516158Seric { 88616158Seric syserr("%s: cannot fork", label); 88716158Seric return (1); 88816158Seric } 88916158Seric if (childpid > 0) 89016158Seric { 89116158Seric auto int st; 8929339Seric 89316158Seric /* parent -- wait for child to complete */ 89461093Seric setproctitle("server %s child wait", CurHostName); 89516158Seric st = waitfor(childpid); 89616158Seric if (st == -1) 89716158Seric syserr("%s: lost child", label); 8989339Seric 89916158Seric /* if we exited on a QUIT command, complete the process */ 90016158Seric if (st == (EX_QUIT << 8)) 90116158Seric finis(); 9029339Seric 90316158Seric return (1); 90416158Seric } 90516158Seric else 90616158Seric { 90716158Seric /* child */ 90816158Seric InChild = TRUE; 90925050Seric QuickAbort = FALSE; 91055012Seric clearenvelope(e, FALSE); 91116158Seric } 9129339Seric } 91315256Seric 91416158Seric /* open alias database */ 91560537Seric initmaps(FALSE, e); 91616158Seric 91716158Seric return (0); 9189339Seric } 9199339Seric 92056795Seric # endif /* SMTP */ 921