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*57642Seric static char sccsid[] = "@(#)srvrsmtp.c 6.3 (Berkeley) 01/21/93 (with SMTP)"; 1433731Sbostic #else 15*57642Seric static char sccsid[] = "@(#)srvrsmtp.c 6.3 (Berkeley) 01/21/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 */ 519339Seric # define CMDHELP 6 /* help -- give usage info */ 529339Seric # define CMDNOOP 7 /* noop -- do nothing */ 539339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 549339Seric # define CMDHELO 9 /* helo -- be polite */ 5536230Skarels # define CMDONEX 10 /* onex -- sending one transaction only */ 5636230Skarels # define CMDVERB 11 /* verb -- go into verbose mode */ 5736230Skarels /* debugging-only commands, only enabled if SMTPDEBUG is defined */ 5836230Skarels # define CMDDBGQSHOW 12 /* showq -- show send queue */ 5936230Skarels # define CMDDBGDEBUG 13 /* debug -- set debug mode */ 604549Seric 614549Seric static struct cmd CmdTab[] = 624549Seric { 634549Seric "mail", CMDMAIL, 644976Seric "rcpt", CMDRCPT, 654549Seric "data", CMDDATA, 664549Seric "rset", CMDRSET, 674549Seric "vrfy", CMDVRFY, 687762Seric "expn", CMDVRFY, 694549Seric "help", CMDHELP, 704549Seric "noop", CMDNOOP, 714549Seric "quit", CMDQUIT, 724976Seric "helo", CMDHELO, 738544Seric "verb", CMDVERB, 749314Seric "onex", CMDONEX, 7536230Skarels /* 7636230Skarels * remaining commands are here only 7736230Skarels * to trap and log attempts to use them 7836230Skarels */ 799339Seric "showq", CMDDBGQSHOW, 808544Seric "debug", CMDDBGDEBUG, 814549Seric NULL, CMDERROR, 824549Seric }; 834549Seric 849339Seric bool InChild = FALSE; /* true if running in a subprocess */ 859378Seric bool OneXact = FALSE; /* one xaction only this run */ 8611146Seric 879339Seric #define EX_QUIT 22 /* special code for QUIT command */ 888544Seric 8955012Seric smtp(e) 9055012Seric register ENVELOPE *e; 914549Seric { 924549Seric register char *p; 938544Seric register struct cmd *c; 944549Seric char *cmd; 9546928Sbostic static char *skipword(); 964549Seric bool hasmail; /* mail command received */ 975003Seric auto ADDRESS *vrfyqueue; 9812612Seric ADDRESS *a; 9930448Seric char *sendinghost; 1008544Seric char inp[MAXLINE]; 10157232Seric char cmdbuf[MAXLINE]; 1027124Seric extern char Version[]; 10311151Seric extern char *macvalue(); 10412612Seric extern ADDRESS *recipient(); 10524943Seric extern ENVELOPE BlankEnvelope; 10624943Seric extern ENVELOPE *newenvelope(); 1074549Seric 1085003Seric hasmail = FALSE; 1097363Seric if (OutChannel != stdout) 1107363Seric { 1117363Seric /* arrange for debugging output to go to remote host */ 1127363Seric (void) close(1); 1137363Seric (void) dup(fileno(OutChannel)); 1147363Seric } 11555012Seric settime(e); 116*57642Seric if (RealHostName == NULL) 117*57642Seric RealHostName = MyHostName; 118*57642Seric CurHostName = RealHostName; 119*57642Seric setproctitle("srvrsmtp %s", CurHostName); 12055012Seric expand("\001e", inp, &inp[sizeof inp], e); 12155360Seric message("220", "%s", inp); 12224943Seric SmtpPhase = "startup"; 12330448Seric sendinghost = NULL; 1244549Seric for (;;) 1254549Seric { 12612612Seric /* arrange for backout */ 12712612Seric if (setjmp(TopFrame) > 0 && InChild) 12812612Seric finis(); 12912612Seric QuickAbort = FALSE; 13012612Seric HoldErrs = FALSE; 13151951Seric LogUsrErrs = FALSE; 13212612Seric 1337356Seric /* setup for the read */ 13455012Seric e->e_to = NULL; 1354577Seric Errors = 0; 1367275Seric (void) fflush(stdout); 1377356Seric 1387356Seric /* read the input line */ 13957389Seric p = sfgets(inp, sizeof inp, InChannel, ReadTimeout); 1407356Seric 1417685Seric /* handle errors */ 1427356Seric if (p == NULL) 1437356Seric { 1444549Seric /* end of file, just die */ 14536230Skarels message("421", "%s Lost input channel from %s", 14625050Seric MyHostName, CurHostName); 14755464Seric #ifdef LOG 14855464Seric if (LogLevel >= 4) 14955464Seric syslog(LOG_NOTICE, "lost input channel from %s", 15055464Seric CurHostName); 15155464Seric #endif 1524549Seric finis(); 1534549Seric } 1544549Seric 1554549Seric /* clean up end of line */ 1564558Seric fixcrlf(inp, TRUE); 1574549Seric 1584713Seric /* echo command to transcript */ 15955012Seric if (e->e_xfp != NULL) 16055012Seric fprintf(e->e_xfp, "<<< %s\n", inp); 1614713Seric 1624549Seric /* break off command */ 1634549Seric for (p = inp; isspace(*p); p++) 1644549Seric continue; 16557232Seric cmd = cmdbuf; 16657232Seric while (*p != '\0' && !isspace(*p) && cmd < &cmdbuf[sizeof cmdbuf - 2]) 16724981Seric *cmd++ = *p++; 16824981Seric *cmd = '\0'; 1694549Seric 17025691Seric /* throw away leading whitespace */ 17125691Seric while (isspace(*p)) 17225691Seric p++; 17325691Seric 1744549Seric /* decode command */ 1754549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1764549Seric { 17733725Sbostic if (!strcasecmp(c->cmdname, cmdbuf)) 1784549Seric break; 1794549Seric } 1804549Seric 18151954Seric /* reset errors */ 18251954Seric errno = 0; 18351954Seric 1844549Seric /* process command */ 1854549Seric switch (c->cmdcode) 1864549Seric { 1874976Seric case CMDHELO: /* hello -- introduce yourself */ 18824943Seric SmtpPhase = "HELO"; 18925050Seric setproctitle("%s: %s", CurHostName, inp); 19033725Sbostic if (!strcasecmp(p, MyHostName)) 19114877Seric { 19236230Skarels /* 19336230Skarels * didn't know about alias, 19436230Skarels * or connected to an echo server 19536230Skarels */ 19647570Seric message("553", "%s config error: mail loops back to myself", 19747570Seric MyHostName); 19814877Seric break; 19914877Seric } 20033725Sbostic if (RealHostName != NULL && strcasecmp(p, RealHostName)) 20111146Seric { 20224981Seric char hostbuf[MAXNAME]; 20311146Seric 20424981Seric (void) sprintf(hostbuf, "%s (%s)", p, RealHostName); 20530448Seric sendinghost = newstr(hostbuf); 20611146Seric } 20711146Seric else 20830448Seric sendinghost = newstr(p); 2094997Seric message("250", "%s Hello %s, pleased to meet you", 21036230Skarels MyHostName, sendinghost); 2114976Seric break; 2124976Seric 2134549Seric case CMDMAIL: /* mail -- designate sender */ 21424943Seric SmtpPhase = "MAIL"; 21524943Seric 21611151Seric /* force a sending host even if no HELO given */ 21755012Seric if (RealHostName != NULL && macvalue('s', e) == NULL) 21830448Seric sendinghost = RealHostName; 21911151Seric 2209314Seric /* check for validity of this command */ 2214558Seric if (hasmail) 2224558Seric { 2234558Seric message("503", "Sender already specified"); 2244558Seric break; 2254558Seric } 2269339Seric if (InChild) 2279339Seric { 22836230Skarels errno = 0; 2299339Seric syserr("Nested MAIL command"); 2309339Seric exit(0); 2319339Seric } 2329339Seric 2339339Seric /* fork a subprocess to process this command */ 23455012Seric if (runinchild("SMTP-MAIL", e) > 0) 2359339Seric break; 23655012Seric define('s', sendinghost, e); 23755012Seric define('r', "SMTP", e); 23855012Seric initsys(e); 23957389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 2409339Seric 2419339Seric /* child -- go do the processing */ 2424549Seric p = skipword(p, "from"); 2434549Seric if (p == NULL) 2444549Seric break; 24555012Seric setsender(p, e); 2464577Seric if (Errors == 0) 2474549Seric { 2484549Seric message("250", "Sender ok"); 2494549Seric hasmail = TRUE; 2504549Seric } 2519339Seric else if (InChild) 2529339Seric finis(); 2534549Seric break; 2544549Seric 2554976Seric case CMDRCPT: /* rcpt -- designate recipient */ 25624943Seric SmtpPhase = "RCPT"; 25757389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 25812612Seric if (setjmp(TopFrame) > 0) 25914785Seric { 26055012Seric e->e_flags &= ~EF_FATALERRS; 26112612Seric break; 26214785Seric } 26312612Seric QuickAbort = TRUE; 26451951Seric LogUsrErrs = TRUE; 2654549Seric p = skipword(p, "to"); 2664549Seric if (p == NULL) 2674549Seric break; 26855012Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0', e); 26912612Seric if (a == NULL) 27012612Seric break; 27116886Seric a->q_flags |= QPRIMARY; 27255012Seric a = recipient(a, &e->e_sendqueue, e); 27312612Seric if (Errors != 0) 27412612Seric break; 27512612Seric 27612612Seric /* no errors during parsing, but might be a duplicate */ 27755012Seric e->e_to = p; 27812612Seric if (!bitset(QBADADDR, a->q_flags)) 27912612Seric message("250", "Recipient ok"); 28012612Seric else 2814549Seric { 28212612Seric /* punt -- should keep message in ADDRESS.... */ 28312612Seric message("550", "Addressee unknown"); 2844549Seric } 28555012Seric e->e_to = NULL; 2864549Seric break; 2874549Seric 2884549Seric case CMDDATA: /* data -- text of mail */ 28924943Seric SmtpPhase = "DATA"; 2904976Seric if (!hasmail) 2914549Seric { 2924976Seric message("503", "Need MAIL command"); 2934976Seric break; 2944549Seric } 29555012Seric else if (e->e_nrcpts <= 0) 2964549Seric { 2974976Seric message("503", "Need RCPT (recipient)"); 2984976Seric break; 2994549Seric } 3004976Seric 3014976Seric /* collect the text of the message */ 30224943Seric SmtpPhase = "collect"; 30357389Seric setproctitle("%s %s: %s", e->e_id, CurHostName, inp); 30455012Seric collect(TRUE, e); 3054976Seric if (Errors != 0) 3064976Seric break; 3074976Seric 3088238Seric /* 3098238Seric ** Arrange to send to everyone. 3108238Seric ** If sending to multiple people, mail back 3118238Seric ** errors rather than reporting directly. 3128238Seric ** In any case, don't mail back errors for 3138238Seric ** anything that has happened up to 3148238Seric ** now (the other end will do this). 31510197Seric ** Truncate our transcript -- the mail has gotten 31610197Seric ** to us successfully, and if we have 31710197Seric ** to mail this back, it will be easier 31810197Seric ** on the reader. 3198238Seric ** Then send to everyone. 3208238Seric ** Finally give a reply code. If an error has 3218238Seric ** already been given, don't mail a 3228238Seric ** message back. 3239339Seric ** We goose error returns by clearing error bit. 3248238Seric */ 3258238Seric 32624943Seric SmtpPhase = "delivery"; 32755012Seric if (e->e_nrcpts != 1) 3289378Seric { 3299378Seric HoldErrs = TRUE; 33016886Seric ErrorMode = EM_MAIL; 3319378Seric } 33255012Seric e->e_flags &= ~EF_FATALERRS; 33355012Seric e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); 3344976Seric 3354976Seric /* send to all recipients */ 33655012Seric sendall(e, SM_DEFAULT); 33755012Seric e->e_to = NULL; 3384976Seric 33923516Seric /* save statistics */ 34055012Seric markstats(e, (ADDRESS *) NULL); 34123516Seric 3428238Seric /* issue success if appropriate and reset */ 3438238Seric if (Errors == 0 || HoldErrs) 3449283Seric message("250", "Ok"); 3458238Seric else 34655012Seric e->e_flags &= ~EF_FATALERRS; 3479339Seric 3489339Seric /* if in a child, pop back to our parent */ 3499339Seric if (InChild) 3509339Seric finis(); 35124943Seric 35224943Seric /* clean up a bit */ 35324943Seric hasmail = 0; 35455012Seric dropenvelope(e); 35555012Seric CurEnv = e = newenvelope(e); 35655012Seric e->e_flags = BlankEnvelope.e_flags; 3574549Seric break; 3584549Seric 3594549Seric case CMDRSET: /* rset -- reset state */ 3604549Seric message("250", "Reset state"); 3619339Seric if (InChild) 3629339Seric finis(); 3639339Seric break; 3644549Seric 3654549Seric case CMDVRFY: /* vrfy -- verify address */ 36655012Seric if (runinchild("SMTP-VRFY", e) > 0) 3679339Seric break; 36825050Seric setproctitle("%s: %s", CurHostName, inp); 36955173Seric #ifdef LOG 37055173Seric if (LogLevel >= 9) 37155173Seric syslog(LOG_INFO, "%s: %s", CurHostName, inp); 37255173Seric #endif 3735003Seric vrfyqueue = NULL; 3747762Seric QuickAbort = TRUE; 37555012Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue, e); 3767762Seric if (Errors != 0) 3779339Seric { 3789339Seric if (InChild) 3799339Seric finis(); 3807762Seric break; 3819339Seric } 3825003Seric while (vrfyqueue != NULL) 3835003Seric { 3845003Seric register ADDRESS *a = vrfyqueue->q_next; 3855003Seric char *code; 3865003Seric 3877685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3885003Seric a = a->q_next; 3895003Seric 3907685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3915003Seric { 3925003Seric if (a != NULL) 3935003Seric code = "250-"; 3945003Seric else 3955003Seric code = "250"; 3965003Seric if (vrfyqueue->q_fullname == NULL) 3975003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3985003Seric else 3995003Seric message(code, "%s <%s>", 4005003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 4015003Seric } 4025003Seric else if (a == NULL) 4035003Seric message("554", "Self destructive alias loop"); 4045003Seric vrfyqueue = a; 4055003Seric } 4069339Seric if (InChild) 4079339Seric finis(); 4084549Seric break; 4094549Seric 4104549Seric case CMDHELP: /* help -- give user info */ 4114577Seric help(p); 4124549Seric break; 4134549Seric 4144549Seric case CMDNOOP: /* noop -- do nothing */ 4154549Seric message("200", "OK"); 4164549Seric break; 4174549Seric 4184549Seric case CMDQUIT: /* quit -- leave mail */ 41925050Seric message("221", "%s closing connection", MyHostName); 4209339Seric if (InChild) 4219339Seric ExitStat = EX_QUIT; 4224549Seric finis(); 4234549Seric 4248544Seric case CMDVERB: /* set verbose mode */ 4258544Seric Verbose = TRUE; 42625025Seric SendMode = SM_DELIVER; 4278544Seric message("200", "Verbose mode"); 4288544Seric break; 4298544Seric 4309314Seric case CMDONEX: /* doing one transaction only */ 4319378Seric OneXact = TRUE; 4329314Seric message("200", "Only one transaction"); 4339314Seric break; 4349314Seric 43536230Skarels # ifdef SMTPDEBUG 4369339Seric case CMDDBGQSHOW: /* show queues */ 4376907Seric printf("Send Queue="); 43855012Seric printaddr(e->e_sendqueue, TRUE); 4395003Seric break; 4407275Seric 4417275Seric case CMDDBGDEBUG: /* set debug mode */ 4427676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 4437676Seric tTflag(p); 4447676Seric message("200", "Debug set"); 4457275Seric break; 4467275Seric 44736230Skarels # else /* not SMTPDEBUG */ 44824945Seric 44936230Skarels case CMDDBGQSHOW: /* show queues */ 45036230Skarels case CMDDBGDEBUG: /* set debug mode */ 45136233Skarels # ifdef LOG 45236233Skarels if (RealHostName != NULL && LogLevel > 0) 45336230Skarels syslog(LOG_NOTICE, 45436230Skarels "\"%s\" command from %s (%s)\n", 45536230Skarels c->cmdname, RealHostName, 45636230Skarels inet_ntoa(RealHostAddr.sin_addr)); 45736233Skarels # endif 45836230Skarels /* FALL THROUGH */ 45936230Skarels # endif /* SMTPDEBUG */ 46036230Skarels 4614549Seric case CMDERROR: /* unknown command */ 4624549Seric message("500", "Command unrecognized"); 4634549Seric break; 4644549Seric 4654549Seric default: 46636230Skarels errno = 0; 4674549Seric syserr("smtp: unknown code %d", c->cmdcode); 4684549Seric break; 4694549Seric } 4704549Seric } 4714549Seric } 4724549Seric /* 4734549Seric ** SKIPWORD -- skip a fixed word. 4744549Seric ** 4754549Seric ** Parameters: 4764549Seric ** p -- place to start looking. 4774549Seric ** w -- word to skip. 4784549Seric ** 4794549Seric ** Returns: 4804549Seric ** p following w. 4814549Seric ** NULL on error. 4824549Seric ** 4834549Seric ** Side Effects: 4844549Seric ** clobbers the p data area. 4854549Seric */ 4864549Seric 4874549Seric static char * 4884549Seric skipword(p, w) 4894549Seric register char *p; 4904549Seric char *w; 4914549Seric { 4924549Seric register char *q; 4934549Seric 4944549Seric /* find beginning of word */ 4954549Seric while (isspace(*p)) 4964549Seric p++; 4974549Seric q = p; 4984549Seric 4994549Seric /* find end of word */ 5004549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 5014549Seric p++; 5024549Seric while (isspace(*p)) 5034549Seric *p++ = '\0'; 5044549Seric if (*p != ':') 5054549Seric { 5064549Seric syntax: 5074549Seric message("501", "Syntax error"); 5084549Seric Errors++; 5094549Seric return (NULL); 5104549Seric } 5114549Seric *p++ = '\0'; 5124549Seric while (isspace(*p)) 5134549Seric p++; 5144549Seric 5154549Seric /* see if the input word matches desired word */ 51633725Sbostic if (strcasecmp(q, w)) 5174549Seric goto syntax; 5184549Seric 5194549Seric return (p); 5204549Seric } 5214577Seric /* 5224577Seric ** HELP -- implement the HELP command. 5234577Seric ** 5244577Seric ** Parameters: 5254577Seric ** topic -- the topic we want help for. 5264577Seric ** 5274577Seric ** Returns: 5284577Seric ** none. 5294577Seric ** 5304577Seric ** Side Effects: 5314577Seric ** outputs the help file to message output. 5324577Seric */ 5334577Seric 5344577Seric help(topic) 5354577Seric char *topic; 5364577Seric { 5374577Seric register FILE *hf; 5384577Seric int len; 5394577Seric char buf[MAXLINE]; 5404577Seric bool noinfo; 5414577Seric 5428269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5434577Seric { 5444577Seric /* no help */ 54511931Seric errno = 0; 5464577Seric message("502", "HELP not implemented"); 5474577Seric return; 5484577Seric } 5494577Seric 55049669Seric if (topic == NULL || *topic == '\0') 55149669Seric topic = "smtp"; 55249669Seric else 55349669Seric makelower(topic); 55449669Seric 5554577Seric len = strlen(topic); 5564577Seric noinfo = TRUE; 5574577Seric 5584577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5594577Seric { 5604577Seric if (strncmp(buf, topic, len) == 0) 5614577Seric { 5624577Seric register char *p; 5634577Seric 56456795Seric p = strchr(buf, '\t'); 5654577Seric if (p == NULL) 5664577Seric p = buf; 5674577Seric else 5684577Seric p++; 5694577Seric fixcrlf(p, TRUE); 5704577Seric message("214-", p); 5714577Seric noinfo = FALSE; 5724577Seric } 5734577Seric } 5744577Seric 5754577Seric if (noinfo) 5764577Seric message("504", "HELP topic unknown"); 5774577Seric else 5784577Seric message("214", "End of HELP info"); 5794628Seric (void) fclose(hf); 5804577Seric } 5818544Seric /* 5829339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5839339Seric ** 5849339Seric ** Parameters: 5859339Seric ** label -- a string used in error messages 5869339Seric ** 5879339Seric ** Returns: 5889339Seric ** zero in the child 5899339Seric ** one in the parent 5909339Seric ** 5919339Seric ** Side Effects: 5929339Seric ** none. 5939339Seric */ 5948544Seric 59555012Seric runinchild(label, e) 5969339Seric char *label; 59755012Seric register ENVELOPE *e; 5989339Seric { 5999339Seric int childpid; 6009339Seric 60116158Seric if (!OneXact) 6029339Seric { 60316158Seric childpid = dofork(); 60416158Seric if (childpid < 0) 60516158Seric { 60616158Seric syserr("%s: cannot fork", label); 60716158Seric return (1); 60816158Seric } 60916158Seric if (childpid > 0) 61016158Seric { 61116158Seric auto int st; 6129339Seric 61316158Seric /* parent -- wait for child to complete */ 61416158Seric st = waitfor(childpid); 61516158Seric if (st == -1) 61616158Seric syserr("%s: lost child", label); 6179339Seric 61816158Seric /* if we exited on a QUIT command, complete the process */ 61916158Seric if (st == (EX_QUIT << 8)) 62016158Seric finis(); 6219339Seric 62216158Seric return (1); 62316158Seric } 62416158Seric else 62516158Seric { 62616158Seric /* child */ 62716158Seric InChild = TRUE; 62825050Seric QuickAbort = FALSE; 62955012Seric clearenvelope(e, FALSE); 63016158Seric } 6319339Seric } 63215256Seric 63316158Seric /* open alias database */ 63455012Seric initaliases(AliasFile, FALSE, e); 63516158Seric 63616158Seric return (0); 6379339Seric } 6389339Seric 63956795Seric # endif /* SMTP */ 640