]> code.citadel.org Git - citadel.git/blobdiff - citadel/modules/smtp/serv_smtpeventclient.c
libev migration; relaying implementation
[citadel.git] / citadel / modules / smtp / serv_smtpeventclient.c
index f1f3371282e984046c30c0cfaa0c37af15041e94..591d5d59d03f4b2229f3e6b5eaf9bf0f2d46de12 100644 (file)
@@ -108,35 +108,32 @@ typedef enum _eSMTP_C_States {
        eMaxSMTPC
 } eSMTP_C_States;
 
-const long SMTP_C_ConnTimeout = 60; /* wail 1 minute for connections... */
-const long SMTP_C_ReadTimeouts[eMaxSMTPC] = {
-       300, /* Greeting... */
-       30, /* EHLO */
-       30, /* HELO */
-       30, /* Auth */
-       30, /* From */
-       90, /* RCPT */
-       30, /* DATA */
-       90, /* DATABody */
-       0, /* end of body... */
-       30  /* QUIT */
+const double SMTP_C_ConnTimeout = 300.; /* 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] = {
-       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_SendTimeouts[eMaxSMTPC] = {
+       90., /* Greeting... */
+       30., /* EHLO */
+       30., /* HELO */
+       30., /* Auth */
+       30., /* From */
+       30., /* RCPT */
+       30., /* DATA */
+       90., /* DATABody */
+       900., /* end of body... */
+       30.  /* QUIT */
 };
-/*
-const long SMTP_C_SendTimeouts[eMaxSMTPC] = {
 
-}; */
 static const ConstStr ReadErrors[eMaxSMTPC] = {
        {HKEY("Connection broken during SMTP conversation")},
        {HKEY("Connection broken during SMTP EHLO")},
@@ -165,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];
@@ -183,7 +180,7 @@ void DeleteSmtpOutMsg(void *v)
        
        FreeStrBuf(&Msg->msgtext);
        FreeAsyncIOContents(&Msg->IO);
-///    free(Msg);
+       free(Msg);
 }
 
 eNextState SMTP_C_Timeout(AsyncIO *IO);
@@ -213,7 +210,7 @@ typedef eNextState (*SMTPSendHandler)(SmtpOutMsg *Msg);
 
 #define SMTP_IS_STATE(WHICH_STATE) (ChrPtr(SendMsg->IO.IOBuf)[0] == WHICH_STATE)
 
-#define SMTP_DBG_SEND() CtdlLogPrintf(CTDL_DEBUG, "SMTP client[%ld]: > %s\n", SendMsg->n, ChrPtr(SendMsg->IO.IOBuf))
+#define SMTP_DBG_SEND() CtdlLogPrintf(CTDL_DEBUG, "SMTP client[%ld]: > %s\n", SendMsg->n, ChrPtr(SendMsg->IO.SendBuf.Buf))
 #define SMTP_DBG_READ() CtdlLogPrintf(CTDL_DEBUG, "SMTP client[%ld]: < %s\n", SendMsg->n, ChrPtr(SendMsg->IO.IOBuf))
 
 
@@ -244,15 +241,13 @@ 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);
        }
@@ -260,22 +255,26 @@ void FinalizeMessageSend(SmtpOutMsg *Msg)
 }
 
 
