]> code.citadel.org Git - citadel.git/blobdiff - citadel/modules/smtp/serv_smtpeventclient.c
libev migration: reinstantiate MX-Relay; unfinished.
[citadel.git] / citadel / modules / smtp / serv_smtpeventclient.c
index 676c1812f4dd220f61e47598e34ece87df2cc0ac..919f97f60769ba5306afdff3c5b437dcf0731c03 100644 (file)
@@ -108,33 +108,42 @@ typedef enum _eSMTP_C_States {
        eMaxSMTPC
 } eSMTP_C_States;
 
-const long SMTP_C_ConnTimeout = 300; /* wail 5 minutes for connections... */
-const long SMTP_C_ReadTimeouts[eMaxSMTPC] = {
-       90, /* Greeting... */
-       30, /* EHLO */
-       30, /* HELO */
-       30, /* Auth */
-       30, /* From */
-       30, /* RCPT */
-       30, /* DATA */
-       90, /* DATABody */
-       900, /* end of body... */
-       30  /* QUIT */
+const double SMTP_C_ConnTimeout = 60.; /* wail 1 minute for connections... */
+const double SMTP_C_ReadTimeouts[eMaxSMTPC] = {
+       300., /* Greeting... */
+       30., /* EHLO */
+       30., /* HELO */
+       30., /* Auth */
+       30., /* From */
+       90., /* RCPT */
+       30., /* DATA */
+       90., /* DATABody */
+       90., /* end of body... */
+       30.  /* QUIT */
 };
-/*
-const long SMTP_C_SendTimeouts[eMaxSMTPC] = {
-
-}; */
-const char *ReadErrors[eMaxSMTPC] = {
-       "Connection broken during SMTP conversation",
-       "Connection broken during SMTP EHLO",
-       "Connection broken during SMTP HELO",
-       "Connection broken during SMTP AUTH",
-       "Connection broken during SMTP MAIL FROM",
-       "Connection broken during SMTP RCPT",
-       "Connection broken during SMTP DATA",
-       "Connection broken during SMTP message transmit",
-       ""/* quit reply, don't care. */
+const double SMTP_C_SendTimeouts[eMaxSMTPC] = {
+       90., /* Greeting... */
+       30., /* EHLO */
+       30., /* HELO */
+       30., /* Auth */
+       30., /* From */
+       30., /* RCPT */
+       30., /* DATA */
+       90., /* DATABody */
+       900., /* end of body... */
+       30.  /* QUIT */
+};
+
+static const ConstStr ReadErrors[eMaxSMTPC] = {
+       {HKEY("Connection broken during SMTP conversation")},
+       {HKEY("Connection broken during SMTP EHLO")},
+       {HKEY("Connection broken during SMTP HELO")},
+       {HKEY("Connection broken during SMTP AUTH")},
+       {HKEY("Connection broken during SMTP MAIL FROM")},
+       {HKEY("Connection broken during SMTP RCPT")},
+       {HKEY("Connection broken during SMTP DATA")},
+       {HKEY("Connection broken during SMTP message transmit")},
+       {HKEY("")}/* quit reply, don't care. */
 };
 
 
@@ -153,8 +162,8 @@ typedef struct _stmp_out_msg {
 
        struct hostent *OneMX;
 
-       char mx_user[1024];
-       char mx_pass[1024];
+       ParsedURL *Relay;
+       ParsedURL *pCurrRelay;
        StrBuf *msgtext;
        char *envelope_from;
        char user[1024];
@@ -174,11 +183,11 @@ void DeleteSmtpOutMsg(void *v)
        free(Msg);
 }
 
-eNextState SMTP_C_Timeout(void *Data);
-eNextState SMTP_C_ConnFail(void *Data);
-eNextState SMTP_C_DispatchReadDone(void *Data);
-eNextState SMTP_C_DispatchWriteDone(void *Data);
-eNextState SMTP_C_Terminate(void *Data);
+eNextState SMTP_C_Timeout(AsyncIO *IO);
+eNextState SMTP_C_ConnFail(AsyncIO *IO);
+eNextState SMTP_C_DispatchReadDone(AsyncIO *IO);
+eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO);
+eNextState SMTP_C_Terminate(AsyncIO *IO);
 eReadState SMTP_C_ReadServerStatus(AsyncIO *IO);
 
 typedef eNextState (*SMTPReadHandler)(SmtpOutMsg *Msg);
@@ -207,6 +216,8 @@ typedef eNextState (*SMTPSendHandler)(SmtpOutMsg *Msg);
 
 void FinalizeMessageSend(SmtpOutMsg *Msg)
 {
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+       
        if (DecreaseQReference(Msg->MyQItem)) 
        {
                int nRemain;
@@ -230,98 +241,157 @@ void FinalizeMessageSend(SmtpOutMsg *Msg)
                        msg->cm_anon_type = MES_NORMAL;
                        msg->cm_format_type = FMT_RFC822;
                        msg->cm_fields['M'] = SmashStrBuf(&MsgData);
-                       /* Generate 'bounce' messages */
-                       smtp_do_bounce(msg->cm_fields['M'],
-                                      Msg->msgtext); 
-
                        CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
                        CtdlFreeMessage(msg);
                }
-               else 
+               else {
                        CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->MessageID, 1, "");
+                       FreeStrBuf(&MsgData);
+               }
 
                RemoveQItem(Msg->MyQItem);
        }
