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*64718Seric static char sccsid[] = "@(#)srvrsmtp.c 8.17 (Berkeley) 10/15/93 (with SMTP)"; 1433731Sbostic #else 15*64718Seric static char sccsid[] = "@(#)srvrsmtp.c 8.17 (Berkeley) 10/15/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 */ 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 */ 9111146Seric 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); 12664496Seric if (BrokenSmtpPeers) 12764496Seric { 12864496Seric message("220 %s", inp); 12964496Seric } 13064496Seric else 13164496Seric { 13264496Seric message("220-%s", inp); 13364496Seric message("220 ESMTP spoken here"); 13464496Seric } 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 { 462*64718Seric message("250 Recipient ok%s", 463*64718Seric bitset(QQUEUEUP, a->q_flags) ? 464*64718Seric " (will queue)" : ""); 46559747Seric nrcpts++; 46659747Seric } 46712612Seric else 4684549Seric { 46912612Seric /* punt -- should keep message in ADDRESS.... */ 47058151Seric message("550 Addressee unknown"); 4714549Seric } 47255012Seric e->e_to = NULL; 4734549Seric break; 4744549Seric 4754549Seric case CMDDATA: /* data -- text of mail */ 47661093Seric SmtpPhase = "server DATA"; 47758109Seric if (!gotmail) 4784549Seric { 47958151Seric message("503 Need MAIL command"); 4804976Seric break; 4814549Seric } 482*64718Seric else if (nrcpts <= 0) 4834549Seric { 48458151Seric message("503 Need RCPT (recipient)"); 4854976Seric break; 4864549Seric } 4874976Seric 48858929Seric /* check to see if we need to re-expand aliases */ 48963787Seric /* also reset QBADADDR on already-diagnosted addrs */ 49063787Seric doublequeue = FALSE; 49158929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 49258929Seric { 49358929Seric if (bitset(QVERIFIED, a->q_flags)) 49463787Seric { 49563787Seric /* need to re-expand aliases */ 49663787Seric doublequeue = TRUE; 49763787Seric } 49863787Seric if (bitset(QBADADDR, a->q_flags)) 49963787Seric { 50063787Seric /* make this "go away" */ 50163787Seric a->q_flags |= QDONTSEND; 50263787Seric a->q_flags &= ~QBADADDR; 50363787Seric } 50458929Seric } 50558929Seric 5064976Seric /* collect the text of the message */ 50724943Seric SmtpPhase = "collect"; 50863965Seric HoldErrs = TRUE; 50963787Seric collect(TRUE, doublequeue, e); 5104976Seric 5118238Seric /* 5128238Seric ** Arrange to send to everyone. 5138238Seric ** If sending to multiple people, mail back 5148238Seric ** errors rather than reporting directly. 5158238Seric ** In any case, don't mail back errors for 5168238Seric ** anything that has happened up to 5178238Seric ** now (the other end will do this). 51810197Seric ** Truncate our transcript -- the mail has gotten 51910197Seric ** to us successfully, and if we have 52010197Seric ** to mail this back, it will be easier 52110197Seric ** on the reader. 5228238Seric ** Then send to everyone. 5238238Seric ** Finally give a reply code. If an error has 5248238Seric ** already been given, don't mail a 5258238Seric ** message back. 5269339Seric ** We goose error returns by clearing error bit. 5278238Seric */ 5288238Seric 52924943Seric SmtpPhase = "delivery"; 53063787Seric if (nrcpts != 1 && !doublequeue) 5319378Seric { 5329378Seric HoldErrs = TRUE; 53358734Seric e->e_errormode = EM_MAIL; 5349378Seric } 53555012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 53658714Seric id = e->e_id; 5374976Seric 5384976Seric /* send to all recipients */ 53963787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 54055012Seric e->e_to = NULL; 5414976Seric 5428238Seric /* issue success if appropriate and reset */ 5438238Seric if (Errors == 0 || HoldErrs) 54458855Seric message("250 %s Message accepted for delivery", id); 54559747Seric 54659747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 54759730Seric { 54859730Seric /* avoid sending back an extra message */ 54959730Seric e->e_flags &= ~EF_FATALERRS; 55059747Seric e->e_flags |= EF_CLRQUEUE; 55159730Seric } 5528238Seric else 55358919Seric { 55459747Seric /* from now on, we have to operate silently */ 55559747Seric HoldErrs = TRUE; 55659747Seric e->e_errormode = EM_MAIL; 55759747Seric 55859730Seric /* if we just queued, poke it */ 55963787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 56059730Seric { 56164296Seric extern pid_t dowork(); 56264296Seric 56359730Seric unlockqueue(e); 56464296Seric (void) dowork(id, TRUE, TRUE, e); 56559730Seric } 56658919Seric } 56758883Seric 56859747Seric abortmessage: 5699339Seric /* if in a child, pop back to our parent */ 5709339Seric if (InChild) 5719339Seric finis(); 57224943Seric 57324943Seric /* clean up a bit */ 57458109Seric gotmail = FALSE; 57555012Seric dropenvelope(e); 57658179Seric CurEnv = e = newenvelope(e, CurEnv); 57755012Seric e->e_flags = BlankEnvelope.e_flags; 5784549Seric break; 5794549Seric 5804549Seric case CMDRSET: /* rset -- reset state */ 58158151Seric message("250 Reset state"); 58264359Seric e->e_flags |= EF_CLRQUEUE; 5839339Seric if (InChild) 5849339Seric finis(); 58558109Seric 58658109Seric /* clean up a bit */ 58758109Seric gotmail = FALSE; 58858109Seric dropenvelope(e); 58958179Seric CurEnv = e = newenvelope(e, CurEnv); 5909339Seric break; 5914549Seric 5924549Seric case CMDVRFY: /* vrfy -- verify address */ 59358092Seric case CMDEXPN: /* expn -- expand address */ 59458092Seric vrfy = c->cmdcode == CMDVRFY; 59558092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 59658092Seric PrivacyFlags)) 59758082Seric { 59858412Seric if (vrfy) 59958412Seric message("252 Who's to say?"); 60058412Seric else 60158412Seric message("502 That's none of your business"); 60258082Seric break; 60358082Seric } 60458082Seric else if (!gothello && 60558092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 60658092Seric PrivacyFlags)) 60758082Seric { 60858151Seric message("503 I demand that you introduce yourself first"); 60958082Seric break; 61058082Seric } 61158092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6129339Seric break; 61355173Seric #ifdef LOG 61458020Seric if (LogLevel > 5) 61555173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 61655173Seric #endif 6175003Seric vrfyqueue = NULL; 6187762Seric QuickAbort = TRUE; 61958092Seric if (vrfy) 62058092Seric e->e_flags |= EF_VRFYONLY; 62162373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 62262373Seric *p++; 62362373Seric if (*p == '\0') 62462373Seric { 62562373Seric message("501 Argument required"); 62662373Seric Errors++; 62762373Seric } 62862373Seric else 62962373Seric { 63064284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 63162373Seric } 6327762Seric if (Errors != 0) 6339339Seric { 6349339Seric if (InChild) 6359339Seric finis(); 6367762Seric break; 6379339Seric } 63862373Seric if (vrfyqueue == NULL) 63962373Seric { 64062373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 64162373Seric } 6425003Seric while (vrfyqueue != NULL) 6435003Seric { 64463971Seric a = vrfyqueue; 64563971Seric while ((a = a->q_next) != NULL && 64663971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 64763971Seric continue; 6487685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 64958151Seric printvrfyaddr(vrfyqueue, a == NULL); 65063847Seric vrfyqueue = vrfyqueue->q_next; 6515003Seric } 6529339Seric if (InChild) 6539339Seric finis(); 6544549Seric break; 6554549Seric 6564549Seric case CMDHELP: /* help -- give user info */ 6574577Seric help(p); 6584549Seric break; 6594549Seric 6604549Seric case CMDNOOP: /* noop -- do nothing */ 66164122Seric message("250 OK"); 6624549Seric break; 6634549Seric 6644549Seric case CMDQUIT: /* quit -- leave mail */ 66558151Seric message("221 %s closing connection", MyHostName); 66661051Seric 66761051Seric /* avoid future 050 messages */ 66861051Seric Verbose = FALSE; 66961051Seric 6709339Seric if (InChild) 6719339Seric ExitStat = EX_QUIT; 6724549Seric finis(); 6734549Seric 6748544Seric case CMDVERB: /* set verbose mode */ 67559957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 67659957Seric { 67759957Seric /* this would give out the same info */ 67859957Seric message("502 Verbose unavailable"); 67959957Seric break; 68059957Seric } 6818544Seric Verbose = TRUE; 68258734Seric e->e_sendmode = SM_DELIVER; 68359957Seric message("250 Verbose mode"); 6848544Seric break; 6858544Seric 6869314Seric case CMDONEX: /* doing one transaction only */ 6879378Seric OneXact = TRUE; 68859957Seric message("250 Only one transaction"); 6899314Seric break; 6909314Seric 69136230Skarels # ifdef SMTPDEBUG 6929339Seric case CMDDBGQSHOW: /* show queues */ 6936907Seric printf("Send Queue="); 69455012Seric printaddr(e->e_sendqueue, TRUE); 6955003Seric break; 6967275Seric 6977275Seric case CMDDBGDEBUG: /* set debug mode */ 6987676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6997676Seric tTflag(p); 70058151Seric message("200 Debug set"); 7017275Seric break; 7027275Seric 70336230Skarels # else /* not SMTPDEBUG */ 70436230Skarels case CMDDBGQSHOW: /* show queues */ 70536230Skarels case CMDDBGDEBUG: /* set debug mode */ 70664685Seric # endif /* SMTPDEBUG */ 70764685Seric case CMDLOGBOGUS: /* bogus command */ 70836233Skarels # ifdef LOG 70958308Seric if (LogLevel > 0) 71064685Seric syslog(LOG_CRIT, 71158020Seric "\"%s\" command from %s (%s)", 71236230Skarels c->cmdname, RealHostName, 71358755Seric anynet_ntoa(&RealHostAddr)); 71436233Skarels # endif 71536230Skarels /* FALL THROUGH */ 71636230Skarels 7174549Seric case CMDERROR: /* unknown command */ 71858151Seric message("500 Command unrecognized"); 7194549Seric break; 7204549Seric 7214549Seric default: 72236230Skarels errno = 0; 72358151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7244549Seric break; 7254549Seric } 7264549Seric } 7274549Seric } 7284549Seric /* 7294549Seric ** SKIPWORD -- skip a fixed word. 7304549Seric ** 7314549Seric ** Parameters: 7324549Seric ** p -- place to start looking. 7334549Seric ** w -- word to skip. 7344549Seric ** 7354549Seric ** Returns: 7364549Seric ** p following w. 7374549Seric ** NULL on error. 7384549Seric ** 7394549Seric ** Side Effects: 7404549Seric ** clobbers the p data area. 7414549Seric */ 7424549Seric 7434549Seric static char * 7444549Seric skipword(p, w) 7454549Seric register char *p; 7464549Seric char *w; 7474549Seric { 7484549Seric register char *q; 7494549Seric 7504549Seric /* find beginning of word */ 75158050Seric while (isascii(*p) && isspace(*p)) 7524549Seric p++; 7534549Seric q = p; 7544549Seric 7554549Seric /* find end of word */ 75658050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7574549Seric p++; 75858050Seric while (isascii(*p) && isspace(*p)) 7594549Seric *p++ = '\0'; 7604549Seric if (*p != ':') 7614549Seric { 7624549Seric syntax: 76362373Seric message("501 Syntax error in parameters"); 7644549Seric Errors++; 7654549Seric return (NULL); 7664549Seric } 7674549Seric *p++ = '\0'; 76858050Seric while (isascii(*p) && isspace(*p)) 7694549Seric p++; 7704549Seric 77162373Seric if (*p == '\0') 77262373Seric goto syntax; 77362373Seric 7744549Seric /* see if the input word matches desired word */ 77533725Sbostic if (strcasecmp(q, w)) 7764549Seric goto syntax; 7774549Seric 7784549Seric return (p); 7794549Seric } 7804577Seric /* 78158151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 78258151Seric ** 78358151Seric ** Parameters: 78458151Seric ** a -- the address to print 78558151Seric ** last -- set if this is the last one. 78658151Seric ** 78758151Seric ** Returns: 78858151Seric ** none. 78958151Seric ** 79058151Seric ** Side Effects: 79158151Seric ** Prints the appropriate 250 codes. 79258151Seric */ 79358151Seric 79458151Seric printvrfyaddr(a, last) 79558151Seric register ADDRESS *a; 79658151Seric bool last; 79758151Seric { 79858151Seric char fmtbuf[20]; 79958151Seric 80058151Seric strcpy(fmtbuf, "250"); 80158151Seric fmtbuf[3] = last ? ' ' : '-'; 80258151Seric 80359746Seric if (a->q_fullname == NULL) 80459746Seric { 80559746Seric if (strchr(a->q_user, '@') == NULL) 80659746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 80759746Seric else 80859746Seric strcpy(&fmtbuf[4], "<%s>"); 80959746Seric message(fmtbuf, a->q_user, MyHostName); 81059746Seric } 81158151Seric else 81258151Seric { 81359746Seric if (strchr(a->q_user, '@') == NULL) 81459746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 81559746Seric else 81659746Seric strcpy(&fmtbuf[4], "%s <%s>"); 81759746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 81858151Seric } 81958151Seric } 82058151Seric /* 8214577Seric ** HELP -- implement the HELP command. 8224577Seric ** 8234577Seric ** Parameters: 8244577Seric ** topic -- the topic we want help for. 8254577Seric ** 8264577Seric ** Returns: 8274577Seric ** none. 8284577Seric ** 8294577Seric ** Side Effects: 8304577Seric ** outputs the help file to message output. 8314577Seric */ 8324577Seric 8334577Seric help(topic) 8344577Seric char *topic; 8354577Seric { 8364577Seric register FILE *hf; 8374577Seric int len; 8384577Seric char buf[MAXLINE]; 8394577Seric bool noinfo; 8404577Seric 8418269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8424577Seric { 8434577Seric /* no help */ 84411931Seric errno = 0; 84558151Seric message("502 HELP not implemented"); 8464577Seric return; 8474577Seric } 8484577Seric 84949669Seric if (topic == NULL || *topic == '\0') 85049669Seric topic = "smtp"; 85149669Seric else 85249669Seric makelower(topic); 85349669Seric 8544577Seric len = strlen(topic); 8554577Seric noinfo = TRUE; 8564577Seric 8574577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8584577Seric { 8594577Seric if (strncmp(buf, topic, len) == 0) 8604577Seric { 8614577Seric register char *p; 8624577Seric 86356795Seric p = strchr(buf, '\t'); 8644577Seric if (p == NULL) 8654577Seric p = buf; 8664577Seric else 8674577Seric p++; 8684577Seric fixcrlf(p, TRUE); 86958151Seric message("214-%s", p); 8704577Seric noinfo = FALSE; 8714577Seric } 8724577Seric } 8734577Seric 8744577Seric if (noinfo) 87558151Seric message("504 HELP topic unknown"); 8764577Seric else 87758151Seric message("214 End of HELP info"); 8784628Seric (void) fclose(hf); 8794577Seric } 8808544Seric /* 8819339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8829339Seric ** 8839339Seric ** Parameters: 8849339Seric ** label -- a string used in error messages 8859339Seric ** 8869339Seric ** Returns: 8879339Seric ** zero in the child 8889339Seric ** one in the parent 8899339Seric ** 8909339Seric ** Side Effects: 8919339Seric ** none. 8929339Seric */ 8938544Seric 89455012Seric runinchild(label, e) 8959339Seric char *label; 89655012Seric register ENVELOPE *e; 8979339Seric { 8989339Seric int childpid; 8999339Seric 90016158Seric if (!OneXact) 9019339Seric { 90216158Seric childpid = dofork(); 90316158Seric if (childpid < 0) 90416158Seric { 90516158Seric syserr("%s: cannot fork", label); 90616158Seric return (1); 90716158Seric } 90816158Seric if (childpid > 0) 90916158Seric { 91016158Seric auto int st; 9119339Seric 91216158Seric /* parent -- wait for child to complete */ 91361093Seric setproctitle("server %s child wait", CurHostName); 91416158Seric st = waitfor(childpid); 91516158Seric if (st == -1) 91616158Seric syserr("%s: lost child", label); 9179339Seric 91816158Seric /* if we exited on a QUIT command, complete the process */ 91916158Seric if (st == (EX_QUIT << 8)) 92016158Seric finis(); 9219339Seric 92216158Seric return (1); 92316158Seric } 92416158Seric else 92516158Seric { 92616158Seric /* child */ 92716158Seric InChild = TRUE; 92825050Seric QuickAbort = FALSE; 92955012Seric clearenvelope(e, FALSE); 93016158Seric } 9319339Seric } 93215256Seric 93316158Seric /* open alias database */ 93460537Seric initmaps(FALSE, e); 93516158Seric 93616158Seric return (0); 9379339Seric } 9389339Seric 93956795Seric # endif /* SMTP */ 940