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*64496Seric static char sccsid[] = "@(#)srvrsmtp.c 8.15 (Berkeley) 09/18/93 (with SMTP)"; 1433731Sbostic #else 15*64496Seric static char sccsid[] = "@(#)srvrsmtp.c 8.15 (Berkeley) 09/18/93 (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 */ 5936230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6058092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6158092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 624549Seric 634549Seric static struct cmd CmdTab[] = 644549Seric { 654549Seric "mail", CMDMAIL, 664976Seric "rcpt", CMDRCPT, 674549Seric "data", CMDDATA, 684549Seric "rset", CMDRSET, 694549Seric "vrfy", CMDVRFY, 7058092Seric "expn", CMDEXPN, 714549Seric "help", CMDHELP, 724549Seric "noop", CMDNOOP, 734549Seric "quit", CMDQUIT, 744976Seric "helo", CMDHELO, 7558323Seric "ehlo", CMDEHLO, 768544Seric "verb", CMDVERB, 779314Seric "onex", CMDONEX, 7836230Skarels /* 7936230Skarels * remaining commands are here only 8036230Skarels * to trap and log attempts to use them 8136230Skarels */ 829339Seric "showq", CMDDBGQSHOW, 838544Seric "debug", CMDDBGDEBUG, 844549Seric NULL, CMDERROR, 854549Seric }; 864549Seric 879339Seric bool InChild = FALSE; /* true if running in a subprocess */ 889378Seric bool OneXact = FALSE; /* one xaction only this run */ 8911146Seric 909339Seric #define EX_QUIT 22 /* special code for QUIT command */ 918544Seric 9263937Seric static char *skipword(); 9363937Seric 9455012Seric smtp(e) 9555012Seric register ENVELOPE *e; 964549Seric { 974549Seric register char *p; 988544Seric register struct cmd *c; 994549Seric char *cmd; 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 */ 11163787Seric bool doublequeue; 1128544Seric char inp[MAXLINE]; 11357232Seric char cmdbuf[MAXLINE]; 1147124Seric extern char Version[]; 11524943Seric extern ENVELOPE BlankEnvelope; 1164549Seric 11759066Seric if (fileno(OutChannel) != fileno(stdout)) 1187363Seric { 1197363Seric /* arrange for debugging output to go to remote host */ 12059066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1217363Seric } 12255012Seric settime(e); 12357642Seric CurHostName = RealHostName; 12461093Seric setproctitle("server %s startup", CurHostName); 12558050Seric expand("\201e", inp, &inp[sizeof inp], e); 126*64496Seric if (BrokenSmtpPeers) 127*64496Seric { 128*64496Seric message("220 %s", inp); 129*64496Seric } 130*64496Seric else 131*64496Seric { 132*64496Seric message("220-%s", inp); 133*64496Seric message("220 ESMTP spoken here"); 134*64496Seric } 13558330Seric protocol = NULL; 13659016Seric sendinghost = macvalue('s', e); 13758082Seric gothello = FALSE; 13858330Seric gotmail = FALSE; 1394549Seric for (;;) 1404549Seric { 14112612Seric /* arrange for backout */ 14212612Seric if (setjmp(TopFrame) > 0 && InChild) 14359058Seric { 14459058Seric QuickAbort = FALSE; 14559058Seric SuprErrs = TRUE; 14612612Seric finis(); 14759058Seric } 14812612Seric QuickAbort = FALSE; 14912612Seric HoldErrs = FALSE; 15051951Seric LogUsrErrs = FALSE; 15163843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 15212612Seric 1537356Seric /* setup for the read */ 15455012Seric e->e_to = NULL; 1554577Seric Errors = 0; 1567275Seric (void) fflush(stdout); 1577356Seric 1587356Seric /* read the input line */ 15961093Seric SmtpPhase = "server cmd read"; 16061093Seric setproctitle("server %s cmd read", CurHostName); 16161093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 16261093Seric SmtpPhase); 1637356Seric 1647685Seric /* handle errors */ 1657356Seric if (p == NULL) 1667356Seric { 1674549Seric /* end of file, just die */ 16858151Seric message("421 %s Lost input channel from %s", 16925050Seric MyHostName, CurHostName); 17055464Seric #ifdef LOG 17163843Seric if (LogLevel > (gotmail ? 1 : 19)) 17255464Seric syslog(LOG_NOTICE, "lost input channel from %s", 17355464Seric CurHostName); 17455464Seric #endif 17558069Seric if (InChild) 17658069Seric ExitStat = EX_QUIT; 1774549Seric finis(); 1784549Seric } 1794549Seric 1804549Seric /* clean up end of line */ 1814558Seric fixcrlf(inp, TRUE); 1824549Seric 1834713Seric /* echo command to transcript */ 18455012Seric if (e->e_xfp != NULL) 18555012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1864713Seric 18759060Seric if (e->e_id == NULL) 18859060Seric setproctitle("%s: %s", CurHostName, inp); 18959060Seric else 19059060Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 19159060Seric 1924549Seric /* break off command */ 19358050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1944549Seric continue; 19557232Seric cmd = cmdbuf; 19658050Seric while (*p != '\0' && 19758050Seric !(isascii(*p) && isspace(*p)) && 19858050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 19924981Seric *cmd++ = *p++; 20024981Seric *cmd = '\0'; 2014549Seric 20225691Seric /* throw away leading whitespace */ 20358050Seric while (isascii(*p) && isspace(*p)) 20425691Seric p++; 20525691Seric 2064549Seric /* decode command */ 2074549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2084549Seric { 20933725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2104549Seric break; 2114549Seric } 2124549Seric 21351954Seric /* reset errors */ 21451954Seric errno = 0; 21551954Seric 2164549Seric /* process command */ 2174549Seric switch (c->cmdcode) 2184549Seric { 2194976Seric case CMDHELO: /* hello -- introduce yourself */ 22058323Seric case CMDEHLO: /* extended hello */ 22158323Seric if (c->cmdcode == CMDEHLO) 22258323Seric { 22358323Seric protocol = "ESMTP"; 22461093Seric SmtpPhase = "server EHLO"; 22558323Seric } 22658323Seric else 22758323Seric { 22858323Seric protocol = "SMTP"; 22961093Seric SmtpPhase = "server HELO"; 23058323Seric } 23159016Seric sendinghost = newstr(p); 23264284Seric if (strcasecmp(p, RealHostName) != 0 && 23364284Seric (strcasecmp(RealHostName, "localhost") != 0 || 23464284Seric strcasecmp(p, MyHostName) != 0)) 23511146Seric { 23658789Seric auth_warning(e, "Host %s claimed to be %s", 23758789Seric RealHostName, p); 23811146Seric } 23958957Seric p = macvalue('_', e); 24058957Seric if (p == NULL) 24159016Seric p = RealHostName; 24258323Seric 24360210Seric gothello = TRUE; 24460210Seric if (c->cmdcode != CMDEHLO) 24560239Seric { 24660239Seric /* print old message and be done with it */ 24760239Seric message("250 %s Hello %s, pleased to meet you", 24860239Seric MyHostName, p); 24960210Seric break; 25060239Seric } 25160239Seric 25260239Seric /* print extended message and brag */ 25360239Seric message("250-%s Hello %s, pleased to meet you", 25460239Seric MyHostName, p); 25558323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 25658323Seric message("250-EXPN"); 25764359Seric if (MaxMessageSize > 0) 25864359Seric message("250-SIZE %ld", MaxMessageSize); 25959271Seric else 26059271Seric message("250-SIZE"); 26158323Seric message("250 HELP"); 2624976Seric break; 2634976Seric 2644549Seric case CMDMAIL: /* mail -- designate sender */ 26561093Seric SmtpPhase = "server MAIL"; 26624943Seric 2679314Seric /* check for validity of this command */ 26858789Seric if (!gothello) 26958082Seric { 27058957Seric /* set sending host to our known value */ 27159016Seric if (sendinghost == NULL) 27259016Seric sendinghost = RealHostName; 27358957Seric 27458789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 27558821Seric { 27658789Seric message("503 Polite people say HELO first"); 27758821Seric break; 27858821Seric } 27958082Seric } 28058109Seric if (gotmail) 2814558Seric { 28258151Seric message("503 Sender already specified"); 28363843Seric if (InChild) 28463843Seric finis(); 2854558Seric break; 2864558Seric } 2879339Seric if (InChild) 2889339Seric { 28936230Skarels errno = 0; 29058151Seric syserr("503 Nested MAIL command: MAIL %s", p); 29158069Seric finis(); 2929339Seric } 2939339Seric 2949339Seric /* fork a subprocess to process this command */ 29555012Seric if (runinchild("SMTP-MAIL", e) > 0) 2969339Seric break; 29763753Seric if (!gothello) 29863753Seric { 29963753Seric auth_warning(e, 30063753Seric "Host %s didn't use HELO protocol", 30163753Seric RealHostName); 30263753Seric } 30358323Seric if (protocol == NULL) 30458323Seric protocol = "SMTP"; 30558323Seric define('r', protocol, e); 30659016Seric define('s', sendinghost, e); 30755012Seric initsys(e); 30859747Seric nrcpts = 0; 30957389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 3109339Seric 3119339Seric /* child -- go do the processing */ 3124549Seric p = skipword(p, "from"); 3134549Seric if (p == NULL) 3144549Seric break; 31557977Seric if (setjmp(TopFrame) > 0) 31658147Seric { 31758147Seric /* this failed -- undo work */ 31858147Seric if (InChild) 31959058Seric { 32059058Seric QuickAbort = FALSE; 32159058Seric SuprErrs = TRUE; 32263787Seric e->e_flags &= ~EF_FATALERRS; 32358147Seric finis(); 32459058Seric } 32557977Seric break; 32658147Seric } 32757977Seric QuickAbort = TRUE; 32858333Seric 32958333Seric /* must parse sender first */ 33058333Seric delimptr = NULL; 33158704Seric setsender(p, e, &delimptr, FALSE); 33258333Seric p = delimptr; 33358333Seric if (p != NULL && *p != '\0') 33458333Seric *p++ = '\0'; 33558333Seric 33658333Seric /* now parse ESMTP arguments */ 33758333Seric msize = 0; 33858333Seric for (; p != NULL && *p != '\0'; p++) 33958333Seric { 34058333Seric char *kp; 34158333Seric char *vp; 34258333Seric 34358333Seric /* locate the beginning of the keyword */ 34458333Seric while (isascii(*p) && isspace(*p)) 34558333Seric p++; 34658333Seric if (*p == '\0') 34758333Seric break; 34858333Seric kp = p; 34958333Seric 35058333Seric /* skip to the value portion */ 35158333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 35258333Seric p++; 35358333Seric if (*p == '=') 35458333Seric { 35558333Seric *p++ = '\0'; 35658333Seric vp = p; 35758333Seric 35858333Seric /* skip to the end of the value */ 35958333Seric while (*p != '\0' && *p != ' ' && 36058333Seric !(isascii(*p) && iscntrl(*p)) && 36158333Seric *p != '=') 36258333Seric p++; 36358333Seric } 36458333Seric 36558333Seric if (*p != '\0') 36658333Seric *p++ = '\0'; 36758333Seric 36858333Seric if (tTd(19, 1)) 36958333Seric printf("MAIL: got arg %s=%s\n", kp, 37058333Seric vp == NULL ? "<null>" : vp); 37158333Seric 37258333Seric if (strcasecmp(kp, "size") == 0) 37358333Seric { 37459093Seric if (vp == NULL) 37558333Seric { 37658333Seric usrerr("501 SIZE requires a value"); 37758333Seric /* NOTREACHED */ 37858333Seric } 37958333Seric msize = atol(vp); 38058333Seric } 38159093Seric else if (strcasecmp(kp, "body") == 0) 38259093Seric { 38359093Seric if (vp == NULL) 38459093Seric { 38559093Seric usrerr("501 BODY requires a value"); 38659093Seric /* NOTREACHED */ 38759093Seric } 38859093Seric # ifdef MIME 38959093Seric if (strcasecmp(vp, "8bitmime") == 0) 39059093Seric { 39159093Seric e->e_bodytype = "8BITMIME"; 39259709Seric SevenBit = FALSE; 39359093Seric } 39459093Seric else if (strcasecmp(vp, "7bit") == 0) 39559093Seric { 39659093Seric e->e_bodytype = "7BIT"; 39759709Seric SevenBit = TRUE; 39859093Seric } 39959093Seric else 40059093Seric { 40159093Seric usrerr("501 Unknown BODY type %s", 40259093Seric vp); 40359093Seric } 40459093Seric # endif 40559093Seric } 40658333Seric else 40758333Seric { 40858333Seric usrerr("501 %s parameter unrecognized", kp); 40958333Seric /* NOTREACHED */ 41058333Seric } 41158333Seric } 41259284Seric 41359284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 41459284Seric { 41559284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 41659284Seric MaxMessageSize); 41759284Seric /* NOTREACHED */ 41859284Seric } 41958333Seric 42058333Seric if (!enoughspace(msize)) 42158333Seric { 42258333Seric message("452 Insufficient disk space; try again later"); 42358333Seric break; 42458333Seric } 42558151Seric message("250 Sender ok"); 42658147Seric gotmail = TRUE; 4274549Seric break; 4284549Seric 4294976Seric case CMDRCPT: /* rcpt -- designate recipient */ 43058850Seric if (!gotmail) 43158850Seric { 43258850Seric usrerr("503 Need MAIL before RCPT"); 43358850Seric break; 43458850Seric } 43561093Seric SmtpPhase = "server RCPT"; 43612612Seric if (setjmp(TopFrame) > 0) 43714785Seric { 43855012Seric e->e_flags &= ~EF_FATALERRS; 43912612Seric break; 44014785Seric } 44112612Seric QuickAbort = TRUE; 44251951Seric LogUsrErrs = TRUE; 44358093Seric 44459699Seric if (e->e_sendmode != SM_DELIVER) 44559699Seric e->e_flags |= EF_VRFYONLY; 44658919Seric 4474549Seric p = skipword(p, "to"); 4484549Seric if (p == NULL) 4494549Seric break; 45064284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', NULL, e); 45112612Seric if (a == NULL) 45212612Seric break; 45316886Seric a->q_flags |= QPRIMARY; 45455012Seric a = recipient(a, &e->e_sendqueue, e); 45512612Seric if (Errors != 0) 45612612Seric break; 45712612Seric 45812612Seric /* no errors during parsing, but might be a duplicate */ 45955012Seric e->e_to = p; 46012612Seric if (!bitset(QBADADDR, a->q_flags)) 46159747Seric { 46258151Seric message("250 Recipient ok"); 46359747Seric nrcpts++; 46459747Seric } 46512612Seric else 4664549Seric { 46712612Seric /* punt -- should keep message in ADDRESS.... */ 46858151Seric message("550 Addressee unknown"); 4694549Seric } 47055012Seric e->e_to = NULL; 4714549Seric break; 4724549Seric 4734549Seric case CMDDATA: /* data -- text of mail */ 47461093Seric SmtpPhase = "server DATA"; 47558109Seric if (!gotmail) 4764549Seric { 47758151Seric message("503 Need MAIL command"); 4784976Seric break; 4794549Seric } 48055012Seric else if (e->e_nrcpts <= 0) 4814549Seric { 48258151Seric message("503 Need RCPT (recipient)"); 4834976Seric break; 4844549Seric } 4854976Seric 48658929Seric /* check to see if we need to re-expand aliases */ 48763787Seric /* also reset QBADADDR on already-diagnosted addrs */ 48863787Seric doublequeue = FALSE; 48958929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 49058929Seric { 49158929Seric if (bitset(QVERIFIED, a->q_flags)) 49263787Seric { 49363787Seric /* need to re-expand aliases */ 49463787Seric doublequeue = TRUE; 49563787Seric } 49663787Seric if (bitset(QBADADDR, a->q_flags)) 49763787Seric { 49863787Seric /* make this "go away" */ 49963787Seric a->q_flags |= QDONTSEND; 50063787Seric a->q_flags &= ~QBADADDR; 50163787Seric } 50258929Seric } 50358929Seric 5044976Seric /* collect the text of the message */ 50524943Seric SmtpPhase = "collect"; 50663965Seric HoldErrs = TRUE; 50763787Seric collect(TRUE, doublequeue, e); 5084976Seric 5098238Seric /* 5108238Seric ** Arrange to send to everyone. 5118238Seric ** If sending to multiple people, mail back 5128238Seric ** errors rather than reporting directly. 5138238Seric ** In any case, don't mail back errors for 5148238Seric ** anything that has happened up to 5158238Seric ** now (the other end will do this). 51610197Seric ** Truncate our transcript -- the mail has gotten 51710197Seric ** to us successfully, and if we have 51810197Seric ** to mail this back, it will be easier 51910197Seric ** on the reader. 5208238Seric ** Then send to everyone. 5218238Seric ** Finally give a reply code. If an error has 5228238Seric ** already been given, don't mail a 5238238Seric ** message back. 5249339Seric ** We goose error returns by clearing error bit. 5258238Seric */ 5268238Seric 52724943Seric SmtpPhase = "delivery"; 52863787Seric if (nrcpts != 1 && !doublequeue) 5299378Seric { 5309378Seric HoldErrs = TRUE; 53158734Seric e->e_errormode = EM_MAIL; 5329378Seric } 53355012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 53458714Seric id = e->e_id; 5354976Seric 5364976Seric /* send to all recipients */ 53763787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 53855012Seric e->e_to = NULL; 5394976Seric 5408238Seric /* issue success if appropriate and reset */ 5418238Seric if (Errors == 0 || HoldErrs) 54258855Seric message("250 %s Message accepted for delivery", id); 54359747Seric 54459747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 54559730Seric { 54659730Seric /* avoid sending back an extra message */ 54759730Seric e->e_flags &= ~EF_FATALERRS; 54859747Seric e->e_flags |= EF_CLRQUEUE; 54959730Seric } 5508238Seric else 55158919Seric { 55259747Seric /* from now on, we have to operate silently */ 55359747Seric HoldErrs = TRUE; 55459747Seric e->e_errormode = EM_MAIL; 55559747Seric 55659730Seric /* if we just queued, poke it */ 55763787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 55859730Seric { 55964296Seric extern pid_t dowork(); 56064296Seric 56159730Seric unlockqueue(e); 56264296Seric (void) dowork(id, TRUE, TRUE, e); 56359730Seric } 56458919Seric } 56558883Seric 56659747Seric abortmessage: 5679339Seric /* if in a child, pop back to our parent */ 5689339Seric if (InChild) 5699339Seric finis(); 57024943Seric 57124943Seric /* clean up a bit */ 57258109Seric gotmail = FALSE; 57355012Seric dropenvelope(e); 57458179Seric CurEnv = e = newenvelope(e, CurEnv); 57555012Seric e->e_flags = BlankEnvelope.e_flags; 5764549Seric break; 5774549Seric 5784549Seric case CMDRSET: /* rset -- reset state */ 57958151Seric message("250 Reset state"); 58064359Seric e->e_flags |= EF_CLRQUEUE; 5819339Seric if (InChild) 5829339Seric finis(); 58358109Seric 58458109Seric /* clean up a bit */ 58558109Seric gotmail = FALSE; 58658109Seric dropenvelope(e); 58758179Seric CurEnv = e = newenvelope(e, CurEnv); 5889339Seric break; 5894549Seric 5904549Seric case CMDVRFY: /* vrfy -- verify address */ 59158092Seric case CMDEXPN: /* expn -- expand address */ 59258092Seric vrfy = c->cmdcode == CMDVRFY; 59358092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 59458092Seric PrivacyFlags)) 59558082Seric { 59658412Seric if (vrfy) 59758412Seric message("252 Who's to say?"); 59858412Seric else 59958412Seric message("502 That's none of your business"); 60058082Seric break; 60158082Seric } 60258082Seric else if (!gothello && 60358092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 60458092Seric PrivacyFlags)) 60558082Seric { 60658151Seric message("503 I demand that you introduce yourself first"); 60758082Seric break; 60858082Seric } 60958092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6109339Seric break; 61155173Seric #ifdef LOG 61258020Seric if (LogLevel > 5) 61355173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 61455173Seric #endif 6155003Seric vrfyqueue = NULL; 6167762Seric QuickAbort = TRUE; 61758092Seric if (vrfy) 61858092Seric e->e_flags |= EF_VRFYONLY; 61962373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 62062373Seric *p++; 62162373Seric if (*p == '\0') 62262373Seric { 62362373Seric message("501 Argument required"); 62462373Seric Errors++; 62562373Seric } 62662373Seric else 62762373Seric { 62864284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 62962373Seric } 6307762Seric if (Errors != 0) 6319339Seric { 6329339Seric if (InChild) 6339339Seric finis(); 6347762Seric break; 6359339Seric } 63662373Seric if (vrfyqueue == NULL) 63762373Seric { 63862373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 63962373Seric } 6405003Seric while (vrfyqueue != NULL) 6415003Seric { 64263971Seric a = vrfyqueue; 64363971Seric while ((a = a->q_next) != NULL && 64463971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 64563971Seric continue; 6467685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 64758151Seric printvrfyaddr(vrfyqueue, a == NULL); 64863847Seric vrfyqueue = vrfyqueue->q_next; 6495003Seric } 6509339Seric if (InChild) 6519339Seric finis(); 6524549Seric break; 6534549Seric 6544549Seric case CMDHELP: /* help -- give user info */ 6554577Seric help(p); 6564549Seric break; 6574549Seric 6584549Seric case CMDNOOP: /* noop -- do nothing */ 65964122Seric message("250 OK"); 6604549Seric break; 6614549Seric 6624549Seric case CMDQUIT: /* quit -- leave mail */ 66358151Seric message("221 %s closing connection", MyHostName); 66461051Seric 66561051Seric /* avoid future 050 messages */ 66661051Seric Verbose = FALSE; 66761051Seric 6689339Seric if (InChild) 6699339Seric ExitStat = EX_QUIT; 6704549Seric finis(); 6714549Seric 6728544Seric case CMDVERB: /* set verbose mode */ 67359957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 67459957Seric { 67559957Seric /* this would give out the same info */ 67659957Seric message("502 Verbose unavailable"); 67759957Seric break; 67859957Seric } 6798544Seric Verbose = TRUE; 68058734Seric e->e_sendmode = SM_DELIVER; 68159957Seric message("250 Verbose mode"); 6828544Seric break; 6838544Seric 6849314Seric case CMDONEX: /* doing one transaction only */ 6859378Seric OneXact = TRUE; 68659957Seric message("250 Only one transaction"); 6879314Seric break; 6889314Seric 68936230Skarels # ifdef SMTPDEBUG 6909339Seric case CMDDBGQSHOW: /* show queues */ 6916907Seric printf("Send Queue="); 69255012Seric printaddr(e->e_sendqueue, TRUE); 6935003Seric break; 6947275Seric 6957275Seric case CMDDBGDEBUG: /* set debug mode */ 6967676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6977676Seric tTflag(p); 69858151Seric message("200 Debug set"); 6997275Seric break; 7007275Seric 70136230Skarels # else /* not SMTPDEBUG */ 70224945Seric 70336230Skarels case CMDDBGQSHOW: /* show queues */ 70436230Skarels case CMDDBGDEBUG: /* set debug mode */ 70536233Skarels # ifdef LOG 70658308Seric if (LogLevel > 0) 70736230Skarels syslog(LOG_NOTICE, 70858020Seric "\"%s\" command from %s (%s)", 70936230Skarels c->cmdname, RealHostName, 71058755Seric anynet_ntoa(&RealHostAddr)); 71136233Skarels # endif 71236230Skarels /* FALL THROUGH */ 71336230Skarels # endif /* SMTPDEBUG */ 71436230Skarels 7154549Seric case CMDERROR: /* unknown command */ 71658151Seric message("500 Command unrecognized"); 7174549Seric break; 7184549Seric 7194549Seric default: 72036230Skarels errno = 0; 72158151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7224549Seric break; 7234549Seric } 7244549Seric } 7254549Seric } 7264549Seric /* 7274549Seric ** SKIPWORD -- skip a fixed word. 7284549Seric ** 7294549Seric ** Parameters: 7304549Seric ** p -- place to start looking. 7314549Seric ** w -- word to skip. 7324549Seric ** 7334549Seric ** Returns: 7344549Seric ** p following w. 7354549Seric ** NULL on error. 7364549Seric ** 7374549Seric ** Side Effects: 7384549Seric ** clobbers the p data area. 7394549Seric */ 7404549Seric 7414549Seric static char * 7424549Seric skipword(p, w) 7434549Seric register char *p; 7444549Seric char *w; 7454549Seric { 7464549Seric register char *q; 7474549Seric 7484549Seric /* find beginning of word */ 74958050Seric while (isascii(*p) && isspace(*p)) 7504549Seric p++; 7514549Seric q = p; 7524549Seric 7534549Seric /* find end of word */ 75458050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7554549Seric p++; 75658050Seric while (isascii(*p) && isspace(*p)) 7574549Seric *p++ = '\0'; 7584549Seric if (*p != ':') 7594549Seric { 7604549Seric syntax: 76162373Seric message("501 Syntax error in parameters"); 7624549Seric Errors++; 7634549Seric return (NULL); 7644549Seric } 7654549Seric *p++ = '\0'; 76658050Seric while (isascii(*p) && isspace(*p)) 7674549Seric p++; 7684549Seric 76962373Seric if (*p == '\0') 77062373Seric goto syntax; 77162373Seric 7724549Seric /* see if the input word matches desired word */ 77333725Sbostic if (strcasecmp(q, w)) 7744549Seric goto syntax; 7754549Seric 7764549Seric return (p); 7774549Seric } 7784577Seric /* 77958151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 78058151Seric ** 78158151Seric ** Parameters: 78258151Seric ** a -- the address to print 78358151Seric ** last -- set if this is the last one. 78458151Seric ** 78558151Seric ** Returns: 78658151Seric ** none. 78758151Seric ** 78858151Seric ** Side Effects: 78958151Seric ** Prints the appropriate 250 codes. 79058151Seric */ 79158151Seric 79258151Seric printvrfyaddr(a, last) 79358151Seric register ADDRESS *a; 79458151Seric bool last; 79558151Seric { 79658151Seric char fmtbuf[20]; 79758151Seric 79858151Seric strcpy(fmtbuf, "250"); 79958151Seric fmtbuf[3] = last ? ' ' : '-'; 80058151Seric 80159746Seric if (a->q_fullname == NULL) 80259746Seric { 80359746Seric if (strchr(a->q_user, '@') == NULL) 80459746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 80559746Seric else 80659746Seric strcpy(&fmtbuf[4], "<%s>"); 80759746Seric message(fmtbuf, a->q_user, MyHostName); 80859746Seric } 80958151Seric else 81058151Seric { 81159746Seric if (strchr(a->q_user, '@') == NULL) 81259746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 81359746Seric else 81459746Seric strcpy(&fmtbuf[4], "%s <%s>"); 81559746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 81658151Seric } 81758151Seric } 81858151Seric /* 8194577Seric ** HELP -- implement the HELP command. 8204577Seric ** 8214577Seric ** Parameters: 8224577Seric ** topic -- the topic we want help for. 8234577Seric ** 8244577Seric ** Returns: 8254577Seric ** none. 8264577Seric ** 8274577Seric ** Side Effects: 8284577Seric ** outputs the help file to message output. 8294577Seric */ 8304577Seric 8314577Seric help(topic) 8324577Seric char *topic; 8334577Seric { 8344577Seric register FILE *hf; 8354577Seric int len; 8364577Seric char buf[MAXLINE]; 8374577Seric bool noinfo; 8384577Seric 8398269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8404577Seric { 8414577Seric /* no help */ 84211931Seric errno = 0; 84358151Seric message("502 HELP not implemented"); 8444577Seric return; 8454577Seric } 8464577Seric 84749669Seric if (topic == NULL || *topic == '\0') 84849669Seric topic = "smtp"; 84949669Seric else 85049669Seric makelower(topic); 85149669Seric 8524577Seric len = strlen(topic); 8534577Seric noinfo = TRUE; 8544577Seric 8554577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8564577Seric { 8574577Seric if (strncmp(buf, topic, len) == 0) 8584577Seric { 8594577Seric register char *p; 8604577Seric 86156795Seric p = strchr(buf, '\t'); 8624577Seric if (p == NULL) 8634577Seric p = buf; 8644577Seric else 8654577Seric p++; 8664577Seric fixcrlf(p, TRUE); 86758151Seric message("214-%s", p); 8684577Seric noinfo = FALSE; 8694577Seric } 8704577Seric } 8714577Seric 8724577Seric if (noinfo) 87358151Seric message("504 HELP topic unknown"); 8744577Seric else 87558151Seric message("214 End of HELP info"); 8764628Seric (void) fclose(hf); 8774577Seric } 8788544Seric /* 8799339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8809339Seric ** 8819339Seric ** Parameters: 8829339Seric ** label -- a string used in error messages 8839339Seric ** 8849339Seric ** Returns: 8859339Seric ** zero in the child 8869339Seric ** one in the parent 8879339Seric ** 8889339Seric ** Side Effects: 8899339Seric ** none. 8909339Seric */ 8918544Seric 89255012Seric runinchild(label, e) 8939339Seric char *label; 89455012Seric register ENVELOPE *e; 8959339Seric { 8969339Seric int childpid; 8979339Seric 89816158Seric if (!OneXact) 8999339Seric { 90016158Seric childpid = dofork(); 90116158Seric if (childpid < 0) 90216158Seric { 90316158Seric syserr("%s: cannot fork", label); 90416158Seric return (1); 90516158Seric } 90616158Seric if (childpid > 0) 90716158Seric { 90816158Seric auto int st; 9099339Seric 91016158Seric /* parent -- wait for child to complete */ 91161093Seric setproctitle("server %s child wait", CurHostName); 91216158Seric st = waitfor(childpid); 91316158Seric if (st == -1) 91416158Seric syserr("%s: lost child", label); 9159339Seric 91616158Seric /* if we exited on a QUIT command, complete the process */ 91716158Seric if (st == (EX_QUIT << 8)) 91816158Seric finis(); 9199339Seric 92016158Seric return (1); 92116158Seric } 92216158Seric else 92316158Seric { 92416158Seric /* child */ 92516158Seric InChild = TRUE; 92625050Seric QuickAbort = FALSE; 92755012Seric clearenvelope(e, FALSE); 92816158Seric } 9299339Seric } 93015256Seric 93116158Seric /* open alias database */ 93260537Seric initmaps(FALSE, e); 93316158Seric 93416158Seric return (0); 9359339Seric } 9369339Seric 93756795Seric # endif /* SMTP */ 938