-
-
-void get_one_mx_host_ip_done(void *Ctx, 
-                            int status,
-                            int timeouts,
-                            struct hostent *hostent)
+void SetConnectStatus(AsyncIO *IO)
 {
-       AsyncIO *IO = Ctx;
+       
        SmtpOutMsg *SendMsg = IO->Data;
-       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
-       if ((status == ARES_SUCCESS) && (hostent != NULL) ) {
+       char buf[256];
+       void *src;
+
+       buf[0] = '\0';
+
+       if (IO->IP6) {
+               src = &IO->Addr.sin6_addr;
+       }
+       else {
                unsigned long psaddr;
-               // TODO: IPV6
-               memcpy(&psaddr, hostent->h_addr_list[0], sizeof(psaddr));
-               psaddr = ntohl(psaddr); 
+               struct sockaddr_in *addr = (struct sockaddr_in *)&IO->Addr;
 
+               src = &addr->sin_addr.s_addr;
+               memcpy(&psaddr, &addr->sin_addr.s_addr, sizeof(psaddr));
+///            psaddr = ntohl(psaddr); 
+/*
                CtdlLogPrintf(CTDL_DEBUG, 
                              "SMTP client[%ld]: connecting to %s [%ld.%ld.%ld.%ld:%d] ...\n", 
                              SendMsg->n, 
@@ -296,67 +295,144 @@ void get_one_mx_host_ip_done(void *Ctx,
                             (psaddr >>  0) & 0xFF,
                             SendMsg->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],
+*/
+       }
+
+       inet_ntop((IO->IP6)?AF_INET6:AF_INET,
+                 src,
+                 buf, sizeof(buf));
+
+       CtdlLogPrintf(CTDL_DEBUG, 
+                     "SMTP client[%ld]: connecting to %s [%s]:%d ...\n", 
+                     SendMsg->n, 
+                     SendMsg->mx_host, 
+                     buf,
+                     SendMsg->IO.dport);
+
+       SendMsg->MyQEntry->Status = 5; 
+       StrBufPrintf(SendMsg->MyQEntry->StatusMessage, 
+                    "Timeout while connecting %s [%s]:%d ", 
+                    SendMsg->mx_host,
+                    buf,
+                    SendMsg->IO.dport);
+}
+
+eNextState mx_connect_relay_ip(AsyncIO *IO)
+{
+       
+       SmtpOutMsg *SendMsg = IO->Data;
+
+       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,///.s_addr, 
+                      &SendMsg->pCurrRelay->Addr,
+                      sizeof(struct in_addr));
+               
+               addr->sin_family = AF_INET;
+               addr->sin_port = htons(IO->dport);
+       }
+
+       SetConnectStatus(IO);
+
+       return 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;
+       eNextState State = eAbort;
 
+       if ((status == ARES_SUCCESS) && (hostent != NULL) ) {
+               
+               IO->IP6  = hostent->h_addrtype == AF_INET6;
+               IO->HEnt = hostent;
+
+               memset(&IO->Addr, 0, sizeof(struct in6_addr));
+               if (IO->IP6) {
+                       memcpy(&IO->Addr.sin6_addr.s6_addr, 
+                              &hostent->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"); */
+//                     addr->sin_addr.s_addr = htonl((uint32_t)&hostent->h_addr_list[0]);
+                       memcpy(&addr->sin_addr.s_addr, hostent->h_addr_list[0], sizeof(uint32_t));
+                       
+                       addr->sin_family = hostent->h_addrtype;
+                       addr->sin_port = htons(IO->dport);
+                       
+               }
+               SendMsg->IO.HEnt = hostent;
+               SetConnectStatus(IO);
+               State = InitEventIO(IO, SendMsg, 
+                                  SMTP_C_ConnTimeout, 
+                                  SMTP_C_ReadTimeouts[0],
+                                  1);
        }
+       if (State == eAbort)
+               SMTP_C_Terminate(IO);
 }
 
 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;
+       const char *Hostname;
        //char *endpart;
        //char buf[SIZ];
+       InitC_ares_dns(IO);
 
-       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);
-       }               
+       if (SendMsg->CurrMX) {
+               SendMsg->mx_host = SendMsg->CurrMX->host;
+               SendMsg->CurrMX = SendMsg->CurrMX->next;
        }
-       else
-*/
-       SendMsg->mx_host = SendMsg->CurrMX->host;
-       SendMsg->CurrMX = SendMsg->CurrMX->next;
+
+       if (SendMsg->pCurrRelay != NULL) Hostname = SendMsg->pCurrRelay->Host;
+               else if (SendMsg->mx_host != NULL) Hostname = SendMsg->mx_host;
+       else Hostname = SendMsg->node;
+
+       CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
 
        CtdlLogPrintf(CTDL_DEBUG, 
                      "SMTP client[%ld]: looking up %s : %d ...\n", 
                      SendMsg->n, 
-                     SendMsg->mx_host
+                     Hostname
                      SendMsg->IO.dport);
 
        ares_gethostbyname(SendMsg->IO.DNSChannel,
-                          SendMsg->mx_host,   
+                          Hostname,   
                           AF_INET6, /* it falls back to ipv4 in doubt... */
                           get_one_mx_host_ip_done,
                           &SendMsg->IO);
