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*58092Seric static char sccsid[] = "@(#)srvrsmtp.c 6.12 (Berkeley) 02/20/93 (with SMTP)"; 1433731Sbostic #else 15*58092Seric static char sccsid[] = "@(#)srvrsmtp.c 6.12 (Berkeley) 02/20/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 */ 51*58092Seric # 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 */ 55*58092Seric # define CMDHELP 10 /* help -- give usage info */ 56*58092Seric /* non-standard commands */ 57*58092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 58*58092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 5936230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 60*58092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 61*58092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 624549Seric 634549Seric static struct cmd CmdTab[] = 644549Seric { 654549Seric "mail", CMDMAIL, 664976Seric "rcpt", CMDRCPT, 674549Seric "data", CMDDATA, 684549Seric "rset", CMDRSET, 694549Seric "vrfy", CMDVRFY, 70*58092Seric "expn", CMDEXPN, 714549Seric "help", CMDHELP, 724549Seric "noop", CMDNOOP, 734549Seric "quit", CMDQUIT, 744976Seric "helo", CMDHELO, 758544Seric "verb", CMDVERB, 769314Seric "onex", CMDONEX, 7736230Skarels /* 7836230Skarels * remaining commands are here only 7936230Skarels * to trap and log attempts to use them 8036230Skarels */ 819339Seric "showq", CMDDBGQSHOW, 828544Seric "debug", CMDDBGDEBUG, 834549Seric NULL, CMDERROR, 844549Seric }; 854549Seric 869339Seric bool InChild = FALSE; /* true if running in a subprocess */ 879378Seric bool OneXact = FALSE; /* one xaction only this run */ 8811146Seric 899339Seric #define EX_QUIT 22 /* special code for QUIT command */ 908544Seric 9155012Seric smtp(e) 9255012Seric register ENVELOPE *e; 934549Seric { 944549Seric register char *p; 958544Seric register struct cmd *c; 964549Seric char *cmd; 9746928Sbostic static char *skipword(); 985003Seric auto ADDRESS *vrfyqueue; 9912612Seric ADDRESS *a; 10030448Seric char *sendinghost; 101*58092Seric bool hasmail; /* mail command received */ 102*58092Seric bool gothello; /* helo command received */ 103*58092Seric bool vrfy; /* set if this is a vrfy command */ 1048544Seric char inp[MAXLINE]; 10557232Seric char cmdbuf[MAXLINE]; 1067124Seric extern char Version[]; 10711151Seric extern char *macvalue(); 10812612Seric extern ADDRESS *recipient(); 10924943Seric extern ENVELOPE BlankEnvelope; 11024943Seric extern ENVELOPE *newenvelope(); 1114549Seric 1125003Seric hasmail = FALSE; 1137363Seric if (OutChannel != stdout) 1147363Seric { 1157363Seric /* arrange for debugging output to go to remote host */ 1167363Seric (void) close(1); 1177363Seric (void) dup(fileno(OutChannel)); 1187363Seric } 11955012Seric settime(e); 12057642Seric if (RealHostName == NULL) 12157642Seric RealHostName = MyHostName; 12257642Seric CurHostName = RealHostName; 12357642Seric setproctitle("srvrsmtp %s", CurHostName); 12458050Seric expand("\201e", inp, &inp[sizeof inp], e); 12555360Seric message("220", "%s", inp); 12624943Seric SmtpPhase = "startup"; 12730448Seric sendinghost = NULL; 12858082Seric gothello = FALSE; 1294549Seric for (;;) 1304549Seric { 13112612Seric /* arrange for backout */ 13212612Seric if (setjmp(TopFrame) > 0 && InChild) 13312612Seric finis(); 13412612Seric QuickAbort = FALSE; 13512612Seric HoldErrs = FALSE; 13651951Seric LogUsrErrs = FALSE; 137*58092Seric e->e_flags &= ~EF_VRFYONLY; 13812612Seric 1397356Seric /* setup for the read */ 14055012Seric e->e_to = NULL; 1414577Seric Errors = 0; 1427275Seric (void) fflush(stdout); 1437356Seric 1447356Seric /* read the input line */ 14557389Seric p = sfgets(inp, sizeof inp, InChannel, ReadTimeout); 1467356Seric 1477685Seric /* handle errors */ 1487356Seric if (p == NULL) 1497356Seric { 1504549Seric /* end of file, just die */ 15136230Skarels message("421", "%s Lost input channel from %s", 15225050Seric MyHostName, CurHostName); 15355464Seric #ifdef LOG 15458020Seric if (LogLevel > 1) 15555464Seric syslog(LOG_NOTICE, "lost input channel from %s", 15655464Seric CurHostName); 15755464Seric #endif 15858069Seric if (InChild) 15958069Seric ExitStat = EX_QUIT; 1604549Seric finis(); 1614549Seric } 1624549Seric 1634549Seric /* clean up end of line */ 1644558Seric fixcrlf(inp, TRUE); 1654549Seric 1664713Seric /* echo command to transcript */ 16755012Seric if (e->e_xfp != NULL) 16855012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1694713Seric 1704549Seric /* break off command */ 17158050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1724549Seric continue; 17357232Seric cmd = cmdbuf; 17458050Seric while (*p != '\0' && 17558050Seric !(isascii(*p) && isspace(*p)) && 17658050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 17724981Seric *cmd++ = *p++; 17824981Seric *cmd = '\0'; 1794549Seric 18025691Seric /* throw away leading whitespace */ 18158050Seric while (isascii(*p) && isspace(*p)) 18225691Seric p++; 18325691Seric 1844549Seric /* decode command */ 1854549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1864549Seric { 18733725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 1884549Seric break; 1894549Seric } 1904549Seric 19151954Seric /* reset errors */ 19251954Seric errno = 0; 19351954Seric 1944549Seric /* process command */ 1954549Seric switch (c->cmdcode) 1964549Seric { 1974976Seric case CMDHELO: /* hello -- introduce yourself */ 19824943Seric SmtpPhase = "HELO"; 19925050Seric setproctitle("%s: %s", CurHostName, inp); 20033725Sbostic if (!strcasecmp(p, MyHostName)) 20114877Seric { 20236230Skarels /* 20336230Skarels * didn't know about alias, 20436230Skarels * or connected to an echo server 20536230Skarels */ 20647570Seric message("553", "%s config error: mail loops back to myself", 20747570Seric MyHostName); 20814877Seric break; 20914877Seric } 21033725Sbostic if (RealHostName != NULL && strcasecmp(p, RealHostName)) 21111146Seric { 21224981Seric char hostbuf[MAXNAME]; 21311146Seric 21424981Seric (void) sprintf(hostbuf, "%s (%s)", p, RealHostName); 21530448Seric sendinghost = newstr(hostbuf); 21611146Seric } 21711146Seric else 21830448Seric sendinghost = newstr(p); 2194997Seric message("250", "%s Hello %s, pleased to meet you", 22036230Skarels MyHostName, sendinghost); 22158082Seric gothello = TRUE; 2224976Seric break; 2234976Seric 2244549Seric case CMDMAIL: /* mail -- designate sender */ 22524943Seric SmtpPhase = "MAIL"; 22624943Seric 22711151Seric /* force a sending host even if no HELO given */ 22858064Seric if (sendinghost == NULL && macvalue('s', e) == NULL) 22930448Seric sendinghost = RealHostName; 23011151Seric 2319314Seric /* check for validity of this command */ 23258082Seric if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 23358082Seric { 23458082Seric message("503", "Polite people say HELO first"); 23558082Seric break; 23658082Seric } 2374558Seric if (hasmail) 2384558Seric { 2394558Seric message("503", "Sender already specified"); 2404558Seric break; 2414558Seric } 2429339Seric if (InChild) 2439339Seric { 24436230Skarels errno = 0; 24558008Seric syserr("Nested MAIL command: MAIL %s", p); 24658069Seric finis(); 2479339Seric } 24858082Seric if (!enoughspace()) 24958082Seric { 25058082Seric message("452", "Insufficient disk space; try again later"); 25158082Seric break; 25258082Seric } 2539339Seric 2549339Seric /* fork a subprocess to process this command */ 25555012Seric if (runinchild("SMTP-MAIL", e) > 0) 2569339Seric break; 25758064Seric if (sendinghost != NULL) 25858064Seric define('s', sendinghost, e); 25955012Seric define('r', "SMTP", e); 26055012Seric initsys(e); 26157389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2629339Seric 2639339Seric /* child -- go do the processing */ 2644549Seric p = skipword(p, "from"); 2654549Seric if (p == NULL) 2664549Seric break; 26757977Seric if (setjmp(TopFrame) > 0) 26857977Seric break; 26957977Seric QuickAbort = TRUE; 27055012Seric setsender(p, e); 2714577Seric if (Errors == 0) 2724549Seric { 2734549Seric message("250", "Sender ok"); 2744549Seric hasmail = TRUE; 2754549Seric } 2769339Seric else if (InChild) 2779339Seric finis(); 2784549Seric break; 2794549Seric 2804976Seric case CMDRCPT: /* rcpt -- designate recipient */ 28124943Seric SmtpPhase = "RCPT"; 28257389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 28312612Seric if (setjmp(TopFrame) > 0) 28414785Seric { 28555012Seric e->e_flags &= ~EF_FATALERRS; 28612612Seric break; 28714785Seric } 28812612Seric QuickAbort = TRUE; 28951951Seric LogUsrErrs = TRUE; 2904549Seric p = skipword(p, "to"); 2914549Seric if (p == NULL) 2924549Seric break; 29355012Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e); 29412612Seric if (a == NULL) 29512612Seric break; 29616886Seric a->q_flags |= QPRIMARY; 29755012Seric a = recipient(a, &e->e_sendqueue, e); 29812612Seric if (Errors != 0) 29912612Seric break; 30012612Seric 30112612Seric /* no errors during parsing, but might be a duplicate */ 30255012Seric e->e_to = p; 30312612Seric if (!bitset(QBADADDR, a->q_flags)) 30412612Seric message("250", "Recipient ok"); 30512612Seric else 3064549Seric { 30712612Seric /* punt -- should keep message in ADDRESS.... */ 30812612Seric message("550", "Addressee unknown"); 3094549Seric } 31055012Seric e->e_to = NULL; 3114549Seric break; 3124549Seric 3134549Seric case CMDDATA: /* data -- text of mail */ 31424943Seric SmtpPhase = "DATA"; 3154976Seric if (!hasmail) 3164549Seric { 3174976Seric message("503", "Need MAIL command"); 3184976Seric break; 3194549Seric } 32055012Seric else if (e->e_nrcpts <= 0) 3214549Seric { 3224976Seric message("503", "Need RCPT (recipient)"); 3234976Seric break; 3244549Seric } 3254976Seric 3264976Seric /* collect the text of the message */ 32724943Seric SmtpPhase = "collect"; 32857389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 32955012Seric collect(TRUE, e); 3304976Seric if (Errors != 0) 3314976Seric break; 3324976Seric 3338238Seric /* 3348238Seric ** Arrange to send to everyone. 3358238Seric ** If sending to multiple people, mail back 3368238Seric ** errors rather than reporting directly. 3378238Seric ** In any case, don't mail back errors for 3388238Seric ** anything that has happened up to 3398238Seric ** now (the other end will do this). 34010197Seric ** Truncate our transcript -- the mail has gotten 34110197Seric ** to us successfully, and if we have 34210197Seric ** to mail this back, it will be easier 34310197Seric ** on the reader. 3448238Seric ** Then send to everyone. 3458238Seric ** Finally give a reply code. If an error has 3468238Seric ** already been given, don't mail a 3478238Seric ** message back. 3489339Seric ** We goose error returns by clearing error bit. 3498238Seric */ 3508238Seric 35124943Seric SmtpPhase = "delivery"; 35255012Seric if (e->e_nrcpts != 1) 3539378Seric { 3549378Seric HoldErrs = TRUE; 35516886Seric ErrorMode = EM_MAIL; 3569378Seric } 35755012Seric e->e_flags &= ~EF_FATALERRS; 35855012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 3594976Seric 3604976Seric /* send to all recipients */ 36155012Seric sendall(e, SM_DEFAULT); 36255012Seric e->e_to = NULL; 3634976Seric 36423516Seric /* save statistics */ 36555012Seric markstats(e, (ADDRESS *) NULL); 36623516Seric 3678238Seric /* issue success if appropriate and reset */ 3688238Seric if (Errors == 0 || HoldErrs) 3699283Seric message("250", "Ok"); 3708238Seric else 37155012Seric e->e_flags &= ~EF_FATALERRS; 3729339Seric 3739339Seric /* if in a child, pop back to our parent */ 3749339Seric if (InChild) 3759339Seric finis(); 37624943Seric 37724943Seric /* clean up a bit */ 37858008Seric hasmail = FALSE; 37955012Seric dropenvelope(e); 38055012Seric CurEnv = e = newenvelope(e); 38155012Seric e->e_flags = BlankEnvelope.e_flags; 3824549Seric break; 3834549Seric 3844549Seric case CMDRSET: /* rset -- reset state */ 3854549Seric message("250", "Reset state"); 3869339Seric if (InChild) 3879339Seric finis(); 3889339Seric break; 3894549Seric 3904549Seric case CMDVRFY: /* vrfy -- verify address */ 391*58092Seric case CMDEXPN: /* expn -- expand address */ 392*58092Seric vrfy = c->cmdcode == CMDVRFY; 393*58092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 394*58092Seric PrivacyFlags)) 39558082Seric { 39658082Seric message("502", "That's none of your business"); 39758082Seric break; 39858082Seric } 39958082Seric else if (!gothello && 400*58092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 401*58092Seric PrivacyFlags)) 40258082Seric { 40358082Seric message("503", "I demand that you introduce yourself first"); 40458082Seric break; 40558082Seric } 406*58092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 4079339Seric break; 40825050Seric setproctitle("%s: %s", CurHostName, inp); 40955173Seric #ifdef LOG 41058020Seric if (LogLevel > 5) 41155173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 41255173Seric #endif 4135003Seric vrfyqueue = NULL; 4147762Seric QuickAbort = TRUE; 415*58092Seric if (vrfy) 416*58092Seric e->e_flags |= EF_VRFYONLY; 41758082Seric (void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e); 4187762Seric if (Errors != 0) 4199339Seric { 4209339Seric if (InChild) 4219339Seric finis(); 4227762Seric break; 4239339Seric } 4245003Seric while (vrfyqueue != NULL) 4255003Seric { 4265003Seric register ADDRESS *a = vrfyqueue->q_next; 4275003Seric char *code; 4285003Seric 4297685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 4305003Seric a = a->q_next; 4315003Seric 4327685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 4335003Seric { 4345003Seric if (a != NULL) 4355003Seric code = "250-"; 4365003Seric else 4375003Seric code = "250"; 43858010Seric if (strchr(vrfyqueue->q_paddr, '<') != NULL) 43958010Seric message(code, "%s", vrfyqueue->q_paddr); 44058010Seric else if (vrfyqueue->q_fullname == NULL) 4415003Seric message(code, "<%s>", vrfyqueue->q_paddr); 4425003Seric else 4435003Seric message(code, "%s <%s>", 4445003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 4455003Seric } 4465003Seric else if (a == NULL) 4475003Seric message("554", "Self destructive alias loop"); 4485003Seric vrfyqueue = a; 4495003Seric } 4509339Seric if (InChild) 4519339Seric finis(); 4524549Seric break; 4534549Seric 4544549Seric case CMDHELP: /* help -- give user info */ 4554577Seric help(p); 4564549Seric break; 4574549Seric 4584549Seric case CMDNOOP: /* noop -- do nothing */ 4594549Seric message("200", "OK"); 4604549Seric break; 4614549Seric 4624549Seric case CMDQUIT: /* quit -- leave mail */ 46325050Seric message("221", "%s closing connection", MyHostName); 4649339Seric if (InChild) 4659339Seric ExitStat = EX_QUIT; 4664549Seric finis(); 4674549Seric 4688544Seric case CMDVERB: /* set verbose mode */ 4698544Seric Verbose = TRUE; 47025025Seric SendMode = SM_DELIVER; 4718544Seric message("200", "Verbose mode"); 4728544Seric break; 4738544Seric 4749314Seric case CMDONEX: /* doing one transaction only */ 4759378Seric OneXact = TRUE; 4769314Seric message("200", "Only one transaction"); 4779314Seric break; 4789314Seric 47936230Skarels # ifdef SMTPDEBUG 4809339Seric case CMDDBGQSHOW: /* show queues */ 4816907Seric printf("Send Queue="); 48255012Seric printaddr(e->e_sendqueue, TRUE); 4835003Seric break; 4847275Seric 4857275Seric case CMDDBGDEBUG: /* set debug mode */ 4867676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 4877676Seric tTflag(p); 4887676Seric message("200", "Debug set"); 4897275Seric break; 4907275Seric 49136230Skarels # else /* not SMTPDEBUG */ 49224945Seric 49336230Skarels case CMDDBGQSHOW: /* show queues */ 49436230Skarels case CMDDBGDEBUG: /* set debug mode */ 49536233Skarels # ifdef LOG 49636233Skarels if (RealHostName != NULL && LogLevel > 0) 49736230Skarels syslog(LOG_NOTICE, 49858020Seric "\"%s\" command from %s (%s)", 49936230Skarels c->cmdname, RealHostName, 50036230Skarels inet_ntoa(RealHostAddr.sin_addr)); 50136233Skarels # endif 50236230Skarels /* FALL THROUGH */ 50336230Skarels # endif /* SMTPDEBUG */ 50436230Skarels 5054549Seric case CMDERROR: /* unknown command */ 5064549Seric message("500", "Command unrecognized"); 5074549Seric break; 5084549Seric 5094549Seric default: 51036230Skarels errno = 0; 5114549Seric syserr("smtp: unknown code %d", c->cmdcode); 5124549Seric break; 5134549Seric } 5144549Seric } 5154549Seric } 5164549Seric /* 5174549Seric ** SKIPWORD -- skip a fixed word. 5184549Seric ** 5194549Seric ** Parameters: 5204549Seric ** p -- place to start looking. 5214549Seric ** w -- word to skip. 5224549Seric ** 5234549Seric ** Returns: 5244549Seric ** p following w. 5254549Seric ** NULL on error. 5264549Seric ** 5274549Seric ** Side Effects: 5284549Seric ** clobbers the p data area. 5294549Seric */ 5304549Seric 5314549Seric static char * 5324549Seric skipword(p, w) 5334549Seric register char *p; 5344549Seric char *w; 5354549Seric { 5364549Seric register char *q; 5374549Seric 5384549Seric /* find beginning of word */ 53958050Seric while (isascii(*p) && isspace(*p)) 5404549Seric p++; 5414549Seric q = p; 5424549Seric 5434549Seric /* find end of word */ 54458050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 5454549Seric p++; 54658050Seric while (isascii(*p) && isspace(*p)) 5474549Seric *p++ = '\0'; 5484549Seric if (*p != ':') 5494549Seric { 5504549Seric syntax: 5514549Seric message("501", "Syntax error"); 5524549Seric Errors++; 5534549Seric return (NULL); 5544549Seric } 5554549Seric *p++ = '\0'; 55658050Seric while (isascii(*p) && isspace(*p)) 5574549Seric p++; 5584549Seric 5594549Seric /* see if the input word matches desired word */ 56033725Sbostic if (strcasecmp(q, w)) 5614549Seric goto syntax; 5624549Seric 5634549Seric return (p); 5644549Seric } 5654577Seric /* 5664577Seric ** HELP -- implement the HELP command. 5674577Seric ** 5684577Seric ** Parameters: 5694577Seric ** topic -- the topic we want help for. 5704577Seric ** 5714577Seric ** Returns: 5724577Seric ** none. 5734577Seric ** 5744577Seric ** Side Effects: 5754577Seric ** outputs the help file to message output. 5764577Seric */ 5774577Seric 5784577Seric help(topic) 5794577Seric char *topic; 5804577Seric { 5814577Seric register FILE *hf; 5824577Seric int len; 5834577Seric char buf[MAXLINE]; 5844577Seric bool noinfo; 5854577Seric 5868269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5874577Seric { 5884577Seric /* no help */ 58911931Seric errno = 0; 5904577Seric message("502", "HELP not implemented"); 5914577Seric return; 5924577Seric } 5934577Seric 59449669Seric if (topic == NULL || *topic == '\0') 59549669Seric topic = "smtp"; 59649669Seric else 59749669Seric makelower(topic); 59849669Seric 5994577Seric len = strlen(topic); 6004577Seric noinfo = TRUE; 6014577Seric 6024577Seric while (fgets(buf, sizeof buf, hf) != NULL) 6034577Seric { 6044577Seric if (strncmp(buf, topic, len) == 0) 6054577Seric { 6064577Seric register char *p; 6074577Seric 60856795Seric p = strchr(buf, '\t'); 6094577Seric if (p == NULL) 6104577Seric p = buf; 6114577Seric else 6124577Seric p++; 6134577Seric fixcrlf(p, TRUE); 6144577Seric message("214-", p); 6154577Seric noinfo = FALSE; 6164577Seric } 6174577Seric } 6184577Seric 6194577Seric if (noinfo) 6204577Seric message("504", "HELP topic unknown"); 6214577Seric else 6224577Seric message("214", "End of HELP info"); 6234628Seric (void) fclose(hf); 6244577Seric } 6258544Seric /* 6269339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 6279339Seric ** 6289339Seric ** Parameters: 6299339Seric ** label -- a string used in error messages 6309339Seric ** 6319339Seric ** Returns: 6329339Seric ** zero in the child 6339339Seric ** one in the parent 6349339Seric ** 6359339Seric ** Side Effects: 6369339Seric ** none. 6379339Seric */ 6388544Seric 63955012Seric runinchild(label, e) 6409339Seric char *label; 64155012Seric register ENVELOPE *e; 6429339Seric { 6439339Seric int childpid; 6449339Seric 64516158Seric if (!OneXact) 6469339Seric { 64716158Seric childpid = dofork(); 64816158Seric if (childpid < 0) 64916158Seric { 65016158Seric syserr("%s: cannot fork", label); 65116158Seric return (1); 65216158Seric } 65316158Seric if (childpid > 0) 65416158Seric { 65516158Seric auto int st; 6569339Seric 65716158Seric /* parent -- wait for child to complete */ 65816158Seric st = waitfor(childpid); 65916158Seric if (st == -1) 66016158Seric syserr("%s: lost child", label); 6619339Seric 66216158Seric /* if we exited on a QUIT command, complete the process */ 66316158Seric if (st == (EX_QUIT << 8)) 66416158Seric finis(); 6659339Seric 66616158Seric return (1); 66716158Seric } 66816158Seric else 66916158Seric { 67016158Seric /* child */ 67116158Seric InChild = TRUE; 67225050Seric QuickAbort = FALSE; 67355012Seric clearenvelope(e, FALSE); 67416158Seric } 6759339Seric } 67615256Seric 67716158Seric /* open alias database */ 67855012Seric initaliases(AliasFile, FALSE, e); 67916158Seric 68016158Seric return (0); 6819339Seric } 6829339Seric 68356795Seric # endif /* SMTP */ 684