Back to index

citadel  8.12
serv_smtpeventclient.c
Go to the documentation of this file.
00001 /*
00002  * This module is an SMTP and ESMTP implementation for the Citadel system.
00003  * It is compliant with all of the following:
00004  *
00005  * RFC  821 - Simple Mail Transfer Protocol
00006  * RFC  876 - Survey of SMTP Implementations
00007  * RFC 1047 - Duplicate messages and SMTP
00008  * RFC 1652 - 8 bit MIME
00009  * RFC 1869 - Extended Simple Mail Transfer Protocol
00010  * RFC 1870 - SMTP Service Extension for Message Size Declaration
00011  * RFC 2033 - Local Mail Transfer Protocol
00012  * RFC 2197 - SMTP Service Extension for Command Pipelining
00013  * RFC 2476 - Message Submission
00014  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
00015  * RFC 2554 - SMTP Service Extension for Authentication
00016  * RFC 2821 - Simple Mail Transfer Protocol
00017  * RFC 2822 - Internet Message Format
00018  * RFC 2920 - SMTP Service Extension for Command Pipelining
00019  *
00020  * The VRFY and EXPN commands have been removed from this implementation
00021  * because nobody uses these commands anymore, except for spammers.
00022  *
00023  * Copyright (c) 1998-2012 by the citadel.org team
00024  *
00025  *  This program is open source software; you can redistribute it and/or modify
00026  *  it under the terms of the GNU General Public License version 3.
00027  *  
00028  *  
00029  *
00030  *  This program is distributed in the hope that it will be useful,
00031  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00032  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00033  *  GNU General Public License for more details.
00034  *
00035  *  
00036  *  
00037  *  
00038  */
00039 
00040 #include "sysdep.h"
00041 #include <stdlib.h>
00042 #include <unistd.h>
00043 #include <stdio.h>
00044 #include <termios.h>
00045 #include <fcntl.h>
00046 #include <signal.h>
00047 #include <pwd.h>
00048 #include <errno.h>
00049 #include <sys/types.h>
00050 #include <syslog.h>
00051 
00052 #if TIME_WITH_SYS_TIME
00053 # include <sys/time.h>
00054 # include <time.h>
00055 #else
00056 # if HAVE_SYS_TIME_H
00057 #  include <sys/time.h>
00058 # else
00059 #  include <time.h>
00060 # endif
00061 #endif
00062 #include <sys/wait.h>
00063 #include <ctype.h>
00064 #include <string.h>
00065 #include <limits.h>
00066 #include <sys/socket.h>
00067 #include <netinet/in.h>
00068 #include <arpa/inet.h>
00069 #include <libcitadel.h>
00070 #include "citadel.h"
00071 #include "server.h"
00072 #include "citserver.h"
00073 #include "support.h"
00074 #include "config.h"
00075 #include "control.h"
00076 #include "user_ops.h"
00077 #include "database.h"
00078 #include "msgbase.h"
00079 #include "internet_addressing.h"
00080 #include "genstamp.h"
00081 #include "domain.h"
00082 #include "clientsocket.h"
00083 #include "locate_host.h"
00084 #include "citadel_dirs.h"
00085 
00086 #include "ctdl_module.h"
00087 
00088 #include "smtp_util.h"
00089 #include "event_client.h"
00090 #include "smtpqueue.h"
00091 #include "smtp_clienthandlers.h"
00092 
00093 int SMTPClientDebugEnabled = 0;
00094 void DeleteSmtpOutMsg(void *v)
00095 {
00096        SmtpOutMsg *Msg = v;
00097        AsyncIO *IO = &Msg->IO;
00098        EV_syslog(LOG_DEBUG, "%s Exit\n", __FUNCTION__);
00099 
00100        /* these are kept in our own space and free'd below */
00101        Msg->IO.ConnectMe = NULL;
00102 
00103        ares_free_data(Msg->AllMX);
00104        if (Msg->HostLookup.VParsedDNSReply != NULL)
00105               Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
00106        FreeURL(&Msg->Relay);
00107        FreeStrBuf(&Msg->msgtext);
00108        FreeAsyncIOContents(&Msg->IO);
00109        memset (Msg, 0, sizeof(SmtpOutMsg)); /* just to be shure... */
00110        free(Msg);
00111 }
00112 
00113 eNextState SMTP_C_Shutdown(AsyncIO *IO);
00114 eNextState SMTP_C_Timeout(AsyncIO *IO);
00115 eNextState SMTP_C_ConnFail(AsyncIO *IO);
00116 eNextState SMTP_C_DispatchReadDone(AsyncIO *IO);
00117 eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO);
00118 eNextState SMTP_C_DNSFail(AsyncIO *IO);
00119 eNextState SMTP_C_Terminate(AsyncIO *IO);
00120 eNextState SMTP_C_TerminateDB(AsyncIO *IO);
00121 eReadState SMTP_C_ReadServerStatus(AsyncIO *IO);
00122 
00123 eNextState mx_connect_ip(AsyncIO *IO);
00124 eNextState get_one_mx_host_ip(AsyncIO *IO);
00125 
00126 /******************************************************************************
00127  * So, we're finished with sending (regardless of success or failure)         *
00128  * This Message might be referenced by several Queue-Items, if we're the last,*
00129  * we need to free the memory and send bounce messages (on terminal failure)  *
00130  * else we just free our SMTP-Message struct.                                 *
00131  ******************************************************************************/
00132 eNextState FinalizeMessageSend_DB(AsyncIO *IO)
00133 {
00134        const char *Status;
00135        SmtpOutMsg *Msg = IO->Data;
00136        
00137        if (Msg->MyQEntry->Status == 2) 
00138               Status = "Delivery successful.";
00139        else if (Msg->MyQEntry->Status == 5) 
00140               Status = "Delivery failed permanently; giving up.";
00141        else
00142               Status = "Delivery failed temporarily; will retry later.";
00143                      
00144        EVS_syslog(LOG_INFO,
00145                  "%s Time[%fs] Recipient <%s> @ <%s> (%s) Status message: %s\n",
00146                  Status,
00147                  Msg->IO.Now - Msg->IO.StartIO,
00148                  Msg->user,
00149                  Msg->node,
00150                  Msg->name,
00151                  ChrPtr(Msg->MyQEntry->StatusMessage));
00152 
00153 
00154        Msg->IDestructQueItem = DecreaseQReference(Msg->MyQItem);
00155 
00156        Msg->nRemain = CountActiveQueueEntries(Msg->MyQItem);
00157 
00158        if (Msg->MyQEntry->Active && 
00159            CheckQEntryIsBounce(Msg->MyQEntry))
00160        {
00161               /* are we casue for a bounce mail? */
00162               Msg->MyQItem->SendBounceMail |= (1<<Msg->MyQEntry->Status);
00163        }
00164 
00165        if ((Msg->nRemain > 0) || Msg->IDestructQueItem)
00166               Msg->QMsgData = SerializeQueueItem(Msg->MyQItem);
00167        else
00168               Msg->QMsgData = NULL;
00169 
00170        /*
00171         * Uncompleted delivery instructions remain, so delete the old
00172         * instructions and replace with the updated ones.
00173         */
00174        EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
00175        CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, "");
00176        Msg->MyQItem->QueMsgID = -1;
00177 
00178        if (Msg->IDestructQueItem)
00179               smtpq_do_bounce(Msg->MyQItem, Msg->msgtext);
00180 
00181        if (Msg->nRemain > 0)
00182        {
00183               struct CtdlMessage *msg;
00184               msg = malloc(sizeof(struct CtdlMessage));
00185               memset(msg, 0, sizeof(struct CtdlMessage));
00186               msg->cm_magic = CTDLMESSAGE_MAGIC;
00187               msg->cm_anon_type = MES_NORMAL;
00188               msg->cm_format_type = FMT_RFC822;
00189               msg->cm_fields['M'] = SmashStrBuf(&Msg->QMsgData);
00190               msg->cm_fields['U'] = strdup("QMSG");
00191               Msg->MyQItem->QueMsgID =
00192                      CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
00193               EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
00194               CtdlFreeMessage(msg);
00195        }
00196        else {
00197               CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM,
00198                                &Msg->MyQItem->MessageID,
00199                                1,
00200                                "");
00201               FreeStrBuf(&Msg->QMsgData);
00202        }
00203 
00204        RemoveContext(Msg->IO.CitContext);
00205        return eAbort;
00206 }
00207 
00208 eNextState Terminate(AsyncIO *IO)
00209 {
00210        SmtpOutMsg *Msg = IO->Data;
00211 
00212        if (Msg->IDestructQueItem)
00213               RemoveQItem(Msg->MyQItem);
00214 
00215        DeleteSmtpOutMsg(Msg);
00216        return eAbort;
00217 }
00218 eNextState FinalizeMessageSend(SmtpOutMsg *Msg)
00219 {
00220        /* hand over to DB Queue */
00221        return QueueDBOperation(&Msg->IO, FinalizeMessageSend_DB);
00222 }
00223 
00224 eNextState FailOneAttempt(AsyncIO *IO)
00225 {
00226        SmtpOutMsg *Msg = IO->Data;
00227 
00228        if (Msg->MyQEntry->Status == 2)
00229               return eAbort;
00230 
00231        /*
00232         * possible ways here:
00233         * - connection timeout
00234         * - dns lookup failed
00235         */
00236        StopClientWatchers(IO, 1);
00237 
00238        if (Msg->pCurrRelay != NULL)
00239               Msg->pCurrRelay = Msg->pCurrRelay->Next;
00240 
00241        if (Msg->pCurrRelay == NULL) {
00242               EVS_syslog(LOG_DEBUG, "%s Aborting\n", __FUNCTION__);
00243               return eAbort;
00244        }
00245        if (Msg->pCurrRelay->IsIP) {
00246               EVS_syslog(LOG_DEBUG, "%s connecting IP\n", __FUNCTION__);
00247               return mx_connect_ip(IO);
00248        }
00249        else {
00250               EVS_syslog(LOG_DEBUG,
00251                         "%s resolving next MX Record\n",
00252                         __FUNCTION__);
00253               return get_one_mx_host_ip(IO);
00254        }
00255 }
00256 
00257 
00258 void SetConnectStatus(AsyncIO *IO)
00259 {
00260        SmtpOutMsg *Msg = IO->Data;
00261        char buf[256];
00262        void *src;
00263 
00264        buf[0] = '\0';
00265 
00266        if (IO->ConnectMe->IPv6) {
00267               src = &IO->ConnectMe->Addr.sin6_addr;
00268        }
00269        else {
00270               struct sockaddr_in *addr;
00271 
00272               addr = (struct sockaddr_in *)&IO->ConnectMe->Addr;
00273               src = &addr->sin_addr.s_addr;
00274        }
00275 
00276        inet_ntop((IO->ConnectMe->IPv6)?AF_INET6:AF_INET,
00277                 src,
00278                 buf,
00279                 sizeof(buf));
00280 
00281        if (Msg->mx_host == NULL)
00282               Msg->mx_host = "<no MX-Record>";
00283 
00284        EVS_syslog(LOG_INFO,
00285                 "connecting to %s [%s]:%d ...\n",
00286                 Msg->mx_host,
00287                 buf,
00288                 Msg->IO.ConnectMe->Port);
00289 
00290        Msg->MyQEntry->Status = 4;
00291        StrBufPrintf(Msg->MyQEntry->StatusMessage,
00292                    "Timeout while connecting %s [%s]:%d ",
00293                    Msg->mx_host,
00294                    buf,
00295                    Msg->IO.ConnectMe->Port);
00296        Msg->IO.NextState = eConnect;
00297 }
00298 
00299 /*****************************************************************************
00300  * So we connect our Relay IP here.                                          *
00301  *****************************************************************************/
00302 eNextState mx_connect_ip(AsyncIO *IO)
00303 {
00304        SmtpOutMsg *Msg = IO->Data;
00305 
00306        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00307 
00308        IO->ConnectMe = Msg->pCurrRelay;
00309        Msg->State = eConnectMX;
00310 
00311        SetConnectStatus(IO);
00312 
00313        return EvConnectSock(IO,
00314                           SMTP_C_ConnTimeout,
00315                           SMTP_C_ReadTimeouts[0],
00316                           1);
00317 }
00318 
00319 eNextState get_one_mx_host_ip_done(AsyncIO *IO)
00320 {
00321        SmtpOutMsg *Msg = IO->Data;
00322        struct hostent *hostent;
00323 
00324        IO->ConnectMe = Msg->pCurrRelay;
00325 
00326        QueryCbDone(IO);
00327        EVS_syslog(LOG_DEBUG, "%s Time[%fs]\n",
00328                  __FUNCTION__,
00329                  IO->Now - IO->DNS.Start);
00330 
00331        hostent = Msg->HostLookup.VParsedDNSReply;
00332        if ((Msg->HostLookup.DNSStatus == ARES_SUCCESS) &&
00333            (hostent != NULL) ) {
00334               memset(&Msg->pCurrRelay->Addr, 0, sizeof(struct in6_addr));
00335               if (Msg->pCurrRelay->IPv6) {
00336                      memcpy(&Msg->pCurrRelay->Addr.sin6_addr.s6_addr,
00337                             &hostent->h_addr_list[0],
00338                             sizeof(struct in6_addr));
00339 
00340                      Msg->pCurrRelay->Addr.sin6_family =
00341                             hostent->h_addrtype;
00342                      Msg->pCurrRelay->Addr.sin6_port =
00343                             htons(Msg->IO.ConnectMe->Port);
00344               }
00345               else {
00346                      struct sockaddr_in *addr;
00347                      /*
00348                       * Bypass the ns lookup result like this:
00349                       * IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1");
00350                       * addr->sin_addr.s_addr =
00351                       *   htonl((uint32_t)&hostent->h_addr_list[0]);
00352                       */
00353 
00354                      addr = (struct sockaddr_in*) &Msg->pCurrRelay->Addr;
00355 
00356                      memcpy(&addr->sin_addr.s_addr,
00357                             hostent->h_addr_list[0],
00358                             sizeof(uint32_t));
00359 
00360                      addr->sin_family = hostent->h_addrtype;
00361                      addr->sin_port   = htons(Msg->IO.ConnectMe->Port);
00362               }
00363               Msg->mx_host = Msg->pCurrRelay->Host;
00364               if (Msg->HostLookup.VParsedDNSReply != NULL) {
00365                      Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
00366                      Msg->HostLookup.VParsedDNSReply = NULL;
00367               }
00368               return mx_connect_ip(IO);
00369        }
00370        else {
00371               if (Msg->HostLookup.VParsedDNSReply != NULL) {
00372                      Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
00373                      Msg->HostLookup.VParsedDNSReply = NULL;
00374               }
00375               return FailOneAttempt(IO);
00376        }
00377 }
00378 
00379 eNextState get_one_mx_host_ip(AsyncIO *IO)
00380 {
00381        SmtpOutMsg * Msg = IO->Data;
00382        /*
00383         * here we start with the lookup of one host. it might be...
00384         * - the relay host *sigh*
00385         * - the direct hostname if there was no mx record
00386         * - one of the mx'es
00387         */
00388 
00389        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00390 
00391        EVS_syslog(LOG_DEBUG,
00392                 "looking up %s-Record %s : %d ...\n",
00393                 (Msg->pCurrRelay->IPv6)? "aaaa": "a",
00394                 Msg->pCurrRelay->Host,
00395                 Msg->pCurrRelay->Port);
00396 
00397        if (!QueueQuery((Msg->pCurrRelay->IPv6)? ns_t_aaaa : ns_t_a,
00398                      Msg->pCurrRelay->Host,
00399                      &Msg->IO,
00400                      &Msg->HostLookup,
00401                      get_one_mx_host_ip_done))
00402        {
00403               Msg->MyQEntry->Status = 5;
00404               StrBufPrintf(Msg->MyQEntry->StatusMessage,
00405                           "No MX hosts found for <%s>", Msg->node);
00406               Msg->IO.NextState = eTerminateConnection;
00407               return IO->NextState;
00408        }
00409        IO->NextState = eReadDNSReply;
00410        return IO->NextState;
00411 }
00412 
00413 
00414 /*****************************************************************************
00415  * here we try to find out about the MX records for our recipients.          *
00416  *****************************************************************************/
00417 eNextState smtp_resolve_mx_record_done(AsyncIO *IO)
00418 {
00419        SmtpOutMsg * Msg = IO->Data;
00420        ParsedURL **pp;
00421 
00422        QueryCbDone(IO);
00423 
00424        EVS_syslog(LOG_DEBUG, "%s Time[%fs]\n",
00425                  __FUNCTION__,
00426                  IO->Now - IO->DNS.Start);
00427 
00428        pp = &Msg->Relay;
00429        while ((pp != NULL) && (*pp != NULL) && ((*pp)->Next != NULL))
00430               pp = &(*pp)->Next;
00431 
00432        if ((IO->DNS.Query->DNSStatus == ARES_SUCCESS) &&
00433            (IO->DNS.Query->VParsedDNSReply != NULL))
00434        { /* ok, we found mx records. */
00435 
00436               Msg->CurrMX
00437                      = Msg->AllMX
00438                      = IO->DNS.Query->VParsedDNSReply;
00439               while (Msg->CurrMX) {
00440                      int i;
00441                      for (i = 0; i < 2; i++) {
00442                             ParsedURL *p;
00443 
00444                             p = (ParsedURL*) malloc(sizeof(ParsedURL));
00445                             memset(p, 0, sizeof(ParsedURL));
00446                             p->Priority = Msg->CurrMX->priority;
00447                             p->IsIP = 0;
00448                             p->Port = DefaultMXPort;
00449                             p->IPv6 = i == 1;
00450                             p->Host = Msg->CurrMX->host;
00451                             if (*pp == NULL)
00452                                    *pp = p;
00453                             else {
00454                                    ParsedURL *ppp = *pp;
00455 
00456                                    while ((ppp->Next != NULL) &&
00457                                           (ppp->Next->Priority <= p->Priority))
00458                                           ppp = ppp->Next;
00459                                    if ((ppp == *pp) &&
00460                                        (ppp->Priority > p->Priority)) {
00461                                           p->Next = *pp;
00462                                           *pp = p;
00463                                    }
00464                                    else {
00465                                           p->Next = ppp->Next;
00466                                           ppp->Next = p;
00467                                    }
00468                             }
00469                      }
00470                      Msg->CurrMX    = Msg->CurrMX->next;
00471               }
00472               Msg->CXFlags   = Msg->CXFlags & F_HAVE_MX;
00473        }
00474        else { /* else fall back to the plain hostname */
00475               int i;
00476               for (i = 0; i < 2; i++) {
00477                      ParsedURL *p;
00478 
00479                      p = (ParsedURL*) malloc(sizeof(ParsedURL));
00480                      memset(p, 0, sizeof(ParsedURL));
00481                      p->IsIP = 0;
00482                      p->Port = DefaultMXPort;
00483                      p->IPv6 = i == 1;
00484                      p->Host = Msg->node;
00485 
00486                      *pp = p;
00487                      pp = &p->Next;
00488               }
00489               Msg->CXFlags   = Msg->CXFlags & F_DIRECT;
00490        }
00491        if (Msg->MyQItem->FallBackHost != NULL)
00492        {
00493               Msg->MyQItem->FallBackHost->Next = *pp;
00494               *pp = Msg->MyQItem->FallBackHost;
00495        }
00496        Msg->pCurrRelay = Msg->Relay;
00497        return get_one_mx_host_ip(IO);
00498 }
00499 
00500 eNextState resolve_mx_records(AsyncIO *IO)
00501 {
00502        SmtpOutMsg * Msg = IO->Data;
00503 
00504        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00505        /* start resolving MX records here. */
00506        if (!QueueQuery(ns_t_mx,
00507                      Msg->node,
00508                      &Msg->IO,
00509                      &Msg->MxLookup,
00510                      smtp_resolve_mx_record_done))
00511        {
00512               Msg->MyQEntry->Status = 5;
00513               StrBufPrintf(Msg->MyQEntry->StatusMessage,
00514                           "No MX hosts found for <%s>", Msg->node);
00515               return IO->NextState;
00516        }
00517        Msg->IO.NextState = eReadDNSReply;
00518        return IO->NextState;
00519 }
00520 
00521 
00522 
00523 /******************************************************************************
00524  *  so, we're going to start a SMTP delivery.  lets get it on.                *
00525  ******************************************************************************/
00526 
00527 SmtpOutMsg *new_smtp_outmsg(OneQueItem *MyQItem,
00528                          MailQEntry *MyQEntry,
00529                          int MsgCount)
00530 {
00531        SmtpOutMsg * Msg;
00532 
00533        Msg = (SmtpOutMsg *) malloc(sizeof(SmtpOutMsg));
00534        if (Msg == NULL)
00535               return NULL;
00536        memset(Msg, 0, sizeof(SmtpOutMsg));
00537 
00538        Msg->n                = MsgCount;
00539        Msg->MyQEntry         = MyQEntry;
00540        Msg->MyQItem          = MyQItem;
00541        Msg->pCurrRelay       = MyQItem->URL;
00542 
00543        InitIOStruct(&Msg->IO,
00544                    Msg,
00545                    eReadMessage,
00546                    SMTP_C_ReadServerStatus,
00547                    SMTP_C_DNSFail,
00548                    SMTP_C_DispatchWriteDone,
00549                    SMTP_C_DispatchReadDone,
00550                    SMTP_C_Terminate,
00551                    SMTP_C_TerminateDB,
00552                    SMTP_C_ConnFail,
00553                    SMTP_C_Timeout,
00554                    SMTP_C_Shutdown);
00555 
00556        Msg->IO.ErrMsg = Msg->MyQEntry->StatusMessage;
00557 
00558        return Msg;
00559 }
00560 
00561 void smtp_try_one_queue_entry(OneQueItem *MyQItem,
00562                            MailQEntry *MyQEntry,
00563                            StrBuf *MsgText,
00564                      /*KeepMsgText allows us to use MsgText as ours.*/
00565                            int KeepMsgText,
00566                            int MsgCount)
00567 {
00568        SmtpOutMsg *Msg;
00569 
00570        SMTPC_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00571 
00572        Msg = new_smtp_outmsg(MyQItem, MyQEntry, MsgCount);
00573        if (Msg == NULL) {
00574               SMTPC_syslog(LOG_DEBUG, "%s Failed to alocate message context.\n", __FUNCTION__);
00575               if (KeepMsgText) 
00576                      FreeStrBuf (&MsgText);
00577               return;
00578        }
00579        if (KeepMsgText) Msg->msgtext = MsgText;
00580        else           Msg->msgtext = NewStrBufDup(MsgText);
00581 
00582        if (smtp_resolve_recipients(Msg)) {
00583 
00584               safestrncpy(
00585                      ((CitContext *)Msg->IO.CitContext)->cs_host,
00586                      Msg->node,
00587                      sizeof(((CitContext *)
00588                             Msg->IO.CitContext)->cs_host));
00589 
00590               SMTPC_syslog(LOG_DEBUG, "Starting: [%ld] <%s> CC <%d> \n",
00591                           Msg->MyQItem->MessageID,
00592                           ChrPtr(Msg->MyQEntry->Recipient),
00593                           ((CitContext*)Msg->IO.CitContext)->cs_pid);
00594               if (Msg->pCurrRelay == NULL)
00595                      QueueEventContext(&Msg->IO,
00596                                      resolve_mx_records);
00597               else { /* oh... via relay host */
00598                      if (Msg->pCurrRelay->IsIP) {
00599                             QueueEventContext(&Msg->IO,
00600                                             mx_connect_ip);
00601                      }
00602                      else {
00603                             /* uneducated admin has chosen to
00604                                add DNS to the equation... */
00605                             QueueEventContext(&Msg->IO,
00606                                             get_one_mx_host_ip);
00607                      }
00608               }
00609        }
00610        else {
00611               /* No recipients? well fail then. */
00612               if (Msg->MyQEntry != NULL) {
00613                      Msg->MyQEntry->Status = 5;
00614                      StrBufPlain(Msg->MyQEntry->StatusMessage,
00615                                 HKEY("Invalid Recipient!"));
00616               }
00617               FinalizeMessageSend_DB(&Msg->IO);
00618               DeleteSmtpOutMsg(&Msg->IO);
00619        }
00620 }
00621 
00622 
00623 
00624 
00625 
00626 
00627 /*****************************************************************************/
00628 /*                     SMTP CLIENT DISPATCHER                                */
00629 /*****************************************************************************/
00630 
00631 void SMTPSetTimeout(eNextState NextTCPState, SmtpOutMsg *Msg)
00632 {
00633        double Timeout = 0.0;
00634        AsyncIO *IO = &Msg->IO;
00635 
00636        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00637 
00638        switch (NextTCPState) {
00639        case eSendFile:
00640        case eSendReply:
00641        case eSendMore:
00642               Timeout = SMTP_C_SendTimeouts[Msg->State];
00643               if (Msg->State == eDATABody) {
00644                      /* if we're sending a huge message,
00645                       * we need more time.
00646                       */
00647                      Timeout += StrLength(Msg->msgtext) / 512;
00648               }
00649               break;
00650        case eReadMessage:
00651               Timeout = SMTP_C_ReadTimeouts[Msg->State];
00652               if (Msg->State == eDATATerminateBody) {
00653                      /*
00654                       * some mailservers take a nap before accepting
00655                       * the message content inspection and such.
00656                       */
00657                      Timeout += StrLength(Msg->msgtext) / 512;
00658               }
00659               break;
00660        case eSendDNSQuery:
00661        case eReadDNSReply:
00662        case eDBQuery:
00663        case eReadFile:
00664        case eReadMore:
00665        case eReadPayload:
00666        case eConnect:
00667        case eTerminateConnection:
00668        case eAbort:
00669               return;
00670        }
00671        SetNextTimeout(&Msg->IO, Timeout);
00672 }
00673 eNextState SMTP_C_DispatchReadDone(AsyncIO *IO)
00674 {
00675        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00676        SmtpOutMsg *Msg = IO->Data;
00677        eNextState rc;
00678 
00679        rc = ReadHandlers[Msg->State](Msg);
00680        if (rc != eAbort)
00681        {
00682               Msg->State++;
00683               SMTPSetTimeout(rc, Msg);
00684        }
00685        return rc;
00686 }
00687 eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO)
00688 {
00689        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00690        SmtpOutMsg *Msg = IO->Data;
00691        eNextState rc;
00692 
00693        rc = SendHandlers[Msg->State](Msg);
00694        SMTPSetTimeout(rc, Msg);
00695        return rc;
00696 }
00697 
00698 
00699 /*****************************************************************************/
00700 /*                     SMTP CLIENT ERROR CATCHERS                            */
00701 /*****************************************************************************/
00702 eNextState SMTP_C_Terminate(AsyncIO *IO)
00703 {
00704        SmtpOutMsg *Msg = IO->Data;
00705 
00706        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00707        return FinalizeMessageSend(Msg);
00708 }
00709 eNextState SMTP_C_TerminateDB(AsyncIO *IO)
00710 {
00711        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00712        return Terminate(IO);
00713 }
00714 eNextState SMTP_C_Timeout(AsyncIO *IO)
00715 {
00716        SmtpOutMsg *Msg = IO->Data;
00717 
00718        Msg->MyQEntry->Status = 4;
00719        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00720        StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[Msg->State]));
00721        if (Msg->State > eRCPT)
00722               return eAbort;
00723        else
00724               return FailOneAttempt(IO);
00725 }
00726 eNextState SMTP_C_ConnFail(AsyncIO *IO)
00727 {
00728        SmtpOutMsg *Msg = IO->Data;
00729 
00730        Msg->MyQEntry->Status = 4;
00731        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00732        StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[Msg->State]));
00733        return FailOneAttempt(IO);
00734 }
00735 eNextState SMTP_C_DNSFail(AsyncIO *IO)
00736 {
00737        SmtpOutMsg *Msg = IO->Data;
00738        Msg->MyQEntry->Status = 4;
00739        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00740        return FailOneAttempt(IO);
00741 }
00742 eNextState SMTP_C_Shutdown(AsyncIO *IO)
00743 {
00744        EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00745        SmtpOutMsg *Msg = IO->Data;
00746 
00747        Msg->MyQEntry->Status = 3;
00748        StrBufPlain(Msg->MyQEntry->StatusMessage,
00749                   HKEY("server shutdown during message submit."));
00750        return FinalizeMessageSend(Msg);
00751 }
00752 
00753 
00758 eReadState SMTP_C_ReadServerStatus(AsyncIO *IO)
00759 {
00760        eReadState Finished = eBufferNotEmpty;
00761 
00762        while (Finished == eBufferNotEmpty) {
00763               Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
00764 
00765               switch (Finished) {
00766               case eMustReadMore: 
00767                      return Finished;
00768                      break;
00769               case eBufferNotEmpty: /* shouldn't happen... */
00770               case eReadSuccess: 
00771                      if (StrLength(IO->IOBuf) < 4)
00772                             continue;
00773                      if (ChrPtr(IO->IOBuf)[3] == '-')
00774                             Finished = eBufferNotEmpty;
00775                      else
00776                             return Finished;
00777                      break;
00778               case eReadFail: 
00779 
00780                      break;
00781               }
00782        }
00783        return Finished;
00784 }
00785 
00786 void LogDebugEnableSMTPClient(const int n)
00787 {
00788        SMTPClientDebugEnabled = n;
00789 }
00790 
00791 CTDL_MODULE_INIT(smtp_eventclient)
00792 {
00793        if (!threading)
00794               CtdlRegisterDebugFlagHook(HKEY("smtpeventclient"), LogDebugEnableSMTPClient, &SMTPClientDebugEnabled);
00795        return "smtpeventclient";
00796 }