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*59093Seric static char sccsid[] = "@(#)srvrsmtp.c 6.44 (Berkeley) 04/15/93 (with SMTP)"; 1433731Sbostic #else 15*59093Seric static char sccsid[] = "@(#)srvrsmtp.c 6.44 (Berkeley) 04/15/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; 1108544Seric char inp[MAXLINE]; 11157232Seric char cmdbuf[MAXLINE]; 1127124Seric extern char Version[]; 11311151Seric extern char *macvalue(); 11412612Seric extern ADDRESS *recipient(); 11524943Seric extern ENVELOPE BlankEnvelope; 11624943Seric extern ENVELOPE *newenvelope(); 11758755Seric extern char *anynet_ntoa(); 1184549Seric 11959066Seric if (fileno(OutChannel) != fileno(stdout)) 1207363Seric { 1217363Seric /* arrange for debugging output to go to remote host */ 12259066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1237363Seric } 12455012Seric settime(e); 12557642Seric CurHostName = RealHostName; 12659060Seric setproctitle("srvrsmtp %s startup", CurHostName); 12758050Seric expand("\201e", inp, &inp[sizeof inp], e); 12858151Seric message("220 %s", inp); 12958330Seric protocol = NULL; 13059016Seric sendinghost = macvalue('s', e); 13158082Seric gothello = FALSE; 13258330Seric gotmail = FALSE; 1334549Seric for (;;) 1344549Seric { 13512612Seric /* arrange for backout */ 13612612Seric if (setjmp(TopFrame) > 0 && InChild) 13759058Seric { 13859058Seric QuickAbort = FALSE; 13959058Seric SuprErrs = TRUE; 14012612Seric finis(); 14159058Seric } 14212612Seric QuickAbort = FALSE; 14312612Seric HoldErrs = FALSE; 14451951Seric LogUsrErrs = FALSE; 14558092Seric e->e_flags &= ~EF_VRFYONLY; 14612612Seric 1477356Seric /* setup for the read */ 14855012Seric e->e_to = NULL; 1494577Seric Errors = 0; 1507275Seric (void) fflush(stdout); 1517356Seric 1527356Seric /* read the input line */ 15359060Seric SmtpPhase = "srvrsmtp cmd read"; 15459060Seric setproctitle("srvrsmtp %s cmd read", CurHostName); 15558109Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand); 1567356Seric 1577685Seric /* handle errors */ 1587356Seric if (p == NULL) 1597356Seric { 1604549Seric /* end of file, just die */ 16158151Seric message("421 %s Lost input channel from %s", 16225050Seric MyHostName, CurHostName); 16355464Seric #ifdef LOG 16458020Seric if (LogLevel > 1) 16555464Seric syslog(LOG_NOTICE, "lost input channel from %s", 16655464Seric CurHostName); 16755464Seric #endif 16858069Seric if (InChild) 16958069Seric ExitStat = EX_QUIT; 1704549Seric finis(); 1714549Seric } 1724549Seric 1734549Seric /* clean up end of line */ 1744558Seric fixcrlf(inp, TRUE); 1754549Seric 1764713Seric /* echo command to transcript */ 17755012Seric if (e->e_xfp != NULL) 17855012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1794713Seric 18059060Seric if (e->e_id == NULL) 18159060Seric setproctitle("%s: %s", CurHostName, inp); 18259060Seric else 18359060Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 18459060Seric 1854549Seric /* break off command */ 18658050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1874549Seric continue; 18857232Seric cmd = cmdbuf; 18958050Seric while (*p != '\0' && 19058050Seric !(isascii(*p) && isspace(*p)) && 19158050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 19224981Seric *cmd++ = *p++; 19324981Seric *cmd = '\0'; 1944549Seric 19525691Seric /* throw away leading whitespace */ 19658050Seric while (isascii(*p) && isspace(*p)) 19725691Seric p++; 19825691Seric 1994549Seric /* decode command */ 2004549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2014549Seric { 20233725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2034549Seric break; 2044549Seric } 2054549Seric 20651954Seric /* reset errors */ 20751954Seric errno = 0; 20851954Seric 2094549Seric /* process command */ 2104549Seric switch (c->cmdcode) 2114549Seric { 2124976Seric case CMDHELO: /* hello -- introduce yourself */ 21358323Seric case CMDEHLO: /* extended hello */ 21458323Seric if (c->cmdcode == CMDEHLO) 21558323Seric { 21658323Seric protocol = "ESMTP"; 21758323Seric SmtpPhase = "EHLO"; 21858323Seric } 21958323Seric else 22058323Seric { 22158323Seric protocol = "SMTP"; 22258323Seric SmtpPhase = "HELO"; 22358323Seric } 22459016Seric sendinghost = newstr(p); 22558308Seric if (strcasecmp(p, RealHostName) != 0) 22611146Seric { 22758789Seric auth_warning(e, "Host %s claimed to be %s", 22858789Seric RealHostName, p); 22911146Seric } 23058957Seric p = macvalue('_', e); 23158957Seric if (p == NULL) 23259016Seric p = RealHostName; 23358323Seric 23458323Seric /* send ext. message -- old systems must ignore */ 23558323Seric message("250-%s Hello %s, pleased to meet you", 23658957Seric MyHostName, p); 23758323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 23858323Seric message("250-EXPN"); 23958338Seric message("250-SIZE"); 24058323Seric message("250 HELP"); 24158082Seric gothello = TRUE; 2424976Seric break; 2434976Seric 2444549Seric case CMDMAIL: /* mail -- designate sender */ 24524943Seric SmtpPhase = "MAIL"; 24624943Seric 2479314Seric /* check for validity of this command */ 24858789Seric if (!gothello) 24958082Seric { 25058957Seric /* set sending host to our known value */ 25159016Seric if (sendinghost == NULL) 25259016Seric sendinghost = RealHostName; 25358957Seric 25458789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 25558821Seric { 25658789Seric message("503 Polite people say HELO first"); 25758821Seric break; 25858821Seric } 25958789Seric else 26058821Seric { 26158789Seric auth_warning(e, 26258789Seric "Host %s didn't use HELO protocol", 26358789Seric RealHostName); 26458821Seric } 26558082Seric } 26658109Seric if (gotmail) 2674558Seric { 26858151Seric message("503 Sender already specified"); 2694558Seric break; 2704558Seric } 2719339Seric if (InChild) 2729339Seric { 27336230Skarels errno = 0; 27458151Seric syserr("503 Nested MAIL command: MAIL %s", p); 27558069Seric finis(); 2769339Seric } 2779339Seric 2789339Seric /* fork a subprocess to process this command */ 27955012Seric if (runinchild("SMTP-MAIL", e) > 0) 2809339Seric break; 28158323Seric if (protocol == NULL) 28258323Seric protocol = "SMTP"; 28358323Seric define('r', protocol, e); 28459016Seric define('s', sendinghost, e); 28555012Seric initsys(e); 28657389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2879339Seric 2889339Seric /* child -- go do the processing */ 2894549Seric p = skipword(p, "from"); 2904549Seric if (p == NULL) 2914549Seric break; 29257977Seric if (setjmp(TopFrame) > 0) 29358147Seric { 29458147Seric /* this failed -- undo work */ 29558147Seric if (InChild) 29659058Seric { 29759058Seric QuickAbort = FALSE; 29859058Seric SuprErrs = TRUE; 29958147Seric finis(); 30059058Seric } 30157977Seric break; 30258147Seric } 30357977Seric QuickAbort = TRUE; 30458333Seric 30558333Seric /* must parse sender first */ 30658333Seric delimptr = NULL; 30758704Seric setsender(p, e, &delimptr, FALSE); 30858333Seric p = delimptr; 30958333Seric if (p != NULL && *p != '\0') 31058333Seric *p++ = '\0'; 31158333Seric 31258333Seric /* now parse ESMTP arguments */ 31358333Seric msize = 0; 31458333Seric for (; p != NULL && *p != '\0'; p++) 31558333Seric { 31658333Seric char *kp; 31758333Seric char *vp; 31858333Seric 31958333Seric /* locate the beginning of the keyword */ 32058333Seric while (isascii(*p) && isspace(*p)) 32158333Seric p++; 32258333Seric if (*p == '\0') 32358333Seric break; 32458333Seric kp = p; 32558333Seric 32658333Seric /* skip to the value portion */ 32758333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 32858333Seric p++; 32958333Seric if (*p == '=') 33058333Seric { 33158333Seric *p++ = '\0'; 33258333Seric vp = p; 33358333Seric 33458333Seric /* skip to the end of the value */ 33558333Seric while (*p != '\0' && *p != ' ' && 33658333Seric !(isascii(*p) && iscntrl(*p)) && 33758333Seric *p != '=') 33858333Seric p++; 33958333Seric } 34058333Seric 34158333Seric if (*p != '\0') 34258333Seric *p++ = '\0'; 34358333Seric 34458333Seric if (tTd(19, 1)) 34558333Seric printf("MAIL: got arg %s=%s\n", kp, 34658333Seric vp == NULL ? "<null>" : vp); 34758333Seric 34858333Seric if (strcasecmp(kp, "size") == 0) 34958333Seric { 350*59093Seric if (vp == NULL) 35158333Seric { 35258333Seric usrerr("501 SIZE requires a value"); 35358333Seric /* NOTREACHED */ 35458333Seric } 35558333Seric msize = atol(vp); 35658333Seric } 357*59093Seric else if (strcasecmp(kp, "body") == 0) 358*59093Seric { 359*59093Seric if (vp == NULL) 360*59093Seric { 361*59093Seric usrerr("501 BODY requires a value"); 362*59093Seric /* NOTREACHED */ 363*59093Seric } 364*59093Seric # ifdef MIME 365*59093Seric if (strcasecmp(vp, "8bitmime") == 0) 366*59093Seric { 367*59093Seric e->e_bodytype = "8BITMIME"; 368*59093Seric EightBit = TRUE; 369*59093Seric } 370*59093Seric else if (strcasecmp(vp, "7bit") == 0) 371*59093Seric { 372*59093Seric e->e_bodytype = "7BIT"; 373*59093Seric EightBit = FALSE; 374*59093Seric } 375*59093Seric else 376*59093Seric { 377*59093Seric usrerr("501 Unknown BODY type %s", 378*59093Seric vp); 379*59093Seric } 380*59093Seric # endif 381*59093Seric } 38258333Seric else 38358333Seric { 38458333Seric usrerr("501 %s parameter unrecognized", kp); 38558333Seric /* NOTREACHED */ 38658333Seric } 38758333Seric } 38858333Seric 38958333Seric if (!enoughspace(msize)) 39058333Seric { 39158333Seric message("452 Insufficient disk space; try again later"); 39258333Seric break; 39358333Seric } 39458151Seric message("250 Sender ok"); 39558147Seric gotmail = TRUE; 3964549Seric break; 3974549Seric 3984976Seric case CMDRCPT: /* rcpt -- designate recipient */ 39958850Seric if (!gotmail) 40058850Seric { 40158850Seric usrerr("503 Need MAIL before RCPT"); 40258850Seric break; 40358850Seric } 40424943Seric SmtpPhase = "RCPT"; 40512612Seric if (setjmp(TopFrame) > 0) 40614785Seric { 40755012Seric e->e_flags &= ~EF_FATALERRS; 40812612Seric break; 40914785Seric } 41012612Seric QuickAbort = TRUE; 41151951Seric LogUsrErrs = TRUE; 41258093Seric 41358919Seric e->e_flags |= EF_VRFYONLY; 41458919Seric 4154549Seric p = skipword(p, "to"); 4164549Seric if (p == NULL) 4174549Seric break; 41858333Seric a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); 41912612Seric if (a == NULL) 42012612Seric break; 42116886Seric a->q_flags |= QPRIMARY; 42255012Seric a = recipient(a, &e->e_sendqueue, e); 42312612Seric if (Errors != 0) 42412612Seric break; 42512612Seric 42612612Seric /* no errors during parsing, but might be a duplicate */ 42755012Seric e->e_to = p; 42812612Seric if (!bitset(QBADADDR, a->q_flags)) 42958151Seric message("250 Recipient ok"); 43012612Seric else 4314549Seric { 43212612Seric /* punt -- should keep message in ADDRESS.... */ 43358151Seric message("550 Addressee unknown"); 4344549Seric } 43555012Seric e->e_to = NULL; 4364549Seric break; 4374549Seric 4384549Seric case CMDDATA: /* data -- text of mail */ 43924943Seric SmtpPhase = "DATA"; 44058109Seric if (!gotmail) 4414549Seric { 44258151Seric message("503 Need MAIL command"); 4434976Seric break; 4444549Seric } 44555012Seric else if (e->e_nrcpts <= 0) 4464549Seric { 44758151Seric message("503 Need RCPT (recipient)"); 4484976Seric break; 4494549Seric } 4504976Seric 45158929Seric /* check to see if we need to re-expand aliases */ 45258929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 45358929Seric { 45458929Seric if (bitset(QVERIFIED, a->q_flags)) 45558929Seric break; 45658929Seric } 45758929Seric 4584976Seric /* collect the text of the message */ 45924943Seric SmtpPhase = "collect"; 46058929Seric collect(TRUE, a != NULL, e); 4614976Seric if (Errors != 0) 4624976Seric break; 4634976Seric 4648238Seric /* 4658238Seric ** Arrange to send to everyone. 4668238Seric ** If sending to multiple people, mail back 4678238Seric ** errors rather than reporting directly. 4688238Seric ** In any case, don't mail back errors for 4698238Seric ** anything that has happened up to 4708238Seric ** now (the other end will do this). 47110197Seric ** Truncate our transcript -- the mail has gotten 47210197Seric ** to us successfully, and if we have 47310197Seric ** to mail this back, it will be easier 47410197Seric ** on the reader. 4758238Seric ** Then send to everyone. 4768238Seric ** Finally give a reply code. If an error has 4778238Seric ** already been given, don't mail a 4788238Seric ** message back. 4799339Seric ** We goose error returns by clearing error bit. 4808238Seric */ 4818238Seric 48224943Seric SmtpPhase = "delivery"; 48355012Seric if (e->e_nrcpts != 1) 4849378Seric { 4859378Seric HoldErrs = TRUE; 48658734Seric e->e_errormode = EM_MAIL; 4879378Seric } 48855012Seric e->e_flags &= ~EF_FATALERRS; 48955012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 49058714Seric id = e->e_id; 4914976Seric 4924976Seric /* send to all recipients */ 49358919Seric sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE); 49455012Seric e->e_to = NULL; 4954976Seric 49623516Seric /* save statistics */ 49755012Seric markstats(e, (ADDRESS *) NULL); 49823516Seric 4998238Seric /* issue success if appropriate and reset */ 5008238Seric if (Errors == 0 || HoldErrs) 50158855Seric message("250 %s Message accepted for delivery", id); 5028238Seric else 50355012Seric e->e_flags &= ~EF_FATALERRS; 5049339Seric 50558919Seric /* if we just queued, poke it */ 50658919Seric if (a != NULL && e->e_sendmode != SM_QUEUE) 50758919Seric { 50858919Seric unlockqueue(e); 50958924Seric dowork(id, TRUE, TRUE, e); 51058919Seric e->e_id = NULL; 51158919Seric } 51258883Seric 5139339Seric /* if in a child, pop back to our parent */ 5149339Seric if (InChild) 5159339Seric finis(); 51624943Seric 51724943Seric /* clean up a bit */ 51858109Seric gotmail = FALSE; 51955012Seric dropenvelope(e); 52058179Seric CurEnv = e = newenvelope(e, CurEnv); 52155012Seric e->e_flags = BlankEnvelope.e_flags; 5224549Seric break; 5234549Seric 5244549Seric case CMDRSET: /* rset -- reset state */ 52558151Seric message("250 Reset state"); 5269339Seric if (InChild) 5279339Seric finis(); 52858109Seric 52958109Seric /* clean up a bit */ 53058109Seric gotmail = FALSE; 53158109Seric dropenvelope(e); 53258179Seric CurEnv = e = newenvelope(e, CurEnv); 5339339Seric break; 5344549Seric 5354549Seric case CMDVRFY: /* vrfy -- verify address */ 53658092Seric case CMDEXPN: /* expn -- expand address */ 53758092Seric vrfy = c->cmdcode == CMDVRFY; 53858092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 53958092Seric PrivacyFlags)) 54058082Seric { 54158412Seric if (vrfy) 54258412Seric message("252 Who's to say?"); 54358412Seric else 54458412Seric message("502 That's none of your business"); 54558082Seric break; 54658082Seric } 54758082Seric else if (!gothello && 54858092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 54958092Seric PrivacyFlags)) 55058082Seric { 55158151Seric message("503 I demand that you introduce yourself first"); 55258082Seric break; 55358082Seric } 55458092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 5559339Seric break; 55655173Seric #ifdef LOG 55758020Seric if (LogLevel > 5) 55855173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 55955173Seric #endif 5605003Seric vrfyqueue = NULL; 5617762Seric QuickAbort = TRUE; 56258092Seric if (vrfy) 56358092Seric e->e_flags |= EF_VRFYONLY; 56458082Seric (void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e); 5657762Seric if (Errors != 0) 5669339Seric { 5679339Seric if (InChild) 5689339Seric finis(); 5697762Seric break; 5709339Seric } 5715003Seric while (vrfyqueue != NULL) 5725003Seric { 5735003Seric register ADDRESS *a = vrfyqueue->q_next; 5745003Seric char *code; 5755003Seric 5767685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 5775003Seric a = a->q_next; 5785003Seric 5797685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 58058151Seric printvrfyaddr(vrfyqueue, a == NULL); 5815003Seric else if (a == NULL) 58258151Seric message("554 Self destructive alias loop"); 5835003Seric vrfyqueue = a; 5845003Seric } 5859339Seric if (InChild) 5869339Seric finis(); 5874549Seric break; 5884549Seric 5894549Seric case CMDHELP: /* help -- give user info */ 5904577Seric help(p); 5914549Seric break; 5924549Seric 5934549Seric case CMDNOOP: /* noop -- do nothing */ 59458151Seric message("200 OK"); 5954549Seric break; 5964549Seric 5974549Seric case CMDQUIT: /* quit -- leave mail */ 59858151Seric message("221 %s closing connection", MyHostName); 5999339Seric if (InChild) 6009339Seric ExitStat = EX_QUIT; 6014549Seric finis(); 6024549Seric 6038544Seric case CMDVERB: /* set verbose mode */ 6048544Seric Verbose = TRUE; 60558734Seric e->e_sendmode = SM_DELIVER; 60658151Seric message("200 Verbose mode"); 6078544Seric break; 6088544Seric 6099314Seric case CMDONEX: /* doing one transaction only */ 6109378Seric OneXact = TRUE; 61158151Seric message("200 Only one transaction"); 6129314Seric break; 6139314Seric 61436230Skarels # ifdef SMTPDEBUG 6159339Seric case CMDDBGQSHOW: /* show queues */ 6166907Seric printf("Send Queue="); 61755012Seric printaddr(e->e_sendqueue, TRUE); 6185003Seric break; 6197275Seric 6207275Seric case CMDDBGDEBUG: /* set debug mode */ 6217676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6227676Seric tTflag(p); 62358151Seric message("200 Debug set"); 6247275Seric break; 6257275Seric 62636230Skarels # else /* not SMTPDEBUG */ 62724945Seric 62836230Skarels case CMDDBGQSHOW: /* show queues */ 62936230Skarels case CMDDBGDEBUG: /* set debug mode */ 63036233Skarels # ifdef LOG 63158308Seric if (LogLevel > 0) 63236230Skarels syslog(LOG_NOTICE, 63358020Seric "\"%s\" command from %s (%s)", 63436230Skarels c->cmdname, RealHostName, 63558755Seric anynet_ntoa(&RealHostAddr)); 63636233Skarels # endif 63736230Skarels /* FALL THROUGH */ 63836230Skarels # endif /* SMTPDEBUG */ 63936230Skarels 6404549Seric case CMDERROR: /* unknown command */ 64158151Seric message("500 Command unrecognized"); 6424549Seric break; 6434549Seric 6444549Seric default: 64536230Skarels errno = 0; 64658151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 6474549Seric break; 6484549Seric } 6494549Seric } 6504549Seric } 6514549Seric /* 6524549Seric ** SKIPWORD -- skip a fixed word. 6534549Seric ** 6544549Seric ** Parameters: 6554549Seric ** p -- place to start looking. 6564549Seric ** w -- word to skip. 6574549Seric ** 6584549Seric ** Returns: 6594549Seric ** p following w. 6604549Seric ** NULL on error. 6614549Seric ** 6624549Seric ** Side Effects: 6634549Seric ** clobbers the p data area. 6644549Seric */ 6654549Seric 6664549Seric static char * 6674549Seric skipword(p, w) 6684549Seric register char *p; 6694549Seric char *w; 6704549Seric { 6714549Seric register char *q; 6724549Seric 6734549Seric /* find beginning of word */ 67458050Seric while (isascii(*p) && isspace(*p)) 6754549Seric p++; 6764549Seric q = p; 6774549Seric 6784549Seric /* find end of word */ 67958050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 6804549Seric p++; 68158050Seric while (isascii(*p) && isspace(*p)) 6824549Seric *p++ = '\0'; 6834549Seric if (*p != ':') 6844549Seric { 6854549Seric syntax: 68658151Seric message("501 Syntax error"); 6874549Seric Errors++; 6884549Seric return (NULL); 6894549Seric } 6904549Seric *p++ = '\0'; 69158050Seric while (isascii(*p) && isspace(*p)) 6924549Seric p++; 6934549Seric 6944549Seric /* see if the input word matches desired word */ 69533725Sbostic if (strcasecmp(q, w)) 6964549Seric goto syntax; 6974549Seric 6984549Seric return (p); 6994549Seric } 7004577Seric /* 70158151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 70258151Seric ** 70358151Seric ** Parameters: 70458151Seric ** a -- the address to print 70558151Seric ** last -- set if this is the last one. 70658151Seric ** 70758151Seric ** Returns: 70858151Seric ** none. 70958151Seric ** 71058151Seric ** Side Effects: 71158151Seric ** Prints the appropriate 250 codes. 71258151Seric */ 71358151Seric 71458151Seric printvrfyaddr(a, last) 71558151Seric register ADDRESS *a; 71658151Seric bool last; 71758151Seric { 71858151Seric char fmtbuf[20]; 71958151Seric 72058151Seric strcpy(fmtbuf, "250"); 72158151Seric fmtbuf[3] = last ? ' ' : '-'; 72258151Seric 72358151Seric if (strchr(a->q_paddr, '<') != NULL) 72458151Seric strcpy(&fmtbuf[4], "%s"); 72558151Seric else if (a->q_fullname == NULL) 72658151Seric strcpy(&fmtbuf[4], "<%s>"); 72758151Seric else 72858151Seric { 72958151Seric strcpy(&fmtbuf[4], "%s <%s>"); 73058151Seric message(fmtbuf, a->q_fullname, a->q_paddr); 73158151Seric return; 73258151Seric } 73358151Seric message(fmtbuf, a->q_paddr); 73458151Seric } 73558151Seric /* 7364577Seric ** HELP -- implement the HELP command. 7374577Seric ** 7384577Seric ** Parameters: 7394577Seric ** topic -- the topic we want help for. 7404577Seric ** 7414577Seric ** Returns: 7424577Seric ** none. 7434577Seric ** 7444577Seric ** Side Effects: 7454577Seric ** outputs the help file to message output. 7464577Seric */ 7474577Seric 7484577Seric help(topic) 7494577Seric char *topic; 7504577Seric { 7514577Seric register FILE *hf; 7524577Seric int len; 7534577Seric char buf[MAXLINE]; 7544577Seric bool noinfo; 7554577Seric 7568269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 7574577Seric { 7584577Seric /* no help */ 75911931Seric errno = 0; 76058151Seric message("502 HELP not implemented"); 7614577Seric return; 7624577Seric } 7634577Seric 76449669Seric if (topic == NULL || *topic == '\0') 76549669Seric topic = "smtp"; 76649669Seric else 76749669Seric makelower(topic); 76849669Seric 7694577Seric len = strlen(topic); 7704577Seric noinfo = TRUE; 7714577Seric 7724577Seric while (fgets(buf, sizeof buf, hf) != NULL) 7734577Seric { 7744577Seric if (strncmp(buf, topic, len) == 0) 7754577Seric { 7764577Seric register char *p; 7774577Seric 77856795Seric p = strchr(buf, '\t'); 7794577Seric if (p == NULL) 7804577Seric p = buf; 7814577Seric else 7824577Seric p++; 7834577Seric fixcrlf(p, TRUE); 78458151Seric message("214-%s", p); 7854577Seric noinfo = FALSE; 7864577Seric } 7874577Seric } 7884577Seric 7894577Seric if (noinfo) 79058151Seric message("504 HELP topic unknown"); 7914577Seric else 79258151Seric message("214 End of HELP info"); 7934628Seric (void) fclose(hf); 7944577Seric } 7958544Seric /* 7969339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 7979339Seric ** 7989339Seric ** Parameters: 7999339Seric ** label -- a string used in error messages 8009339Seric ** 8019339Seric ** Returns: 8029339Seric ** zero in the child 8039339Seric ** one in the parent 8049339Seric ** 8059339Seric ** Side Effects: 8069339Seric ** none. 8079339Seric */ 8088544Seric 80955012Seric runinchild(label, e) 8109339Seric char *label; 81155012Seric register ENVELOPE *e; 8129339Seric { 8139339Seric int childpid; 8149339Seric 81516158Seric if (!OneXact) 8169339Seric { 81716158Seric childpid = dofork(); 81816158Seric if (childpid < 0) 81916158Seric { 82016158Seric syserr("%s: cannot fork", label); 82116158Seric return (1); 82216158Seric } 82316158Seric if (childpid > 0) 82416158Seric { 82516158Seric auto int st; 8269339Seric 82716158Seric /* parent -- wait for child to complete */ 82859060Seric setproctitle("srvrsmtp %s child wait", CurHostName); 82916158Seric st = waitfor(childpid); 83016158Seric if (st == -1) 83116158Seric syserr("%s: lost child", label); 8329339Seric 83316158Seric /* if we exited on a QUIT command, complete the process */ 83416158Seric if (st == (EX_QUIT << 8)) 83516158Seric finis(); 8369339Seric 83716158Seric return (1); 83816158Seric } 83916158Seric else 84016158Seric { 84116158Seric /* child */ 84216158Seric InChild = TRUE; 84325050Seric QuickAbort = FALSE; 84455012Seric clearenvelope(e, FALSE); 84516158Seric } 8469339Seric } 84715256Seric 84816158Seric /* open alias database */ 84955012Seric initaliases(AliasFile, FALSE, e); 85016158Seric 85116158Seric return (0); 8529339Seric } 8539339Seric 85456795Seric # endif /* SMTP */ 855