122712Sdist /* 268839Seric * Copyright (c) 1983, 1995 Eric P. Allman 362532Sbostic * Copyright (c) 1988, 1993 462532Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822712Sdist 933731Sbostic # include "sendmail.h" 1022712Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*69801Seric static char sccsid[] = "@(#)srvrsmtp.c 8.80 (Berkeley) 06/05/95 (with SMTP)"; 1433731Sbostic #else 15*69801Seric static char sccsid[] = "@(#)srvrsmtp.c 8.80 (Berkeley) 06/05/95 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 199339Seric # include <errno.h> 204549Seric 2133731Sbostic # ifdef SMTP 224556Seric 234549Seric /* 244549Seric ** SMTP -- run the SMTP protocol. 254549Seric ** 264549Seric ** Parameters: 274549Seric ** none. 284549Seric ** 294549Seric ** Returns: 304549Seric ** never. 314549Seric ** 324549Seric ** Side Effects: 334549Seric ** Reads commands from the input channel and processes 344549Seric ** them. 354549Seric */ 364549Seric 374549Seric struct cmd 384549Seric { 394549Seric char *cmdname; /* command name */ 404549Seric int cmdcode; /* internal code, see below */ 414549Seric }; 424549Seric 434549Seric /* values for cmdcode */ 444549Seric # define CMDERROR 0 /* bad command */ 454549Seric # define CMDMAIL 1 /* mail -- designate sender */ 464976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 474549Seric # define CMDDATA 3 /* data -- send message text */ 489339Seric # define CMDRSET 4 /* rset -- reset state */ 499339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 5058092Seric # define CMDEXPN 6 /* expn -- expand address */ 519339Seric # define CMDNOOP 7 /* noop -- do nothing */ 529339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 539339Seric # define CMDHELO 9 /* helo -- be polite */ 5458092Seric # define CMDHELP 10 /* help -- give usage info */ 5558323Seric # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 5658092Seric /* non-standard commands */ 5758092Seric # define CMDONEX 16 /* onex -- sending one transaction only */ 5858092Seric # define CMDVERB 17 /* verb -- go into verbose mode */ 5964685Seric /* use this to catch and log "door handle" attempts on your system */ 6064685Seric # define CMDLOGBOGUS 23 /* bogus command that should be logged */ 6136230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 6258092Seric # define CMDDBGQSHOW 24 /* showq -- show send queue */ 6358092Seric # define CMDDBGDEBUG 25 /* debug -- set debug mode */ 644549Seric 654549Seric static struct cmd CmdTab[] = 664549Seric { 674549Seric "mail", CMDMAIL, 684976Seric "rcpt", CMDRCPT, 694549Seric "data", CMDDATA, 704549Seric "rset", CMDRSET, 714549Seric "vrfy", CMDVRFY, 7258092Seric "expn", CMDEXPN, 734549Seric "help", CMDHELP, 744549Seric "noop", CMDNOOP, 754549Seric "quit", CMDQUIT, 764976Seric "helo", CMDHELO, 7758323Seric "ehlo", CMDEHLO, 788544Seric "verb", CMDVERB, 799314Seric "onex", CMDONEX, 8036230Skarels /* 8136230Skarels * remaining commands are here only 8236230Skarels * to trap and log attempts to use them 8336230Skarels */ 849339Seric "showq", CMDDBGQSHOW, 858544Seric "debug", CMDDBGDEBUG, 8664685Seric "wiz", CMDLOGBOGUS, 874549Seric NULL, CMDERROR, 884549Seric }; 894549Seric 909378Seric bool OneXact = FALSE; /* one xaction only this run */ 9165017Seric char *CurSmtpClient; /* who's at the other end of channel */ 9211146Seric 9363937Seric static char *skipword(); 9463937Seric 9566325Seric 9666283Seric #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 9766283Seric 9869748Seric void 9955012Seric smtp(e) 10055012Seric register ENVELOPE *e; 1014549Seric { 1024549Seric register char *p; 1038544Seric register struct cmd *c; 1044549Seric char *cmd; 1055003Seric auto ADDRESS *vrfyqueue; 10612612Seric ADDRESS *a; 10758109Seric bool gotmail; /* mail command received */ 10858092Seric bool gothello; /* helo command received */ 10958092Seric bool vrfy; /* set if this is a vrfy command */ 11058323Seric char *protocol; /* sending protocol */ 11159016Seric char *sendinghost; /* sending hostname */ 11266005Seric char *peerhostname; /* name of SMTP peer or "localhost" */ 11358333Seric auto char *delimptr; 11458714Seric char *id; 11568433Seric int nrcpts = 0; /* number of RCPT commands */ 11663787Seric bool doublequeue; 11766283Seric int badcommands = 0; /* count of bad commands */ 1188544Seric char inp[MAXLINE]; 11957232Seric char cmdbuf[MAXLINE]; 12024943Seric extern ENVELOPE BlankEnvelope; 12169748Seric extern void help __P((char *)); 1224549Seric 12359066Seric if (fileno(OutChannel) != fileno(stdout)) 1247363Seric { 1257363Seric /* arrange for debugging output to go to remote host */ 12659066Seric (void) dup2(fileno(OutChannel), fileno(stdout)); 1277363Seric } 12855012Seric settime(e); 12966005Seric peerhostname = RealHostName; 13066005Seric if (peerhostname == NULL) 13166005Seric peerhostname = "localhost"; 13266005Seric CurHostName = peerhostname; 13365017Seric CurSmtpClient = macvalue('_', e); 13465017Seric if (CurSmtpClient == NULL) 13566003Seric CurSmtpClient = CurHostName; 13665017Seric 13765017Seric setproctitle("server %s startup", CurSmtpClient); 13869792Seric #ifdef LOG 13969792Seric if (LogLevel > 11) 14069792Seric { 14169792Seric /* log connection information */ 14269792Seric syslog(LOG_INFO, "SMTP connect from %s (%s)", 14369792Seric CurSmtpClient, anynet_ntoa(&RealHostAddr)); 14469792Seric } 14569792Seric #endif 14668769Seric 14768769Seric /* output the first line, inserting "ESMTP" as second word */ 14869792Seric expand("\201e", inp, sizeof inp, e); 14968769Seric p = strchr(inp, '\n'); 15068769Seric if (p != NULL) 15168769Seric *p++ = '\0'; 15268769Seric id = strchr(inp, ' '); 15368769Seric if (id == NULL) 15468769Seric id = &inp[strlen(inp)]; 15568769Seric cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; 15668769Seric message(cmd, id - inp, inp, id); 15768769Seric 15868769Seric /* output remaining lines */ 15968769Seric while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) 16064496Seric { 16168769Seric *p++ = '\0'; 16269106Seric if (isascii(*id) && isspace(*id)) 16369106Seric id++; 16468769Seric message("220-%s", id); 16564496Seric } 16668769Seric if (id != NULL) 16769106Seric { 16869106Seric if (isascii(*id) && isspace(*id)) 16969106Seric id++; 17068769Seric message("220 %s", id); 17169106Seric } 17266745Seric 17358330Seric protocol = NULL; 17459016Seric sendinghost = macvalue('s', e); 17558082Seric gothello = FALSE; 17658330Seric gotmail = FALSE; 1774549Seric for (;;) 1784549Seric { 17912612Seric /* arrange for backout */ 18065751Seric if (setjmp(TopFrame) > 0) 18159058Seric { 18265751Seric /* if() nesting is necessary for Cray UNICOS */ 18365751Seric if (InChild) 18465751Seric { 18565751Seric QuickAbort = FALSE; 18665751Seric SuprErrs = TRUE; 18765751Seric finis(); 18865751Seric } 18959058Seric } 19012612Seric QuickAbort = FALSE; 19112612Seric HoldErrs = FALSE; 19251951Seric LogUsrErrs = FALSE; 19363843Seric e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 19412612Seric 1957356Seric /* setup for the read */ 19655012Seric e->e_to = NULL; 1974577Seric Errors = 0; 1987275Seric (void) fflush(stdout); 1997356Seric 2007356Seric /* read the input line */ 20161093Seric SmtpPhase = "server cmd read"; 20269788Seric setproctitle("server %s cmd read", CurSmtpClient); 20361093Seric p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, 20461093Seric SmtpPhase); 2057356Seric 2067685Seric /* handle errors */ 2077356Seric if (p == NULL) 2087356Seric { 2094549Seric /* end of file, just die */ 21066017Seric disconnect(1, e); 21158151Seric message("421 %s Lost input channel from %s", 21265017Seric MyHostName, CurSmtpClient); 21355464Seric #ifdef LOG 21463843Seric if (LogLevel > (gotmail ? 1 : 19)) 21555464Seric syslog(LOG_NOTICE, "lost input channel from %s", 21665017Seric CurSmtpClient); 21755464Seric #endif 21858069Seric if (InChild) 21958069Seric ExitStat = EX_QUIT; 2204549Seric finis(); 2214549Seric } 2224549Seric 2234549Seric /* clean up end of line */ 2244558Seric fixcrlf(inp, TRUE); 2254549Seric 2264713Seric /* echo command to transcript */ 22755012Seric if (e->e_xfp != NULL) 22855012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 2294713Seric 23059060Seric if (e->e_id == NULL) 23165058Seric setproctitle("%s: %.80s", CurSmtpClient, inp); 23259060Seric else 23365058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 23459060Seric 2354549Seric /* break off command */ 23658050Seric for (p = inp; isascii(*p) && isspace(*p); p++) 2374549Seric continue; 23857232Seric cmd = cmdbuf; 23958050Seric while (*p != '\0' && 24058050Seric !(isascii(*p) && isspace(*p)) && 24158050Seric cmd < &cmdbuf[sizeof cmdbuf - 2]) 24224981Seric *cmd++ = *p++; 24324981Seric *cmd = '\0'; 2444549Seric 24525691Seric /* throw away leading whitespace */ 24658050Seric while (isascii(*p) && isspace(*p)) 24725691Seric p++; 24825691Seric 2494549Seric /* decode command */ 2504549Seric for (c = CmdTab; c->cmdname != NULL; c++) 2514549Seric { 25233725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 2534549Seric break; 2544549Seric } 2554549Seric 25651954Seric /* reset errors */ 25751954Seric errno = 0; 25851954Seric 2594549Seric /* process command */ 2604549Seric switch (c->cmdcode) 2614549Seric { 2624976Seric case CMDHELO: /* hello -- introduce yourself */ 26358323Seric case CMDEHLO: /* extended hello */ 26458323Seric if (c->cmdcode == CMDEHLO) 26558323Seric { 26658323Seric protocol = "ESMTP"; 26761093Seric SmtpPhase = "server EHLO"; 26858323Seric } 26958323Seric else 27058323Seric { 27158323Seric protocol = "SMTP"; 27261093Seric SmtpPhase = "server HELO"; 27358323Seric } 27467445Seric 27567445Seric /* check for valid domain name (re 1123 5.2.5) */ 27667445Seric if (*p == '\0') 27767445Seric { 27867445Seric message("501 %s requires domain address", 27967445Seric cmdbuf); 28067445Seric break; 28167445Seric } 28267445Seric else 28367445Seric { 28467445Seric register char *q; 28567445Seric 28667445Seric for (q = p; *q != '\0'; q++) 28767445Seric { 28867445Seric if (!isascii(*q)) 28967445Seric break; 29067445Seric if (isalnum(*q)) 29167445Seric continue; 29267445Seric if (strchr("[].-_#", *q) == NULL) 29367445Seric break; 29467445Seric } 29567445Seric if (*q != '\0') 29667445Seric { 29767445Seric message("501 Invalid domain name"); 29867445Seric break; 29967445Seric } 30067445Seric } 30167445Seric 30259016Seric sendinghost = newstr(p); 30360210Seric gothello = TRUE; 30460210Seric if (c->cmdcode != CMDEHLO) 30560239Seric { 30660239Seric /* print old message and be done with it */ 30760239Seric message("250 %s Hello %s, pleased to meet you", 30865017Seric MyHostName, CurSmtpClient); 30960210Seric break; 31060239Seric } 31160239Seric 31260239Seric /* print extended message and brag */ 31360239Seric message("250-%s Hello %s, pleased to meet you", 31466760Seric MyHostName, CurSmtpClient); 31558323Seric if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 31658323Seric message("250-EXPN"); 31769480Seric #if MIME8TO7 31867417Seric message("250-8BITMIME"); 31969480Seric #endif 32064359Seric if (MaxMessageSize > 0) 32164359Seric message("250-SIZE %ld", MaxMessageSize); 32259271Seric else 32359271Seric message("250-SIZE"); 32468848Seric #if DSN 32569545Seric message("250-X-DSN-03 (Draft of 12 Mar 1995)"); 32668028Seric #endif 32758323Seric message("250 HELP"); 3284976Seric break; 3294976Seric 3304549Seric case CMDMAIL: /* mail -- designate sender */ 33161093Seric SmtpPhase = "server MAIL"; 33224943Seric 3339314Seric /* check for validity of this command */ 33458789Seric if (!gothello) 33558082Seric { 33658957Seric /* set sending host to our known value */ 33759016Seric if (sendinghost == NULL) 33866005Seric sendinghost = peerhostname; 33958957Seric 34058789Seric if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 34158821Seric { 34258789Seric message("503 Polite people say HELO first"); 34358821Seric break; 34458821Seric } 34558082Seric } 34658109Seric if (gotmail) 3474558Seric { 34858151Seric message("503 Sender already specified"); 34963843Seric if (InChild) 35063843Seric finis(); 3514558Seric break; 3524558Seric } 3539339Seric if (InChild) 3549339Seric { 35536230Skarels errno = 0; 35658151Seric syserr("503 Nested MAIL command: MAIL %s", p); 35758069Seric finis(); 3589339Seric } 3599339Seric 3609339Seric /* fork a subprocess to process this command */ 36155012Seric if (runinchild("SMTP-MAIL", e) > 0) 3629339Seric break; 36363753Seric if (!gothello) 36463753Seric { 36563753Seric auth_warning(e, 36666005Seric "Host %s didn't use HELO protocol", 36769788Seric CurSmtpClient); 36863753Seric } 36965947Seric #ifdef PICKY_HELO_CHECK 37066005Seric if (strcasecmp(sendinghost, peerhostname) != 0 && 37166005Seric (strcasecmp(peerhostname, "localhost") != 0 || 37265823Seric strcasecmp(sendinghost, MyHostName) != 0)) 37365823Seric { 37465823Seric auth_warning(e, "Host %s claimed to be %s", 37569788Seric CurSmtpClient, sendinghost); 37665823Seric } 37765947Seric #endif 37865823Seric 37958323Seric if (protocol == NULL) 38058323Seric protocol = "SMTP"; 38158323Seric define('r', protocol, e); 38259016Seric define('s', sendinghost, e); 38355012Seric initsys(e); 38459747Seric nrcpts = 0; 38568582Seric e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; 38665058Seric setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); 3879339Seric 3889339Seric /* child -- go do the processing */ 3894549Seric p = skipword(p, "from"); 3904549Seric if (p == NULL) 3914549Seric break; 39257977Seric if (setjmp(TopFrame) > 0) 39358147Seric { 39458147Seric /* this failed -- undo work */ 39558147Seric if (InChild) 39659058Seric { 39759058Seric QuickAbort = FALSE; 39859058Seric SuprErrs = TRUE; 39963787Seric e->e_flags &= ~EF_FATALERRS; 40058147Seric finis(); 40159058Seric } 40257977Seric break; 40358147Seric } 40457977Seric QuickAbort = TRUE; 40558333Seric 40658333Seric /* must parse sender first */ 40758333Seric delimptr = NULL; 40858704Seric setsender(p, e, &delimptr, FALSE); 40958333Seric p = delimptr; 41058333Seric if (p != NULL && *p != '\0') 41158333Seric *p++ = '\0'; 41258333Seric 41366325Seric /* check for possible spoofing */ 41466325Seric if (RealUid != 0 && OpMode == MD_SMTP && 41569757Seric !wordinclass(RealUserName, 't') && 41667473Seric !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 41767473Seric strcmp(e->e_from.q_user, RealUserName) != 0) 41866325Seric { 41966325Seric auth_warning(e, "%s owned process doing -bs", 42066325Seric RealUserName); 42166325Seric } 42266325Seric 42358333Seric /* now parse ESMTP arguments */ 42468560Seric e->e_msgsize = 0; 42566764Seric while (p != NULL && *p != '\0') 42658333Seric { 42758333Seric char *kp; 42866304Seric char *vp = NULL; 42969748Seric extern void mail_esmtp_args __P((char *, char *, ENVELOPE *)); 43058333Seric 43158333Seric /* locate the beginning of the keyword */ 43258333Seric while (isascii(*p) && isspace(*p)) 43358333Seric p++; 43458333Seric if (*p == '\0') 43558333Seric break; 43658333Seric kp = p; 43758333Seric 43858333Seric /* skip to the value portion */ 43958333Seric while (isascii(*p) && isalnum(*p) || *p == '-') 44058333Seric p++; 44158333Seric if (*p == '=') 44258333Seric { 44358333Seric *p++ = '\0'; 44458333Seric vp = p; 44558333Seric 44658333Seric /* skip to the end of the value */ 44758333Seric while (*p != '\0' && *p != ' ' && 44858333Seric !(isascii(*p) && iscntrl(*p)) && 44958333Seric *p != '=') 45058333Seric p++; 45158333Seric } 45258333Seric 45358333Seric if (*p != '\0') 45458333Seric *p++ = '\0'; 45558333Seric 45658333Seric if (tTd(19, 1)) 45766764Seric printf("MAIL: got arg %s=\"%s\"\n", kp, 45858333Seric vp == NULL ? "<null>" : vp); 45958333Seric 46068559Seric mail_esmtp_args(kp, vp, e); 46158333Seric } 46259284Seric 46368560Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 46459284Seric { 46559284Seric usrerr("552 Message size exceeds fixed maximum message size (%ld)", 46659284Seric MaxMessageSize); 46759284Seric /* NOTREACHED */ 46859284Seric } 46958333Seric 470*69801Seric if (!enoughdiskspace(e->e_msgsize)) 47158333Seric { 47258333Seric message("452 Insufficient disk space; try again later"); 47358333Seric break; 47458333Seric } 47558151Seric message("250 Sender ok"); 47658147Seric gotmail = TRUE; 4774549Seric break; 4784549Seric 4794976Seric case CMDRCPT: /* rcpt -- designate recipient */ 48058850Seric if (!gotmail) 48158850Seric { 48258850Seric usrerr("503 Need MAIL before RCPT"); 48358850Seric break; 48458850Seric } 48561093Seric SmtpPhase = "server RCPT"; 48612612Seric if (setjmp(TopFrame) > 0) 48714785Seric { 48855012Seric e->e_flags &= ~EF_FATALERRS; 48912612Seric break; 49014785Seric } 49112612Seric QuickAbort = TRUE; 49251951Seric LogUsrErrs = TRUE; 49358093Seric 49459699Seric if (e->e_sendmode != SM_DELIVER) 49559699Seric e->e_flags |= EF_VRFYONLY; 49658919Seric 4974549Seric p = skipword(p, "to"); 4984549Seric if (p == NULL) 4994549Seric break; 50067880Seric a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 50112612Seric if (a == NULL) 50212612Seric break; 50367880Seric p = delimptr; 50467880Seric 50567880Seric /* now parse ESMTP arguments */ 50667880Seric while (p != NULL && *p != '\0') 50767880Seric { 50867880Seric char *kp; 50967880Seric char *vp = NULL; 51069748Seric extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); 51167880Seric 51267880Seric /* locate the beginning of the keyword */ 51367880Seric while (isascii(*p) && isspace(*p)) 51467880Seric p++; 51567880Seric if (*p == '\0') 51667880Seric break; 51767880Seric kp = p; 51867880Seric 51967880Seric /* skip to the value portion */ 52067880Seric while (isascii(*p) && isalnum(*p) || *p == '-') 52167880Seric p++; 52267880Seric if (*p == '=') 52367880Seric { 52467880Seric *p++ = '\0'; 52567880Seric vp = p; 52667880Seric 52767880Seric /* skip to the end of the value */ 52867880Seric while (*p != '\0' && *p != ' ' && 52967880Seric !(isascii(*p) && iscntrl(*p)) && 53067880Seric *p != '=') 53167880Seric p++; 53267880Seric } 53367880Seric 53467880Seric if (*p != '\0') 53567880Seric *p++ = '\0'; 53667880Seric 53767880Seric if (tTd(19, 1)) 53867880Seric printf("RCPT: got arg %s=\"%s\"\n", kp, 53967880Seric vp == NULL ? "<null>" : vp); 54067880Seric 54167963Seric rcpt_esmtp_args(a, kp, vp, e); 54267880Seric } 54367980Seric 54467980Seric /* save in recipient list after ESMTP mods */ 54567982Seric a = recipient(a, &e->e_sendqueue, 0, e); 54667980Seric 54712612Seric if (Errors != 0) 54812612Seric break; 54912612Seric 55012612Seric /* no errors during parsing, but might be a duplicate */ 55155012Seric e->e_to = p; 55212612Seric if (!bitset(QBADADDR, a->q_flags)) 55359747Seric { 55464718Seric message("250 Recipient ok%s", 55564718Seric bitset(QQUEUEUP, a->q_flags) ? 55664718Seric " (will queue)" : ""); 55759747Seric nrcpts++; 55859747Seric } 55912612Seric else 5604549Seric { 56112612Seric /* punt -- should keep message in ADDRESS.... */ 56258151Seric message("550 Addressee unknown"); 5634549Seric } 56455012Seric e->e_to = NULL; 5654549Seric break; 5664549Seric 5674549Seric case CMDDATA: /* data -- text of mail */ 56861093Seric SmtpPhase = "server DATA"; 56958109Seric if (!gotmail) 5704549Seric { 57158151Seric message("503 Need MAIL command"); 5724976Seric break; 5734549Seric } 57464718Seric else if (nrcpts <= 0) 5754549Seric { 57658151Seric message("503 Need RCPT (recipient)"); 5774976Seric break; 5784549Seric } 5794976Seric 58058929Seric /* check to see if we need to re-expand aliases */ 58163787Seric /* also reset QBADADDR on already-diagnosted addrs */ 58263787Seric doublequeue = FALSE; 58358929Seric for (a = e->e_sendqueue; a != NULL; a = a->q_next) 58458929Seric { 58558929Seric if (bitset(QVERIFIED, a->q_flags)) 58663787Seric { 58763787Seric /* need to re-expand aliases */ 58863787Seric doublequeue = TRUE; 58963787Seric } 59063787Seric if (bitset(QBADADDR, a->q_flags)) 59163787Seric { 59263787Seric /* make this "go away" */ 59363787Seric a->q_flags |= QDONTSEND; 59463787Seric a->q_flags &= ~QBADADDR; 59563787Seric } 59658929Seric } 59758929Seric 5984976Seric /* collect the text of the message */ 59924943Seric SmtpPhase = "collect"; 60068692Seric buffer_errors(); 60167546Seric collect(InChannel, TRUE, doublequeue, NULL, e); 60268692Seric flush_errors(TRUE); 60364766Seric if (Errors != 0) 60464766Seric goto abortmessage; 60567131Seric 60668582Seric /* make sure we actually do delivery */ 60768582Seric e->e_flags &= ~EF_CLRQUEUE; 60868582Seric 60967131Seric /* from now on, we have to operate silently */ 61068692Seric buffer_errors(); 61167131Seric e->e_errormode = EM_MAIL; 6124976Seric 6138238Seric /* 6148238Seric ** Arrange to send to everyone. 6158238Seric ** If sending to multiple people, mail back 6168238Seric ** errors rather than reporting directly. 6178238Seric ** In any case, don't mail back errors for 6188238Seric ** anything that has happened up to 6198238Seric ** now (the other end will do this). 62010197Seric ** Truncate our transcript -- the mail has gotten 62110197Seric ** to us successfully, and if we have 62210197Seric ** to mail this back, it will be easier 62310197Seric ** on the reader. 6248238Seric ** Then send to everyone. 6258238Seric ** Finally give a reply code. If an error has 6268238Seric ** already been given, don't mail a 6278238Seric ** message back. 6289339Seric ** We goose error returns by clearing error bit. 6298238Seric */ 6308238Seric 63124943Seric SmtpPhase = "delivery"; 63255012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 63358714Seric id = e->e_id; 6344976Seric 63567131Seric if (doublequeue) 63659730Seric { 63767131Seric /* make sure it is in the queue */ 63867131Seric queueup(e, TRUE, FALSE); 63968035Seric if (e->e_sendmode == SM_QUEUE) 64068035Seric e->e_flags |= EF_KEEPQUEUE; 64159730Seric } 6428238Seric else 64358919Seric { 64467131Seric /* send to all recipients */ 64567131Seric sendall(e, SM_DEFAULT); 64667131Seric } 64767131Seric e->e_to = NULL; 64859747Seric 64967131Seric /* issue success message */ 65067131Seric message("250 %s Message accepted for delivery", id); 65164296Seric 65267131Seric /* if we just queued, poke it */ 65367131Seric if (doublequeue && e->e_sendmode != SM_QUEUE) 65467131Seric { 65567131Seric extern pid_t dowork(); 65667131Seric 65767131Seric unlockqueue(e); 65867131Seric (void) dowork(id, TRUE, TRUE, e); 65958919Seric } 66058883Seric 66159747Seric abortmessage: 6629339Seric /* if in a child, pop back to our parent */ 6639339Seric if (InChild) 6649339Seric finis(); 66524943Seric 66624943Seric /* clean up a bit */ 66758109Seric gotmail = FALSE; 66855012Seric dropenvelope(e); 66958179Seric CurEnv = e = newenvelope(e, CurEnv); 67055012Seric e->e_flags = BlankEnvelope.e_flags; 6714549Seric break; 6724549Seric 6734549Seric case CMDRSET: /* rset -- reset state */ 67458151Seric message("250 Reset state"); 67568603Seric 67668603Seric /* arrange to ignore any current send list */ 67768603Seric e->e_sendqueue = NULL; 67864359Seric e->e_flags |= EF_CLRQUEUE; 6799339Seric if (InChild) 6809339Seric finis(); 68158109Seric 68258109Seric /* clean up a bit */ 68358109Seric gotmail = FALSE; 68458109Seric dropenvelope(e); 68558179Seric CurEnv = e = newenvelope(e, CurEnv); 6869339Seric break; 6874549Seric 6884549Seric case CMDVRFY: /* vrfy -- verify address */ 68958092Seric case CMDEXPN: /* expn -- expand address */ 69058092Seric vrfy = c->cmdcode == CMDVRFY; 69158092Seric if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 69258092Seric PrivacyFlags)) 69358082Seric { 69458412Seric if (vrfy) 69567160Seric message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 69658412Seric else 69765192Seric message("502 Sorry, we do not allow this operation"); 69865017Seric #ifdef LOG 69965017Seric if (LogLevel > 5) 70065017Seric syslog(LOG_INFO, "%s: %s [rejected]", 70165017Seric CurSmtpClient, inp); 70265017Seric #endif 70358082Seric break; 70458082Seric } 70558082Seric else if (!gothello && 70658092Seric bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 70758092Seric PrivacyFlags)) 70858082Seric { 70958151Seric message("503 I demand that you introduce yourself first"); 71058082Seric break; 71158082Seric } 71258092Seric if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 7139339Seric break; 71455173Seric #ifdef LOG 71558020Seric if (LogLevel > 5) 71665017Seric syslog(LOG_INFO, "%s: %s", CurSmtpClient, inp); 71755173Seric #endif 7185003Seric vrfyqueue = NULL; 7197762Seric QuickAbort = TRUE; 72058092Seric if (vrfy) 72158092Seric e->e_flags |= EF_VRFYONLY; 72262373Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 72367615Seric p++; 72462373Seric if (*p == '\0') 72562373Seric { 72662373Seric message("501 Argument required"); 72762373Seric Errors++; 72862373Seric } 72962373Seric else 73062373Seric { 73167990Seric (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 73262373Seric } 7337762Seric if (Errors != 0) 7349339Seric { 7359339Seric if (InChild) 7369339Seric finis(); 7377762Seric break; 7389339Seric } 73962373Seric if (vrfyqueue == NULL) 74062373Seric { 74162373Seric message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 74262373Seric } 7435003Seric while (vrfyqueue != NULL) 7445003Seric { 74569748Seric extern void printvrfyaddr __P((ADDRESS *, bool)); 74669748Seric 74763971Seric a = vrfyqueue; 74863971Seric while ((a = a->q_next) != NULL && 74963971Seric bitset(QDONTSEND|QBADADDR, a->q_flags)) 75063971Seric continue; 7517685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 75258151Seric printvrfyaddr(vrfyqueue, a == NULL); 75363847Seric vrfyqueue = vrfyqueue->q_next; 7545003Seric } 7559339Seric if (InChild) 7569339Seric finis(); 7574549Seric break; 7584549Seric 7594549Seric case CMDHELP: /* help -- give user info */ 7604577Seric help(p); 7614549Seric break; 7624549Seric 7634549Seric case CMDNOOP: /* noop -- do nothing */ 76464122Seric message("250 OK"); 7654549Seric break; 7664549Seric 7674549Seric case CMDQUIT: /* quit -- leave mail */ 76858151Seric message("221 %s closing connection", MyHostName); 76961051Seric 77066283Seric doquit: 77168603Seric /* arrange to ignore any current send list */ 77268603Seric e->e_sendqueue = NULL; 77368603Seric 77461051Seric /* avoid future 050 messages */ 77566017Seric disconnect(1, e); 77661051Seric 7779339Seric if (InChild) 7789339Seric ExitStat = EX_QUIT; 7794549Seric finis(); 7804549Seric 7818544Seric case CMDVERB: /* set verbose mode */ 78259957Seric if (bitset(PRIV_NOEXPN, PrivacyFlags)) 78359957Seric { 78459957Seric /* this would give out the same info */ 78559957Seric message("502 Verbose unavailable"); 78659957Seric break; 78759957Seric } 7888544Seric Verbose = TRUE; 78958734Seric e->e_sendmode = SM_DELIVER; 79059957Seric message("250 Verbose mode"); 7918544Seric break; 7928544Seric 7939314Seric case CMDONEX: /* doing one transaction only */ 7949378Seric OneXact = TRUE; 79559957Seric message("250 Only one transaction"); 7969314Seric break; 7979314Seric 79836230Skarels # ifdef SMTPDEBUG 7999339Seric case CMDDBGQSHOW: /* show queues */ 8006907Seric printf("Send Queue="); 80155012Seric printaddr(e->e_sendqueue, TRUE); 8025003Seric break; 8037275Seric 8047275Seric case CMDDBGDEBUG: /* set debug mode */ 8057676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 8067676Seric tTflag(p); 80758151Seric message("200 Debug set"); 8087275Seric break; 8097275Seric 81036230Skarels # else /* not SMTPDEBUG */ 81136230Skarels case CMDDBGQSHOW: /* show queues */ 81236230Skarels case CMDDBGDEBUG: /* set debug mode */ 81364685Seric # endif /* SMTPDEBUG */ 81464685Seric case CMDLOGBOGUS: /* bogus command */ 81536233Skarels # ifdef LOG 81658308Seric if (LogLevel > 0) 81764685Seric syslog(LOG_CRIT, 81858020Seric "\"%s\" command from %s (%s)", 81969788Seric c->cmdname, CurSmtpClient, 82058755Seric anynet_ntoa(&RealHostAddr)); 82136233Skarels # endif 82236230Skarels /* FALL THROUGH */ 82336230Skarels 8244549Seric case CMDERROR: /* unknown command */ 82566283Seric if (++badcommands > MAXBADCOMMANDS) 82666283Seric { 82766283Seric message("421 %s Too many bad commands; closing connection", 82866283Seric MyHostName); 82966283Seric goto doquit; 83066283Seric } 83166283Seric 83258151Seric message("500 Command unrecognized"); 8334549Seric break; 8344549Seric 8354549Seric default: 83636230Skarels errno = 0; 83758151Seric syserr("500 smtp: unknown code %d", c->cmdcode); 8384549Seric break; 8394549Seric } 8404549Seric } 8414549Seric } 8424549Seric /* 8434549Seric ** SKIPWORD -- skip a fixed word. 8444549Seric ** 8454549Seric ** Parameters: 8464549Seric ** p -- place to start looking. 8474549Seric ** w -- word to skip. 8484549Seric ** 8494549Seric ** Returns: 8504549Seric ** p following w. 8514549Seric ** NULL on error. 8524549Seric ** 8534549Seric ** Side Effects: 8544549Seric ** clobbers the p data area. 8554549Seric */ 8564549Seric 8574549Seric static char * 8584549Seric skipword(p, w) 8594549Seric register char *p; 8604549Seric char *w; 8614549Seric { 8624549Seric register char *q; 86366005Seric char *firstp = p; 8644549Seric 8654549Seric /* find beginning of word */ 86658050Seric while (isascii(*p) && isspace(*p)) 8674549Seric p++; 8684549Seric q = p; 8694549Seric 8704549Seric /* find end of word */ 87158050Seric while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 8724549Seric p++; 87358050Seric while (isascii(*p) && isspace(*p)) 8744549Seric *p++ = '\0'; 8754549Seric if (*p != ':') 8764549Seric { 8774549Seric syntax: 87866005Seric message("501 Syntax error in parameters scanning \"%s\"", 87966005Seric firstp); 8804549Seric Errors++; 8814549Seric return (NULL); 8824549Seric } 8834549Seric *p++ = '\0'; 88458050Seric while (isascii(*p) && isspace(*p)) 8854549Seric p++; 8864549Seric 88762373Seric if (*p == '\0') 88862373Seric goto syntax; 88962373Seric 8904549Seric /* see if the input word matches desired word */ 89133725Sbostic if (strcasecmp(q, w)) 8924549Seric goto syntax; 8934549Seric 8944549Seric return (p); 8954549Seric } 8964577Seric /* 89768559Seric ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 89868559Seric ** 89968559Seric ** Parameters: 90068559Seric ** kp -- the parameter key. 90168559Seric ** vp -- the value of that parameter. 90268559Seric ** e -- the envelope. 90368559Seric ** 90468559Seric ** Returns: 90568559Seric ** none. 90668559Seric */ 90768559Seric 90869748Seric void 90968559Seric mail_esmtp_args(kp, vp, e) 91068559Seric char *kp; 91168559Seric char *vp; 91268559Seric ENVELOPE *e; 91368559Seric { 91468559Seric if (strcasecmp(kp, "size") == 0) 91568559Seric { 91668559Seric if (vp == NULL) 91768559Seric { 91868559Seric usrerr("501 SIZE requires a value"); 91968559Seric /* NOTREACHED */ 92068559Seric } 92168890Seric # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) 92268560Seric e->e_msgsize = strtoul(vp, (char **) NULL, 10); 92368559Seric # else 92468560Seric e->e_msgsize = strtol(vp, (char **) NULL, 10); 92568559Seric # endif 92668559Seric } 92768559Seric else if (strcasecmp(kp, "body") == 0) 92868559Seric { 92968559Seric if (vp == NULL) 93068559Seric { 93168559Seric usrerr("501 BODY requires a value"); 93268559Seric /* NOTREACHED */ 93368559Seric } 93468559Seric if (strcasecmp(vp, "8bitmime") == 0) 93568559Seric { 93668559Seric SevenBitInput = FALSE; 93768883Seric e->e_flags |= EF_NL_NOT_EOL; 93868559Seric } 93968559Seric else if (strcasecmp(vp, "7bit") == 0) 94068559Seric { 94168559Seric SevenBitInput = TRUE; 94268559Seric } 94368559Seric else 94468559Seric { 94568559Seric usrerr("501 Unknown BODY type %s", 94668559Seric vp); 94768559Seric /* NOTREACHED */ 94868559Seric } 94968559Seric e->e_bodytype = newstr(vp); 95068559Seric } 95168559Seric else if (strcasecmp(kp, "envid") == 0) 95268559Seric { 95368559Seric if (vp == NULL) 95468559Seric { 95568559Seric usrerr("501 ENVID requires a value"); 95668559Seric /* NOTREACHED */ 95768559Seric } 95868583Seric if (!xtextok(vp)) 95968583Seric { 96068583Seric usrerr("501 Syntax error in ENVID parameter value"); 96168583Seric /* NOTREACHED */ 96268583Seric } 96368583Seric if (e->e_envid != NULL) 96468583Seric { 96568583Seric usrerr("501 Duplicate ENVID parameter"); 96668583Seric /* NOTREACHED */ 96768583Seric } 96868559Seric e->e_envid = newstr(vp); 96968559Seric } 97068559Seric else if (strcasecmp(kp, "ret") == 0) 97168559Seric { 97268559Seric if (vp == NULL) 97368559Seric { 97468559Seric usrerr("501 RET requires a value"); 97568559Seric /* NOTREACHED */ 97668559Seric } 97768583Seric if (bitset(EF_RET_PARAM, e->e_flags)) 97868583Seric { 97968583Seric usrerr("501 Duplicate RET parameter"); 98068583Seric /* NOTREACHED */ 98168583Seric } 98268559Seric e->e_flags |= EF_RET_PARAM; 98368559Seric if (strcasecmp(vp, "hdrs") == 0) 98468559Seric e->e_flags |= EF_NO_BODY_RETN; 98568559Seric else if (strcasecmp(vp, "full") != 0) 98668559Seric { 98768559Seric usrerr("501 Bad argument \"%s\" to RET", vp); 98868559Seric /* NOTREACHED */ 98968559Seric } 99068559Seric } 99168559Seric else 99268559Seric { 99368559Seric usrerr("501 %s parameter unrecognized", kp); 99468559Seric /* NOTREACHED */ 99568559Seric } 99668559Seric } 99768559Seric /* 99867963Seric ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 99967963Seric ** 100067963Seric ** Parameters: 100167963Seric ** a -- the address corresponding to the To: parameter. 100267963Seric ** kp -- the parameter key. 100367963Seric ** vp -- the value of that parameter. 100467963Seric ** e -- the envelope. 100567963Seric ** 100667963Seric ** Returns: 100767963Seric ** none. 100867963Seric */ 100967963Seric 101069748Seric void 101167963Seric rcpt_esmtp_args(a, kp, vp, e) 101267963Seric ADDRESS *a; 101367963Seric char *kp; 101467963Seric char *vp; 101567963Seric ENVELOPE *e; 101667963Seric { 101767963Seric if (strcasecmp(kp, "notify") == 0) 101867963Seric { 101967963Seric char *p; 102067963Seric 102167963Seric if (vp == NULL) 102267963Seric { 102367963Seric usrerr("501 NOTIFY requires a value"); 102467963Seric /* NOTREACHED */ 102567963Seric } 102667963Seric a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 102768595Seric a->q_flags |= QHASNOTIFY; 102867963Seric if (strcasecmp(vp, "never") == 0) 102967963Seric return; 103067963Seric for (p = vp; p != NULL; vp = p) 103167963Seric { 103267963Seric p = strchr(p, ','); 103367963Seric if (p != NULL) 103467963Seric *p++ = '\0'; 103567963Seric if (strcasecmp(vp, "success") == 0) 103667963Seric a->q_flags |= QPINGONSUCCESS; 103767963Seric else if (strcasecmp(vp, "failure") == 0) 103867963Seric a->q_flags |= QPINGONFAILURE; 103967963Seric else if (strcasecmp(vp, "delay") == 0) 104067963Seric a->q_flags |= QPINGONDELAY; 104167963Seric else 104267963Seric { 104367963Seric usrerr("501 Bad argument \"%s\" to NOTIFY", 104467963Seric vp); 104567963Seric /* NOTREACHED */ 104667963Seric } 104767963Seric } 104867963Seric } 104967963Seric else if (strcasecmp(kp, "orcpt") == 0) 105067963Seric { 105167963Seric if (vp == NULL) 105267963Seric { 105367963Seric usrerr("501 ORCPT requires a value"); 105467963Seric /* NOTREACHED */ 105567963Seric } 105668583Seric if (!xtextok(vp)) 105768583Seric { 105868583Seric usrerr("501 Syntax error in ORCPT parameter value"); 105968583Seric /* NOTREACHED */ 106068583Seric } 106168583Seric if (a->q_orcpt != NULL) 106268583Seric { 106368583Seric usrerr("501 Duplicate ORCPT parameter"); 106468583Seric /* NOTREACHED */ 106568583Seric } 106667963Seric a->q_orcpt = newstr(vp); 106767963Seric } 106867963Seric else 106967963Seric { 107067963Seric usrerr("501 %s parameter unrecognized", kp); 107167963Seric /* NOTREACHED */ 107267963Seric } 107367963Seric } 107467963Seric /* 107558151Seric ** PRINTVRFYADDR -- print an entry in the verify queue 107658151Seric ** 107758151Seric ** Parameters: 107858151Seric ** a -- the address to print 107958151Seric ** last -- set if this is the last one. 108058151Seric ** 108158151Seric ** Returns: 108258151Seric ** none. 108358151Seric ** 108458151Seric ** Side Effects: 108558151Seric ** Prints the appropriate 250 codes. 108658151Seric */ 108758151Seric 108869748Seric void 108958151Seric printvrfyaddr(a, last) 109058151Seric register ADDRESS *a; 109158151Seric bool last; 109258151Seric { 109358151Seric char fmtbuf[20]; 109458151Seric 109558151Seric strcpy(fmtbuf, "250"); 109658151Seric fmtbuf[3] = last ? ' ' : '-'; 109758151Seric 109859746Seric if (a->q_fullname == NULL) 109959746Seric { 110059746Seric if (strchr(a->q_user, '@') == NULL) 110159746Seric strcpy(&fmtbuf[4], "<%s@%s>"); 110259746Seric else 110359746Seric strcpy(&fmtbuf[4], "<%s>"); 110459746Seric message(fmtbuf, a->q_user, MyHostName); 110559746Seric } 110658151Seric else 110758151Seric { 110859746Seric if (strchr(a->q_user, '@') == NULL) 110959746Seric strcpy(&fmtbuf[4], "%s <%s@%s>"); 111059746Seric else 111159746Seric strcpy(&fmtbuf[4], "%s <%s>"); 111259746Seric message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 111358151Seric } 111458151Seric } 111558151Seric /* 11164577Seric ** HELP -- implement the HELP command. 11174577Seric ** 11184577Seric ** Parameters: 11194577Seric ** topic -- the topic we want help for. 11204577Seric ** 11214577Seric ** Returns: 11224577Seric ** none. 11234577Seric ** 11244577Seric ** Side Effects: 11254577Seric ** outputs the help file to message output. 11264577Seric */ 11274577Seric 112869748Seric void 11294577Seric help(topic) 11304577Seric char *topic; 11314577Seric { 11324577Seric register FILE *hf; 11334577Seric int len; 113468751Seric bool noinfo; 11354577Seric char buf[MAXLINE]; 113668751Seric extern char Version[]; 11374577Seric 113868751Seric 11398269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 11404577Seric { 11414577Seric /* no help */ 114211931Seric errno = 0; 114368751Seric message("502 Sendmail %s -- HELP not implemented", Version); 11444577Seric return; 11454577Seric } 11464577Seric 114749669Seric if (topic == NULL || *topic == '\0') 114868751Seric { 114949669Seric topic = "smtp"; 115068751Seric message("214-This is Sendmail version %s", Version); 115168751Seric noinfo = FALSE; 115268751Seric } 115349669Seric else 115468751Seric { 115549669Seric makelower(topic); 115668751Seric noinfo = TRUE; 115768751Seric } 115849669Seric 11594577Seric len = strlen(topic); 11604577Seric 11614577Seric while (fgets(buf, sizeof buf, hf) != NULL) 11624577Seric { 11634577Seric if (strncmp(buf, topic, len) == 0) 11644577Seric { 11654577Seric register char *p; 11664577Seric 116756795Seric p = strchr(buf, '\t'); 11684577Seric if (p == NULL) 11694577Seric p = buf; 11704577Seric else 11714577Seric p++; 11724577Seric fixcrlf(p, TRUE); 117358151Seric message("214-%s", p); 11744577Seric noinfo = FALSE; 11754577Seric } 11764577Seric } 11774577Seric 11784577Seric if (noinfo) 117958151Seric message("504 HELP topic unknown"); 11804577Seric else 118158151Seric message("214 End of HELP info"); 11824628Seric (void) fclose(hf); 11834577Seric } 11848544Seric /* 11859339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 11869339Seric ** 11879339Seric ** Parameters: 11889339Seric ** label -- a string used in error messages 11899339Seric ** 11909339Seric ** Returns: 11919339Seric ** zero in the child 11929339Seric ** one in the parent 11939339Seric ** 11949339Seric ** Side Effects: 11959339Seric ** none. 11969339Seric */ 11978544Seric 119869748Seric int 119955012Seric runinchild(label, e) 12009339Seric char *label; 120155012Seric register ENVELOPE *e; 12029339Seric { 12039339Seric int childpid; 12049339Seric 120516158Seric if (!OneXact) 12069339Seric { 120716158Seric childpid = dofork(); 120816158Seric if (childpid < 0) 120916158Seric { 121069716Seric syserr("451 %s: cannot fork", label); 121116158Seric return (1); 121216158Seric } 121316158Seric if (childpid > 0) 121416158Seric { 121516158Seric auto int st; 12169339Seric 121716158Seric /* parent -- wait for child to complete */ 121869788Seric setproctitle("server %s child wait", CurSmtpClient); 121916158Seric st = waitfor(childpid); 122016158Seric if (st == -1) 122169716Seric syserr("451 %s: lost child", label); 122264948Seric else if (!WIFEXITED(st)) 122369716Seric syserr("451 %s: died on signal %d", 122464948Seric label, st & 0177); 12259339Seric 122616158Seric /* if we exited on a QUIT command, complete the process */ 122766017Seric if (WEXITSTATUS(st) == EX_QUIT) 122866017Seric { 122966017Seric disconnect(1, e); 123016158Seric finis(); 123166017Seric } 12329339Seric 123316158Seric return (1); 123416158Seric } 123516158Seric else 123616158Seric { 123716158Seric /* child */ 123816158Seric InChild = TRUE; 123925050Seric QuickAbort = FALSE; 124055012Seric clearenvelope(e, FALSE); 124116158Seric } 12429339Seric } 124315256Seric 124416158Seric /* open alias database */ 124560537Seric initmaps(FALSE, e); 124616158Seric 124716158Seric return (0); 12489339Seric } 12499339Seric 125056795Seric # endif /* SMTP */ 1251