SMTP-Client: Implement AUTH LOGIN
[citadel.git] / citadel / modules / smtp / smtp_clienthandlers.c
index 30f6a6f50df5fa26c7153e0dafb1aa955af6273f..70373a89623d7a51ed115d600a2196c06e9a4743 100644 (file)
 
 #define SMTP_ERROR(WHICH_ERR, ERRSTR) do {                            \
                Msg->MyQEntry->Status = WHICH_ERR;                     \
-               StrBufAppendBufPlain(Msg->MyQEntry->StatusMessage, \
+               StrBufAppendBufPlain(Msg->MyQEntry->StatusMessage,     \
                                     HKEY(ERRSTR), 0);                 \
+               StrBufTrim(Msg->MyQEntry->StatusMessage);              \
                return eAbort; }                                       \
        while (0)
 
 #define SMTP_VERROR(WHICH_ERR) do {                           \
                Msg->MyQEntry->Status = WHICH_ERR;             \
-               StrBufPlain(Msg->MyQEntry->StatusMessage,  \
-                           ChrPtr(Msg->IO.IOBuf) + 4,     \
-                           StrLength(Msg->IO.IOBuf) - 4); \
+               StrBufPlain(Msg->MyQEntry->StatusMessage,      \
+                           ChrPtr(Msg->IO.IOBuf) + 4,         \
+                           StrLength(Msg->IO.IOBuf) - 4);     \
+               StrBufTrim(Msg->MyQEntry->StatusMessage);      \
                return eAbort; }                               \
        while (0)
 
 #define SMTP_IS_STATE(WHICH_STATE) (ChrPtr(Msg->IO.IOBuf)[0] == WHICH_STATE)
 
 #define SMTP_DBG_SEND() \
-       EVS_syslog(LOG_DEBUG, "SMTP: > %s\n", ChrPtr(Msg->IO.SendBuf.Buf))
+       EVS_syslog(LOG_DEBUG, "> %s\n", ChrPtr(Msg->IO.SendBuf.Buf))
 
 #define SMTP_DBG_READ() \
-       EVS_syslog(LOG_DEBUG, "SMTP: < %s\n", ChrPtr(Msg->IO.IOBuf))
+       EVS_syslog(LOG_DEBUG, "< %s\n", ChrPtr(Msg->IO.IOBuf))
 
+/*
+ * if a Read handler wants to skip to a specific part use this macro.
+ * the -1 is here since the auto-forward following has to be taken into account.
+ */
+#define READ_NEXT_STATE(state) Msg->State = state - 1
 
 /*****************************************************************************/
 /*                     SMTP CLIENT STATE CALLBACKS                           */
@@ -120,6 +127,7 @@ eNextState SMTPC_read_greeting(SmtpOutMsg *Msg)
        /* Process the SMTP greeting from the server */
        AsyncIO *IO = &Msg->IO;
        SMTP_DBG_READ();
+       SetSMTPState(IO, eSTMPsmtp);
 
        if (!SMTP_IS_STATE('2')) {
                if (SMTP_IS_STATE('4'))
@@ -149,11 +157,16 @@ eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *Msg)
        SMTP_DBG_READ();
 
        if (SMTP_IS_STATE('2')) {
-               Msg->State ++;
+               READ_NEXT_STATE(eSMTPAuth);
 
                if ((Msg->pCurrRelay == NULL) ||
                    (Msg->pCurrRelay->User == NULL))
-                       Msg->State ++; /* Skip auth... */
+                       READ_NEXT_STATE(eFROM); /* Skip auth... */
+               if (Msg->pCurrRelay != NULL)
+               {
+                       if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL)
+                               Msg->SendLogin = 1;
+               }
        }
        /* else we fall back to 'helo' */
        return eSendReply;
@@ -181,9 +194,14 @@ eNextState SMTPC_read_HELO_reply(SmtpOutMsg *Msg)
                else
                        SMTP_VERROR(5);
        }
+       if (Msg->pCurrRelay != NULL)
+       {
+               if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL)
+                       Msg->SendLogin = 1;
+       }
        if ((Msg->pCurrRelay == NULL) ||
            (Msg->pCurrRelay->User == NULL))
-               Msg->State ++; /* Skip auth... */
+               READ_NEXT_STATE(eFROM); /* Skip auth... */
 
        return eSendReply;
 }
@@ -196,26 +214,129 @@ eNextState SMTPC_send_auth(SmtpOutMsg *Msg)
 
        if ((Msg->pCurrRelay == NULL) ||
            (Msg->pCurrRelay->User == NULL))
