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*64948Seric static char sccsid[] = "@(#)srvrsmtp.c 8.19 (Berkeley) 11/26/93 (with SMTP)"; 1433731Sbostic #else 15*64948Seric static char sccsid[] = "@(#)srvrsmtp.c 8.19 (Berkeley) 11/26/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 { 46264718Seric message("250 Recipient ok%s", 46364718Seric bitset(QQUEUEUP, a->q_flags) ? 46464718Seric " (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 } 48264718Seric 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"; 50864766Seric collect(TRUE, doublequeue, e); 50964766Seric if (Errors != 0) 51064766Seric goto abortmessage; 51163965Seric HoldErrs = TRUE; 5124976Seric 5138238Seric /* 5148238Seric ** Arrange to send to everyone. 5158238Seric ** If sending to multiple people, mail back 5168238Seric ** errors rather than reporting directly. 5178238Seric ** In any case, don't mail back errors for 5188238Seric ** anything that has happened up to 5198238Seric ** now (the other end will do this). 52010197Seric ** Truncate our transcript -- the mail has gotten 52110197Seric ** to us successfully, and if we have 52210197Seric ** to mail this back, it will be easier 52310197Seric ** on the reader. 5248238Seric ** Then send to everyone. 5258238Seric ** Finally give a reply code. If an error has 5268238Seric ** already been given, don't mail a 5278238Seric ** message back. 5289339Seric ** We goose error returns by clearing error bit. 5298238Seric */ 5308238Seric 53124943Seric SmtpPhase = "delivery"; 53263787Seric if (nrcpts != 1 && !doublequeue) 5339378Seric { 5349378Seric HoldErrs = TRUE; 53558734Seric e->e_errormode = EM_MAIL; 5369378Seric } 53755012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 53858714Seric id = e->e_id; 5394976Seric 5404976Seric /* send to all recipients */ 54163787Seric sendall(e, doublequeue ? SM_QUEUE : SM_DEFAULT); 54255012Seric e->e_to = NULL; 5434976Seric 5448238Seric /* issue success if appropriate and reset */ 5458238Seric if (Errors == 0 || HoldErrs) 54658855Seric message("250 %s Message accepted for delivery", id); 54759747Seric 54859747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 54959730Seric { 55059730Seric /* avoid sending back an extra message */ 55159730Seric e->e_flags &= ~EF_FATALERRS; 55259747Seric e->e_flags |= EF_CLRQUEUE; 55359730Seric } 5548238Seric else 55558919Seric { 55659747Seric /* from now on, we have to operate silently */ 55759747Seric HoldErrs = TRUE; 55859747Seric e->e_errormode = EM_MAIL; 55959747Seric 56059730Seric /* if we just queued, poke it */ 56163787Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 56259730Seric { 56364296Seric extern pid_t dowork(); 56464296Seric 56559730Seric unlockqueue(e); 56664296Seric (void) dowork(id, TRUE, TRUE, e); 56759730Seric } 56858919Seric } 56958883Seric 57059747Seric abortmessage: 5719339Seric /* if in a child, pop back to our parent */ 5729339Seric if (InChild) 5739339Seric finis(); 57424943Seric 57524943Seric /* clean up a bit */ 57658109Seric gotmail = FALSE; 57755012Seric dropenvelope(e); 57858179Seric CurEnv = e = newenvelope(e, CurEnv); 57955012Seric e->e_flags = BlankEnvelope.e_flags; 5804549Seric break; 5814549Seric 5824549Seric case CMDRSET: /* rset -- reset state */ 58358151Seric message("250 Reset state"); 58464359Seric e->e_flags |= EF_CLRQUEUE; 5859339Seric if (InChild) 5869339Seric finis(); 58758109Seric 58858109Seric /* clean up a bit */ 58958109Seric gotmail = FALSE; 59058109Seric dropenvelope(e); 59158179Seric CurEnv = e = newenvelope(e, CurEnv); 5929339Seric break; 5934549Seric 5944549Seric case CMDVRFY: /* vrfy -- verify address */ 59558092Seric case CMDEXPN: /* expn -- expand address */ 59658092Seric vrfy = c->cmdcode == CMDVRFY; 59758092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 59858092Seric PrivacyFlags)) 59958082Seric { 60058412Seric if (vrfy) 60158412Seric message("252 Who's to say?"); 60258412Seric else 60358412Seric message("502 That's none of your business"); 60458082Seric break; 60558082Seric } 60658082Seric else if (!gothello && 60758092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 60858092Seric PrivacyFlags)) 60958082Seric { 61058151Seric message("503 I demand that you introduce yourself first"); 61158082Seric break; 61258082Seric } 61358092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 6149339Seric break; 61555173Seric #ifdef LOG 61658020Seric if (LogLevel > 5) 61755173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 61855173Seric #endif 6195003Seric vrfyqueue = NULL; 6207762Seric QuickAbort = TRUE; 62158092Seric if (vrfy) 62258092Seric e->e_flags |= EF_VRFYONLY; 62362373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 62462373Seric *p++; 62562373Seric if (*p == '\0') 62662373Seric { 62762373Seric message("501 Argument required"); 62862373Seric Errors++; 62962373Seric } 63062373Seric else 63162373Seric { 63264284Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, e); 63362373Seric } 6347762Seric if (Errors != 0) 6359339Seric { 6369339Seric if (InChild) 6379339Seric finis(); 6387762Seric break; 6399339Seric } 64062373Seric if (vrfyqueue == NULL) 64162373Seric { 64262373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 64362373Seric } 6445003Seric while (vrfyqueue != NULL) 6455003Seric { 64663971Seric a = vrfyqueue; 64763971Seric while ((a = a->q_next) != NULL && 64863971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 64963971Seric continue; 6507685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 65158151Seric printvrfyaddr(vrfyqueue, a == NULL); 65263847Seric vrfyqueue = vrfyqueue->q_next; 6535003Seric } 6549339Seric if (InChild) 6559339Seric finis(); 6564549Seric break; 6574549Seric 6584549Seric case CMDHELP: /* help -- give user info */ 6594577Seric help(p); 6604549Seric break; 6614549Seric 6624549Seric case CMDNOOP: /* noop -- do nothing */ 66364122Seric message("250 OK"); 6644549Seric break; 6654549Seric 6664549Seric case CMDQUIT: /* quit -- leave mail */ 66758151Seric message("221 %s closing connection", MyHostName); 66861051Seric 66961051Seric /* avoid future 050 messages */ 67061051Seric Verbose = FALSE; 67161051Seric 6729339Seric if (InChild) 6739339Seric ExitStat = EX_QUIT; 6744549Seric finis(); 6754549Seric 6768544Seric case CMDVERB: /* set verbose mode */ 67759957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 67859957Seric { 67959957Seric /* this would give out the same info */ 68059957Seric message("502 Verbose unavailable"); 68159957Seric break; 68259957Seric } 6838544Seric Verbose = TRUE; 68458734Seric e->e_sendmode = SM_DELIVER; 68559957Seric message("250 Verbose mode"); 6868544Seric break; 6878544Seric 6889314Seric case CMDONEX: /* doing one transaction only */ 6899378Seric OneXact = TRUE; 69059957Seric message("250 Only one transaction"); 6919314Seric break; 6929314Seric 69336230Skarels # ifdef SMTPDEBUG 6949339Seric case CMDDBGQSHOW: /* show queues */ 6956907Seric printf("Send Queue="); 69655012Seric printaddr(e->e_sendqueue, TRUE); 6975003Seric break; 6987275Seric 6997275Seric case CMDDBGDEBUG: /* set debug mode */ 7007676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 7017676Seric tTflag(p); 70258151Seric message("200 Debug set"); 7037275Seric break; 7047275Seric 70536230Skarels # else /* not SMTPDEBUG */ 70636230Skarels case CMDDBGQSHOW: /* show queues */ 70736230Skarels case CMDDBGDEBUG: /* set debug mode */ 70864685Seric # endif /* SMTPDEBUG */ 70964685Seric case CMDLOGBOGUS: /* bogus command */ 71036233Skarels # ifdef LOG 71158308Seric if (LogLevel > 0) 71264685Seric syslog(LOG_CRIT, 71358020Seric "\"%s\" command from %s (%s)", 71436230Skarels c->cmdname, RealHostName, 71558755Seric anynet_ntoa(&RealHostAddr)); 71636233Skarels # endif 71736230Skarels /* FALL THROUGH */ 71836230Skarels 7194549Seric case CMDERROR: /* unknown command */ 72058151Seric message("500 Command unrecognized"); 7214549Seric break; 7224549Seric 7234549Seric default: 72436230Skarels errno = 0; 72558151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 7264549Seric break; 7274549Seric } 7284549Seric } 7294549Seric } 7304549Seric /* 7314549Seric ** SKIPWORD -- skip a fixed word. 7324549Seric ** 7334549Seric ** Parameters: 7344549Seric ** p -- place to start looking. 7354549Seric ** w -- word to skip. 7364549Seric ** 7374549Seric ** Returns: 7384549Seric ** p following w. 7394549Seric ** NULL on error. 7404549Seric ** 7414549Seric ** Side Effects: 7424549Seric ** clobbers the p data area. 7434549Seric */ 7444549Seric 7454549Seric static char * 7464549Seric skipword(p, w) 7474549Seric register char *p; 7484549Seric char *w; 7494549Seric { 7504549Seric register char *q; 7514549Seric 7524549Seric /* find beginning of word */ 75358050Seric while (isascii(*p) && isspace(*p)) 7544549Seric p++; 7554549Seric q = p; 7564549Seric 7574549Seric /* find end of word */ 75858050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7594549Seric p++; 76058050Seric while (isascii(*p) && isspace(*p)) 7614549Seric *p++ = '\0'; 7624549Seric if (*p != ':') 7634549Seric { 7644549Seric syntax: 76562373Seric message("501 Syntax error in parameters"); 7664549Seric Errors++; 7674549Seric return (NULL); 7684549Seric } 7694549Seric *p++ = '\0'; 77058050Seric while (isascii(*p) && isspace(*p)) 7714549Seric p++; 7724549Seric 77362373Seric if (*p == '\0') 77462373Seric goto syntax; 77562373Seric 7764549Seric /* see if the input word matches desired word */ 77733725Sbostic if (strcasecmp(q, w)) 7784549Seric goto syntax; 7794549Seric 7804549Seric return (p); 7814549Seric } 7824577Seric /* 78358151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 78458151Seric ** 78558151Seric ** Parameters: 78658151Seric ** a -- the address to print 78758151Seric ** last -- set if this is the last one. 78858151Seric ** 78958151Seric ** Returns: 79058151Seric ** none. 79158151Seric ** 79258151Seric ** Side Effects: 79358151Seric ** Prints the appropriate 250 codes. 79458151Seric */ 79558151Seric 79658151Seric printvrfyaddr(a, last) 79758151Seric register ADDRESS *a; 79858151Seric bool last; 79958151Seric { 80058151Seric char fmtbuf[20]; 80158151Seric 80258151Seric strcpy(fmtbuf, "250"); 80358151Seric fmtbuf[3] = last ? ' ' : '-'; 80458151Seric 80559746Seric if (a->q_fullname == NULL) 80659746Seric { 80759746Seric if (strchr(a->q_user, '@') == NULL) 80859746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 80959746Seric else 81059746Seric strcpy(&fmtbuf[4], "<%s>"); 81159746Seric message(fmtbuf, a->q_user, MyHostName); 81259746Seric } 81358151Seric else 81458151Seric { 81559746Seric if (strchr(a->q_user, '@') == NULL) 81659746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 81759746Seric else 81859746Seric strcpy(&fmtbuf[4], "%s <%s>"); 81959746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 82058151Seric } 82158151Seric } 82258151Seric /* 8234577Seric ** HELP -- implement the HELP command. 8244577Seric ** 8254577Seric ** Parameters: 8264577Seric ** topic -- the topic we want help for. 8274577Seric ** 8284577Seric ** Returns: 8294577Seric ** none. 8304577Seric ** 8314577Seric ** Side Effects: 8324577Seric ** outputs the help file to message output. 8334577Seric */ 8344577Seric 8354577Seric help(topic) 8364577Seric char *topic; 8374577Seric { 8384577Seric register FILE *hf; 8394577Seric int len; 8404577Seric char buf[MAXLINE]; 8414577Seric bool noinfo; 8424577Seric 8438269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8444577Seric { 8454577Seric /* no help */ 84611931Seric errno = 0; 84758151Seric message("502 HELP not implemented"); 8484577Seric return; 8494577Seric } 8504577Seric 85149669Seric if (topic == NULL || *topic == '\0') 85249669Seric topic = "smtp"; 85349669Seric else 85449669Seric makelower(topic); 85549669Seric 8564577Seric len = strlen(topic); 8574577Seric noinfo = TRUE; 8584577Seric 8594577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8604577Seric { 8614577Seric if (strncmp(buf, topic, len) == 0) 8624577Seric { 8634577Seric register char *p; 8644577Seric 86556795Seric p = strchr(buf, '\t'); 8664577Seric if (p == NULL) 8674577Seric p = buf; 8684577Seric else 8694577Seric p++; 8704577Seric fixcrlf(p, TRUE); 87158151Seric message("214-%s", p); 8724577Seric noinfo = FALSE; 8734577Seric } 8744577Seric } 8754577Seric 8764577Seric if (noinfo) 87758151Seric message("504 HELP topic unknown"); 8784577Seric else 87958151Seric message("214 End of HELP info"); 8804628Seric (void) fclose(hf); 8814577Seric } 8828544Seric /* 8839339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8849339Seric ** 8859339Seric ** Parameters: 8869339Seric ** label -- a string used in error messages 8879339Seric ** 8889339Seric ** Returns: 8899339Seric ** zero in the child 8909339Seric ** one in the parent 8919339Seric ** 8929339Seric ** Side Effects: 8939339Seric ** none. 8949339Seric */ 8958544Seric 89655012Seric runinchild(label, e) 8979339Seric char *label; 89855012Seric register ENVELOPE *e; 8999339Seric { 9009339Seric int childpid; 9019339Seric 90216158Seric if (!OneXact) 9039339Seric { 90416158Seric childpid = dofork(); 90516158Seric if (childpid < 0) 90616158Seric { 90716158Seric syserr("%s: cannot fork", label); 90816158Seric return (1); 90916158Seric } 91016158Seric if (childpid > 0) 91116158Seric { 91216158Seric auto int st; 9139339Seric 91416158Seric /* parent -- wait for child to complete */ 91561093Seric setproctitle("server %s child wait", CurHostName); 91616158Seric st = waitfor(childpid); 91716158Seric if (st == -1) 91816158Seric syserr("%s: lost child", label); 919*64948Seric else if (!WIFEXITED(st)) 920*64948Seric syserr("%s: died on signal %d", 921*64948Seric label, st & 0177); 9229339Seric 92316158Seric /* if we exited on a QUIT command, complete the process */ 92416158Seric if (st == (EX_QUIT << 8)) 92516158Seric finis(); 9269339Seric 92716158Seric return (1); 92816158Seric } 92916158Seric else 93016158Seric { 93116158Seric /* child */ 93216158Seric InChild = TRUE; 93325050Seric QuickAbort = FALSE; 93455012Seric clearenvelope(e, FALSE); 93516158Seric } 9369339Seric } 93715256Seric 93816158Seric /* open alias database */ 93960537Seric initmaps(FALSE, e); 94016158Seric 94116158Seric return (0); 9429339Seric } 9439339Seric 94456795Seric # endif /* SMTP */ 945