122712Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333731Sbostic * Copyright (c) 1988 Regents of the University of California. 433731Sbostic * All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822712Sdist 933731Sbostic # include "sendmail.h" 1022712Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*61093Seric static char sccsid[] = "@(#)srvrsmtp.c 6.59 (Berkeley) 06/03/93 (with SMTP)"; 1433731Sbostic #else 15*61093Seric static char sccsid[] = "@(#)srvrsmtp.c 6.59 (Berkeley) 06/03/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; 123*61093Seric 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 */ 151*61093Seric SmtpPhase = "server cmd read"; 152*61093Seric setproctitle("server %s cmd read", CurHostName); 153*61093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 154*61093Seric 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"; 216*61093Seric SmtpPhase = "server EHLO"; 21758323Seric } 21858323Seric else 21958323Seric { 22058323Seric protocol = "SMTP"; 221*61093Seric 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 */ 255*61093Seric 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 } 422*61093Seric 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 */ 461*61093Seric 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; 59858082Seric (void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e); 5997762Seric if (Errors != 0) 6009339Seric { 6019339Seric if (InChild) 6029339Seric finis(); 6037762Seric break; 6049339Seric } 6055003Seric while (vrfyqueue != NULL) 6065003Seric { 6075003Seric register ADDRESS *a = vrfyqueue->q_next; 6085003Seric char *code; 6095003Seric 6107685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 6115003Seric a = a->q_next; 6125003Seric 6137685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 61458151Seric printvrfyaddr(vrfyqueue, a == NULL); 6155003Seric else if (a == NULL) 61658151Seric message("554 Self destructive alias loop"); 6175003Seric vrfyqueue = a; 6185003Seric } 6199339Seric if (InChild) 6209339Seric finis(); 6214549Seric break; 6224549Seric 6234549Seric case CMDHELP: /* help -- give user info */ 6244577Seric help(p); 6254549Seric break; 6264549Seric 6274549Seric case CMDNOOP: /* noop -- do nothing */ 62858151Seric message("200 OK"); 6294549Seric break; 6304549Seric 6314549Seric case CMDQUIT: /* quit -- leave mail */ 63258151Seric message("221 %s closing connection", MyHostName); 63361051Seric 63461051Seric /* avoid future 050 messages */ 63561051Seric Verbose = FALSE; 63661051Seric 6379339Seric if (InChild) 6389339Seric ExitStat = EX_QUIT; 6394549Seric finis(); 6404549Seric 6418544Seric case CMDVERB: /* set verbose mode */ 64259957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 64359957Seric { 64459957Seric /* this would give out the same info */ 64559957Seric message("502 Verbose unavailable"); 64659957Seric break; 64759957Seric } 6488544Seric Verbose = TRUE; 64958734Seric e->e_sendmode = SM_DELIVER; 65059957Seric message("250 Verbose mode"); 6518544Seric break; 6528544Seric 6539314Seric case CMDONEX: /* doing one transaction only */ 6549378Seric OneXact = TRUE; 65559957Seric message("250 Only one transaction"); 6569314Seric break; 6579314Seric 65836230Skarels # ifdef SMTPDEBUG 6599339Seric case CMDDBGQSHOW: /* show queues */ 6606907Seric printf("Send Queue="); 66155012Seric printaddr(e->e_sendqueue, TRUE); 6625003Seric break; 6637275Seric 6647275Seric case CMDDBGDEBUG: /* set debug mode */ 6657676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6667676Seric tTflag(p); 66758151Seric message("200 Debug set"); 6687275Seric break; 6697275Seric 67036230Skarels # else /* not SMTPDEBUG */ 67124945Seric 67236230Skarels case CMDDBGQSHOW: /* show queues */ 67336230Skarels case CMDDBGDEBUG: /* set debug mode */ 67436233Skarels # ifdef LOG 67558308Seric if (LogLevel > 0) 67636230Skarels syslog(LOG_NOTICE, 67758020Seric "\"%s\" command from %s (%s)", 67836230Skarels c->cmdname, RealHostName, 67958755Seric anynet_ntoa(&RealHostAddr)); 68036233Skarels # endif 68136230Skarels /* FALL THROUGH */ 68236230Skarels # endif /* SMTPDEBUG */ 68336230Skarels 6844549Seric case CMDERROR: /* unknown command */ 68558151Seric message("500 Command unrecognized"); 6864549Seric break; 6874549Seric 6884549Seric default: 68936230Skarels errno = 0; 69058151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 6914549Seric break; 6924549Seric } 6934549Seric } 6944549Seric } 6954549Seric /* 6964549Seric ** SKIPWORD -- skip a fixed word. 6974549Seric ** 6984549Seric ** Parameters: 6994549Seric ** p -- place to start looking. 7004549Seric ** w -- word to skip. 7014549Seric ** 7024549Seric ** Returns: 7034549Seric ** p following w. 7044549Seric ** NULL on error. 7054549Seric ** 7064549Seric ** Side Effects: 7074549Seric ** clobbers the p data area. 7084549Seric */ 7094549Seric 7104549Seric static char * 7114549Seric skipword(p, w) 7124549Seric register char *p; 7134549Seric char *w; 7144549Seric { 7154549Seric register char *q; 7164549Seric 7174549Seric /* find beginning of word */ 71858050Seric while (isascii(*p) && isspace(*p)) 7194549Seric p++; 7204549Seric q = p; 7214549Seric 7224549Seric /* find end of word */ 72358050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7244549Seric p++; 72558050Seric while (isascii(*p) && isspace(*p)) 7264549Seric *p++ = '\0'; 7274549Seric if (*p != ':') 7284549Seric { 7294549Seric syntax: 73058151Seric message("501 Syntax error"); 7314549Seric Errors++; 7324549Seric return (NULL); 7334549Seric } 7344549Seric *p++ = '\0'; 73558050Seric while (isascii(*p) && isspace(*p)) 7364549Seric p++; 7374549Seric 7384549Seric /* see if the input word matches desired word */ 73933725Sbostic if (strcasecmp(q, w)) 7404549Seric goto syntax; 7414549Seric 7424549Seric return (p); 7434549Seric } 7444577Seric /* 74558151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 74658151Seric ** 74758151Seric ** Parameters: 74858151Seric ** a -- the address to print 74958151Seric ** last -- set if this is the last one. 75058151Seric ** 75158151Seric ** Returns: 75258151Seric ** none. 75358151Seric ** 75458151Seric ** Side Effects: 75558151Seric ** Prints the appropriate 250 codes. 75658151Seric */ 75758151Seric 75858151Seric printvrfyaddr(a, last) 75958151Seric register ADDRESS *a; 76058151Seric bool last; 76158151Seric { 76258151Seric char fmtbuf[20]; 76358151Seric 76458151Seric strcpy(fmtbuf, "250"); 76558151Seric fmtbuf[3] = last ? ' ' : '-'; 76658151Seric 76759746Seric if (a->q_fullname == NULL) 76859746Seric { 76959746Seric if (strchr(a->q_user, '@') == NULL) 77059746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 77159746Seric else 77259746Seric strcpy(&fmtbuf[4], "<%s>"); 77359746Seric message(fmtbuf, a->q_user, MyHostName); 77459746Seric } 77558151Seric else 77658151Seric { 77759746Seric if (strchr(a->q_user, '@') == NULL) 77859746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 77959746Seric else 78059746Seric strcpy(&fmtbuf[4], "%s <%s>"); 78159746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 78258151Seric } 78358151Seric } 78458151Seric /* 7854577Seric ** HELP -- implement the HELP command. 7864577Seric ** 7874577Seric ** Parameters: 7884577Seric ** topic -- the topic we want help for. 7894577Seric ** 7904577Seric ** Returns: 7914577Seric ** none. 7924577Seric ** 7934577Seric ** Side Effects: 7944577Seric ** outputs the help file to message output. 7954577Seric */ 7964577Seric 7974577Seric help(topic) 7984577Seric char *topic; 7994577Seric { 8004577Seric register FILE *hf; 8014577Seric int len; 8024577Seric char buf[MAXLINE]; 8034577Seric bool noinfo; 8044577Seric 8058269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8064577Seric { 8074577Seric /* no help */ 80811931Seric errno = 0; 80958151Seric message("502 HELP not implemented"); 8104577Seric return; 8114577Seric } 8124577Seric 81349669Seric if (topic == NULL || *topic == '\0') 81449669Seric topic = "smtp"; 81549669Seric else 81649669Seric makelower(topic); 81749669Seric 8184577Seric len = strlen(topic); 8194577Seric noinfo = TRUE; 8204577Seric 8214577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8224577Seric { 8234577Seric if (strncmp(buf, topic, len) == 0) 8244577Seric { 8254577Seric register char *p; 8264577Seric 82756795Seric p = strchr(buf, '\t'); 8284577Seric if (p == NULL) 8294577Seric p = buf; 8304577Seric else 8314577Seric p++; 8324577Seric fixcrlf(p, TRUE); 83358151Seric message("214-%s", p); 8344577Seric noinfo = FALSE; 8354577Seric } 8364577Seric } 8374577Seric 8384577Seric if (noinfo) 83958151Seric message("504 HELP topic unknown"); 8404577Seric else 84158151Seric message("214 End of HELP info"); 8424628Seric (void) fclose(hf); 8434577Seric } 8448544Seric /* 8459339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8469339Seric ** 8479339Seric ** Parameters: 8489339Seric ** label -- a string used in error messages 8499339Seric ** 8509339Seric ** Returns: 8519339Seric ** zero in the child 8529339Seric ** one in the parent 8539339Seric ** 8549339Seric ** Side Effects: 8559339Seric ** none. 8569339Seric */ 8578544Seric 85855012Seric runinchild(label, e) 8599339Seric char *label; 86055012Seric register ENVELOPE *e; 8619339Seric { 8629339Seric int childpid; 8639339Seric 86416158Seric if (!OneXact) 8659339Seric { 86616158Seric childpid = dofork(); 86716158Seric if (childpid < 0) 86816158Seric { 86916158Seric syserr("%s: cannot fork", label); 87016158Seric return (1); 87116158Seric } 87216158Seric if (childpid > 0) 87316158Seric { 87416158Seric auto int st; 8759339Seric 87616158Seric /* parent -- wait for child to complete */ 877*61093Seric setproctitle("server %s child wait", CurHostName); 87816158Seric st = waitfor(childpid); 87916158Seric if (st == -1) 88016158Seric syserr("%s: lost child", label); 8819339Seric 88216158Seric /* if we exited on a QUIT command, complete the process */ 88316158Seric if (st == (EX_QUIT << 8)) 88416158Seric finis(); 8859339Seric 88616158Seric return (1); 88716158Seric } 88816158Seric else 88916158Seric { 89016158Seric /* child */ 89116158Seric InChild = TRUE; 89225050Seric QuickAbort = FALSE; 89355012Seric clearenvelope(e, FALSE); 89416158Seric } 8959339Seric } 89615256Seric 89716158Seric /* open alias database */ 89860537Seric initmaps(FALSE, e); 89916158Seric 90016158Seric return (0); 9019339Seric } 9029339Seric 90356795Seric # endif /* SMTP */ 904