-               Msg->State ++; /* Skip auth, shouldn't even come here!... */
+               READ_NEXT_STATE(eFROM); /* Skip auth, shouldn't even come here!... */
        else {
                /* Do an AUTH command if necessary */
-               sprintf(buf, "%s%c%s%c%s",
-                       Msg->pCurrRelay->User, '\0',
-                       Msg->pCurrRelay->User, '\0',
-                       Msg->pCurrRelay->Pass);
+               if (Msg->SendLogin)
+               {
+                       StrBufPlain(Msg->IO.SendBuf.Buf,
+                                   HKEY("AUTH LOGIN\r\n"));
+               }
+               else
+               {
+                       sprintf(buf, "%s%c%s%c%s",
+                               Msg->pCurrRelay->User, '\0',
+                               Msg->pCurrRelay->User, '\0',
+                               Msg->pCurrRelay->Pass);
+                       
+                       CtdlEncodeBase64(encoded, buf,
+                                        strlen(Msg->pCurrRelay->User) * 2 +
+                                        strlen(Msg->pCurrRelay->Pass) + 2, 0);
+                       
+                       StrBufPrintf(Msg->IO.SendBuf.Buf,
+                                    "AUTH PLAIN %s\r\n",
+                                    encoded);
+               }
+       }
+       SMTP_DBG_SEND();
+       return eReadMessage;
+}
+
+
+eNextState SMTPC_read_auth_reply(SmtpOutMsg *Msg)
+{
+       AsyncIO *IO = &Msg->IO;
+       /* Do an AUTH command if necessary */
 
-               CtdlEncodeBase64(encoded, buf,
-                                strlen(Msg->pCurrRelay->User) * 2 +
-                                strlen(Msg->pCurrRelay->Pass) + 2, 0);
+       SMTP_DBG_READ();
 
-               StrBufPrintf(Msg->IO.SendBuf.Buf,
-                            "AUTH PLAIN %s\r\n", encoded);
+       if (Msg->SendLogin)
+       {
+               if (!SMTP_IS_STATE('3'))
+                       SMTP_VERROR(5);
+       }
+       else
+       {
+               if (!SMTP_IS_STATE('2')) {
+                       if (SMTP_IS_STATE('4'))
+                               SMTP_VERROR(4);
+                       else
+                               SMTP_VERROR(5);
+               }
+               READ_NEXT_STATE(eFROM);
        }
+       return eSendReply;
+}
+
+
+eNextState SMTPC_send_authplain_1(SmtpOutMsg *Msg)
+{
+       AsyncIO *IO = &Msg->IO;
+       char buf[SIZ];
+       char encoded[1024];
+       long encodedlen;
+
+       sprintf(buf, "%s",
+               Msg->pCurrRelay->User);
+       
+       encodedlen = CtdlEncodeBase64(
+               encoded,
+               Msg->pCurrRelay->User,
+               strlen(Msg->pCurrRelay->User),
+               0);
+
+       StrBufPlain(Msg->IO.SendBuf.Buf,
+                   encoded,
+                   encodedlen);
+
+       StrBufAppendBufPlain(Msg->IO.SendBuf.Buf,
+                            HKEY("\r\n"), 0);
+
        SMTP_DBG_SEND();
+
        return eReadMessage;
 }
+eNextState SMTPC_read_auth_plain_reply_1(SmtpOutMsg *Msg)
+{
+       AsyncIO *IO = &Msg->IO;
+       /* Do an AUTH command if necessary */
 
-eNextState SMTPC_read_auth_reply(SmtpOutMsg *Msg)
+       SMTP_DBG_READ();
+
+       if (!SMTP_IS_STATE('3'))
+               SMTP_VERROR(5);
+       return eSendReply;
+}
+
+
+eNextState SMTPC_send_authplain_2(SmtpOutMsg *Msg)
+{
+       AsyncIO *IO = &Msg->IO;
+       char buf[SIZ];
+       char encoded[1024];
+       long encodedlen;
+
+       sprintf(buf, "%s",
+               Msg->pCurrRelay->Pass);
+       
+       encodedlen = CtdlEncodeBase64(
+               encoded,
+               Msg->pCurrRelay->User,
+               strlen(Msg->pCurrRelay->User),
+               0);
+
+       StrBufPlain(Msg->IO.SendBuf.Buf,
+                   encoded,
+                   encodedlen);
+
+       StrBufAppendBufPlain(Msg->IO.SendBuf.Buf,
+                            HKEY("\r\n"), 0);
+
+       SMTP_DBG_SEND();
+
+       return eReadMessage;
+}
+eNextState SMTPC_read_auth_plain_reply_2(SmtpOutMsg *Msg)
 {
        AsyncIO *IO = &Msg->IO;
        /* Do an AUTH command if necessary */
@@ -302,11 +423,13 @@ eNextState SMTPC_read_DATAcmd_reply(SmtpOutMsg *Msg)
        SMTP_DBG_READ();
 
        if (!SMTP_IS_STATE('3')) {
+               SetSMTPState(IO, eSTMPfailOne);
                if (SMTP_IS_STATE('4'))
                        SMTP_VERROR(3);
                else
                        SMTP_VERROR(5);
        }
+       SetSMTPState(IO, eSTMPsmtpdata);
        return eSendReply;
 }
 