-       
-       close(Msg->IO.sock);
        DeleteSmtpOutMsg(Msg);
 }
 
 
 
-
-void get_one_mx_host_ip_done(void *Ctx, 
-                              int status,
-                              int timeouts,
-                              struct hostent *hostent)
+eNextState mx_connect_relay_ip(AsyncIO *IO)
 {
-       AsyncIO *IO = Ctx;
+       
        SmtpOutMsg *SendMsg = IO->Data;
-       if ((status == ARES_SUCCESS) && (hostent != NULL) ) {
+
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+
+       IO->IP6 = SendMsg->pCurrRelay->af == AF_INET6;
+       
+       if (SendMsg->pCurrRelay->Port != 0)
+               IO->dport = SendMsg->pCurrRelay->Port;
+
+       memset(&IO->Addr, 0, sizeof(struct in6_addr));
+       if (IO->IP6) {
+               memcpy(&IO->Addr.sin6_addr.s6_addr, 
+                      &SendMsg->pCurrRelay->Addr,
+                      sizeof(struct in6_addr));
+               
+               IO->Addr.sin6_family = AF_INET6;
+               IO->Addr.sin6_port = htons(IO->dport);
+       }
+       else {
+               struct sockaddr_in *addr = (struct sockaddr_in*) &IO->Addr;
+               /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
+               memcpy(&addr->sin_addr, 
+                      &SendMsg->pCurrRelay->Addr,
+                      sizeof(struct in_addr));
+               
+               addr->sin_family = AF_INET;
+               addr->sin_port = htons(IO->dport);
+       }
+
+
+
+/*
+
+       SendMsg->pCurrRelay->
+       if ((SendMsg->pCurrRelay != NULL) && (SendMsg->pCurrRelay->IsIP)) {
+               connect = 1;
+
+       }
+               unsigned long psaddr;
+
+
+               memcpy(&psaddr, hostent->h_addr_list[0], sizeof(psaddr));
+               psaddr = ntohl(psaddr); 
+
                CtdlLogPrintf(CTDL_DEBUG, 
-                             "SMTP client[%ld]: connecting to %s [ip]: %d ...\n", 
+                             "SMTP client[%ld]: connecting to %s [%ld.%ld.%ld.%ld:%d] ...\n", 
                              SendMsg->n, 
                              SendMsg->mx_host, 
+                             (psaddr >> 24) & 0xFF,
+                             (psaddr >> 16) & 0xFF,
+                             (psaddr >>  8) & 0xFF,
+                             (psaddr >>  0) & 0xFF,
                              SendMsg->IO.dport);
 
                SendMsg->MyQEntry->Status = 5; 
                StrBufPrintf(SendMsg->MyQEntry->StatusMessage, 
-                            "Timeout while connecting %s", 
-                            SendMsg->mx_host);
+                            "Timeout while connecting %s [%ld.%ld.%ld.%ld:%d] ", 
+                            SendMsg->mx_host,
+                            (psaddr >> 24) & 0xFF,
+                            (psaddr >> 16) & 0xFF,
+                            (psaddr >>  8) & 0xFF,
+                            (psaddr >>  0) & 0xFF,
+                            SendMsg->IO.dport);
+
+
+       }
+*/
+       /// TODO: else fail!
+       InitEventIO(IO, SendMsg, 
+                   SMTP_C_ConnTimeout, 
+                   SMTP_C_ReadTimeouts[0],
+                           1);
+}
+
+void get_one_mx_host_ip_done(void *Ctx, 
+                            int status,
+                            int timeouts,
+                            struct hostent *hostent)
+{
+       AsyncIO *IO = (AsyncIO *) Ctx;
+       SmtpOutMsg *SendMsg = IO->Data;
 
+       if ((status == ARES_SUCCESS) && (hostent != NULL) ) {
+               
+               IO->IP6 = hostent->h_addrtype == AF_INET6;
+               
+               memset(&IO->Addr, 0, sizeof(struct in6_addr));
+               if (IO->IP6) {
+                       memcpy(&IO->Addr.sin6_addr.s6_addr, 
+                              IO->HEnt->h_addr_list[0],
+                              sizeof(struct in6_addr));
+                       
+                       IO->Addr.sin6_family = hostent->h_addrtype;
+                       IO->Addr.sin6_port = htons(IO->dport);
+               }
+               else {
+                       struct sockaddr_in *addr = (struct sockaddr_in*) &IO->Addr;
+                       /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
+                       memcpy(&addr->sin_addr, 
+                              IO->HEnt->h_addr_list[0],
+                              sizeof(struct in_addr));
+                       
+                       addr->sin_family = hostent->h_addrtype;
+                       addr->sin_port = htons(IO->dport);
+                       
+               }
                SendMsg->IO.HEnt = hostent;
                InitEventIO(IO, SendMsg, 
-                           SMTP_C_DispatchReadDone, 
-                           SMTP_C_DispatchWriteDone, 
-                           SMTP_C_Terminate,
-                           SMTP_C_Timeout,
-                           SMTP_C_ConnFail,
-                           SMTP_C_ReadServerStatus,
                            SMTP_C_ConnTimeout, 
                            SMTP_C_ReadTimeouts[0],
                            1);
-
        }
 }
 
 const unsigned short DefaultMXPort = 25;
-void get_one_mx_host_ip(SmtpOutMsg *SendMsg)
+eNextState get_one_mx_host_ip(AsyncIO *IO)
 {
+       SmtpOutMsg * SendMsg = IO->Data;
+
        //char *endpart;
        //char buf[SIZ];
 
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
        SendMsg->IO.dport = DefaultMXPort;
 
-
-/* TODO: Relay!
-       *SendMsg->mx_user =  '\0';
-       *SendMsg->mx_pass = '\0';
-       if (num_tokens(buf, '@') > 1) {
-               strcpy (SendMsg->mx_user, buf);
-               endpart = strrchr(SendMsg->mx_user, '@');
-               *endpart = '\0';
-               strcpy (SendMsg->mx_host, endpart + 1);
-               endpart = strrchr(SendMsg->mx_user, ':');
-               if (endpart != NULL) {
-                       strcpy(SendMsg->mx_pass, endpart+1);
-                       *endpart = '\0';
-               }
-
-       endpart = strrchr(SendMsg->mx_host, ':');
-       if (endpart != 0){
-               *endpart = '\0';
-               strcpy(SendMsg->mx_port, endpart + 1);
-       }               
-       }
-       else
-*/
        SendMsg->mx_host = SendMsg->CurrMX->host;
        SendMsg->CurrMX = SendMsg->CurrMX->next;
 
        CtdlLogPrintf(CTDL_DEBUG, 
                      "SMTP client[%ld]: looking up %s : %d ...\n", 
                      SendMsg->n, 
-                     SendMsg->mx_host);
+                     SendMsg->mx_host, 
+                     SendMsg->IO.dport);
 
        ares_gethostbyname(SendMsg->IO.DNSChannel,
                           SendMsg->mx_host,   
@@ -331,11 +401,12 @@ void get_one_mx_host_ip(SmtpOutMsg *SendMsg)
 }
 
 
-eNextState smtp_resolve_mx_done(void *data)
+eNextState smtp_resolve_mx_done(AsyncIO *IO)
 {
-       AsyncIO *IO = data;
        SmtpOutMsg * SendMsg = IO->Data;
 
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+
        SendMsg->IO.SendBuf.Buf = NewStrBufPlain(NULL, 1024);
        SendMsg->IO.RecvBuf.Buf = NewStrBufPlain(NULL, 1024);
        SendMsg->IO.IOBuf = NewStrBuf();
@@ -343,14 +414,16 @@ eNextState smtp_resolve_mx_done(void *data)
 
        SendMsg->CurrMX = SendMsg->AllMX = IO->VParsedDNSReply;
        //// TODO: should we remove the current ares context???
-       get_one_mx_host_ip(SendMsg);
+       get_one_mx_host_ip(IO);
        return 0;
 }
 
 
-int resolve_mx_records(void *Ctx)
+eNextState resolve_mx_records(AsyncIO *IO)
 {
-       SmtpOutMsg * SendMsg = Ctx;
+       SmtpOutMsg * SendMsg = IO->Data;
+
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
 
        if (!QueueQuery(ns_t_mx, 
                        SendMsg->node, 
@@ -374,6 +447,8 @@ int smtp_resolve_recipients(SmtpOutMsg *SendMsg)
        int lp, rp;
        int i;
 
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+
        if ((SendMsg==NULL) || 
            (SendMsg->MyQEntry == NULL) || 
            (StrLength(SendMsg->MyQEntry->Recipient) == 0)) {
@@ -454,22 +529,45 @@ void smtp_try(OneQueItem *MyQItem,
 {
        SmtpOutMsg * SendMsg;
 
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+
        SendMsg = (SmtpOutMsg *) malloc(sizeof(SmtpOutMsg));
        memset(SendMsg, 0, sizeof(SmtpOutMsg));
-       SendMsg->IO.sock = (-1);
-       SendMsg->n = MsgCount++;
-       SendMsg->MyQEntry = MyQEntry;
-       SendMsg->MyQItem = MyQItem;
-       SendMsg->IO.Data = SendMsg;
-       if (KeepMsgText)
-               SendMsg->msgtext = MsgText;
-       else 
+       SendMsg->IO.sock    = (-1);
+       SendMsg->n          = MsgCount++;
+       SendMsg->MyQEntry   = MyQEntry;
+       SendMsg->MyQItem    = MyQItem;
+       SendMsg->pCurrRelay = MyQItem->URL;
+
+       SendMsg->IO.Data        = SendMsg;
+       SendMsg->IO.SendDone    = SMTP_C_DispatchWriteDone;
+       SendMsg->IO.ReadDone    = SMTP_C_DispatchReadDone;
+       SendMsg->IO.Terminate   = SMTP_C_Terminate;
+       SendMsg->IO.LineReader  = SMTP_C_ReadServerStatus;
+       SendMsg->IO.ConnFail    = SMTP_C_ConnFail;
+       SendMsg->IO.Timeout     = SMTP_C_Timeout;
+
+       if (KeepMsgText) {
+               SendMsg->msgtext    = MsgText;
+       }
+       else {
                SendMsg->msgtext = NewStrBufDup(MsgText);
+       }
 
        if (smtp_resolve_recipients(SendMsg)) {
-               QueueEventContext(SendMsg, 
-                                 &SendMsg->IO,
-                                 resolve_mx_records);
+               if (SendMsg->pCurrRelay == NULL)
+                       QueueEventContext(&SendMsg->IO,
+                                         resolve_mx_records);
+               else {
+                       if (SendMsg->pCurrRelay->IsIP) {
+                               QueueEventContext(&SendMsg->IO,
+                                                 mx_connect_relay_ip);
+                       }
+                       else {
+                               QueueEventContext(&SendMsg->IO,
+                                                 get_one_mx_host_ip);
+                       }
+               }
        }
        else {
                if ((SendMsg==NULL) || 
@@ -521,7 +619,8 @@ eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *SendMsg)
 
        if (SMTP_IS_STATE('2')) {
                SendMsg->State ++;
-               if (IsEmptyStr(SendMsg->mx_user))
+
+               if (SendMsg->pCurrRelay->User == NULL)
                        SendMsg->State ++; /* Skip auth... */
        }
        /* else we fall back to 'helo' */
@@ -547,7 +646,7 @@ eNextState SMTPC_read_HELO_reply(SmtpOutMsg *SendMsg)
                else 
                        SMTP_VERROR(5);
        }
-       if (!IsEmptyStr(SendMsg->mx_user))
+       if (SendMsg->pCurrRelay->User == NULL)
                SendMsg->State ++; /* Skip auth... */
        return eSendReply;
 }
@@ -559,16 +658,15 @@ eNextState SMTPC_send_auth(SmtpOutMsg *SendMsg)
 
        /* Do an AUTH command if necessary */
        sprintf(buf, "%s%c%s%c%s", 
-               SendMsg->mx_user, '\0', 
-               SendMsg->mx_user, '\0', 
-               SendMsg->mx_pass);
+               SendMsg->pCurrRelay->User, '\0', 
+               SendMsg->pCurrRelay->User, '\0', 
+               SendMsg->pCurrRelay->Pass);
        CtdlEncodeBase64(encoded, buf, 
-                        strlen(SendMsg->mx_user) + 
-                        strlen(SendMsg->mx_user) + 
-                        strlen(SendMsg->mx_pass) + 2, 0);
+                        strlen(SendMsg->pCurrRelay->User) * 2 +
+                        strlen(SendMsg->pCurrRelay->Pass) + 2, 0);
        StrBufPrintf(SendMsg->IO.SendBuf.Buf,
                     "AUTH PLAIN %s\r\n", encoded);
-       
+
        SMTP_DBG_SEND();
        return eReadMessage;
 }
@@ -669,7 +767,6 @@ eNextState SMTPC_send_data_body(SmtpOutMsg *SendMsg)
        Buf = SendMsg->IO.SendBuf.Buf;
        SendMsg->IO.SendBuf.Buf = SendMsg->msgtext;
        SendMsg->msgtext = Buf;
-       //// TODO timeout like that: (SendMsg->msg_size / 128) + 50);
        SendMsg->State ++;
 
        return eSendMore;
@@ -765,38 +862,81 @@ SMTPSendHandler SendHandlers[eMaxSMTPC] = {
        SMTPC_send_terminate_data_body,
        SMTPC_send_QUIT
 };
-eNextState SMTP_C_DispatchReadDone(void *Data)
+
+void SMTPSetTimeout(eNextState NextTCPState, SmtpOutMsg *pMsg)
+{
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+       double Timeout;
+       switch (NextTCPState) {
+       case eSendReply:
+       case eSendMore:
+               Timeout = SMTP_C_SendTimeouts[pMsg->State];
+               if (pMsg->State == eDATABody) {
+                       /* if we're sending a huge message, we need more time. */
+                       Timeout += StrLength(pMsg->msgtext) / 1024;
+               }
+               break;
+       case eReadMessage:
+               Timeout = SMTP_C_ReadTimeouts[pMsg->State];
+               if (pMsg->State == eDATATerminateBody) {
+                       /* 
+                        * some mailservers take a nap before accepting the message
+                        * content inspection and such.
+                        */
+                       Timeout += StrLength(pMsg->msgtext) / 1024;
+               }
+               break;
+       case eTerminateConnection:
+       case eAbort:
+               return;
+       }
+       SetNextTimeout(&pMsg->IO, Timeout);
+}
+eNextState SMTP_C_DispatchReadDone(AsyncIO *IO)
 {
-       SmtpOutMsg *pMsg = Data;
-       eNextState rc = ReadHandlers[pMsg->State](pMsg);
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+       SmtpOutMsg *pMsg = IO->Data;
+       eNextState rc;
+
+       rc = ReadHandlers[pMsg->State](pMsg);
        pMsg->State++;
+       SMTPSetTimeout(rc, pMsg);
        return rc;
 }
-eNextState SMTP_C_DispatchWriteDone(void *Data)
+eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO)
 {
-       SmtpOutMsg *pMsg = Data;
-       return SendHandlers[pMsg->State](pMsg); 
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+       SmtpOutMsg *pMsg = IO->Data;
+       eNextState rc;
+
+       rc = SendHandlers[pMsg->State](pMsg);
+       SMTPSetTimeout(rc, pMsg);
+       return rc;
 }
 
 
 /*****************************************************************************/
 /*                     SMTP CLIENT ERROR CATCHERS                            */
 /*****************************************************************************/
-eNextState SMTP_C_Terminate(void *Data)
+eNextState SMTP_C_Terminate(AsyncIO *IO)
 {
-       SmtpOutMsg *pMsg = Data;
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+       SmtpOutMsg *pMsg = IO->Data;
        FinalizeMessageSend(pMsg);
        return 0;
 }
-eNextState SMTP_C_Timeout(void *Data)
+eNextState SMTP_C_Timeout(AsyncIO *IO)
 {
-       SmtpOutMsg *pMsg = Data;
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+       SmtpOutMsg *pMsg = IO->Data;
+       StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[pMsg->State]));
        FinalizeMessageSend(pMsg);
        return 0;
 }
-eNextState SMTP_C_ConnFail(void *Data)
+eNextState SMTP_C_ConnFail(AsyncIO *IO)
 {
-       SmtpOutMsg *pMsg = Data;
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
+       SmtpOutMsg *pMsg = IO->Data;
        FinalizeMessageSend(pMsg);
        return 0;
 }
@@ -833,7 +973,6 @@ eReadState SMTP_C_ReadServerStatus(AsyncIO *IO)
        return Finished;
 }
 
-
 #endif
 CTDL_MODULE_INIT(smtp_eventclient)
 {