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*59709Seric static char sccsid[] = "@(#)srvrsmtp.c 6.49 (Berkeley) 05/03/93 (with SMTP)"; 1433731Sbostic #else 15*59709Seric static char sccsid[] = "@(#)srvrsmtp.c 6.49 (Berkeley) 05/03/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"); 23959271Seric if (MaxMessageSize > 0) 24059271Seric message("250-SIZE %ld", MaxMessageSize); 24159271Seric else 24259271Seric message("250-SIZE"); 24358323Seric message("250 HELP"); 24458082Seric gothello = TRUE; 2454976Seric break; 2464976Seric 2474549Seric case CMDMAIL: /* mail -- designate sender */ 24824943Seric SmtpPhase = "MAIL"; 24924943Seric 2509314Seric /* check for validity of this command */ 25158789Seric if (!gothello) 25258082Seric { 25358957Seric /* set sending host to our known value */ 25459016Seric if (sendinghost == NULL) 25559016Seric sendinghost = RealHostName; 25658957Seric 25758789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 25858821Seric { 25958789Seric message("503 Polite people say HELO first"); 26058821Seric break; 26158821Seric } 26258789Seric else 26358821Seric { 26458789Seric auth_warning(e, 26558789Seric "Host %s didn't use HELO protocol", 26658789Seric RealHostName); 26758821Seric } 26858082Seric } 26958109Seric if (gotmail) 2704558Seric { 27158151Seric message("503 Sender already specified"); 2724558Seric break; 2734558Seric } 2749339Seric if (InChild) 2759339Seric { 27636230Skarels errno = 0; 27758151Seric syserr("503 Nested MAIL command: MAIL %s", p); 27858069Seric finis(); 2799339Seric } 2809339Seric 2819339Seric /* fork a subprocess to process this command */ 28255012Seric if (runinchild("SMTP-MAIL", e) > 0) 2839339Seric break; 28458323Seric if (protocol == NULL) 28558323Seric protocol = "SMTP"; 28658323Seric define('r', protocol, e); 28759016Seric define('s', sendinghost, e); 28855012Seric initsys(e); 28957389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2909339Seric 2919339Seric /* child -- go do the processing */ 2924549Seric p = skipword(p, "from"); 2934549Seric if (p == NULL) 2944549Seric break; 29557977Seric if (setjmp(TopFrame) > 0) 29658147Seric { 29758147Seric /* this failed -- undo work */ 29858147Seric if (InChild) 29959058Seric { 30059058Seric QuickAbort = FALSE; 30159058Seric SuprErrs = TRUE; 30258147Seric finis(); 30359058Seric } 30457977Seric break; 30558147Seric } 30657977Seric QuickAbort = TRUE; 30758333Seric 30858333Seric /* must parse sender first */ 30958333Seric delimptr = NULL; 31058704Seric setsender(p, e, &delimptr, FALSE); 31158333Seric p = delimptr; 31258333Seric if (p != NULL && *p != '\0') 31358333Seric *p++ = '\0'; 31458333Seric 31558333Seric /* now parse ESMTP arguments */ 31658333Seric msize = 0; 31758333Seric for (; p != NULL && *p != '\0'; p++) 31858333Seric { 31958333Seric char *kp; 32058333Seric char *vp; 32158333Seric 32258333Seric /* locate the beginning of the keyword */ 32358333Seric while (isascii(*p) && isspace(*p)) 32458333Seric p++; 32558333Seric if (*p == '\0') 32658333Seric break; 32758333Seric kp = p; 32858333Seric 32958333Seric /* skip to the value portion */ 33058333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 33158333Seric p++; 33258333Seric if (*p == '=') 33358333Seric { 33458333Seric *p++ = '\0'; 33558333Seric vp = p; 33658333Seric 33758333Seric /* skip to the end of the value */ 33858333Seric while (*p != '\0' && *p != ' ' && 33958333Seric !(isascii(*p) && iscntrl(*p)) && 34058333Seric *p != '=') 34158333Seric p++; 34258333Seric } 34358333Seric 34458333Seric if (*p != '\0') 34558333Seric *p++ = '\0'; 34658333Seric 34758333Seric if (tTd(19, 1)) 34858333Seric printf("MAIL: got arg %s=%s\n", kp, 34958333Seric vp == NULL ? "<null>" : vp); 35058333Seric 35158333Seric if (strcasecmp(kp, "size") == 0) 35258333Seric { 35359093Seric if (vp == NULL) 35458333Seric { 35558333Seric usrerr("501 SIZE requires a value"); 35658333Seric /* NOTREACHED */ 35758333Seric } 35858333Seric msize = atol(vp); 35958333Seric } 36059093Seric else if (strcasecmp(kp, "body") == 0) 36159093Seric { 36259093Seric if (vp == NULL) 36359093Seric { 36459093Seric usrerr("501 BODY requires a value"); 36559093Seric /* NOTREACHED */ 36659093Seric } 36759093Seric # ifdef MIME 36859093Seric if (strcasecmp(vp, "8bitmime") == 0) 36959093Seric { 37059093Seric e->e_bodytype = "8BITMIME"; 371*59709Seric SevenBit = FALSE; 37259093Seric } 37359093Seric else if (strcasecmp(vp, "7bit") == 0) 37459093Seric { 37559093Seric e->e_bodytype = "7BIT"; 376*59709Seric SevenBit = TRUE; 37759093Seric } 37859093Seric else 37959093Seric { 38059093Seric usrerr("501 Unknown BODY type %s", 38159093Seric vp); 38259093Seric } 38359093Seric # endif 38459093Seric } 38558333Seric else 38658333Seric { 38758333Seric usrerr("501 %s parameter unrecognized", kp); 38858333Seric /* NOTREACHED */ 38958333Seric } 39058333Seric } 39159284Seric 39259284Seric if (MaxMessageSize > 0 && msize > MaxMessageSize) 39359284Seric { 39459284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 39559284Seric MaxMessageSize); 39659284Seric /* NOTREACHED */ 39759284Seric } 39858333Seric 39958333Seric if (!enoughspace(msize)) 40058333Seric { 40158333Seric message("452 Insufficient disk space; try again later"); 40258333Seric break; 40358333Seric } 40458151Seric message("250 Sender ok"); 40558147Seric gotmail = TRUE; 4064549Seric break; 4074549Seric 4084976Seric case CMDRCPT: /* rcpt -- designate recipient */ 40958850Seric if (!gotmail) 41058850Seric { 41158850Seric usrerr("503 Need MAIL before RCPT"); 41258850Seric break; 41358850Seric } 41424943Seric SmtpPhase = "RCPT"; 41512612Seric if (setjmp(TopFrame) > 0) 41614785Seric { 41755012Seric e->e_flags &= ~EF_FATALERRS; 41812612Seric break; 41914785Seric } 42012612Seric QuickAbort = TRUE; 42151951Seric LogUsrErrs = TRUE; 42258093Seric 42359699Seric if (e->e_sendmode != SM_DELIVER) 42459699Seric e->e_flags |= EF_VRFYONLY; 42558919Seric 4264549Seric p = skipword(p, "to"); 4274549Seric if (p == NULL) 4284549Seric break; 42958333Seric a = parseaddr(p, (ADDRESS *) NULL, 1, ' ', NULL, e); 43012612Seric if (a == NULL) 43112612Seric break; 43216886Seric a->q_flags |= QPRIMARY; 43355012Seric a = recipient(a, &e->e_sendqueue, e); 43412612Seric if (Errors != 0) 43512612Seric break; 43612612Seric 43712612Seric /* no errors during parsing, but might be a duplicate */ 43855012Seric e->e_to = p; 43912612Seric if (!bitset(QBADADDR, a->q_flags)) 44058151Seric message("250 Recipient ok"); 44112612Seric else 4424549Seric { 44312612Seric /* punt -- should keep message in ADDRESS.... */ 44458151Seric message("550 Addressee unknown"); 4454549Seric } 44655012Seric e->e_to = NULL; 4474549Seric break; 4484549Seric 4494549Seric case CMDDATA: /* data -- text of mail */ 45024943Seric SmtpPhase = "DATA"; 45158109Seric if (!gotmail) 4524549Seric { 45358151Seric message("503 Need MAIL command"); 4544976Seric break; 4554549Seric } 45655012Seric else if (e->e_nrcpts <= 0) 4574549Seric { 45858151Seric message("503 Need RCPT (recipient)"); 4594976Seric break; 4604549Seric } 4614976Seric 46258929Seric /* check to see if we need to re-expand aliases */ 46358929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 46458929Seric { 46558929Seric if (bitset(QVERIFIED, a->q_flags)) 46658929Seric break; 46758929Seric } 46858929Seric 4694976Seric /* collect the text of the message */ 47024943Seric SmtpPhase = "collect"; 47158929Seric collect(TRUE, a != NULL, e); 4724976Seric if (Errors != 0) 4734976Seric break; 4744976Seric 4758238Seric /* 4768238Seric ** Arrange to send to everyone. 4778238Seric ** If sending to multiple people, mail back 4788238Seric ** errors rather than reporting directly. 4798238Seric ** In any case, don't mail back errors for 4808238Seric ** anything that has happened up to 4818238Seric ** now (the other end will do this). 48210197Seric ** Truncate our transcript -- the mail has gotten 48310197Seric ** to us successfully, and if we have 48410197Seric ** to mail this back, it will be easier 48510197Seric ** on the reader. 4868238Seric ** Then send to everyone. 4878238Seric ** Finally give a reply code. If an error has 4888238Seric ** already been given, don't mail a 4898238Seric ** message back. 4909339Seric ** We goose error returns by clearing error bit. 4918238Seric */ 4928238Seric 49324943Seric SmtpPhase = "delivery"; 49455012Seric if (e->e_nrcpts != 1) 4959378Seric { 4969378Seric HoldErrs = TRUE; 49758734Seric e->e_errormode = EM_MAIL; 4989378Seric } 49955012Seric e->e_flags &= ~EF_FATALERRS; 50055012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 50158714Seric id = e->e_id; 5024976Seric 5034976Seric /* send to all recipients */ 50458919Seric sendall(e, a == NULL ? SM_DEFAULT : SM_QUEUE); 50555012Seric e->e_to = NULL; 5064976Seric 50723516Seric /* save statistics */ 50855012Seric markstats(e, (ADDRESS *) NULL); 50923516Seric 5108238Seric /* issue success if appropriate and reset */ 5118238Seric if (Errors == 0 || HoldErrs) 51258855Seric message("250 %s Message accepted for delivery", id); 5138238Seric else 51455012Seric e->e_flags &= ~EF_FATALERRS; 5159339Seric 51658919Seric /* if we just queued, poke it */ 51758919Seric if (a != NULL && e->e_sendmode != SM_QUEUE) 51858919Seric { 51958919Seric unlockqueue(e); 52058924Seric dowork(id, TRUE, TRUE, e); 52158919Seric e->e_id = NULL; 52258919Seric } 52358883Seric 5249339Seric /* if in a child, pop back to our parent */ 5259339Seric if (InChild) 5269339Seric finis(); 52724943Seric 52824943Seric /* clean up a bit */ 52958109Seric gotmail = FALSE; 53055012Seric dropenvelope(e); 53158179Seric CurEnv = e = newenvelope(e, CurEnv); 53255012Seric e->e_flags = BlankEnvelope.e_flags; 5334549Seric break; 5344549Seric 5354549Seric case CMDRSET: /* rset -- reset state */ 53658151Seric message("250 Reset state"); 5379339Seric if (InChild) 5389339Seric finis(); 53958109Seric 54058109Seric /* clean up a bit */ 54158109Seric gotmail = FALSE; 54258109Seric dropenvelope(e); 54358179Seric CurEnv = e = newenvelope(e, CurEnv); 5449339Seric break; 5454549Seric 5464549Seric case CMDVRFY: /* vrfy -- verify address */ 54758092Seric case CMDEXPN: /* expn -- expand address */ 54858092Seric vrfy = c->cmdcode == CMDVRFY; 54958092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 55058092Seric PrivacyFlags)) 55158082Seric { 55258412Seric if (vrfy) 55358412Seric message("252 Who's to say?"); 55458412Seric else 55558412Seric message("502 That's none of your business"); 55658082Seric break; 55758082Seric } 55858082Seric else if (!gothello && 55958092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 56058092Seric PrivacyFlags)) 56158082Seric { 56258151Seric message("503 I demand that you introduce yourself first"); 56358082Seric break; 56458082Seric } 56558092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 5669339Seric break; 56755173Seric #ifdef LOG 56858020Seric if (LogLevel > 5) 56955173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 57055173Seric #endif 5715003Seric vrfyqueue = NULL; 5727762Seric QuickAbort = TRUE; 57358092Seric if (vrfy) 57458092Seric e->e_flags |= EF_VRFYONLY; 57558082Seric (void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e); 5767762Seric if (Errors != 0) 5779339Seric { 5789339Seric if (InChild) 5799339Seric finis(); 5807762Seric break; 5819339Seric } 5825003Seric while (vrfyqueue != NULL) 5835003Seric { 5845003Seric register ADDRESS *a = vrfyqueue->q_next; 5855003Seric char *code; 5865003Seric 5877685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 5885003Seric a = a->q_next; 5895003Seric 5907685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 59158151Seric printvrfyaddr(vrfyqueue, a == NULL); 5925003Seric else if (a == NULL) 59358151Seric message("554 Self destructive alias loop"); 5945003Seric vrfyqueue = a; 5955003Seric } 5969339Seric if (InChild) 5979339Seric finis(); 5984549Seric break; 5994549Seric 6004549Seric case CMDHELP: /* help -- give user info */ 6014577Seric help(p); 6024549Seric break; 6034549Seric 6044549Seric case CMDNOOP: /* noop -- do nothing */ 60558151Seric message("200 OK"); 6064549Seric break; 6074549Seric 6084549Seric case CMDQUIT: /* quit -- leave mail */ 60958151Seric message("221 %s closing connection", MyHostName); 6109339Seric if (InChild) 6119339Seric ExitStat = EX_QUIT; 6124549Seric finis(); 6134549Seric 6148544Seric case CMDVERB: /* set verbose mode */ 6158544Seric Verbose = TRUE; 61658734Seric e->e_sendmode = SM_DELIVER; 61758151Seric message("200 Verbose mode"); 6188544Seric break; 6198544Seric 6209314Seric case CMDONEX: /* doing one transaction only */ 6219378Seric OneXact = TRUE; 62258151Seric message("200 Only one transaction"); 6239314Seric break; 6249314Seric 62536230Skarels # ifdef SMTPDEBUG 6269339Seric case CMDDBGQSHOW: /* show queues */ 6276907Seric printf("Send Queue="); 62855012Seric printaddr(e->e_sendqueue, TRUE); 6295003Seric break; 6307275Seric 6317275Seric case CMDDBGDEBUG: /* set debug mode */ 6327676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 6337676Seric tTflag(p); 63458151Seric message("200 Debug set"); 6357275Seric break; 6367275Seric 63736230Skarels # else /* not SMTPDEBUG */ 63824945Seric 63936230Skarels case CMDDBGQSHOW: /* show queues */ 64036230Skarels case CMDDBGDEBUG: /* set debug mode */ 64136233Skarels # ifdef LOG 64258308Seric if (LogLevel > 0) 64336230Skarels syslog(LOG_NOTICE, 64458020Seric "\"%s\" command from %s (%s)", 64536230Skarels c->cmdname, RealHostName, 64658755Seric anynet_ntoa(&RealHostAddr)); 64736233Skarels # endif 64836230Skarels /* FALL THROUGH */ 64936230Skarels # endif /* SMTPDEBUG */ 65036230Skarels 6514549Seric case CMDERROR: /* unknown command */ 65258151Seric message("500 Command unrecognized"); 6534549Seric break; 6544549Seric 6554549Seric default: 65636230Skarels errno = 0; 65758151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 6584549Seric break; 6594549Seric } 6604549Seric } 6614549Seric } 6624549Seric /* 6634549Seric ** SKIPWORD -- skip a fixed word. 6644549Seric ** 6654549Seric ** Parameters: 6664549Seric ** p -- place to start looking. 6674549Seric ** w -- word to skip. 6684549Seric ** 6694549Seric ** Returns: 6704549Seric ** p following w. 6714549Seric ** NULL on error. 6724549Seric ** 6734549Seric ** Side Effects: 6744549Seric ** clobbers the p data area. 6754549Seric */ 6764549Seric 6774549Seric static char * 6784549Seric skipword(p, w) 6794549Seric register char *p; 6804549Seric char *w; 6814549Seric { 6824549Seric register char *q; 6834549Seric 6844549Seric /* find beginning of word */ 68558050Seric while (isascii(*p) && isspace(*p)) 6864549Seric p++; 6874549Seric q = p; 6884549Seric 6894549Seric /* find end of word */ 69058050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 6914549Seric p++; 69258050Seric while (isascii(*p) && isspace(*p)) 6934549Seric *p++ = '\0'; 6944549Seric if (*p != ':') 6954549Seric { 6964549Seric syntax: 69758151Seric message("501 Syntax error"); 6984549Seric Errors++; 6994549Seric return (NULL); 7004549Seric } 7014549Seric *p++ = '\0'; 70258050Seric while (isascii(*p) && isspace(*p)) 7034549Seric p++; 7044549Seric 7054549Seric /* see if the input word matches desired word */ 70633725Sbostic if (strcasecmp(q, w)) 7074549Seric goto syntax; 7084549Seric 7094549Seric return (p); 7104549Seric } 7114577Seric /* 71258151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 71358151Seric ** 71458151Seric ** Parameters: 71558151Seric ** a -- the address to print 71658151Seric ** last -- set if this is the last one. 71758151Seric ** 71858151Seric ** Returns: 71958151Seric ** none. 72058151Seric ** 72158151Seric ** Side Effects: 72258151Seric ** Prints the appropriate 250 codes. 72358151Seric */ 72458151Seric 72558151Seric printvrfyaddr(a, last) 72658151Seric register ADDRESS *a; 72758151Seric bool last; 72858151Seric { 72958151Seric char fmtbuf[20]; 73058151Seric 73158151Seric strcpy(fmtbuf, "250"); 73258151Seric fmtbuf[3] = last ? ' ' : '-'; 73358151Seric 73458151Seric if (strchr(a->q_paddr, '<') != NULL) 73558151Seric strcpy(&fmtbuf[4], "%s"); 73658151Seric else if (a->q_fullname == NULL) 73758151Seric strcpy(&fmtbuf[4], "<%s>"); 73858151Seric else 73958151Seric { 74058151Seric strcpy(&fmtbuf[4], "%s <%s>"); 74158151Seric message(fmtbuf, a->q_fullname, a->q_paddr); 74258151Seric return; 74358151Seric } 74458151Seric message(fmtbuf, a->q_paddr); 74558151Seric } 74658151Seric /* 7474577Seric ** HELP -- implement the HELP command. 7484577Seric ** 7494577Seric ** Parameters: 7504577Seric ** topic -- the topic we want help for. 7514577Seric ** 7524577Seric ** Returns: 7534577Seric ** none. 7544577Seric ** 7554577Seric ** Side Effects: 7564577Seric ** outputs the help file to message output. 7574577Seric */ 7584577Seric 7594577Seric help(topic) 7604577Seric char *topic; 7614577Seric { 7624577Seric register FILE *hf; 7634577Seric int len; 7644577Seric char buf[MAXLINE]; 7654577Seric bool noinfo; 7664577Seric 7678269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 7684577Seric { 7694577Seric /* no help */ 77011931Seric errno = 0; 77158151Seric message("502 HELP not implemented"); 7724577Seric return; 7734577Seric } 7744577Seric 77549669Seric if (topic == NULL || *topic == '\0') 77649669Seric topic = "smtp"; 77749669Seric else 77849669Seric makelower(topic); 77949669Seric 7804577Seric len = strlen(topic); 7814577Seric noinfo = TRUE; 7824577Seric 7834577Seric while (fgets(buf, sizeof buf, hf) != NULL) 7844577Seric { 7854577Seric if (strncmp(buf, topic, len) == 0) 7864577Seric { 7874577Seric register char *p; 7884577Seric 78956795Seric p = strchr(buf, '\t'); 7904577Seric if (p == NULL) 7914577Seric p = buf; 7924577Seric else 7934577Seric p++; 7944577Seric fixcrlf(p, TRUE); 79558151Seric message("214-%s", p); 7964577Seric noinfo = FALSE; 7974577Seric } 7984577Seric } 7994577Seric 8004577Seric if (noinfo) 80158151Seric message("504 HELP topic unknown"); 8024577Seric else 80358151Seric message("214 End of HELP info"); 8044628Seric (void) fclose(hf); 8054577Seric } 8068544Seric /* 8079339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 8089339Seric ** 8099339Seric ** Parameters: 8109339Seric ** label -- a string used in error messages 8119339Seric ** 8129339Seric ** Returns: 8139339Seric ** zero in the child 8149339Seric ** one in the parent 8159339Seric ** 8169339Seric ** Side Effects: 8179339Seric ** none. 8189339Seric */ 8198544Seric 82055012Seric runinchild(label, e) 8219339Seric char *label; 82255012Seric register ENVELOPE *e; 8239339Seric { 8249339Seric int childpid; 8259339Seric 82616158Seric if (!OneXact) 8279339Seric { 82816158Seric childpid = dofork(); 82916158Seric if (childpid < 0) 83016158Seric { 83116158Seric syserr("%s: cannot fork", label); 83216158Seric return (1); 83316158Seric } 83416158Seric if (childpid > 0) 83516158Seric { 83616158Seric auto int st; 8379339Seric 83816158Seric /* parent -- wait for child to complete */ 83959060Seric setproctitle("srvrsmtp %s child wait", CurHostName); 84016158Seric st = waitfor(childpid); 84116158Seric if (st == -1) 84216158Seric syserr("%s: lost child", label); 8439339Seric 84416158Seric /* if we exited on a QUIT command, complete the process */ 84516158Seric if (st == (EX_QUIT << 8)) 84616158Seric finis(); 8479339Seric 84816158Seric return (1); 84916158Seric } 85016158Seric else 85116158Seric { 85216158Seric /* child */ 85316158Seric InChild = TRUE; 85425050Seric QuickAbort = FALSE; 85555012Seric clearenvelope(e, FALSE); 85616158Seric } 8579339Seric } 85815256Seric 85916158Seric /* open alias database */ 86059672Seric initaliases(FALSE, e); 86116158Seric 86216158Seric return (0); 8639339Seric } 8649339Seric 86556795Seric # endif /* SMTP */ 866