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*58147Seric static char sccsid[] = "@(#)srvrsmtp.c 6.15 (Berkeley) 02/22/93 (with SMTP)"; 1433731Sbostic #else 15*58147Seric static char sccsid[] = "@(#)srvrsmtp.c 6.15 (Berkeley) 02/22/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 */ 5658092Seric /* non-standard commands */ 5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 5936230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6058092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6158092Seric # 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, 7058092Seric "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; 10158109Seric bool gotmail; /* mail command received */ 10258092Seric bool gothello; /* helo command received */ 10358092Seric 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 11258109Seric gotmail = 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; 13758092Seric 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 */ 14558109Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand); 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); 20058109Seric if (strcasecmp(p, MyHostName) == 0) 20114877Seric { 20236230Skarels /* 20358109Seric ** Didn't know about alias or MX, 20458109Seric ** or connected to an echo server 20558109Seric */ 20658109Seric 20747570Seric message("553", "%s config error: mail loops back to myself", 20847570Seric MyHostName); 20914877Seric break; 21014877Seric } 21158109Seric if (RealHostName != NULL && strcasecmp(p, RealHostName) != 0) 21211146Seric { 21324981Seric char hostbuf[MAXNAME]; 21411146Seric 21524981Seric (void) sprintf(hostbuf, "%s (%s)", p, RealHostName); 21630448Seric sendinghost = newstr(hostbuf); 21711146Seric } 21811146Seric else 21930448Seric sendinghost = newstr(p); 2204997Seric message("250", "%s Hello %s, pleased to meet you", 22136230Skarels MyHostName, sendinghost); 22258082Seric gothello = TRUE; 2234976Seric break; 2244976Seric 2254549Seric case CMDMAIL: /* mail -- designate sender */ 22624943Seric SmtpPhase = "MAIL"; 22724943Seric 22811151Seric /* force a sending host even if no HELO given */ 22958064Seric if (sendinghost == NULL && macvalue('s', e) == NULL) 23030448Seric sendinghost = RealHostName; 23111151Seric 2329314Seric /* check for validity of this command */ 23358082Seric if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 23458082Seric { 23558082Seric message("503", "Polite people say HELO first"); 23658082Seric break; 23758082Seric } 23858109Seric if (gotmail) 2394558Seric { 2404558Seric message("503", "Sender already specified"); 2414558Seric break; 2424558Seric } 2439339Seric if (InChild) 2449339Seric { 24536230Skarels errno = 0; 24658008Seric syserr("Nested MAIL command: MAIL %s", p); 24758069Seric finis(); 2489339Seric } 24958082Seric if (!enoughspace()) 25058082Seric { 25158082Seric message("452", "Insufficient disk space; try again later"); 25258082Seric break; 25358082Seric } 2549339Seric 2559339Seric /* fork a subprocess to process this command */ 25655012Seric if (runinchild("SMTP-MAIL", e) > 0) 2579339Seric break; 25858064Seric if (sendinghost != NULL) 25958064Seric define('s', sendinghost, e); 26055012Seric define('r', "SMTP", e); 26155012Seric initsys(e); 26257389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2639339Seric 2649339Seric /* child -- go do the processing */ 2654549Seric p = skipword(p, "from"); 2664549Seric if (p == NULL) 2674549Seric break; 26857977Seric if (setjmp(TopFrame) > 0) 269*58147Seric { 270*58147Seric /* this failed -- undo work */ 271*58147Seric if (InChild) 272*58147Seric finis(); 27357977Seric break; 274*58147Seric } 27557977Seric QuickAbort = TRUE; 27655012Seric setsender(p, e); 277*58147Seric message("250", "Sender ok"); 278*58147Seric gotmail = TRUE; 2794549Seric break; 2804549Seric 2814976Seric case CMDRCPT: /* rcpt -- designate recipient */ 28224943Seric SmtpPhase = "RCPT"; 28357389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 28412612Seric if (setjmp(TopFrame) > 0) 28514785Seric { 28655012Seric e->e_flags &= ~EF_FATALERRS; 28712612Seric break; 28814785Seric } 28912612Seric QuickAbort = TRUE; 29051951Seric LogUsrErrs = TRUE; 29158093Seric 29258093Seric /* optimization -- if queueing, don't expand aliases */ 29358093Seric if (SendMode == SM_QUEUE) 29458093Seric e->e_flags |= EF_VRFYONLY; 29558093Seric 2964549Seric p = skipword(p, "to"); 2974549Seric if (p == NULL) 2984549Seric break; 29955012Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e); 30012612Seric if (a == NULL) 30112612Seric break; 30216886Seric a->q_flags |= QPRIMARY; 30355012Seric a = recipient(a, &e->e_sendqueue, e); 30412612Seric if (Errors != 0) 30512612Seric break; 30612612Seric 30712612Seric /* no errors during parsing, but might be a duplicate */ 30855012Seric e->e_to = p; 30912612Seric if (!bitset(QBADADDR, a->q_flags)) 31012612Seric message("250", "Recipient ok"); 31112612Seric else 3124549Seric { 31312612Seric /* punt -- should keep message in ADDRESS.... */ 31412612Seric message("550", "Addressee unknown"); 3154549Seric } 31655012Seric e->e_to = NULL; 3174549Seric break; 3184549Seric 3194549Seric case CMDDATA: /* data -- text of mail */ 32024943Seric SmtpPhase = "DATA"; 32158109Seric if (!gotmail) 3224549Seric { 3234976Seric message("503", "Need MAIL command"); 3244976Seric break; 3254549Seric } 32655012Seric else if (e->e_nrcpts <= 0) 3274549Seric { 3284976Seric message("503", "Need RCPT (recipient)"); 3294976Seric break; 3304549Seric } 3314976Seric 3324976Seric /* collect the text of the message */ 33324943Seric SmtpPhase = "collect"; 33457389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 33555012Seric collect(TRUE, e); 3364976Seric if (Errors != 0) 3374976Seric break; 3384976Seric 3398238Seric /* 3408238Seric ** Arrange to send to everyone. 3418238Seric ** If sending to multiple people, mail back 3428238Seric ** errors rather than reporting directly. 3438238Seric ** In any case, don't mail back errors for 3448238Seric ** anything that has happened up to 3458238Seric ** now (the other end will do this). 34610197Seric ** Truncate our transcript -- the mail has gotten 34710197Seric ** to us successfully, and if we have 34810197Seric ** to mail this back, it will be easier 34910197Seric ** on the reader. 3508238Seric ** Then send to everyone. 3518238Seric ** Finally give a reply code. If an error has 3528238Seric ** already been given, don't mail a 3538238Seric ** message back. 3549339Seric ** We goose error returns by clearing error bit. 3558238Seric */ 3568238Seric 35724943Seric SmtpPhase = "delivery"; 35855012Seric if (e->e_nrcpts != 1) 3599378Seric { 3609378Seric HoldErrs = TRUE; 36116886Seric ErrorMode = EM_MAIL; 3629378Seric } 36355012Seric e->e_flags &= ~EF_FATALERRS; 36455012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 3654976Seric 3664976Seric /* send to all recipients */ 36755012Seric sendall(e, SM_DEFAULT); 36855012Seric e->e_to = NULL; 3694976Seric 37023516Seric /* save statistics */ 37155012Seric markstats(e, (ADDRESS *) NULL); 37223516Seric 3738238Seric /* issue success if appropriate and reset */ 3748238Seric if (Errors == 0 || HoldErrs) 3759283Seric message("250", "Ok"); 3768238Seric else 37755012Seric e->e_flags &= ~EF_FATALERRS; 3789339Seric 3799339Seric /* if in a child, pop back to our parent */ 3809339Seric if (InChild) 3819339Seric finis(); 38224943Seric 38324943Seric /* clean up a bit */ 38458109Seric gotmail = FALSE; 38555012Seric dropenvelope(e); 38655012Seric CurEnv = e = newenvelope(e); 38755012Seric e->e_flags = BlankEnvelope.e_flags; 3884549Seric break; 3894549Seric 3904549Seric case CMDRSET: /* rset -- reset state */ 3914549Seric message("250", "Reset state"); 3929339Seric if (InChild) 3939339Seric finis(); 39458109Seric 39558109Seric /* clean up a bit */ 39658109Seric gotmail = FALSE; 39758109Seric dropenvelope(e); 39858109Seric CurEnv = e = newenvelope(e); 3999339Seric break; 4004549Seric 4014549Seric case CMDVRFY: /* vrfy -- verify address */ 40258092Seric case CMDEXPN: /* expn -- expand address */ 40358092Seric vrfy = c->cmdcode == CMDVRFY; 40458092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 40558092Seric PrivacyFlags)) 40658082Seric { 40758082Seric message("502", "That's none of your business"); 40858082Seric break; 40958082Seric } 41058082Seric else if (!gothello && 41158092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 41258092Seric PrivacyFlags)) 41358082Seric { 41458082Seric message("503", "I demand that you introduce yourself first"); 41558082Seric break; 41658082Seric } 41758092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 4189339Seric break; 41925050Seric setproctitle("%s: %s", CurHostName, inp); 42055173Seric #ifdef LOG 42158020Seric if (LogLevel > 5) 42255173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 42355173Seric #endif 4245003Seric vrfyqueue = NULL; 4257762Seric QuickAbort = TRUE; 42658092Seric if (vrfy) 42758092Seric e->e_flags |= EF_VRFYONLY; 42858082Seric (void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e); 4297762Seric if (Errors != 0) 4309339Seric { 4319339Seric if (InChild) 4329339Seric finis(); 4337762Seric break; 4349339Seric } 4355003Seric while (vrfyqueue != NULL) 4365003Seric { 4375003Seric register ADDRESS *a = vrfyqueue->q_next; 4385003Seric char *code; 4395003Seric 4407685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 4415003Seric a = a->q_next; 4425003Seric 4437685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 4445003Seric { 4455003Seric if (a != NULL) 4465003Seric code = "250-"; 4475003Seric else 4485003Seric code = "250"; 44958010Seric if (strchr(vrfyqueue->q_paddr, '<') != NULL) 45058010Seric message(code, "%s", vrfyqueue->q_paddr); 45158010Seric else if (vrfyqueue->q_fullname == NULL) 4525003Seric message(code, "<%s>", vrfyqueue->q_paddr); 4535003Seric else 4545003Seric message(code, "%s <%s>", 4555003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 4565003Seric } 4575003Seric else if (a == NULL) 4585003Seric message("554", "Self destructive alias loop"); 4595003Seric vrfyqueue = a; 4605003Seric } 4619339Seric if (InChild) 4629339Seric finis(); 4634549Seric break; 4644549Seric 4654549Seric case CMDHELP: /* help -- give user info */ 4664577Seric help(p); 4674549Seric break; 4684549Seric 4694549Seric case CMDNOOP: /* noop -- do nothing */ 4704549Seric message("200", "OK"); 4714549Seric break; 4724549Seric 4734549Seric case CMDQUIT: /* quit -- leave mail */ 47425050Seric message("221", "%s closing connection", MyHostName); 4759339Seric if (InChild) 4769339Seric ExitStat = EX_QUIT; 4774549Seric finis(); 4784549Seric 4798544Seric case CMDVERB: /* set verbose mode */ 4808544Seric Verbose = TRUE; 48125025Seric SendMode = SM_DELIVER; 4828544Seric message("200", "Verbose mode"); 4838544Seric break; 4848544Seric 4859314Seric case CMDONEX: /* doing one transaction only */ 4869378Seric OneXact = TRUE; 4879314Seric message("200", "Only one transaction"); 4889314Seric break; 4899314Seric 49036230Skarels # ifdef SMTPDEBUG 4919339Seric case CMDDBGQSHOW: /* show queues */ 4926907Seric printf("Send Queue="); 49355012Seric printaddr(e->e_sendqueue, TRUE); 4945003Seric break; 4957275Seric 4967275Seric case CMDDBGDEBUG: /* set debug mode */ 4977676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 4987676Seric tTflag(p); 4997676Seric message("200", "Debug set"); 5007275Seric break; 5017275Seric 50236230Skarels # else /* not SMTPDEBUG */ 50324945Seric 50436230Skarels case CMDDBGQSHOW: /* show queues */ 50536230Skarels case CMDDBGDEBUG: /* set debug mode */ 50636233Skarels # ifdef LOG 50736233Skarels if (RealHostName != NULL && LogLevel > 0) 50836230Skarels syslog(LOG_NOTICE, 50958020Seric "\"%s\" command from %s (%s)", 51036230Skarels c->cmdname, RealHostName, 51136230Skarels inet_ntoa(RealHostAddr.sin_addr)); 51236233Skarels # endif 51336230Skarels /* FALL THROUGH */ 51436230Skarels # endif /* SMTPDEBUG */ 51536230Skarels 5164549Seric case CMDERROR: /* unknown command */ 5174549Seric message("500", "Command unrecognized"); 5184549Seric break; 5194549Seric 5204549Seric default: 52136230Skarels errno = 0; 5224549Seric syserr("smtp: unknown code %d", c->cmdcode); 5234549Seric break; 5244549Seric } 5254549Seric } 5264549Seric } 5274549Seric /* 5284549Seric ** SKIPWORD -- skip a fixed word. 5294549Seric ** 5304549Seric ** Parameters: 5314549Seric ** p -- place to start looking. 5324549Seric ** w -- word to skip. 5334549Seric ** 5344549Seric ** Returns: 5354549Seric ** p following w. 5364549Seric ** NULL on error. 5374549Seric ** 5384549Seric ** Side Effects: 5394549Seric ** clobbers the p data area. 5404549Seric */ 5414549Seric 5424549Seric static char * 5434549Seric skipword(p, w) 5444549Seric register char *p; 5454549Seric char *w; 5464549Seric { 5474549Seric register char *q; 5484549Seric 5494549Seric /* find beginning of word */ 55058050Seric while (isascii(*p) && isspace(*p)) 5514549Seric p++; 5524549Seric q = p; 5534549Seric 5544549Seric /* find end of word */ 55558050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 5564549Seric p++; 55758050Seric while (isascii(*p) && isspace(*p)) 5584549Seric *p++ = '\0'; 5594549Seric if (*p != ':') 5604549Seric { 5614549Seric syntax: 5624549Seric message("501", "Syntax error"); 5634549Seric Errors++; 5644549Seric return (NULL); 5654549Seric } 5664549Seric *p++ = '\0'; 56758050Seric while (isascii(*p) && isspace(*p)) 5684549Seric p++; 5694549Seric 5704549Seric /* see if the input word matches desired word */ 57133725Sbostic if (strcasecmp(q, w)) 5724549Seric goto syntax; 5734549Seric 5744549Seric return (p); 5754549Seric } 5764577Seric /* 5774577Seric ** HELP -- implement the HELP command. 5784577Seric ** 5794577Seric ** Parameters: 5804577Seric ** topic -- the topic we want help for. 5814577Seric ** 5824577Seric ** Returns: 5834577Seric ** none. 5844577Seric ** 5854577Seric ** Side Effects: 5864577Seric ** outputs the help file to message output. 5874577Seric */ 5884577Seric 5894577Seric help(topic) 5904577Seric char *topic; 5914577Seric { 5924577Seric register FILE *hf; 5934577Seric int len; 5944577Seric char buf[MAXLINE]; 5954577Seric bool noinfo; 5964577Seric 5978269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5984577Seric { 5994577Seric /* no help */ 60011931Seric errno = 0; 6014577Seric message("502", "HELP not implemented"); 6024577Seric return; 6034577Seric } 6044577Seric 60549669Seric if (topic == NULL || *topic == '\0') 60649669Seric topic = "smtp"; 60749669Seric else 60849669Seric makelower(topic); 60949669Seric 6104577Seric len = strlen(topic); 6114577Seric noinfo = TRUE; 6124577Seric 6134577Seric while (fgets(buf, sizeof buf, hf) != NULL) 6144577Seric { 6154577Seric if (strncmp(buf, topic, len) == 0) 6164577Seric { 6174577Seric register char *p; 6184577Seric 61956795Seric p = strchr(buf, '\t'); 6204577Seric if (p == NULL) 6214577Seric p = buf; 6224577Seric else 6234577Seric p++; 6244577Seric fixcrlf(p, TRUE); 6254577Seric message("214-", p); 6264577Seric noinfo = FALSE; 6274577Seric } 6284577Seric } 6294577Seric 6304577Seric if (noinfo) 6314577Seric message("504", "HELP topic unknown"); 6324577Seric else 6334577Seric message("214", "End of HELP info"); 6344628Seric (void) fclose(hf); 6354577Seric } 6368544Seric /* 6379339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 6389339Seric ** 6399339Seric ** Parameters: 6409339Seric ** label -- a string used in error messages 6419339Seric ** 6429339Seric ** Returns: 6439339Seric ** zero in the child 6449339Seric ** one in the parent 6459339Seric ** 6469339Seric ** Side Effects: 6479339Seric ** none. 6489339Seric */ 6498544Seric 65055012Seric runinchild(label, e) 6519339Seric char *label; 65255012Seric register ENVELOPE *e; 6539339Seric { 6549339Seric int childpid; 6559339Seric 65616158Seric if (!OneXact) 6579339Seric { 65816158Seric childpid = dofork(); 65916158Seric if (childpid < 0) 66016158Seric { 66116158Seric syserr("%s: cannot fork", label); 66216158Seric return (1); 66316158Seric } 66416158Seric if (childpid > 0) 66516158Seric { 66616158Seric auto int st; 6679339Seric 66816158Seric /* parent -- wait for child to complete */ 66916158Seric st = waitfor(childpid); 67016158Seric if (st == -1) 67116158Seric syserr("%s: lost child", label); 6729339Seric 67316158Seric /* if we exited on a QUIT command, complete the process */ 67416158Seric if (st == (EX_QUIT << 8)) 67516158Seric finis(); 6769339Seric 67716158Seric return (1); 67816158Seric } 67916158Seric else 68016158Seric { 68116158Seric /* child */ 68216158Seric InChild = TRUE; 68325050Seric QuickAbort = FALSE; 68455012Seric clearenvelope(e, FALSE); 68516158Seric } 6869339Seric } 68715256Seric 68816158Seric /* open alias database */ 68955012Seric initaliases(AliasFile, FALSE, e); 69016158Seric 69116158Seric return (0); 6929339Seric } 6939339Seric 69456795Seric # endif /* SMTP */ 695