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*60210Seric static char sccsid[] = "@(#)srvrsmtp.c 6.54 (Berkeley) 05/21/93 (with SMTP)"; 1433731Sbostic #else 15*60210Seric static char sccsid[] = "@(#)srvrsmtp.c 6.54 (Berkeley) 05/21/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[]; 11411151Seric extern char *macvalue(); 11512612Seric extern ADDRESS *recipient(); 11624943Seric extern ENVELOPE BlankEnvelope; 11724943Seric extern ENVELOPE *newenvelope(); 11858755Seric extern char *anynet_ntoa(); 1194549Seric 12059066Seric if (fileno(OutChannel) != fileno(stdout)) 1217363Seric { 1227363Seric /* arrange for debugging output to go to remote host */ 12359066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1247363Seric } 12555012Seric settime(e); 12657642Seric CurHostName = RealHostName; 12759060Seric setproctitle("srvrsmtp %s startup", CurHostName); 12858050Seric expand("\201e", inp, &inp[sizeof inp], e); 129*60210Seric message("220-%s", inp); 130*60210Seric message("220 ESMTP spoken here"); 13158330Seric protocol = NULL; 13259016Seric sendinghost = macvalue('s', e); 13358082Seric gothello = FALSE; 13458330Seric gotmail = FALSE; 1354549Seric for (;;) 1364549Seric { 13712612Seric /* arrange for backout */ 13812612Seric if (setjmp(TopFrame) > 0 && InChild) 13959058Seric { 14059058Seric QuickAbort = FALSE; 14159058Seric SuprErrs = TRUE; 14212612Seric finis(); 14359058Seric } 14412612Seric QuickAbort = FALSE; 14512612Seric HoldErrs = FALSE; 14651951Seric LogUsrErrs = FALSE; 14758092Seric e->e_flags &= ~EF_VRFYONLY; 14812612Seric 1497356Seric /* setup for the read */ 15055012Seric e->e_to = NULL; 1514577Seric Errors = 0; 1527275Seric (void) fflush(stdout); 1537356Seric 1547356Seric /* read the input line */ 15559060Seric SmtpPhase = "srvrsmtp cmd read"; 15659060Seric setproctitle("srvrsmtp %s cmd read", CurHostName); 15758109Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand); 1587356Seric 1597685Seric /* handle errors */ 1607356Seric if (p == NULL) 1617356Seric { 1624549Seric /* end of file, just die */ 16358151Seric message("421 %s Lost input channel from %s", 16425050Seric MyHostName, CurHostName); 16555464Seric #ifdef LOG 16658020Seric if (LogLevel > 1) 16755464Seric syslog(LOG_NOTICE, "lost input channel from %s", 16855464Seric CurHostName); 16955464Seric #endif 17058069Seric if (InChild) 17158069Seric ExitStat = EX_QUIT; 1724549Seric finis(); 1734549Seric } 1744549Seric 1754549Seric /* clean up end of line */ 1764558Seric fixcrlf(inp, TRUE); 1774549Seric 1784713Seric /* echo command to transcript */ 17955012Seric if (e->e_xfp != NULL) 18055012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1814713Seric 18259060Seric if (e->e_id == NULL) 18359060Seric setproctitle("%s: %s", CurHostName, inp); 18459060Seric else 18559060Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 18659060Seric 1874549Seric /* break off command */ 18858050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1894549Seric continue; 19057232Seric cmd = cmdbuf; 19158050Seric while (*p != '\0' && 19258050Seric !(isascii(*p) && isspace(*p)) && 19358050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 19424981Seric *cmd++ = *p++; 19524981Seric *cmd = '\0'; 1964549Seric 19725691Seric /* throw away leading whitespace */ 19858050Seric while (isascii(*p) && isspace(*p)) 19925691Seric p++; 20025691Seric 2014549Seric /* decode command */ 2024549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2034549Seric { 20433725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2054549Seric break; 2064549Seric } 2074549Seric 20851954Seric /* reset errors */ 20951954Seric errno = 0; 21051954Seric 2114549Seric /* process command */ 2124549Seric switch (c->cmdcode) 2134549Seric { 2144976Seric case CMDHELO: /* hello -- introduce yourself */ 21558323Seric case CMDEHLO: /* extended hello */ 21658323Seric if (c->cmdcode == CMDEHLO) 21758323Seric { 21858323Seric protocol = "ESMTP"; 21958323Seric SmtpPhase = "EHLO"; 22058323Seric } 22158323Seric else 22258323Seric { 22358323Seric protocol = "SMTP"; 22458323Seric SmtpPhase = "HELO"; 22558323Seric } 22659016Seric sendinghost = newstr(p); 22758308Seric if (strcasecmp(p, RealHostName) != 0) 22811146Seric { 22958789Seric auth_warning(e, "Host %s claimed to be %s", 23058789Seric RealHostName, p); 23111146Seric } 23258957Seric p = macvalue('_', e); 23358957Seric if (p == NULL) 23459016Seric p = RealHostName; 23558323Seric 23658323Seric /* send ext. message -- old systems must ignore */ 237*60210Seric message("250%c%s Hello %s, pleased to meet you", 238*60210Seric c->cmdcode == CMDEHLO ? '-' : ' ', 23958957Seric MyHostName, p); 240*60210Seric gothello = TRUE; 241*60210Seric if (c->cmdcode != CMDEHLO) 242*60210Seric break; 24358323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 24458323Seric message("250-EXPN"); 24559271Seric if (MaxMessageSize > 0) 24659271Seric message("250-SIZE %ld", MaxMessageSize); 24759271Seric else 24859271Seric message("250-SIZE"); 24958323Seric message("250 HELP"); 2504976Seric break; 2514976Seric 2524549Seric case CMDMAIL: /* mail -- designate sender */ 25324943Seric SmtpPhase = "MAIL"; 25424943Seric 2559314Seric /* check for validity of this command */ 25658789Seric if (!gothello) 25758082Seric { 25858957Seric /* set sending host to our known value */ 25959016Seric if (sendinghost == NULL) 26059016Seric sendinghost = RealHostName; 26158957Seric 26258789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 26358821Seric { 26458789Seric message("503 Polite people say HELO first"); 26558821Seric break; 26658821Seric } 26758789Seric else 26858821Seric { 26958789Seric auth_warning(e, 27058789Seric "Host %s didn't use HELO protocol", 27158789Seric RealHostName); 27258821Seric } 27358082Seric } 27458109Seric if (gotmail) 2754558Seric { 27658151Seric message("503 Sender already specified"); 2774558Seric break; 2784558Seric } 2799339Seric if (InChild) 2809339Seric { 28136230Skarels errno = 0; 28258151Seric syserr("503 Nested MAIL command: MAIL %s", p); 28358069Seric finis(); 2849339Seric } 2859339Seric 2869339Seric /* fork a subprocess to process this command */ 28755012Seric if (runinchild("SMTP-MAIL", e) > 0) 2889339Seric break; 28958323Seric if (protocol == NULL) 29058323Seric protocol = "SMTP"; 29158323Seric define('r', protocol, e); 29259016Seric define('s', sendinghost, e); 29355012Seric initsys(e); 29459747Seric nrcpts = 0; 29557389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2969339Seric 2979339Seric /* child -- go do the processing */ 2984549Seric p = skipword(p, "from"); 2994549Seric if (p == NULL) 3004549Seric break; 30157977Seric if (setjmp(TopFrame) > 0) 30258147Seric { 30358147Seric /* this failed -- undo work */ 30458147Seric if (InChild) 30559058Seric { 30659058Seric QuickAbort = FALSE; 30759058Seric SuprErrs = TRUE; 30858147Seric finis(); 30959058Seric } 31057977Seric break; 31158147Seric } 31257977Seric QuickAbort = TRUE; 31358333Seric 31458333Seric /* must parse sender first */ 31558333Seric delimptr = NULL; 31658704Seric setsender(p, e, &delimptr, FALSE); 31758333Seric p = delimptr; 31858333Seric if (p != NULL && *p != '\0') 31958333Seric *p++ = '\0'; 32058333Seric 32158333Seric /* now parse ESMTP arguments */ 32258333Seric msize = 0; 32358333Seric for (; p != NULL && *p != '\0'; p++) 32458333Seric { 32558333Seric char *kp; 32658333Seric char *vp; 32758333Seric 32858333Seric /* locate the beginning of the keyword */ 32958333Seric while (isascii(*p) && isspace(*p)) 33058333Seric p++; 33158333Seric if (*p == '\0') 33258333Seric break; 33358333Seric kp = p; 33458333Seric 33558333Seric /* skip to the value portion */ 33658333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 33758333Seric p++; 33858333Seric if (*p == '=') 33958333Seric { 34058333Seric *p++ = '\0'; 34158333Seric vp = p; 34258333Seric 34358333Seric /* skip to the end of the value */ 34458333Seric while (*p != '\0' && *p != ' ' && 34558333Seric !(isascii(*p) && iscntrl(*p)) && 34658333Seric *p != '=') 34758333Seric p++; 34858333Seric } 34958333Seric 35058333Seric if (*p != '\0') 35158333Seric *p++ = '\0'; 35258333Seric 35358333Seric if (tTd(19, 1)) 35458333Seric printf("MAIL: got arg %s=%s\n", kp, 35558333Seric vp == NULL ? "<null>" : vp); 35658333Seric 35758333Seric if (strcasecmp(kp, "size") == 0) 35858333Seric { 35959093Seric if (vp == NULL) 36058333Seric { 36158333Seric usrerr("501 SIZE requires a value"); 36258333Seric /* NOTREACHED */ 36358333Seric } 36458333Seric msize = atol(vp); 36558333Seric } 36659093Seric else if (strcasecmp(kp, "body") == 0) 36759093Seric { 36859093Seric if (vp == NULL) 36959093Seric { 37059093Seric usrerr("501 BODY requires a value"); 37159093Seric /* NOTREACHED */ 37259093Seric } 37359093Seric # ifdef MIME 37459093Seric if (strcasecmp(vp, "8bitmime") == 0) 37559093Seric { 37659093Seric e->e_bodytype = "8BITMIME"; 37759709Seric SevenBit = FALSE; 37859093Seric } 37959093Seric else if (strcasecmp(vp, "7bit") == 0) 38059093Seric { 38159093Seric e->e_bodytype = "7BIT"; 38259709Seric SevenBit = TRUE; 38359093Seric } 38459093Seric else 38559093Seric { 38659093Seric usrerr("501 Unknown BODY type %s", 38759093Seric vp); 38859093Seric } 38959093Seric # endif 39059093Seric } 39158333Seric else 39258333Seric { 39358333Seric usrerr("501 %s parameter unrecognized", kp); 39458333Seric /* NOTREACHED */ 39558333Seric } 39658333Seric } 39759284Seric 39859284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 39959284Seric { 40059284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 40159284Seric MaxMessageSize); 40259284Seric /* NOTREACHED */ 40359284Seric } 40458333Seric 40558333Seric if (!enoughspace(msize)) 40658333Seric { 40758333Seric message("452 Insufficient disk space; try again later"); 40858333Seric break; 40958333Seric } 41058151Seric message("250 Sender ok"); 41158147Seric gotmail = TRUE; 4124549Seric break; 4134549Seric 4144976Seric case CMDRCPT: /* rcpt -- designate recipient */ 41558850Seric if (!gotmail) 41658850Seric { 41758850Seric usrerr("503 Need MAIL before RCPT"); 41858850Seric break; 41958850Seric } 42024943Seric SmtpPhase = "RCPT"; 42112612Seric if (setjmp(TopFrame) > 0) 42214785Seric { 42355012Seric e->e_flags &= ~EF_FATALERRS; 42412612Seric break; 42514785Seric } 42612612Seric QuickAbort = TRUE; 42751951Seric LogUsrErrs = TRUE; 42858093Seric 42959699Seric if (e->e_sendmode != SM_DELIVER) 43059699Seric e->e_flags |= EF_VRFYONLY; 43158919Seric 4324549Seric p = skipword(p, "to"); 4334549Seric if (p == NULL) 4344549Seric break; 43558333Seric a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); 43612612Seric if (a == NULL) 43712612Seric break; 43816886Seric a->q_flags |= QPRIMARY; 43955012Seric a = recipient(a, &e->e_sendqueue, e); 44012612Seric if (Errors != 0) 44112612Seric break; 44212612Seric 44312612Seric /* no errors during parsing, but might be a duplicate */ 44455012Seric e->e_to = p; 44512612Seric if (!bitset(QBADADDR, a->q_flags)) 44659747Seric { 44758151Seric message("250 Recipient ok"); 44859747Seric nrcpts++; 44959747Seric } 45012612Seric else 4514549Seric { 45212612Seric /* punt -- should keep message in ADDRESS.... */ 45358151Seric message("550 Addressee unknown"); 4544549Seric } 45555012Seric e->e_to = NULL; 4564549Seric break; 4574549Seric 4584549Seric case CMDDATA: /* data -- text of mail */ 45924943Seric SmtpPhase = "DATA"; 46058109Seric if (!gotmail) 4614549Seric { 46258151Seric message("503 Need MAIL command"); 4634976Seric break; 4644549Seric } 46555012Seric else if (e->e_nrcpts <= 0) 4664549Seric { 46758151Seric message("503 Need RCPT (recipient)"); 4684976Seric break; 4694549Seric } 4704976Seric 47158929Seric /* check to see if we need to re-expand aliases */ 47258929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 47358929Seric { 47458929Seric if (bitset(QVERIFIED, a->q_flags)) 47558929Seric break; 47658929Seric } 47758929Seric 4784976Seric /* collect the text of the message */ 47924943Seric SmtpPhase = "collect"; 48058929Seric collect(TRUE, a != NULL, e); 48159730Seric e->e_flags &= ~EF_FATALERRS; 4824976Seric if (Errors != 0) 48359747Seric goto abortmessage; 4844976Seric 4858238Seric /* 4868238Seric ** Arrange to send to everyone. 4878238Seric ** If sending to multiple people, mail back 4888238Seric ** errors rather than reporting directly. 4898238Seric ** In any case, don't mail back errors for 4908238Seric ** anything that has happened up to 4918238Seric ** now (the other end will do this). 49210197Seric ** Truncate our transcript -- the mail has gotten 49310197Seric ** to us successfully, and if we have 49410197Seric ** to mail this back, it will be easier 49510197Seric ** on the reader. 4968238Seric ** Then send to everyone. 4978238Seric ** Finally give a reply code. If an error has 4988238Seric ** already been given, don't mail a 4998238Seric ** message back. 5009339Seric ** We goose error returns by clearing error bit. 5018238Seric */ 5028238Seric 50324943Seric SmtpPhase = "delivery"; 50459747Seric if (nrcpts != 1 && a == NULL) 5059378Seric { 5069378Seric HoldErrs = TRUE; 50758734Seric e->e_errormode = EM_MAIL; 5089378Seric } 50955012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 51058714Seric id = e->e_id; 5114976Seric 5124976Seric /* send to all recipients */ 51358919Seric sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE); 51455012Seric e->e_to = NULL; 5154976Seric 51623516Seric /* save statistics */ 51755012Seric markstats(e, (ADDRESS *) NULL); 51823516Seric 5198238Seric /* issue success if appropriate and reset */ 5208238Seric if (Errors == 0 || HoldErrs) 52158855Seric message("250 %s Message accepted for delivery", id); 52259747Seric 52359747Seric if (bitset(EF_FATALERRS, e->e_flags) && !HoldErrs) 52459730Seric { 52559730Seric /* avoid sending back an extra message */ 52659730Seric e->e_flags &= ~EF_FATALERRS; 52759747Seric e->e_flags |= EF_CLRQUEUE; 52859730Seric } 5298238Seric else 53058919Seric { 53159747Seric /* from now on, we have to operate silently */ 53259747Seric HoldErrs = TRUE; 53359747Seric e->e_errormode = EM_MAIL; 53459747Seric 53559730Seric /* if we just queued, poke it */ 53659730Seric if (a != NULL && e->e_sendmode != SM_QUEUE) 53759730Seric { 53859730Seric unlockqueue(e); 53959730Seric dowork(id, TRUE, TRUE, e); 54059730Seric e->e_id = NULL; 54159730Seric } 54258919Seric } 54358883Seric 54459747Seric abortmessage: 5459339Seric /* if in a child, pop back to our parent */ 5469339Seric if (InChild) 5479339Seric finis(); 54824943Seric 54924943Seric /* clean up a bit */ 55058109Seric gotmail = FALSE; 55155012Seric dropenvelope(e); 55258179Seric CurEnv = e = newenvelope(e, CurEnv); 55355012Seric e->e_flags = BlankEnvelope.e_flags; 5544549Seric break; 5554549Seric 5564549Seric case CMDRSET: /* rset -- reset state */ 55758151Seric message("250 Reset state"); 5589339Seric if (InChild) 5599339Seric finis(); 56058109Seric 56158109Seric /* clean up a bit */ 56258109Seric gotmail = FALSE; 56358109Seric dropenvelope(e); 56458179Seric CurEnv = e = newenvelope(e, CurEnv); 5659339Seric break; 5664549Seric 5674549Seric case CMDVRFY: /* vrfy -- verify address */ 56858092Seric case CMDEXPN: /* expn -- expand address */ 56958092Seric vrfy = c->cmdcode == CMDVRFY; 57058092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 57158092Seric PrivacyFlags)) 57258082Seric { 57358412Seric if (vrfy) 57458412Seric message("252 Who's to say?"); 57558412Seric else 57658412Seric message("502 That's none of your business"); 57758082Seric break; 57858082Seric } 57958082Seric else if (!gothello && 58058092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 58158092Seric PrivacyFlags)) 58258082Seric { 58358151Seric message("503 I demand that you introduce yourself first"); 58458082Seric break; 58558082Seric } 58658092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 5879339Seric break; 58855173Seric #ifdef LOG 58958020Seric if (LogLevel > 5) 59055173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 59155173Seric #endif 5925003Seric vrfyqueue = NULL; 5937762Seric QuickAbort = TRUE; 59458092Seric if (vrfy) 59558092Seric e->e_flags |= EF_VRFYONLY; 59658082Seric (void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e); 5977762Seric if (Errors != 0) 5989339Seric { 5999339Seric if (InChild) 6009339Seric finis(); 6017762Seric break; 6029339Seric } 6035003Seric while (vrfyqueue != NULL) 6045003Seric { 6055003Seric register ADDRESS *a = vrfyqueue->q_next; 6065003Seric char *code; 6075003Seric 6087685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 6095003Seric a = a->q_next; 6105003Seric 6117685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 61258151Seric printvrfyaddr(vrfyqueue, a == NULL); 6135003Seric else if (a == NULL) 61458151Seric message("554 Self destructive alias loop"); 6155003Seric vrfyqueue = a; 6165003Seric } 6179339Seric if (InChild) 6189339Seric finis(); 6194549Seric break; 6204549Seric 6214549Seric case CMDHELP: /* help -- give user info */ 6224577Seric help(p); 6234549Seric break; 6244549Seric 6254549Seric case CMDNOOP: /* noop -- do nothing */ 62658151Seric message("200 OK"); 6274549Seric break; 6284549Seric 6294549Seric case CMDQUIT: /* quit -- leave mail */ 63058151Seric message("221 %s closing connection", MyHostName); 6319339Seric if (InChild) 6329339Seric ExitStat = EX_QUIT; 6334549Seric finis(); 6344549Seric 6358544Seric case CMDVERB: /* set verbose mode */ 63659957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 63759957Seric { 63859957Seric /* this would give out the same info */ 63959957Seric message("502 Verbose unavailable"); 64059957Seric break; 64159957Seric } 6428544Seric Verbose = TRUE; 64358734Seric e->e_sendmode = SM_DELIVER; 64459957Seric message("250 Verbose mode"); 6458544Seric break; 6468544Seric 6479314Seric case CMDONEX: /* doing one transaction only */ 6489378Seric OneXact = TRUE; 64959957Seric message("250 Only one transaction"); 6509314Seric break; 6519314Seric 65236230Skarels # ifdef SMTPDEBUG 6539339Seric case CMDDBGQSHOW: /* show queues */ 6546907Seric printf("Send Queue="); 65555012Seric printaddr(e->e_sendqueue, TRUE); 6565003Seric break; 6577275Seric 6587275Seric case CMDDBGDEBUG: /* set debug mode */ 6597676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6607676Seric tTflag(p); 66158151Seric message("200 Debug set"); 6627275Seric break; 6637275Seric 66436230Skarels # else /* not SMTPDEBUG */ 66524945Seric 66636230Skarels case CMDDBGQSHOW: /* show queues */ 66736230Skarels case CMDDBGDEBUG: /* set debug mode */ 66836233Skarels # ifdef LOG 66958308Seric if (LogLevel > 0) 67036230Skarels syslog(LOG_NOTICE, 67158020Seric "\"%s\" command from %s (%s)", 67236230Skarels c->cmdname, RealHostName, 67358755Seric anynet_ntoa(&RealHostAddr)); 67436233Skarels # endif 67536230Skarels /* FALL THROUGH */ 67636230Skarels # endif /* SMTPDEBUG */ 67736230Skarels 6784549Seric case CMDERROR: /* unknown command */ 67958151Seric message("500 Command unrecognized"); 6804549Seric break; 6814549Seric 6824549Seric default: 68336230Skarels errno = 0; 68458151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 6854549Seric break; 6864549Seric } 6874549Seric } 6884549Seric } 6894549Seric /* 6904549Seric ** SKIPWORD -- skip a fixed word. 6914549Seric ** 6924549Seric ** Parameters: 6934549Seric ** p -- place to start looking. 6944549Seric ** w -- word to skip. 6954549Seric ** 6964549Seric ** Returns: 6974549Seric ** p following w. 6984549Seric ** NULL on error. 6994549Seric ** 7004549Seric ** Side Effects: 7014549Seric ** clobbers the p data area. 7024549Seric */ 7034549Seric 7044549Seric static char * 7054549Seric skipword(p, w) 7064549Seric register char *p; 7074549Seric char *w; 7084549Seric { 7094549Seric register char *q; 7104549Seric 7114549Seric /* find beginning of word */ 71258050Seric while (isascii(*p) && isspace(*p)) 7134549Seric p++; 7144549Seric q = p; 7154549Seric 7164549Seric /* find end of word */ 71758050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 7184549Seric p++; 71958050Seric while (isascii(*p) && isspace(*p)) 7204549Seric *p++ = '\0'; 7214549Seric if (*p != ':') 7224549Seric { 7234549Seric syntax: 72458151Seric message("501 Syntax error"); 7254549Seric Errors++; 7264549Seric return (NULL); 7274549Seric } 7284549Seric *p++ = '\0'; 72958050Seric while (isascii(*p) && isspace(*p)) 7304549Seric p++; 7314549Seric 7324549Seric /* see if the input word matches desired word */ 73333725Sbostic if (strcasecmp(q, w)) 7344549Seric goto syntax; 7354549Seric 7364549Seric return (p); 7374549Seric } 7384577Seric /* 73958151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 74058151Seric ** 74158151Seric ** Parameters: 74258151Seric ** a -- the address to print 74358151Seric ** last -- set if this is the last one. 74458151Seric ** 74558151Seric ** Returns: 74658151Seric ** none. 74758151Seric ** 74858151Seric ** Side Effects: 74958151Seric ** Prints the appropriate 250 codes. 75058151Seric */ 75158151Seric 75258151Seric printvrfyaddr(a, last) 75358151Seric register ADDRESS *a; 75458151Seric bool last; 75558151Seric { 75658151Seric char fmtbuf[20]; 75758151Seric 75858151Seric strcpy(fmtbuf, "250"); 75958151Seric fmtbuf[3] = last ? ' ' : '-'; 76058151Seric 76159746Seric if (a->q_fullname == NULL) 76259746Seric { 76359746Seric if (strchr(a->q_user, '@') == NULL) 76459746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 76559746Seric else 76659746Seric strcpy(&fmtbuf[4], "<%s>"); 76759746Seric message(fmtbuf, a->q_user, MyHostName); 76859746Seric } 76958151Seric else 77058151Seric { 77159746Seric if (strchr(a->q_user, '@') == NULL) 77259746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 77359746Seric else 77459746Seric strcpy(&fmtbuf[4], "%s <%s>"); 77559746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 77658151Seric } 77758151Seric } 77858151Seric /* 7794577Seric ** HELP -- implement the HELP command. 7804577Seric ** 7814577Seric ** Parameters: 7824577Seric ** topic -- the topic we want help for. 7834577Seric ** 7844577Seric ** Returns: 7854577Seric ** none. 7864577Seric ** 7874577Seric ** Side Effects: 7884577Seric ** outputs the help file to message output. 7894577Seric */ 7904577Seric 7914577Seric help(topic) 7924577Seric char *topic; 7934577Seric { 7944577Seric register FILE *hf; 7954577Seric int len; 7964577Seric char buf[MAXLINE]; 7974577Seric bool noinfo; 7984577Seric 7998269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 8004577Seric { 8014577Seric /* no help */ 80211931Seric errno = 0; 80358151Seric message("502 HELP not implemented"); 8044577Seric return; 8054577Seric } 8064577Seric 80749669Seric if (topic == NULL || *topic == '\0') 80849669Seric topic = "smtp"; 80949669Seric else 81049669Seric makelower(topic); 81149669Seric 8124577Seric len = strlen(topic); 8134577Seric noinfo = TRUE; 8144577Seric 8154577Seric while (fgets(buf, sizeof buf, hf) != NULL) 8164577Seric { 8174577Seric if (strncmp(buf, topic, len) == 0) 8184577Seric { 8194577Seric register char *p; 8204577Seric 82156795Seric p = strchr(buf, '\t'); 8224577Seric if (p == NULL) 8234577Seric p = buf; 8244577Seric else 8254577Seric p++; 8264577Seric fixcrlf(p, TRUE); 82758151Seric message("214-%s", p); 8284577Seric noinfo = FALSE; 8294577Seric } 8304577Seric } 8314577Seric 8324577Seric if (noinfo) 83358151Seric message("504 HELP topic unknown"); 8344577Seric else 83558151Seric message("214 End of HELP info"); 8364628Seric (void) fclose(hf); 8374577Seric } 8388544Seric /* 8399339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8409339Seric ** 8419339Seric ** Parameters: 8429339Seric ** label -- a string used in error messages 8439339Seric ** 8449339Seric ** Returns: 8459339Seric ** zero in the child 8469339Seric ** one in the parent 8479339Seric ** 8489339Seric ** Side Effects: 8499339Seric ** none. 8509339Seric */ 8518544Seric 85255012Seric runinchild(label, e) 8539339Seric char *label; 85455012Seric register ENVELOPE *e; 8559339Seric { 8569339Seric int childpid; 8579339Seric 85816158Seric if (!OneXact) 8599339Seric { 86016158Seric childpid = dofork(); 86116158Seric if (childpid < 0) 86216158Seric { 86316158Seric syserr("%s: cannot fork", label); 86416158Seric return (1); 86516158Seric } 86616158Seric if (childpid > 0) 86716158Seric { 86816158Seric auto int st; 8699339Seric 87016158Seric /* parent -- wait for child to complete */ 87159060Seric setproctitle("srvrsmtp %s child wait", CurHostName); 87216158Seric st = waitfor(childpid); 87316158Seric if (st == -1) 87416158Seric syserr("%s: lost child", label); 8759339Seric 87616158Seric /* if we exited on a QUIT command, complete the process */ 87716158Seric if (st == (EX_QUIT << 8)) 87816158Seric finis(); 8799339Seric 88016158Seric return (1); 88116158Seric } 88216158Seric else 88316158Seric { 88416158Seric /* child */ 88516158Seric InChild = TRUE; 88625050Seric QuickAbort = FALSE; 88755012Seric clearenvelope(e, FALSE); 88816158Seric } 8899339Seric } 89015256Seric 89116158Seric /* open alias database */ 89259672Seric initaliases(FALSE, e); 89316158Seric 89416158Seric return (0); 8959339Seric } 8969339Seric 89756795Seric # endif /* SMTP */ 898