+       return IO->NextState;
 }
 
 
@@ -373,14 +449,14 @@ eNextState smtp_resolve_mx_done(AsyncIO *IO)
 
        SendMsg->CurrMX = SendMsg->AllMX = IO->VParsedDNSReply;
        //// TODO: should we remove the current ares context???
-       get_one_mx_host_ip(SendMsg);
-       return 0;
+       get_one_mx_host_ip(IO);
+       return IO->NextState;
 }
 
 
-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__);
 
@@ -392,9 +468,9 @@ int resolve_mx_records(void *Ctx)
                SendMsg->MyQEntry->Status = 5;
                StrBufPrintf(SendMsg->MyQEntry->StatusMessage, 
                             "No MX hosts found for <%s>", SendMsg->node);
-               return 0; ///////TODO: abort!
+               return IO->NextState;
        }
-       return 0;
+       return eAbort;
 }
 
 
@@ -492,20 +568,43 @@ void smtp_try(OneQueItem *MyQItem,
 
        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->IO.NextState = eReadMessage;
+       SendMsg->n            = MsgCount++;
+       SendMsg->MyQEntry     = MyQEntry;
+       SendMsg->MyQItem      = MyQItem;
+       SendMsg->pCurrRelay   = MyQItem->URL;
+
+       SendMsg->IO.dport       = DefaultMXPort;
+       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) || 
@@ -557,7 +656,9 @@ eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *SendMsg)
 
        if (SMTP_IS_STATE('2')) {
                SendMsg->State ++;
-               if (IsEmptyStr(SendMsg->mx_user))
+
+               if ((SendMsg->pCurrRelay == NULL) || 
+                   (SendMsg->pCurrRelay->User == NULL))
                        SendMsg->State ++; /* Skip auth... */
        }
        /* else we fall back to 'helo' */
@@ -583,7 +684,8 @@ eNextState SMTPC_read_HELO_reply(SmtpOutMsg *SendMsg)
                else 
                        SMTP_VERROR(5);
        }
-       if (!IsEmptyStr(SendMsg->mx_user))
+               if ((SendMsg->pCurrRelay == NULL) || 
+                   (SendMsg->pCurrRelay->User == NULL))
                SendMsg->State ++; /* Skip auth... */
        return eSendReply;
 }
@@ -593,18 +695,21 @@ eNextState SMTPC_send_auth(SmtpOutMsg *SendMsg)
        char buf[SIZ];
        char encoded[1024];
 
+       if ((SendMsg->pCurrRelay == NULL) || 
+           (SendMsg->pCurrRelay->User == NULL))
+               SendMsg->State ++; /* Skip auth, shouldn't even come here!... */
+       else {
        /* 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;
 }
@@ -705,7 +810,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;
@@ -805,14 +909,25 @@ SMTPSendHandler SendHandlers[eMaxSMTPC] = {
 void SMTPSetTimeout(eNextState NextTCPState, SmtpOutMsg *pMsg)
 {
        CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
-       long Timeout;
+       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:
@@ -851,7 +966,7 @@ eNextState SMTP_C_Terminate(AsyncIO *IO)
        CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
        SmtpOutMsg *pMsg = IO->Data;
        FinalizeMessageSend(pMsg);
-       return 0;
+       return eAbort;
 }
 eNextState SMTP_C_Timeout(AsyncIO *IO)
 {
@@ -859,14 +974,14 @@ eNextState SMTP_C_Timeout(AsyncIO *IO)
        SmtpOutMsg *pMsg = IO->Data;
        StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[pMsg->State]));
        FinalizeMessageSend(pMsg);
-       return 0;
+       return eAbort;
 }
 eNextState SMTP_C_ConnFail(AsyncIO *IO)
 {
        CtdlLogPrintf(CTDL_DEBUG, "SMTP: %s\n", __FUNCTION__);
        SmtpOutMsg *pMsg = IO->Data;
        FinalizeMessageSend(pMsg);
-       return 0;
+       return eAbort;
 }
 
 
@@ -901,7 +1016,6 @@ eReadState SMTP_C_ReadServerStatus(AsyncIO *IO)
        return Finished;
 }
 
-
 #endif
 CTDL_MODULE_INIT(smtp_eventclient)
 {