@@ -318,6 +441,10 @@ eNextState SMTPC_send_data_body(SmtpOutMsg *Msg)
        Buf = Msg->IO.SendBuf.Buf;
        Msg->IO.SendBuf.Buf = Msg->msgtext;
        Msg->msgtext = Buf;
+       /* 
+        * sending the message itself doesn't use this state machine.
+        * so we have to operate it here by ourselves.
+        */
        Msg->State ++;
 
        return eSendMore;
@@ -350,10 +477,12 @@ eNextState SMTPC_read_data_body_reply(SmtpOutMsg *Msg)
                        SMTP_VERROR(5);
        }
 
+       SetSMTPState(IO, eSTMPsmtpdone);
        /* We did it! */
        StrBufPlain(Msg->MyQEntry->StatusMessage,
                    &ChrPtr(Msg->IO.RecvBuf.Buf)[4],
                    StrLength(Msg->IO.RecvBuf.Buf) - 4);
+       StrBufTrim(Msg->MyQEntry->StatusMessage);
        Msg->MyQEntry->Status = 2;
        return eSendReply;
 }
@@ -373,9 +502,8 @@ eNextState SMTPC_read_QUIT_reply(SmtpOutMsg *Msg)
        AsyncIO *IO = &Msg->IO;
        SMTP_DBG_READ();
 
-       EVS_syslog(LOG_INFO,
-                  "SMTP client[%ld]: delivery to <%s> @ <%s> (%s) succeeded\n",
-                  Msg->n,
+       EVS_syslog(LOG_DEBUG,
+                  "delivery to <%s> @ <%s> (%s) succeeded\n",
                   Msg->user,
                   Msg->node,
                   Msg->name);
@@ -401,6 +529,8 @@ SMTPReadHandler ReadHandlers[eMaxSMTPC] = {
        SMTPC_read_EHLO_reply,
        SMTPC_read_HELO_reply,
        SMTPC_read_auth_reply,
+       SMTPC_read_auth_plain_reply_1,
+       SMTPC_read_auth_plain_reply_2,
        SMTPC_read_FROM_reply,
        SMTPC_read_RCPT_reply,
        SMTPC_read_DATAcmd_reply,
@@ -413,6 +543,8 @@ SMTPSendHandler SendHandlers[eMaxSMTPC] = {
        SMTPC_send_EHLO,
        STMPC_send_HELO,
        SMTPC_send_auth,
+       SMTPC_send_authplain_1,
+       SMTPC_send_authplain_2,
        SMTPC_send_FROM,
        SMTPC_send_RCPT,
        SMTPC_send_DATAcmd,
@@ -428,6 +560,8 @@ const double SMTP_C_ReadTimeouts[eMaxSMTPC] = {
        30., /* EHLO */
        30., /* HELO */
        30., /* Auth */
+       30., /* Auth */
+       30., /* Auth */
        30., /* From */
        90., /* RCPT */
        30., /* DATA */
@@ -440,6 +574,8 @@ const double SMTP_C_SendTimeouts[eMaxSMTPC] = {
        30., /* EHLO */
        30., /* HELO */
        30., /* Auth */
+       30., /* Auth */
+       30., /* Auth */
        30., /* From */
        30., /* RCPT */
        30., /* DATA */
@@ -453,6 +589,8 @@ const ConstStr ReadErrors[eMaxSMTPC + 1] = {
        {HKEY("Connection broken during SMTP EHLO")},
        {HKEY("Connection broken during SMTP HELO")},
        {HKEY("Connection broken during SMTP AUTH")},
+       {HKEY("Connection broken during SMTP AUTH PLAIN I")},
+       {HKEY("Connection broken during SMTP AUTH PLAIN II")},
        {HKEY("Connection broken during SMTP MAIL FROM")},
        {HKEY("Connection broken during SMTP RCPT")},
        {HKEY("Connection broken during SMTP DATA")},
@@ -475,7 +613,7 @@ int smtp_resolve_recipients(SmtpOutMsg *Msg)
        int lp, rp;
        int i;
 
-       EVNCS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__);
+       EVNCS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
 
        if ((Msg==NULL) ||
            (Msg->MyQEntry == NULL) ||
@@ -490,9 +628,7 @@ int smtp_resolve_recipients(SmtpOutMsg *Msg)
                            Msg->name);
 
        EVNCS_syslog(LOG_DEBUG,
-                    "SMTP client[%ld]: Attempting delivery to "
-                    "<%s> @ <%s> (%s)\n",
-                    Msg->n,
+                    "Attempting delivery to <%s> @ <%s> (%s)\n",
                     Msg->user,
                     Msg->node,
                     Msg->name);