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*58308Seric static char sccsid[] = "@(#)srvrsmtp.c 6.18 (Berkeley) 02/28/93 (with SMTP)"; 1433731Sbostic #else 15*58308Seric static char sccsid[] = "@(#)srvrsmtp.c 6.18 (Berkeley) 02/28/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 CurHostName = RealHostName; 12157642Seric setproctitle("srvrsmtp %s", CurHostName); 12258050Seric expand("\201e", inp, &inp[sizeof inp], e); 12358151Seric message("220 %s", inp); 12424943Seric SmtpPhase = "startup"; 12530448Seric sendinghost = NULL; 12658082Seric gothello = FALSE; 1274549Seric for (;;) 1284549Seric { 12912612Seric /* arrange for backout */ 13012612Seric if (setjmp(TopFrame) > 0 && InChild) 13112612Seric finis(); 13212612Seric QuickAbort = FALSE; 13312612Seric HoldErrs = FALSE; 13451951Seric LogUsrErrs = FALSE; 13558092Seric e->e_flags &= ~EF_VRFYONLY; 13612612Seric 1377356Seric /* setup for the read */ 13855012Seric e->e_to = NULL; 1394577Seric Errors = 0; 1407275Seric (void) fflush(stdout); 1417356Seric 1427356Seric /* read the input line */ 14358109Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand); 1447356Seric 1457685Seric /* handle errors */ 1467356Seric if (p == NULL) 1477356Seric { 1484549Seric /* end of file, just die */ 14958151Seric message("421 %s Lost input channel from %s", 15025050Seric MyHostName, CurHostName); 15155464Seric #ifdef LOG 15258020Seric if (LogLevel > 1) 15355464Seric syslog(LOG_NOTICE, "lost input channel from %s", 15455464Seric CurHostName); 15555464Seric #endif 15658069Seric if (InChild) 15758069Seric ExitStat = EX_QUIT; 1584549Seric finis(); 1594549Seric } 1604549Seric 1614549Seric /* clean up end of line */ 1624558Seric fixcrlf(inp, TRUE); 1634549Seric 1644713Seric /* echo command to transcript */ 16555012Seric if (e->e_xfp != NULL) 16655012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1674713Seric 1684549Seric /* break off command */ 16958050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 1704549Seric continue; 17157232Seric cmd = cmdbuf; 17258050Seric while (*p != '\0' && 17358050Seric !(isascii(*p) && isspace(*p)) && 17458050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 17524981Seric *cmd++ = *p++; 17624981Seric *cmd = '\0'; 1774549Seric 17825691Seric /* throw away leading whitespace */ 17958050Seric while (isascii(*p) && isspace(*p)) 18025691Seric p++; 18125691Seric 1824549Seric /* decode command */ 1834549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1844549Seric { 18533725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 1864549Seric break; 1874549Seric } 1884549Seric 18951954Seric /* reset errors */ 19051954Seric errno = 0; 19151954Seric 1924549Seric /* process command */ 1934549Seric switch (c->cmdcode) 1944549Seric { 1954976Seric case CMDHELO: /* hello -- introduce yourself */ 19624943Seric SmtpPhase = "HELO"; 19725050Seric setproctitle("%s: %s", CurHostName, inp); 19858109Seric if (strcasecmp(p, MyHostName) == 0) 19914877Seric { 20036230Skarels /* 20158109Seric ** Didn't know about alias or MX, 20258109Seric ** or connected to an echo server 20358109Seric */ 20458109Seric 20558151Seric message("553 %s config error: mail loops back to myself", 20647570Seric MyHostName); 20714877Seric break; 20814877Seric } 209*58308Seric if (strcasecmp(p, RealHostName) != 0) 21011146Seric { 21124981Seric char hostbuf[MAXNAME]; 21211146Seric 21324981Seric (void) sprintf(hostbuf, "%s (%s)", p, RealHostName); 21430448Seric sendinghost = newstr(hostbuf); 21511146Seric } 21611146Seric else 21730448Seric sendinghost = newstr(p); 21858151Seric message("250 %s Hello %s, pleased to meet you", 21936230Skarels MyHostName, sendinghost); 22058082Seric gothello = TRUE; 2214976Seric break; 2224976Seric 2234549Seric case CMDMAIL: /* mail -- designate sender */ 22424943Seric SmtpPhase = "MAIL"; 22524943Seric 22611151Seric /* force a sending host even if no HELO given */ 22758064Seric if (sendinghost == NULL && macvalue('s', e) == NULL) 22830448Seric sendinghost = RealHostName; 22911151Seric 2309314Seric /* check for validity of this command */ 23158082Seric if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 23258082Seric { 23358151Seric message("503 Polite people say HELO first"); 23458082Seric break; 23558082Seric } 23658109Seric if (gotmail) 2374558Seric { 23858151Seric message("503 Sender already specified"); 2394558Seric break; 2404558Seric } 2419339Seric if (InChild) 2429339Seric { 24336230Skarels errno = 0; 24458151Seric syserr("503 Nested MAIL command: MAIL %s", p); 24558069Seric finis(); 2469339Seric } 24758082Seric if (!enoughspace()) 24858082Seric { 24958151Seric message("452 Insufficient disk space; try again later"); 25058082Seric break; 25158082Seric } 2529339Seric 2539339Seric /* fork a subprocess to process this command */ 25455012Seric if (runinchild("SMTP-MAIL", e) > 0) 2559339Seric break; 25658064Seric if (sendinghost != NULL) 25758064Seric define('s', sendinghost, e); 25855012Seric define('r', "SMTP", e); 25955012Seric initsys(e); 26057389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2619339Seric 2629339Seric /* child -- go do the processing */ 2634549Seric p = skipword(p, "from"); 2644549Seric if (p == NULL) 2654549Seric break; 26657977Seric if (setjmp(TopFrame) > 0) 26758147Seric { 26858147Seric /* this failed -- undo work */ 26958147Seric if (InChild) 27058147Seric finis(); 27157977Seric break; 27258147Seric } 27357977Seric QuickAbort = TRUE; 27455012Seric setsender(p, e); 27558151Seric message("250 Sender ok"); 27658147Seric gotmail = TRUE; 2774549Seric break; 2784549Seric 2794976Seric case CMDRCPT: /* rcpt -- designate recipient */ 28024943Seric SmtpPhase = "RCPT"; 28157389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 28212612Seric if (setjmp(TopFrame) > 0) 28314785Seric { 28455012Seric e->e_flags &= ~EF_FATALERRS; 28512612Seric break; 28614785Seric } 28712612Seric QuickAbort = TRUE; 28851951Seric LogUsrErrs = TRUE; 28958093Seric 29058093Seric /* optimization -- if queueing, don't expand aliases */ 29158093Seric if (SendMode == SM_QUEUE) 29258093Seric e->e_flags |= EF_VRFYONLY; 29358093Seric 2944549Seric p = skipword(p, "to"); 2954549Seric if (p == NULL) 2964549Seric break; 29755012Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e); 29812612Seric if (a == NULL) 29912612Seric break; 30016886Seric a->q_flags |= QPRIMARY; 30155012Seric a = recipient(a, &e->e_sendqueue, e); 30212612Seric if (Errors != 0) 30312612Seric break; 30412612Seric 30512612Seric /* no errors during parsing, but might be a duplicate */ 30655012Seric e->e_to = p; 30712612Seric if (!bitset(QBADADDR, a->q_flags)) 30858151Seric message("250 Recipient ok"); 30912612Seric else 3104549Seric { 31112612Seric /* punt -- should keep message in ADDRESS.... */ 31258151Seric message("550 Addressee unknown"); 3134549Seric } 31455012Seric e->e_to = NULL; 3154549Seric break; 3164549Seric 3174549Seric case CMDDATA: /* data -- text of mail */ 31824943Seric SmtpPhase = "DATA"; 31958109Seric if (!gotmail) 3204549Seric { 32158151Seric message("503 Need MAIL command"); 3224976Seric break; 3234549Seric } 32455012Seric else if (e->e_nrcpts <= 0) 3254549Seric { 32658151Seric message("503 Need RCPT (recipient)"); 3274976Seric break; 3284549Seric } 3294976Seric 3304976Seric /* collect the text of the message */ 33124943Seric SmtpPhase = "collect"; 33257389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 33355012Seric collect(TRUE, e); 3344976Seric if (Errors != 0) 3354976Seric break; 3364976Seric 3378238Seric /* 3388238Seric ** Arrange to send to everyone. 3398238Seric ** If sending to multiple people, mail back 3408238Seric ** errors rather than reporting directly. 3418238Seric ** In any case, don't mail back errors for 3428238Seric ** anything that has happened up to 3438238Seric ** now (the other end will do this). 34410197Seric ** Truncate our transcript -- the mail has gotten 34510197Seric ** to us successfully, and if we have 34610197Seric ** to mail this back, it will be easier 34710197Seric ** on the reader. 3488238Seric ** Then send to everyone. 3498238Seric ** Finally give a reply code. If an error has 3508238Seric ** already been given, don't mail a 3518238Seric ** message back. 3529339Seric ** We goose error returns by clearing error bit. 3538238Seric */ 3548238Seric 35524943Seric SmtpPhase = "delivery"; 35655012Seric if (e->e_nrcpts != 1) 3579378Seric { 3589378Seric HoldErrs = TRUE; 35916886Seric ErrorMode = EM_MAIL; 3609378Seric } 36155012Seric e->e_flags &= ~EF_FATALERRS; 36255012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 3634976Seric 3644976Seric /* send to all recipients */ 36555012Seric sendall(e, SM_DEFAULT); 36655012Seric e->e_to = NULL; 3674976Seric 36823516Seric /* save statistics */ 36955012Seric markstats(e, (ADDRESS *) NULL); 37023516Seric 3718238Seric /* issue success if appropriate and reset */ 3728238Seric if (Errors == 0 || HoldErrs) 37358151Seric message("250 Ok"); 3748238Seric else 37555012Seric e->e_flags &= ~EF_FATALERRS; 3769339Seric 3779339Seric /* if in a child, pop back to our parent */ 3789339Seric if (InChild) 3799339Seric finis(); 38024943Seric 38124943Seric /* clean up a bit */ 38258109Seric gotmail = FALSE; 38355012Seric dropenvelope(e); 38458179Seric CurEnv = e = newenvelope(e, CurEnv); 38555012Seric e->e_flags = BlankEnvelope.e_flags; 3864549Seric break; 3874549Seric 3884549Seric case CMDRSET: /* rset -- reset state */ 38958151Seric message("250 Reset state"); 3909339Seric if (InChild) 3919339Seric finis(); 39258109Seric 39358109Seric /* clean up a bit */ 39458109Seric gotmail = FALSE; 39558109Seric dropenvelope(e); 39658179Seric CurEnv = e = newenvelope(e, CurEnv); 3979339Seric break; 3984549Seric 3994549Seric case CMDVRFY: /* vrfy -- verify address */ 40058092Seric case CMDEXPN: /* expn -- expand address */ 40158092Seric vrfy = c->cmdcode == CMDVRFY; 40258092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 40358092Seric PrivacyFlags)) 40458082Seric { 40558151Seric message("502 That's none of your business"); 40658082Seric break; 40758082Seric } 40858082Seric else if (!gothello && 40958092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 41058092Seric PrivacyFlags)) 41158082Seric { 41258151Seric message("503 I demand that you introduce yourself first"); 41358082Seric break; 41458082Seric } 41558092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 4169339Seric break; 41725050Seric setproctitle("%s: %s", CurHostName, inp); 41855173Seric #ifdef LOG 41958020Seric if (LogLevel > 5) 42055173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 42155173Seric #endif 4225003Seric vrfyqueue = NULL; 4237762Seric QuickAbort = TRUE; 42458092Seric if (vrfy) 42558092Seric e->e_flags |= EF_VRFYONLY; 42658082Seric (void) sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e); 4277762Seric if (Errors != 0) 4289339Seric { 4299339Seric if (InChild) 4309339Seric finis(); 4317762Seric break; 4329339Seric } 4335003Seric while (vrfyqueue != NULL) 4345003Seric { 4355003Seric register ADDRESS *a = vrfyqueue->q_next; 4365003Seric char *code; 4375003Seric 4387685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 4395003Seric a = a->q_next; 4405003Seric 4417685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 44258151Seric printvrfyaddr(vrfyqueue, a == NULL); 4435003Seric else if (a == NULL) 44458151Seric message("554 Self destructive alias loop"); 4455003Seric vrfyqueue = a; 4465003Seric } 4479339Seric if (InChild) 4489339Seric finis(); 4494549Seric break; 4504549Seric 4514549Seric case CMDHELP: /* help -- give user info */ 4524577Seric help(p); 4534549Seric break; 4544549Seric 4554549Seric case CMDNOOP: /* noop -- do nothing */ 45658151Seric message("200 OK"); 4574549Seric break; 4584549Seric 4594549Seric case CMDQUIT: /* quit -- leave mail */ 46058151Seric message("221 %s closing connection", MyHostName); 4619339Seric if (InChild) 4629339Seric ExitStat = EX_QUIT; 4634549Seric finis(); 4644549Seric 4658544Seric case CMDVERB: /* set verbose mode */ 4668544Seric Verbose = TRUE; 46725025Seric SendMode = SM_DELIVER; 46858151Seric message("200 Verbose mode"); 4698544Seric break; 4708544Seric 4719314Seric case CMDONEX: /* doing one transaction only */ 4729378Seric OneXact = TRUE; 47358151Seric message("200 Only one transaction"); 4749314Seric break; 4759314Seric 47636230Skarels # ifdef SMTPDEBUG 4779339Seric case CMDDBGQSHOW: /* show queues */ 4786907Seric printf("Send Queue="); 47955012Seric printaddr(e->e_sendqueue, TRUE); 4805003Seric break; 4817275Seric 4827275Seric case CMDDBGDEBUG: /* set debug mode */ 4837676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 4847676Seric tTflag(p); 48558151Seric message("200 Debug set"); 4867275Seric break; 4877275Seric 48836230Skarels # else /* not SMTPDEBUG */ 48924945Seric 49036230Skarels case CMDDBGQSHOW: /* show queues */ 49136230Skarels case CMDDBGDEBUG: /* set debug mode */ 49236233Skarels # ifdef LOG 493*58308Seric if (LogLevel > 0) 49436230Skarels syslog(LOG_NOTICE, 49558020Seric "\"%s\" command from %s (%s)", 49636230Skarels c->cmdname, RealHostName, 49736230Skarels inet_ntoa(RealHostAddr.sin_addr)); 49836233Skarels # endif 49936230Skarels /* FALL THROUGH */ 50036230Skarels # endif /* SMTPDEBUG */ 50136230Skarels 5024549Seric case CMDERROR: /* unknown command */ 50358151Seric message("500 Command unrecognized"); 5044549Seric break; 5054549Seric 5064549Seric default: 50736230Skarels errno = 0; 50858151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 5094549Seric break; 5104549Seric } 5114549Seric } 5124549Seric } 5134549Seric /* 5144549Seric ** SKIPWORD -- skip a fixed word. 5154549Seric ** 5164549Seric ** Parameters: 5174549Seric ** p -- place to start looking. 5184549Seric ** w -- word to skip. 5194549Seric ** 5204549Seric ** Returns: 5214549Seric ** p following w. 5224549Seric ** NULL on error. 5234549Seric ** 5244549Seric ** Side Effects: 5254549Seric ** clobbers the p data area. 5264549Seric */ 5274549Seric 5284549Seric static char * 5294549Seric skipword(p, w) 5304549Seric register char *p; 5314549Seric char *w; 5324549Seric { 5334549Seric register char *q; 5344549Seric 5354549Seric /* find beginning of word */ 53658050Seric while (isascii(*p) && isspace(*p)) 5374549Seric p++; 5384549Seric q = p; 5394549Seric 5404549Seric /* find end of word */ 54158050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 5424549Seric p++; 54358050Seric while (isascii(*p) && isspace(*p)) 5444549Seric *p++ = '\0'; 5454549Seric if (*p != ':') 5464549Seric { 5474549Seric syntax: 54858151Seric message("501 Syntax error"); 5494549Seric Errors++; 5504549Seric return (NULL); 5514549Seric } 5524549Seric *p++ = '\0'; 55358050Seric while (isascii(*p) && isspace(*p)) 5544549Seric p++; 5554549Seric 5564549Seric /* see if the input word matches desired word */ 55733725Sbostic if (strcasecmp(q, w)) 5584549Seric goto syntax; 5594549Seric 5604549Seric return (p); 5614549Seric } 5624577Seric /* 56358151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 56458151Seric ** 56558151Seric ** Parameters: 56658151Seric ** a -- the address to print 56758151Seric ** last -- set if this is the last one. 56858151Seric ** 56958151Seric ** Returns: 57058151Seric ** none. 57158151Seric ** 57258151Seric ** Side Effects: 57358151Seric ** Prints the appropriate 250 codes. 57458151Seric */ 57558151Seric 57658151Seric printvrfyaddr(a, last) 57758151Seric register ADDRESS *a; 57858151Seric bool last; 57958151Seric { 58058151Seric char fmtbuf[20]; 58158151Seric 58258151Seric strcpy(fmtbuf, "250"); 58358151Seric fmtbuf[3] = last ? ' ' : '-'; 58458151Seric 58558151Seric if (strchr(a->q_paddr, '<') != NULL) 58658151Seric strcpy(&fmtbuf[4], "%s"); 58758151Seric else if (a->q_fullname == NULL) 58858151Seric strcpy(&fmtbuf[4], "<%s>"); 58958151Seric else 59058151Seric { 59158151Seric strcpy(&fmtbuf[4], "%s <%s>"); 59258151Seric message(fmtbuf, a->q_fullname, a->q_paddr); 59358151Seric return; 59458151Seric } 59558151Seric message(fmtbuf, a->q_paddr); 59658151Seric } 59758151Seric /* 5984577Seric ** HELP -- implement the HELP command. 5994577Seric ** 6004577Seric ** Parameters: 6014577Seric ** topic -- the topic we want help for. 6024577Seric ** 6034577Seric ** Returns: 6044577Seric ** none. 6054577Seric ** 6064577Seric ** Side Effects: 6074577Seric ** outputs the help file to message output. 6084577Seric */ 6094577Seric 6104577Seric help(topic) 6114577Seric char *topic; 6124577Seric { 6134577Seric register FILE *hf; 6144577Seric int len; 6154577Seric char buf[MAXLINE]; 6164577Seric bool noinfo; 6174577Seric 6188269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 6194577Seric { 6204577Seric /* no help */ 62111931Seric errno = 0; 62258151Seric message("502 HELP not implemented"); 6234577Seric return; 6244577Seric } 6254577Seric 62649669Seric if (topic == NULL || *topic == '\0') 62749669Seric topic = "smtp"; 62849669Seric else 62949669Seric makelower(topic); 63049669Seric 6314577Seric len = strlen(topic); 6324577Seric noinfo = TRUE; 6334577Seric 6344577Seric while (fgets(buf, sizeof buf, hf) != NULL) 6354577Seric { 6364577Seric if (strncmp(buf, topic, len) == 0) 6374577Seric { 6384577Seric register char *p; 6394577Seric 64056795Seric p = strchr(buf, '\t'); 6414577Seric if (p == NULL) 6424577Seric p = buf; 6434577Seric else 6444577Seric p++; 6454577Seric fixcrlf(p, TRUE); 64658151Seric message("214-%s", p); 6474577Seric noinfo = FALSE; 6484577Seric } 6494577Seric } 6504577Seric 6514577Seric if (noinfo) 65258151Seric message("504 HELP topic unknown"); 6534577Seric else 65458151Seric message("214 End of HELP info"); 6554628Seric (void) fclose(hf); 6564577Seric } 6578544Seric /* 6589339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 6599339Seric ** 6609339Seric ** Parameters: 6619339Seric ** label -- a string used in error messages 6629339Seric ** 6639339Seric ** Returns: 6649339Seric ** zero in the child 6659339Seric ** one in the parent 6669339Seric ** 6679339Seric ** Side Effects: 6689339Seric ** none. 6699339Seric */ 6708544Seric 67155012Seric runinchild(label, e) 6729339Seric char *label; 67355012Seric register ENVELOPE *e; 6749339Seric { 6759339Seric int childpid; 6769339Seric 67716158Seric if (!OneXact) 6789339Seric { 67916158Seric childpid = dofork(); 68016158Seric if (childpid < 0) 68116158Seric { 68216158Seric syserr("%s: cannot fork", label); 68316158Seric return (1); 68416158Seric } 68516158Seric if (childpid > 0) 68616158Seric { 68716158Seric auto int st; 6889339Seric 68916158Seric /* parent -- wait for child to complete */ 69016158Seric st = waitfor(childpid); 69116158Seric if (st == -1) 69216158Seric syserr("%s: lost child", label); 6939339Seric 69416158Seric /* if we exited on a QUIT command, complete the process */ 69516158Seric if (st == (EX_QUIT << 8)) 69616158Seric finis(); 6979339Seric 69816158Seric return (1); 69916158Seric } 70016158Seric else 70116158Seric { 70216158Seric /* child */ 70316158Seric InChild = TRUE; 70425050Seric QuickAbort = FALSE; 70555012Seric clearenvelope(e, FALSE); 70616158Seric } 7079339Seric } 70815256Seric 70916158Seric /* open alias database */ 71055012Seric initaliases(AliasFile, FALSE, e); 71116158Seric 71216158Seric return (0); 7139339Seric } 7149339Seric 71556795Seric # endif /* SMTP */ 716