122712Sdist /* 222712Sdist ** Sendmail 322712Sdist ** Copyright (c) 1983 Eric P. Allman 422712Sdist ** Berkeley, California 522712Sdist ** 622712Sdist ** Copyright (c) 1983 Regents of the University of California. 722712Sdist ** All rights reserved. The Berkeley software License Agreement 822712Sdist ** specifies the terms and conditions for redistribution. 922712Sdist */ 1022712Sdist 1122712Sdist 129339Seric # include <errno.h> 134549Seric # include "sendmail.h" 1411728Seric # include <signal.h> 154549Seric 165181Seric # ifndef SMTP 1723122Seric # ifndef lint 18*24943Seric static char SccsId[] = "@(#)srvrsmtp.c 5.5.1.1 (Berkeley) 09/19/85 (no SMTP)"; 1923122Seric # endif not lint 205181Seric # else SMTP 214556Seric 2223122Seric # ifndef lint 23*24943Seric static char SccsId[] = "@(#)srvrsmtp.c 5.5.1.1 (Berkeley) 09/19/85"; 2423122Seric # endif not lint 255181Seric 264549Seric /* 274549Seric ** SMTP -- run the SMTP protocol. 284549Seric ** 294549Seric ** Parameters: 304549Seric ** none. 314549Seric ** 324549Seric ** Returns: 334549Seric ** never. 344549Seric ** 354549Seric ** Side Effects: 364549Seric ** Reads commands from the input channel and processes 374549Seric ** them. 384549Seric */ 394549Seric 404549Seric struct cmd 414549Seric { 424549Seric char *cmdname; /* command name */ 434549Seric int cmdcode; /* internal code, see below */ 444549Seric }; 454549Seric 464549Seric /* values for cmdcode */ 474549Seric # define CMDERROR 0 /* bad command */ 484549Seric # define CMDMAIL 1 /* mail -- designate sender */ 494976Seric # define CMDRCPT 2 /* rcpt -- designate recipient */ 504549Seric # define CMDDATA 3 /* data -- send message text */ 519339Seric # define CMDRSET 4 /* rset -- reset state */ 529339Seric # define CMDVRFY 5 /* vrfy -- verify address */ 539339Seric # define CMDHELP 6 /* help -- give usage info */ 549339Seric # define CMDNOOP 7 /* noop -- do nothing */ 559339Seric # define CMDQUIT 8 /* quit -- close connection and die */ 569339Seric # define CMDHELO 9 /* helo -- be polite */ 579339Seric # define CMDDBGQSHOW 10 /* showq -- show send queue (DEBUG) */ 589339Seric # define CMDDBGDEBUG 11 /* debug -- set debug mode */ 599339Seric # define CMDVERB 12 /* verb -- go into verbose mode */ 609339Seric # define CMDDBGKILL 13 /* kill -- kill sendmail */ 619339Seric # define CMDDBGWIZ 14 /* wiz -- become a wizard */ 629339Seric # define CMDONEX 15 /* onex -- sending one transaction only */ 634549Seric 644549Seric static struct cmd CmdTab[] = 654549Seric { 664549Seric "mail", CMDMAIL, 674976Seric "rcpt", CMDRCPT, 684549Seric "data", CMDDATA, 694549Seric "rset", CMDRSET, 704549Seric "vrfy", CMDVRFY, 717762Seric "expn", CMDVRFY, 724549Seric "help", CMDHELP, 734549Seric "noop", CMDNOOP, 744549Seric "quit", CMDQUIT, 754976Seric "helo", CMDHELO, 768544Seric "verb", CMDVERB, 779314Seric "onex", CMDONEX, 785003Seric # ifdef DEBUG 799339Seric "showq", CMDDBGQSHOW, 808544Seric "debug", CMDDBGDEBUG, 818544Seric "kill", CMDDBGKILL, 828544Seric "wiz", CMDDBGWIZ, 83*24943Seric # endif DEBUG 844549Seric NULL, CMDERROR, 854549Seric }; 864549Seric 878544Seric bool IsWiz = FALSE; /* set if we are a wizard */ 8815596Seric char *WizWord; /* the wizard word to compare against */ 899339Seric bool InChild = FALSE; /* true if running in a subprocess */ 909378Seric bool OneXact = FALSE; /* one xaction only this run */ 9111146Seric 929339Seric #define EX_QUIT 22 /* special code for QUIT command */ 938544Seric 944549Seric smtp() 954549Seric { 964549Seric register char *p; 978544Seric register struct cmd *c; 984549Seric char *cmd; 994549Seric extern char *skipword(); 1004549Seric extern bool sameword(); 1014549Seric bool hasmail; /* mail command received */ 1024713Seric int rcps; /* number of recipients */ 1035003Seric auto ADDRESS *vrfyqueue; 10412612Seric ADDRESS *a; 1058544Seric char inp[MAXLINE]; 1067124Seric extern char Version[]; 1077356Seric extern tick(); 1088544Seric extern bool iswiz(); 1099349Seric extern char *arpadate(); 11011151Seric extern char *macvalue(); 11112612Seric extern ADDRESS *recipient(); 112*24943Seric extern ENVELOPE BlankEnvelope; 113*24943Seric extern ENVELOPE *newenvelope(); 1144549Seric 1155003Seric hasmail = FALSE; 1164713Seric rcps = 0; 1177363Seric if (OutChannel != stdout) 1187363Seric { 1197363Seric /* arrange for debugging output to go to remote host */ 1207363Seric (void) close(1); 1217363Seric (void) dup(fileno(OutChannel)); 1227363Seric } 12311931Seric settime(); 12416153Seric expand("\001e", inp, &inp[sizeof inp], CurEnv); 12510708Seric message("220", inp); 126*24943Seric SmtpPhase = "startup"; 1274549Seric for (;;) 1284549Seric { 12912612Seric /* arrange for backout */ 13012612Seric if (setjmp(TopFrame) > 0 && InChild) 13112612Seric finis(); 13212612Seric QuickAbort = FALSE; 13312612Seric HoldErrs = FALSE; 13412612Seric 1357356Seric /* setup for the read */ 1366907Seric CurEnv->e_to = NULL; 1374577Seric Errors = 0; 1387275Seric (void) fflush(stdout); 1397356Seric 1407356Seric /* read the input line */ 1417685Seric p = sfgets(inp, sizeof inp, InChannel); 1427356Seric 1437685Seric /* handle errors */ 1447356Seric if (p == NULL) 1457356Seric { 1464549Seric /* end of file, just die */ 1474558Seric message("421", "%s Lost input channel", HostName); 1484549Seric finis(); 1494549Seric } 1504549Seric 1514549Seric /* clean up end of line */ 1524558Seric fixcrlf(inp, TRUE); 1534549Seric 1544713Seric /* echo command to transcript */ 1559545Seric if (CurEnv->e_xfp != NULL) 1569545Seric fprintf(CurEnv->e_xfp, "<<< %s\n", inp); 1574713Seric 1584549Seric /* break off command */ 1594549Seric for (p = inp; isspace(*p); p++) 1604549Seric continue; 1614549Seric cmd = p; 1624549Seric while (*++p != '\0' && !isspace(*p)) 1634549Seric continue; 1644549Seric if (*p != '\0') 1654549Seric *p++ = '\0'; 1664549Seric 1674549Seric /* decode command */ 1684549Seric for (c = CmdTab; c->cmdname != NULL; c++) 1694549Seric { 1704549Seric if (sameword(c->cmdname, cmd)) 1714549Seric break; 1724549Seric } 1734549Seric 1744549Seric /* process command */ 1754549Seric switch (c->cmdcode) 1764549Seric { 1774976Seric case CMDHELO: /* hello -- introduce yourself */ 178*24943Seric SmtpPhase = "HELO"; 17914877Seric if (sameword(p, HostName)) 18014877Seric { 18114877Seric /* connected to an echo server */ 18214877Seric message("553", "%s I refuse to talk to myself", 18314877Seric HostName); 18414877Seric break; 18514877Seric } 18611146Seric if (RealHostName != NULL && !sameword(p, RealHostName)) 18711146Seric { 18811146Seric char buf[MAXNAME]; 18911146Seric 19011146Seric (void) sprintf(buf, "%s (%s)", p, RealHostName); 19111146Seric define('s', newstr(buf), CurEnv); 19211146Seric } 19311146Seric else 19411146Seric define('s', newstr(p), CurEnv); 1954997Seric message("250", "%s Hello %s, pleased to meet you", 1964997Seric HostName, p); 1974976Seric break; 1984976Seric 1994549Seric case CMDMAIL: /* mail -- designate sender */ 200*24943Seric SmtpPhase = "MAIL"; 201*24943Seric 20211151Seric /* force a sending host even if no HELO given */ 20311151Seric if (RealHostName != NULL && macvalue('s', CurEnv) == NULL) 20411151Seric define('s', RealHostName, CurEnv); 20511151Seric 2069314Seric /* check for validity of this command */ 2074558Seric if (hasmail) 2084558Seric { 2094558Seric message("503", "Sender already specified"); 2104558Seric break; 2114558Seric } 2129339Seric if (InChild) 2139339Seric { 2149339Seric syserr("Nested MAIL command"); 2159339Seric exit(0); 2169339Seric } 2179339Seric 2189339Seric /* fork a subprocess to process this command */ 2199339Seric if (runinchild("SMTP-MAIL") > 0) 2209339Seric break; 2219339Seric initsys(); 2229339Seric 2239339Seric /* child -- go do the processing */ 2244549Seric p = skipword(p, "from"); 2254549Seric if (p == NULL) 2264549Seric break; 2274549Seric setsender(p); 2284577Seric if (Errors == 0) 2294549Seric { 2304549Seric message("250", "Sender ok"); 2314549Seric hasmail = TRUE; 2324549Seric } 2339339Seric else if (InChild) 2349339Seric finis(); 2354549Seric break; 2364549Seric 2374976Seric case CMDRCPT: /* rcpt -- designate recipient */ 238*24943Seric SmtpPhase = "RCPT"; 23912612Seric if (setjmp(TopFrame) > 0) 24014785Seric { 24114785Seric CurEnv->e_flags &= ~EF_FATALERRS; 24212612Seric break; 24314785Seric } 24412612Seric QuickAbort = TRUE; 2454549Seric p = skipword(p, "to"); 2464549Seric if (p == NULL) 2474549Seric break; 24816140Seric a = parseaddr(p, (ADDRESS *) NULL, 1, '\0'); 24912612Seric if (a == NULL) 25012612Seric break; 25116886Seric a->q_flags |= QPRIMARY; 25212612Seric a = recipient(a, &CurEnv->e_sendqueue); 25312612Seric if (Errors != 0) 25412612Seric break; 25512612Seric 25612612Seric /* no errors during parsing, but might be a duplicate */ 25712612Seric CurEnv->e_to = p; 25812612Seric if (!bitset(QBADADDR, a->q_flags)) 25912612Seric message("250", "Recipient ok"); 26012612Seric else 2614549Seric { 26212612Seric /* punt -- should keep message in ADDRESS.... */ 26312612Seric message("550", "Addressee unknown"); 2644549Seric } 26512612Seric CurEnv->e_to = NULL; 26612612Seric rcps++; 2674549Seric break; 2684549Seric 2694549Seric case CMDDATA: /* data -- text of mail */ 270*24943Seric SmtpPhase = "DATA"; 2714976Seric if (!hasmail) 2724549Seric { 2734976Seric message("503", "Need MAIL command"); 2744976Seric break; 2754549Seric } 276*24943Seric else if (CurEnv->e_nrcpts <= 0) 2774549Seric { 2784976Seric message("503", "Need RCPT (recipient)"); 2794976Seric break; 2804549Seric } 2814976Seric 2824976Seric /* collect the text of the message */ 283*24943Seric SmtpPhase = "collect"; 2844976Seric collect(TRUE); 2854976Seric if (Errors != 0) 2864976Seric break; 2874976Seric 2888238Seric /* 2898238Seric ** Arrange to send to everyone. 2908238Seric ** If sending to multiple people, mail back 2918238Seric ** errors rather than reporting directly. 2928238Seric ** In any case, don't mail back errors for 2938238Seric ** anything that has happened up to 2948238Seric ** now (the other end will do this). 29510197Seric ** Truncate our transcript -- the mail has gotten 29610197Seric ** to us successfully, and if we have 29710197Seric ** to mail this back, it will be easier 29810197Seric ** on the reader. 2998238Seric ** Then send to everyone. 3008238Seric ** Finally give a reply code. If an error has 3018238Seric ** already been given, don't mail a 3028238Seric ** message back. 3039339Seric ** We goose error returns by clearing error bit. 3048238Seric */ 3058238Seric 306*24943Seric SmtpPhase = "delivery"; 307*24943Seric if (CurEnv->e_nrcpts != 1) 3089378Seric { 3099378Seric HoldErrs = TRUE; 31016886Seric ErrorMode = EM_MAIL; 3119378Seric } 3129339Seric CurEnv->e_flags &= ~EF_FATALERRS; 31310197Seric CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp); 3144976Seric 3154976Seric /* send to all recipients */ 31614877Seric sendall(CurEnv, SM_DEFAULT); 3176907Seric CurEnv->e_to = NULL; 3184976Seric 31923516Seric /* save statistics */ 32023516Seric markstats(CurEnv, (ADDRESS *) NULL); 32123516Seric 3228238Seric /* issue success if appropriate and reset */ 3238238Seric if (Errors == 0 || HoldErrs) 3249283Seric message("250", "Ok"); 3258238Seric else 3269339Seric CurEnv->e_flags &= ~EF_FATALERRS; 3279339Seric 3289339Seric /* if in a child, pop back to our parent */ 3299339Seric if (InChild) 3309339Seric finis(); 331*24943Seric 332*24943Seric /* clean up a bit */ 333*24943Seric hasmail = 0; 334*24943Seric rcps = 0; 335*24943Seric dropenvelope(CurEnv); 336*24943Seric CurEnv = newenvelope(CurEnv); 337*24943Seric CurEnv->e_flags = BlankEnvelope.e_flags; 3384549Seric break; 3394549Seric 3404549Seric case CMDRSET: /* rset -- reset state */ 3414549Seric message("250", "Reset state"); 3429339Seric if (InChild) 3439339Seric finis(); 3449339Seric break; 3454549Seric 3464549Seric case CMDVRFY: /* vrfy -- verify address */ 3479339Seric if (runinchild("SMTP-VRFY") > 0) 3489339Seric break; 3495003Seric vrfyqueue = NULL; 3507762Seric QuickAbort = TRUE; 3519619Seric sendtolist(p, (ADDRESS *) NULL, &vrfyqueue); 3527762Seric if (Errors != 0) 3539339Seric { 3549339Seric if (InChild) 3559339Seric finis(); 3567762Seric break; 3579339Seric } 3585003Seric while (vrfyqueue != NULL) 3595003Seric { 3605003Seric register ADDRESS *a = vrfyqueue->q_next; 3615003Seric char *code; 3625003Seric 3637685Seric while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) 3645003Seric a = a->q_next; 3655003Seric 3667685Seric if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) 3675003Seric { 3685003Seric if (a != NULL) 3695003Seric code = "250-"; 3705003Seric else 3715003Seric code = "250"; 3725003Seric if (vrfyqueue->q_fullname == NULL) 3735003Seric message(code, "<%s>", vrfyqueue->q_paddr); 3745003Seric else 3755003Seric message(code, "%s <%s>", 3765003Seric vrfyqueue->q_fullname, vrfyqueue->q_paddr); 3775003Seric } 3785003Seric else if (a == NULL) 3795003Seric message("554", "Self destructive alias loop"); 3805003Seric vrfyqueue = a; 3815003Seric } 3829339Seric if (InChild) 3839339Seric finis(); 3844549Seric break; 3854549Seric 3864549Seric case CMDHELP: /* help -- give user info */ 3874577Seric if (*p == '\0') 3884577Seric p = "SMTP"; 3894577Seric help(p); 3904549Seric break; 3914549Seric 3924549Seric case CMDNOOP: /* noop -- do nothing */ 3934549Seric message("200", "OK"); 3944549Seric break; 3954549Seric 3964549Seric case CMDQUIT: /* quit -- leave mail */ 3974549Seric message("221", "%s closing connection", HostName); 3989339Seric if (InChild) 3999339Seric ExitStat = EX_QUIT; 4004549Seric finis(); 4014549Seric 4028544Seric case CMDVERB: /* set verbose mode */ 4038544Seric Verbose = TRUE; 4048544Seric message("200", "Verbose mode"); 4058544Seric break; 4068544Seric 4079314Seric case CMDONEX: /* doing one transaction only */ 4089378Seric OneXact = TRUE; 4099314Seric message("200", "Only one transaction"); 4109314Seric break; 4119314Seric 4125003Seric # ifdef DEBUG 4139339Seric case CMDDBGQSHOW: /* show queues */ 4146907Seric printf("Send Queue="); 4156907Seric printaddr(CurEnv->e_sendqueue, TRUE); 4165003Seric break; 4177275Seric 4187275Seric case CMDDBGDEBUG: /* set debug mode */ 4197676Seric tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 4207676Seric tTflag(p); 4217676Seric message("200", "Debug set"); 4227275Seric break; 4237275Seric 4247282Seric case CMDDBGKILL: /* kill the parent */ 4258544Seric if (!iswiz()) 4268544Seric break; 4277282Seric if (kill(MotherPid, SIGTERM) >= 0) 4287282Seric message("200", "Mother is dead"); 4297282Seric else 4307282Seric message("500", "Can't kill Mom"); 4317282Seric break; 4328544Seric 4338544Seric case CMDDBGWIZ: /* become a wizard */ 4348544Seric if (WizWord != NULL) 4358544Seric { 4368544Seric char seed[3]; 4378544Seric extern char *crypt(); 4388544Seric 43923106Seric (void) strncpy(seed, WizWord, 2); 44015596Seric if (strcmp(WizWord, crypt(p, seed)) == 0) 4418544Seric { 44215596Seric IsWiz = TRUE; 44315596Seric message("200", "Please pass, oh mighty wizard"); 4448544Seric break; 4458544Seric } 4468544Seric } 44715596Seric message("500", "You are no wizard!"); 4488544Seric break; 449*24943Seric # endif DEBUG 4505003Seric 4514549Seric case CMDERROR: /* unknown command */ 4524549Seric message("500", "Command unrecognized"); 4534549Seric break; 4544549Seric 4554549Seric default: 4564549Seric syserr("smtp: unknown code %d", c->cmdcode); 4574549Seric break; 4584549Seric } 4594549Seric } 4604549Seric } 4614549Seric /* 4624549Seric ** SKIPWORD -- skip a fixed word. 4634549Seric ** 4644549Seric ** Parameters: 4654549Seric ** p -- place to start looking. 4664549Seric ** w -- word to skip. 4674549Seric ** 4684549Seric ** Returns: 4694549Seric ** p following w. 4704549Seric ** NULL on error. 4714549Seric ** 4724549Seric ** Side Effects: 4734549Seric ** clobbers the p data area. 4744549Seric */ 4754549Seric 4764549Seric static char * 4774549Seric skipword(p, w) 4784549Seric register char *p; 4794549Seric char *w; 4804549Seric { 4814549Seric register char *q; 4824549Seric extern bool sameword(); 4834549Seric 4844549Seric /* find beginning of word */ 4854549Seric while (isspace(*p)) 4864549Seric p++; 4874549Seric q = p; 4884549Seric 4894549Seric /* find end of word */ 4904549Seric while (*p != '\0' && *p != ':' && !isspace(*p)) 4914549Seric p++; 4924549Seric while (isspace(*p)) 4934549Seric *p++ = '\0'; 4944549Seric if (*p != ':') 4954549Seric { 4964549Seric syntax: 4974549Seric message("501", "Syntax error"); 4984549Seric Errors++; 4994549Seric return (NULL); 5004549Seric } 5014549Seric *p++ = '\0'; 5024549Seric while (isspace(*p)) 5034549Seric p++; 5044549Seric 5054549Seric /* see if the input word matches desired word */ 5064549Seric if (!sameword(q, w)) 5074549Seric goto syntax; 5084549Seric 5094549Seric return (p); 5104549Seric } 5114577Seric /* 5124577Seric ** HELP -- implement the HELP command. 5134577Seric ** 5144577Seric ** Parameters: 5154577Seric ** topic -- the topic we want help for. 5164577Seric ** 5174577Seric ** Returns: 5184577Seric ** none. 5194577Seric ** 5204577Seric ** Side Effects: 5214577Seric ** outputs the help file to message output. 5224577Seric */ 5234577Seric 5244577Seric help(topic) 5254577Seric char *topic; 5264577Seric { 5274577Seric register FILE *hf; 5284577Seric int len; 5294577Seric char buf[MAXLINE]; 5304577Seric bool noinfo; 5314577Seric 5328269Seric if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) 5334577Seric { 5344577Seric /* no help */ 53511931Seric errno = 0; 5364577Seric message("502", "HELP not implemented"); 5374577Seric return; 5384577Seric } 5394577Seric 5404577Seric len = strlen(topic); 5414577Seric makelower(topic); 5424577Seric noinfo = TRUE; 5434577Seric 5444577Seric while (fgets(buf, sizeof buf, hf) != NULL) 5454577Seric { 5464577Seric if (strncmp(buf, topic, len) == 0) 5474577Seric { 5484577Seric register char *p; 5494577Seric 5504577Seric p = index(buf, '\t'); 5514577Seric if (p == NULL) 5524577Seric p = buf; 5534577Seric else 5544577Seric p++; 5554577Seric fixcrlf(p, TRUE); 5564577Seric message("214-", p); 5574577Seric noinfo = FALSE; 5584577Seric } 5594577Seric } 5604577Seric 5614577Seric if (noinfo) 5624577Seric message("504", "HELP topic unknown"); 5634577Seric else 5644577Seric message("214", "End of HELP info"); 5654628Seric (void) fclose(hf); 5664577Seric } 5678544Seric /* 5688544Seric ** ISWIZ -- tell us if we are a wizard 5698544Seric ** 5708544Seric ** If not, print a nasty message. 5718544Seric ** 5728544Seric ** Parameters: 5738544Seric ** none. 5748544Seric ** 5758544Seric ** Returns: 5768544Seric ** TRUE if we are a wizard. 5778544Seric ** FALSE if we are not a wizard. 5788544Seric ** 5798544Seric ** Side Effects: 5808544Seric ** Prints a 500 exit stat if we are not a wizard. 5818544Seric */ 5825181Seric 583*24943Seric #ifdef DEBUG 58419038Seric 5858544Seric bool 5868544Seric iswiz() 5878544Seric { 5888544Seric if (!IsWiz) 5898544Seric message("500", "Mere mortals musn't mutter that mantra"); 5908544Seric return (IsWiz); 5918544Seric } 59219038Seric 593*24943Seric #endif DEBUG 5949339Seric /* 5959339Seric ** RUNINCHILD -- return twice -- once in the child, then in the parent again 5969339Seric ** 5979339Seric ** Parameters: 5989339Seric ** label -- a string used in error messages 5999339Seric ** 6009339Seric ** Returns: 6019339Seric ** zero in the child 6029339Seric ** one in the parent 6039339Seric ** 6049339Seric ** Side Effects: 6059339Seric ** none. 6069339Seric */ 6078544Seric 6089339Seric runinchild(label) 6099339Seric char *label; 6109339Seric { 6119339Seric int childpid; 6129339Seric 61316158Seric if (!OneXact) 6149339Seric { 61516158Seric childpid = dofork(); 61616158Seric if (childpid < 0) 61716158Seric { 61816158Seric syserr("%s: cannot fork", label); 61916158Seric return (1); 62016158Seric } 62116158Seric if (childpid > 0) 62216158Seric { 62316158Seric auto int st; 6249339Seric 62516158Seric /* parent -- wait for child to complete */ 62616158Seric st = waitfor(childpid); 62716158Seric if (st == -1) 62816158Seric syserr("%s: lost child", label); 6299339Seric 63016158Seric /* if we exited on a QUIT command, complete the process */ 63116158Seric if (st == (EX_QUIT << 8)) 63216158Seric finis(); 6339339Seric 63416158Seric return (1); 63516158Seric } 63616158Seric else 63716158Seric { 63816158Seric /* child */ 63916158Seric InChild = TRUE; 64016158Seric } 6419339Seric } 64215256Seric 643*24943Seric /* child (or ONEX command specified) */ 644*24943Seric clearenvelope(CurEnv); 645*24943Seric 64616158Seric /* open alias database */ 64716158Seric initaliases(AliasFile, FALSE); 64816158Seric 64916158Seric return (0); 6509339Seric } 6519339Seric 6525181Seric # endif SMTP 653