This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch improve_smtp_diagnostic_messages
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git

commit 90132fa29268a5cc7426f6dc35f65aaa4d54d37f
Author: Stephen Webb <[email protected]>
AuthorDate: Sat Aug 22 13:25:19 2026 +1000

    Make SMTPAppender failures diagnosable
---
 src/main/cpp/smtpappender.cpp | 180 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 165 insertions(+), 15 deletions(-)

diff --git a/src/main/cpp/smtpappender.cpp b/src/main/cpp/smtpappender.cpp
index 5b39a620..8afdf5f1 100644
--- a/src/main/cpp/smtpappender.cpp
+++ b/src/main/cpp/smtpappender.cpp
@@ -75,6 +75,7 @@ LogString stripSmtpControl(const LogString& value, const 
logchar* field)
        }
        return out;
 }
+
 } // namespace
 
 namespace LOG4CXX_NS
@@ -102,13 +103,13 @@ class SMTPSession
                        const LogString& smtpUsername,
                        const LogString& smtpPassword,
                        bool allowPlainTextAuth,
-                       Pool& p) : session(0), authctx(0),
-                       user(toAscii(smtpUsername, p)),
-                       pwd(toAscii(smtpPassword, p))
+                       Pool& p
+                       )
+                       : user{toAscii(smtpUsername, p)}
+                       , pwd{toAscii(smtpPassword, p)}
                {
                        auth_client_init();
                        session = smtp_create_session();
-
                        if (session == 0)
                        {
                                throw Exception("Could not initialize 
session.");
@@ -118,6 +119,8 @@ class SMTPSession
                        host.append(1, ':');
                        host.append(p.itoa(smtpPort));
                        smtp_set_server(session, host.c_str());
+                       smtp_set_monitorcb(session, monitor_cb, (void*)this, 1);
+                       smtp_set_eventcb(session, event_cb, (void*)this);
 
                        authctx = auth_create_context();
                        auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0);
@@ -149,17 +152,26 @@ class SMTPSession
                        auth_destroy_context(authctx);
                }
 
-               void send(Pool& p)
+               void send()
                {
                        int status = smtp_start_session(session);
 
                        if (!status)
                        {
-                               size_t bufSize = 128;
-                               char* buf = p.pstralloc(bufSize);
-                               smtp_strerror(smtp_errno(), buf, bufSize);
-                               throw Exception(buf);
+                               static const size_t smtp_msgSize = 128;
+                               char smtp_msg[smtp_msgSize];
+                               smtp_strerror(smtp_errno(), smtp_msg, 
smtp_msgSize);
+                               char msg[2 * smtp_msgSize];
+                               snprintf(msg, sizeof (msg), "%s (sessionState 
%d isActive? %d tlsStarted? %d)"
+                                       , smtp_msg, sessionState, isActive, 
tlsStarted);
+                               throw Exception(msg);
                        }
+                       else if (incorrectAuthentication)
+                               throw Exception("Incorrect authentication 
data");
+                       else if (relayDenied)
+                               throw Exception("Relay Denied");
+                       else if (!certificateProblem.empty())
+                               throw Exception(("X509 error: " + 
certificateProblem).c_str());
                }
 
                operator smtp_session_t()
@@ -189,10 +201,16 @@ class SMTPSession
        private:
                SMTPSession(SMTPSession&);
                SMTPSession& operator=(SMTPSession&);
-               smtp_session_t session;
-               auth_context_t authctx;
+               smtp_session_t session{0};
+               auth_context_t authctx{0};
                char* user;
                char* pwd;
+               int sessionState{0};
+               bool isActive{false};
+               bool tlsStarted{false};
+               bool incorrectAuthentication{false};
+               bool relayDenied{false};
+               std::string certificateProblem;
 
                /**
                 *   This method is called if the SMTP server requests 
authentication.
@@ -200,7 +218,7 @@ class SMTPSession
                static int authinteract(auth_client_request_t request, char** 
result, int fields,
                        void* arg)
                {
-                       SMTPSession* pThis = (SMTPSession*) arg;
+                       auto pThis = static_cast<SMTPSession*>(arg);
 
                        for (int i = 0; i < fields; i++)
                        {
@@ -219,7 +237,140 @@ class SMTPSession
                        return 1;
                }
 
+               static void monitor_cb(const char *buf, int buflen, int 
writing, void *arg)
+               {
+                       auto pThis = static_cast<SMTPSession*>(arg);
+                       if (writing)
+                               pThis->isActive = true;
+                       else if (auto smtp_response = atoi(buf))
+                       {
+                               if (535 == smtp_response)
+                                       pThis->incorrectAuthentication = true;
+                               if (550 == smtp_response)
+                                       pThis->relayDenied = true;
+                       }
+
+                       if (LogLog::isDebugEnabled())
+                       {
+                               while (0 < buflen && std::isspace(buf[buflen - 
1]))
+                                       --buflen;
+                               std::string data(buf, buflen);
+                               LOG4CXX_DECODE_CHAR(lsData, data);
+                               LogString type = writing ? LOG4CXX_STR("send") 
: LOG4CXX_STR("recv");
+                               LogLog::debug(LOG4CXX_STR("SMTP ") + type + 
LOG4CXX_STR(" [") + lsData + LOG4CXX_STR("]"));
+                       }
+               }
+
+               static void event_cb (smtp_session_t session /* unused */, int 
event_no, void *arg,...)
+               {
+                       auto pThis = static_cast<SMTPSession*>(arg);
+                       va_list alist;
+                       va_start(alist, arg);
+                       switch (event_no)
+                       {
+                       case SMTP_EV_CONNECT:
+                       case SMTP_EV_MAILSTATUS:
+                       case SMTP_EV_RCPTSTATUS:
+                       case SMTP_EV_MESSAGEDATA:
+                       case SMTP_EV_MESSAGESENT:
+                       case SMTP_EV_DISCONNECT:
+                               pThis->sessionState = event_no;
+                               break;
+                       case SMTP_EV_WEAK_CIPHER:
+                       {
+                               auto bitsRequired = va_arg(alist, long);
+                               pThis->certificateProblem = "weak cipher";
+                               if (auto ok = va_arg(alist, int*))
+                                       *ok = 1; // Accept the problem
+                               break;
+                       }
+                       case SMTP_EV_STARTTLS_OK:
+                               pThis->tlsStarted = true;
+                               break;
+                       case SMTP_EV_INVALID_PEER_CERTIFICATE:
+                               pThis->certificateProblem = 
get_X509_error(va_arg(alist, long));
+                               if (auto ok = va_arg(alist, int*))
+                                       *ok = 1; // Accept the problem
+                               break;
+                       case SMTP_EV_NO_PEER_CERTIFICATE:
+                               pThis->certificateProblem = "no peer 
certificate";
+                               if (auto ok = va_arg(alist, int*))
+                                       *ok = 1;
+                               break;
+                       case SMTP_EV_WRONG_PEER_CERTIFICATE:
+                               pThis->certificateProblem = "wrong peer 
certificate";
+                               if (auto ok = va_arg(alist, int*))
+                                       *ok = 1; // Accept the problem
+                               break;
+                       case SMTP_EV_NO_CLIENT_CERTIFICATE:
+                               pThis->certificateProblem = "no client 
certificate";
+                               if (auto ok = va_arg(alist, int*))
+                                       *ok = 1; // Accept the problem
+                               break;
+                       }
+                       va_end(alist);
+               }
 
+               static std::string get_X509_error(long verifyResult)
+               {
+                       switch (verifyResult)
+                       {
+                       case 2: return "unable to get issuer cert";
+                       case 3: return "unable to get crl";
+                       case 4: return "unable to decrypt cert signature";
+                       case 5: return "unable to decrypt crl signature";
+                       case 6: return "unable to decode issuer public key";
+                       case 7: return "cert signature failure";
+                       case 8: return "crl signature failure";
+                       case 9: return "cert not yet valid";
+                       case 10: return "cert has expired";
+                       case 11: return "crl not yet valid";
+                       case 12: return "crl has expired";
+                       case 13: return "error in cert not before field";
+                       case 14: return "error in cert not after field";
+                       case 15: return "error in crl last update field";
+                       case 16: return "error in crl next update field";
+                       case 17: return "out of mem";
+                       case 18: return "depth zero self signed cert";
+                       case 19: return "self signed cert in chain";
+                       case 20: return "unable to get issuer cert locally";
+                       case 21: return "unable to verify leaf signature";
+                       case 22: return "cert chain too long";
+                       case 23: return "cert revoked";
+                       case 24: return "invalid ca";
+                       case 25: return "path length exceeded";
+                       case 26: return "invalid purpose";
+                       case 27: return "cert untrusted";
+                       case 28: return "cert rejected";
+                       case 29: return "subject issuer mismatch";
+                       case 30: return "akid skid mismatch";
+                       case 31: return "akid issuer serial mismatch";
+                       case 32: return "keyusage no certsign";
+                       case 33: return "unable to get crl issuer";
+                       case 34: return "unhandled critical extension";
+                       case 35: return "keyusage no crl sign";
+                       case 36: return "unhandled critical crl extension";
+                       case 37: return "invalid non ca";
+                       case 38: return "proxy path length exceeded";
+                       case 39: return "keyusage no digital signature";
+                       case 40: return "proxy certificates not allowed";
+                       case 41: return "invalid extension";
+                       case 42: return "invalid policy extension";
+                       case 43: return "no explicit policy";
+                       case 44: return "different crl scope";
+                       case 45: return "unsupported extension feature";
+                       case 46: return "unnested resource";
+                       case 47: return "permitted violation";
+                       case 48: return "excluded violation";
+                       case 49: return "subtree minmax";
+                       case 51: return "unsupported constraint type";
+                       case 52: return "unsupported constraint syntax";
+                       case 53: return "unsupported name syntax";
+                       case 54: return "crl path validation error";
+                       case 50: return "application verification";
+                       }
+                       return "unknown";
+               }
 };
 
 /**
@@ -789,8 +940,7 @@ void SMTPAppender::sendBuffer(Pool& p)
 {
 #if LOG4CXX_HAVE_LIBESMTP
 
-       // Note: this code already owns the monitor for this
-       // appender. This frees us from needing to synchronize on 'cb'.
+       // This thread owns the mutex for this appender, hence no need to 
synchronize on 'cb'.
        try
        {
                LogString sbuf;
@@ -811,7 +961,7 @@ void SMTPAppender::sendBuffer(Pool& p)
                SMTPMessage message(session, _priv->from, _priv->to, _priv->cc,
                        _priv->bcc, _priv->subject, sbuf, p);
 
-               session.send(p);
+               session.send();
 
        }
        catch (std::exception& e)

Reply via email to