// -------------------------------------------------------------------------------- // Ixpsmtp.cpp // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved // Steven J. Bailey // -------------------------------------------------------------------------------- #include "pch.hxx" #include "dllmain.h" #include "asynconn.h" #include "ixpsmtp.h" #include "ixputil.h" #include "strconst.h" #include #include // -------------------------------------------------------------------------------- // Useful C++ pointer casting // -------------------------------------------------------------------------------- #define SMTPTHISIXP ((ISMTPTransport *)(CIxpBase *)this) // -------------------------------------------------------------------------------- // Some string constants // -------------------------------------------------------------------------------- // These constants are from the draft spec for SMTP authenication // draft-myers-smtp-auth-11.txt static const char g_szSMTPAUTH11[] = "AUTH "; static const int g_cchSMTPAUTH11 = sizeof(g_szSMTPAUTH11) - 1; // These constants are from the draft spec for SMTP authenication // draft-myers-smtp-auth-10.txt static const char g_szSMTPAUTH10[] = "AUTH="; static const int g_cchSMTPAUTH10 = sizeof(g_szSMTPAUTH10) - 1; // These constants are from the draft spec for Secure SMTP over TLS // draft-hoffman-smtp-ssl-08.txt static const char g_szSMTPSTARTTLS08[] = "STARTTLS"; static const int g_cchSMTPSTARTTLS08 = sizeof(g_szSMTPSTARTTLS08) - 1; // These constants are from the draft spec for Secure SMTP over TLS // draft-hoffman-smtp-ssl-06.txt static const char g_szSMTPSTARTTLS06[] = "TLS"; static const int g_cchSMTPSTARTTLS06 = sizeof(g_szSMTPSTARTTLS06) - 1; // These constants are from RFC1891 for DSN support static const char g_szSMTPDSN[] = "DSN"; static const int g_cchSMTPDSN = sizeof(g_szSMTPDSN) - 1; static const char g_szDSNENVID[] = "ENVID="; static const char g_szDSNRET[] = "RET="; static const char g_szDSNHDRS[] = "HDRS"; static const char g_szDSNFULL[] = "FULL"; static const char g_szDSNNOTIFY[] = "NOTIFY="; static const char g_szDSNNEVER[] = "NEVER"; static const char g_szDSNSUCCESS[] = "SUCCESS"; static const char g_szDSNFAILURE[] = "FAILURE"; static const char g_szDSNDELAY[] = "DELAY"; // -------------------------------------------------------------------------------- // CSMTPTransport::CSMTPTransport // -------------------------------------------------------------------------------- CSMTPTransport::CSMTPTransport(void) : CIxpBase(IXP_SMTP) { DllAddRef(); m_command = SMTP_NONE; m_iAddress = 0; m_cRecipients = 0; m_cbSent = 0; m_cbTotal = 0; m_fReset = FALSE; m_fSendMessage = FALSE; m_fSTARTTLSAvail = FALSE; m_fTLSNegotiation = FALSE; m_fSecured = FALSE; *m_szEmail = '\0'; m_pszResponse = NULL; ZeroMemory(&m_rAuth, sizeof(m_rAuth)); ZeroMemory(&m_rMessage, sizeof(SMTPMESSAGE2)); m_fDSNAvail= FALSE; } // -------------------------------------------------------------------------------- // CSMTPTransport::~CSMTPTransport // -------------------------------------------------------------------------------- CSMTPTransport::~CSMTPTransport(void) { ResetBase(); DllRelease(); } // -------------------------------------------------------------------------------- // CSMTPTransport::ResetBase // -------------------------------------------------------------------------------- void CSMTPTransport::ResetBase(void) { EnterCriticalSection(&m_cs); FreeAuthInfo(&m_rAuth); m_command = SMTP_NONE; m_fSendMessage = FALSE; m_iAddress = 0; m_cRecipients = 0; m_cbSent = 0; m_fSTARTTLSAvail = FALSE; m_fTLSNegotiation = FALSE; m_fSecured = FALSE; SafeRelease(m_rMessage.smtpMsg.pstmMsg); SafeMemFree(m_rMessage.smtpMsg.rAddressList.prgAddress); SafeMemFree(m_rMessage.pszDSNENVID); ZeroMemory(&m_rMessage, sizeof(SMTPMESSAGE2)); m_fDSNAvail= FALSE; LeaveCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // CSMTPTransport::QueryInterface // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::QueryInterface(REFIID riid, LPVOID *ppv) { // Locals HRESULT hr=S_OK; // Bad param if (ppv == NULL) { hr = TrapError(E_INVALIDARG); goto exit; } // Init *ppv=NULL; // IID_IUnknown if (IID_IUnknown == riid) *ppv = ((IUnknown *)(ISMTPTransport2 *)this); // IID_IInternetTransport else if (IID_IInternetTransport == riid) *ppv = ((IInternetTransport *)(CIxpBase *)this); // IID_ISMTPTransport else if (IID_ISMTPTransport == riid) *ppv = (ISMTPTransport *)this; // IID_ISMTPTransport2 else if (IID_ISMTPTransport2 == riid) *ppv = (ISMTPTransport2 *)this; // If not null, addref it and return if (NULL != *ppv) { ((LPUNKNOWN)*ppv)->AddRef(); goto exit; } // No Interface hr = TrapError(E_NOINTERFACE); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::AddRef // -------------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CSMTPTransport::AddRef(void) { return ++m_cRef; } // -------------------------------------------------------------------------------- // CSMTPTransport::Release // -------------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CSMTPTransport::Release(void) { if (0 != --m_cRef) return m_cRef; delete this; return 0; } // -------------------------------------------------------------------------------- // CSMTPTransport::HandsOffCallback // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::HandsOffCallback(void) { return CIxpBase::HandsOffCallback(); } // -------------------------------------------------------------------------------- // CSMTPTransport::GetStatus // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::GetStatus(IXPSTATUS *pCurrentStatus) { return CIxpBase::GetStatus(pCurrentStatus); } // -------------------------------------------------------------------------------- // CSMTPTransport::InitNew // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::InitNew(LPSTR pszLogFilePath, ISMTPCallback *pCallback) { return CIxpBase::OnInitNew("SMTP", pszLogFilePath, FILE_SHARE_READ | FILE_SHARE_WRITE, (ITransportCallback *)pCallback); } // -------------------------------------------------------------------------------- // CSMTPTransport::InetServerFromAccount // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::InetServerFromAccount(IImnAccount *pAccount, LPINETSERVER pInetServer) { return CIxpBase::InetServerFromAccount(pAccount, pInetServer); } // -------------------------------------------------------------------------------- // CSMTPTransport::Connect // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::Connect(LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging) { // Check if user wants us to always prompt for password. Prompt before we connect // to avoid inactivity disconnections if (ISFLAGSET(pInetServer->dwFlags, ISF_ALWAYSPROMPTFORPASSWORD)) { HRESULT hr; if (NULL != m_pCallback) hr = m_pCallback->OnLogonPrompt(pInetServer, SMTPTHISIXP); if (NULL == m_pCallback || S_OK != hr) return IXP_E_USER_CANCEL; } return CIxpBase::Connect(pInetServer, fAuthenticate, fCommandLogging); } // -------------------------------------------------------------------------------- // CSMTPTransport::DropConnection // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::DropConnection(void) { return CIxpBase::DropConnection(); } // -------------------------------------------------------------------------------- // CSMTPTransport::Disconnect // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::Disconnect(void) { return CIxpBase::Disconnect(); } // -------------------------------------------------------------------------------- // CSMTPTransport::IsState // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::IsState(IXPISSTATE isstate) { return CIxpBase::IsState(isstate); } // -------------------------------------------------------------------------------- // CSMTPTransport::GetServerInfo // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::GetServerInfo(LPINETSERVER pInetServer) { return CIxpBase::GetServerInfo(pInetServer); } // -------------------------------------------------------------------------------- // CSMTPTransport::GetIXPType // -------------------------------------------------------------------------------- STDMETHODIMP_(IXPTYPE) CSMTPTransport::GetIXPType(void) { return CIxpBase::GetIXPType(); } // -------------------------------------------------------------------------------- // CSMTPTransport::SendMessage // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::SendMessage(LPSMTPMESSAGE pMessage) { SMTPMESSAGE2 pMessage2= {0}; pMessage2.smtpMsg= *pMessage; return SendMessage2(&pMessage2); } // -------------------------------------------------------------------------------- // CSMTPTransport::SendMessage2 // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::SendMessage2(LPSMTPMESSAGE2 pMessage) { // Locals HRESULT hr=S_OK; BOOL fDSNAvail= FALSE; // check params if (NULL == pMessage || NULL == pMessage->smtpMsg.pstmMsg) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Enter Busy CHECKHR(hr = HrEnterBusy()); // Zero Init Current State fDSNAvail = m_fDSNAvail; // save DSN state! ResetBase(); m_fDSNAvail = fDSNAvail; // Special State in this transport m_fSendMessage = TRUE; // Copy Mesage m_rMessage.smtpMsg.pstmMsg = pMessage->smtpMsg.pstmMsg; m_rMessage.smtpMsg.pstmMsg->AddRef(); // Copy the Address List m_rMessage.smtpMsg.rAddressList.cAddress = pMessage->smtpMsg.rAddressList.cAddress; CHECKHR(hr = HrAlloc((LPVOID *)&m_rMessage.smtpMsg.rAddressList.prgAddress, sizeof(INETADDR) * m_rMessage.smtpMsg.rAddressList.cAddress)); CopyMemory(m_rMessage.smtpMsg.rAddressList.prgAddress, pMessage->smtpMsg.rAddressList.prgAddress, sizeof(INETADDR) * m_rMessage.smtpMsg.rAddressList.cAddress); // Copy the message Size m_rMessage.smtpMsg.cbSize = pMessage->smtpMsg.cbSize; // Copy DSN data if(pMessage->pszDSNENVID) { // ENVID max length is 100 characters ULONG cbAlloc= max(lstrlen(pMessage->pszDSNENVID) + 1, 101); CHECKALLOC(m_rMessage.pszDSNENVID = (LPSTR)g_pMalloc->Alloc(cbAlloc)); StrCpyN(m_rMessage.pszDSNENVID, pMessage->pszDSNENVID, cbAlloc); } m_rMessage.dsnRet = pMessage->dsnRet; // Send RSET command (this initiates a send) if (m_fReset) { // Send the RSET command CHECKHR(hr = CommandRSET()); } // Otherwise, start sending this message else { // Start sending this message SendMessage_MAIL(); // A reset will be needed m_fReset = TRUE; } // return warning if client requested DSN but it isn't available if((m_rServer.dwFlags & ISF_QUERYDSNSUPPORT) && !m_fDSNAvail) hr= IXP_S_SMTP_NO_DSN_SUPPORT; exit: // Failure if (FAILED(hr)) { ResetBase(); LeaveBusy(); } // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::OnNotify // -------------------------------------------------------------------------------- void CSMTPTransport::OnNotify(ASYNCSTATE asOld, ASYNCSTATE asNew, ASYNCEVENT ae) { // Enter Critical Section EnterCriticalSection(&m_cs); switch(ae) { // -------------------------------------------------------------------------------- case AE_RECV: OnSocketReceive(); break; // -------------------------------------------------------------------------------- case AE_SENDDONE: if (SMTP_SEND_STREAM == m_command) { // Leave Busy State LeaveBusy(); // Send Dot Command HRESULT hr = CommandDOT(); // Failure Causes Send Stream Response to finish if (FAILED(hr)) SendStreamResponse(TRUE, hr, 0); } break; // -------------------------------------------------------------------------------- case AE_WRITE: if (SMTP_DOT == m_command || SMTP_SEND_STREAM == m_command) SendStreamResponse(FALSE, S_OK, m_pSocket->UlGetSendByteCount()); break; // -------------------------------------------------------------------------------- default: CIxpBase::OnNotify(asOld, asNew, ae); break; } // Leave Critical Section LeaveCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // CSMTPTransport::OnEnterBusy // -------------------------------------------------------------------------------- void CSMTPTransport::OnEnterBusy(void) { IxpAssert(m_command == SMTP_NONE); } // -------------------------------------------------------------------------------- // CSMTPTransport::OnLeaveBusy // -------------------------------------------------------------------------------- void CSMTPTransport::OnLeaveBusy(void) { m_command = SMTP_NONE; } // -------------------------------------------------------------------------------- // CSMTPTransport::OnConnected // -------------------------------------------------------------------------------- void CSMTPTransport::OnConnected(void) { if (FALSE == m_fTLSNegotiation) { m_command = SMTP_BANNER; CIxpBase::OnConnected(); } else { HRESULT hr = S_OK; CIxpBase::OnConnected(); // Clear out the TLS state m_fSecured = TRUE; // Clear out info from the banner m_fSTARTTLSAvail = FALSE; FreeAuthInfo(&m_rAuth); // Performing auth if (m_fConnectAuth) { // If we aren't doing sicily authenication or querying DSN // then just send a HELO message if ((FALSE == m_rServer.fTrySicily) && (0 == (m_rServer.dwFlags & ISF_QUERYAUTHSUPPORT)) && (0 == (m_rServer.dwFlags & ISF_QUERYDSNSUPPORT))) { // Issue HELO hr = CommandHELO(); if (FAILED(hr)) { OnError(hr); DropConnection(); } } else { // Issue EHLO hr = CommandEHLO(); if (FAILED(hr)) { OnError(hr); DropConnection(); } } // We've finished doing negotiation m_fTLSNegotiation = FALSE; } // Otherwise, were connected, user can send HELO command else { m_command = SMTP_CONNECTED; DispatchResponse(S_OK, TRUE); } // Were not authenticated yet m_fAuthenticated = FALSE; } return; } // -------------------------------------------------------------------------------- // CSMTPTransport::OnDisconnect // -------------------------------------------------------------------------------- void CSMTPTransport::OnDisconnected(void) { ResetBase(); m_fReset = FALSE; CIxpBase::OnDisconnected(); } // -------------------------------------------------------------------------------- // CSMTPTransport::OnSocketReceive // -------------------------------------------------------------------------------- void CSMTPTransport::OnSocketReceive(void) { // Locals HRESULT hr=S_OK; // Enter Critical Section EnterCriticalSection(&m_cs); // Read Server Response... hr = HrGetResponse(); if (IXP_E_INCOMPLETE == hr) goto exit; // Handle smtp state switch(m_command) { // -------------------------------------------------------------------------------- case SMTP_BANNER: // Dispatch the Response DispatchResponse(hr, TRUE); // Failure, were done if (SUCCEEDED(hr)) { // Performing auth if (m_fConnectAuth) { // If we aren't doing sicily authenication or // SSL security via STARTTLS or querying for DSN then just send a HELO message if ((FALSE == m_rServer.fTrySicily) && (0 == (m_rServer.dwFlags & ISF_QUERYAUTHSUPPORT)) && (FALSE == m_fConnectTLS) && (0 == (m_rServer.dwFlags & ISF_QUERYDSNSUPPORT))) { // Issue HELO hr = CommandHELO(); if (FAILED(hr)) { OnError(hr); DropConnection(); } } else { // Issue EHLO hr = CommandEHLO(); if (FAILED(hr)) { OnError(hr); DropConnection(); } } } // Otherwise, were connected, user can send HELO command else { m_command = SMTP_CONNECTED; DispatchResponse(S_OK, TRUE); } // Were not authenticated yet m_fAuthenticated = FALSE; } // Done break; // -------------------------------------------------------------------------------- case SMTP_HELO: // Dispatch the Response DispatchResponse(hr, TRUE); // Failure, were done if (SUCCEEDED(hr)) { // Were performing AUTH if (m_fConnectAuth) { // Were authenticated m_fAuthenticated = TRUE; // Authorized OnAuthorized(); } } break; // -------------------------------------------------------------------------------- case SMTP_EHLO: // Are we just trying to negotiate a SSL connection // EHLO Response if (FALSE == m_fTLSNegotiation) { OnEHLOResponse(m_pszResponse); } // Failure, were done if (m_fConnectAuth) { // Do we need to do STARTTLS? if ((FALSE != m_fConnectTLS) && (FALSE == m_fSecured)) { if (SUCCEEDED(hr)) { if (FALSE == m_fTLSNegotiation) { // Start TLS negotiation StartTLS(); } else { TryNextSecurityPkg(); } } else { OnError(hr); DropConnection(); } } else { // Dispatch Response, always success... DispatchResponse(S_OK, TRUE); // Success ? if (SUCCEEDED(hr)) { // No Auth Tokens, just try normal authentication if (m_rAuth.cAuthToken <= 0) { // Were authenticated m_fAuthenticated = TRUE; // Authorized OnAuthorized(); } // Otherwise, start sasl else { // StartLogon StartLogon(); } } // Otherwise, just try the HELO command else { // Issue HELO hr = CommandHELO(); if (FAILED(hr)) { OnError(hr); DropConnection(); } } } } // Otherwise, just dispatch the Response else DispatchResponse(hr, TRUE); break; // -------------------------------------------------------------------------------- case SMTP_AUTH: Assert(m_rAuth.authstate != AUTH_ENUMPACKS_DATA) // Authenticating if (m_fConnectAuth) ResponseAUTH(hr); else DispatchResponse(hr, TRUE); break; // -------------------------------------------------------------------------------- case SMTP_RSET: // Dispatch the Response if (FALSE == m_fConnectAuth) DispatchResponse(hr, TRUE); // Failure, were done if (SUCCEEDED(hr)) { // If sending message, start it... if (m_fSendMessage) SendMessage_MAIL(); } break; // -------------------------------------------------------------------------------- case SMTP_MAIL: // Dispatch the Response DispatchResponse(hr, TRUE); if (SUCCEEDED(hr)) { // Doing a Send Message.. if (m_fSendMessage) SendMessage_RCPT(); } break; // -------------------------------------------------------------------------------- case SMTP_RCPT: // Dispatch the Response DispatchResponse(hr, TRUE); if (SUCCEEDED(hr)) { // Doing a Send Message.. if (m_fSendMessage) SendMessage_RCPT(); } break; // -------------------------------------------------------------------------------- case SMTP_DATA: // Dispatch the Response DispatchResponse(hr, TRUE); if (SUCCEEDED(hr)) { // Doing a Send Message.. if (m_fSendMessage) { // Send the data stream hr = SendDataStream(m_rMessage.smtpMsg.pstmMsg, m_rMessage.smtpMsg.cbSize); if (FAILED(hr)) { SendMessage_DONE(hr); } } } break; // -------------------------------------------------------------------------------- case SMTP_DOT: // Dispatch the response DispatchResponse(hr, TRUE); if (SUCCEEDED(hr)) { // If doing a send message if (m_fSendMessage) SendMessage_DONE(S_OK); } break; // -------------------------------------------------------------------------------- case SMTP_QUIT: // Doing a Send Message..were not done until disconnected. DispatchResponse(hr, FALSE); m_pSocket->Close(); break; } exit: // Enter Critical Section LeaveCriticalSection(&m_cs); } // ------------------------------------------------------------------------------------ // CSMTPTransport::SendMessage_DONE // ------------------------------------------------------------------------------------ void CSMTPTransport::SendMessage_DONE(HRESULT hrResult, LPSTR pszProblem) { m_command = SMTP_SEND_MESSAGE; m_fSendMessage = FALSE; m_fReset = TRUE; SafeRelease(m_rMessage.smtpMsg.pstmMsg); DispatchResponse(hrResult, TRUE, pszProblem); SafeMemFree(m_rMessage.smtpMsg.rAddressList.prgAddress); SafeMemFree(m_rMessage.pszDSNENVID); ZeroMemory(&m_rMessage, sizeof(m_rMessage)); } // ------------------------------------------------------------------------------------ // CSMTPTransport::OnEHLOResponse // ------------------------------------------------------------------------------------ void CSMTPTransport::OnEHLOResponse(LPCSTR pszResponse) { // Do we have anything to do? if (NULL == pszResponse || FALSE != m_fTLSNegotiation) goto exit; // DSN support? if (m_rServer.dwFlags & ISF_QUERYDSNSUPPORT) { if (0 == StrCmpNI(pszResponse + 4, g_szSMTPDSN, g_cchSMTPDSN)) { m_fDSNAvail = TRUE; } } // Searching for: 250 STARTTLS if (TRUE == m_fConnectTLS) { if (0 == StrCmpNI(pszResponse + 4, g_szSMTPSTARTTLS08, g_cchSMTPSTARTTLS08)) { m_fSTARTTLSAvail = TRUE; } } // Searching for: 250 AUTH=LOGIN NTLM or 250 AUTH LOGIN NTLM if ((FALSE != m_rServer.fTrySicily) || (0 != (m_rServer.dwFlags & ISF_QUERYAUTHSUPPORT))) { if ((0 == StrCmpNI(pszResponse + 4, g_szSMTPAUTH11, g_cchSMTPAUTH11)) || (0 == StrCmpNI(pszResponse + 4, g_szSMTPAUTH10, g_cchSMTPAUTH10))) { // If we haven't read the tokens yet... if (0 == m_rAuth.cAuthToken) { // Locals CStringParser cString; CHAR chToken; // State Check Assert(m_rAuth.cAuthToken == 0); // Set the Members cString.Init(pszResponse + 9, lstrlen(pszResponse + 9), PSF_NOTRAILWS | PSF_NOFRONTWS); // Parse tokens while(1) { // Set Parse Tokens chToken = cString.ChParse(" "); if (0 == cString.CchValue()) break; // Can't take any more if (m_rAuth.cAuthToken == MAX_AUTH_TOKENS) { Assert(FALSE); break; } // Store the auth type m_rAuth.rgpszAuthTokens[m_rAuth.cAuthToken] = PszDupA(cString.PszValue()); if (m_rAuth.rgpszAuthTokens[m_rAuth.cAuthToken]) m_rAuth.cAuthToken++; } } } } exit: return; } // ------------------------------------------------------------------------------------ // CSMTPTransport::_PszGetCurrentAddress // ------------------------------------------------------------------------------------ LPSTR CSMTPTransport::_PszGetCurrentAddress(void) { return (*m_szEmail == '\0') ? NULL : m_szEmail; } // ------------------------------------------------------------------------------------ // CSMTPTransport::DispatchResponse // ------------------------------------------------------------------------------------ void CSMTPTransport::DispatchResponse(HRESULT hrResult, BOOL fDone, LPSTR pszProblem) { // Locals SMTPRESPONSE rResponse; // If not in SendMessage if (FALSE == m_fSendMessage) { // Clear the Response ZeroMemory(&rResponse, sizeof(SMTPRESPONSE)); // Set the HRESULT rResponse.command = m_command; rResponse.fDone = fDone; rResponse.rIxpResult.pszResponse = m_pszResponse; rResponse.rIxpResult.hrResult = hrResult; rResponse.rIxpResult.uiServerError = m_uiResponse; rResponse.rIxpResult.hrServerError = m_hrResponse; rResponse.rIxpResult.dwSocketError = m_pSocket->GetLastError(); rResponse.rIxpResult.pszProblem = NULL; rResponse.pTransport = this; // Map HRESULT and set problem... if (FAILED(hrResult)) { // Handle Rejected Sender if (SMTP_MAIL == m_command) { rResponse.rIxpResult.hrResult = IXP_E_SMTP_REJECTED_SENDER; rResponse.rIxpResult.pszProblem = _PszGetCurrentAddress(); } // Handle Rejected Recipient else if (SMTP_RCPT == m_command) { rResponse.rIxpResult.hrResult = IXP_E_SMTP_REJECTED_RECIPIENTS; rResponse.rIxpResult.pszProblem = _PszGetCurrentAddress(); } } // Finished... if (fDone) { // No current command m_command = SMTP_NONE; // Leave Busy State LeaveBusy(); } // Give the Response to the client if (m_pCallback) ((ISMTPCallback *)m_pCallback)->OnResponse(&rResponse); // Reset Last Response SafeMemFree(m_pszResponse); m_hrResponse = S_OK; m_uiResponse = 0; } // Otherwise, if FAILED else if (FAILED(hrResult)) { // Handle Rejected Sender if (SMTP_MAIL == m_command) SendMessage_DONE(IXP_E_SMTP_REJECTED_SENDER, _PszGetCurrentAddress()); // Handle Rejected Recipient else if (SMTP_RCPT == m_command) SendMessage_DONE(IXP_E_SMTP_REJECTED_RECIPIENTS, _PszGetCurrentAddress()); // General Failure else SendMessage_DONE(hrResult); } } // ------------------------------------------------------------------------------------ // CSMTPTransport::HrGetResponse // ------------------------------------------------------------------------------------ HRESULT CSMTPTransport::HrGetResponse(void) { // Locals HRESULT hr = S_OK; INT cbLine = 0; BOOL fKnownResponse = TRUE; BOOL fComplete = FALSE; BOOL fMoreLinesNeeded = FALSE; // Clear current response IxpAssert(m_pszResponse == NULL && m_hrResponse == S_OK); // We received a line from the host $$ERROR$$ - How do I know if there are more lines while(1) { // Read the line IxpAssert(m_pszResponse == NULL); hr = HrReadLine(&m_pszResponse, &cbLine, &fComplete); if (FAILED(hr)) { hr = TRAPHR(IXP_E_SOCKET_READ_ERROR); goto exit; } // Not complete if (!fComplete) { if (FALSE != fMoreLinesNeeded) { hr = IXP_E_INCOMPLETE; } goto exit; } // Parse the response code if ((cbLine < 3) || (m_pszResponse == NULL) || (m_pszResponse[0] < '0' || m_pszResponse[0] > '9') || (m_pszResponse[1] < '0' || m_pszResponse[1] > '9') || (m_pszResponse[2] < '0' || m_pszResponse[2] > '9')) { hr = TrapError(IXP_E_SMTP_RESPONSE_ERROR); if (m_pCallback && m_fCommandLogging) m_pCallback->OnCommand(CMD_RESP, m_pszResponse, hr, SMTPTHISIXP); goto exit; } // Ignores continuation lines for now if ((cbLine >= 4) && (m_pszResponse[3] == '-')) { // Locals SMTPRESPONSE rResponse; // General command if (m_pCallback && m_fCommandLogging) m_pCallback->OnCommand(CMD_RESP, m_pszResponse, IXP_S_SMTP_CONTINUE, SMTPTHISIXP); // Clear the Response ZeroMemory(&rResponse, sizeof(SMTPRESPONSE)); // Set the HRESULT rResponse.command = m_command; rResponse.fDone = FALSE; rResponse.rIxpResult.pszResponse = m_pszResponse; rResponse.rIxpResult.hrResult = IXP_S_SMTP_CONTINUE; rResponse.rIxpResult.uiServerError = 0; rResponse.rIxpResult.hrServerError = S_OK; rResponse.rIxpResult.dwSocketError = 0; rResponse.rIxpResult.pszProblem = NULL; rResponse.pTransport = this; // Give the Response to the client if (m_pCallback) ((ISMTPCallback *)m_pCallback)->OnResponse(&rResponse); // EHLO Response if (SMTP_EHLO == m_command) OnEHLOResponse(m_pszResponse); // Reset Last Response SafeMemFree(m_pszResponse); m_hrResponse = S_OK; m_uiResponse = 0; // We still need to get more lines from the server fMoreLinesNeeded = TRUE; // Continue continue; } // Not a valid SMTP response line. if ((cbLine >= 4) && (m_pszResponse[3] != ' ')) { hr = TrapError(IXP_E_SMTP_RESPONSE_ERROR); if (m_pCallback && m_fCommandLogging) m_pCallback->OnCommand(CMD_RESP, m_pszResponse, hr, SMTPTHISIXP); goto exit; } // Done break; } // Compute Actual Response code m_uiResponse = (m_pszResponse[0] - '0') * 100 + (m_pszResponse[1] - '0') * 10 + (m_pszResponse[2] - '0'); // Assume it is not recognized switch(m_uiResponse) { case 500: hr = IXP_E_SMTP_500_SYNTAX_ERROR; break; case 501: hr = IXP_E_SMTP_501_PARAM_SYNTAX; break; case 502: hr = IXP_E_SMTP_502_COMMAND_NOTIMPL; break; case 503: hr = IXP_E_SMTP_503_COMMAND_SEQ; break; case 504: hr = IXP_E_SMTP_504_COMMAND_PARAM_NOTIMPL; break; case 421: hr = IXP_E_SMTP_421_NOT_AVAILABLE; break; case 450: hr = IXP_E_SMTP_450_MAILBOX_BUSY; break; case 550: hr = IXP_E_SMTP_550_MAILBOX_NOT_FOUND; break; case 451: hr = IXP_E_SMTP_451_ERROR_PROCESSING; break; case 551: hr = IXP_E_SMTP_551_USER_NOT_LOCAL; break; case 452: hr = IXP_E_SMTP_452_NO_SYSTEM_STORAGE; break; case 552: hr = IXP_E_SMTP_552_STORAGE_OVERFLOW; break; case 553: hr = IXP_E_SMTP_553_MAILBOX_NAME_SYNTAX; break; case 554: hr = IXP_E_SMTP_554_TRANSACT_FAILED; break; case 211: hr = IXP_S_SMTP_211_SYSTEM_STATUS; break; case 214: hr = IXP_S_SMTP_214_HELP_MESSAGE; break; case 220: hr = IXP_S_SMTP_220_READY; break; case 221: hr = IXP_S_SMTP_221_CLOSING; break; case 250: hr = IXP_S_SMTP_250_MAIL_ACTION_OKAY; break; case 251: hr = IXP_S_SMTP_251_FORWARDING_MAIL; break; case 354: hr = IXP_S_SMTP_354_START_MAIL_INPUT; break; case 334: hr = IXP_S_SMTP_334_AUTH_READY_RESPONSE; break; case 235: hr = IXP_S_SMTP_245_AUTH_SUCCESS; break; case 454: hr = IXP_E_SMTP_454_STARTTLS_FAILED; break; case 530: hr = IXP_E_SMTP_530_STARTTLS_REQUIRED; break; default: hr = IXP_E_SMTP_UNKNOWN_RESPONSE_CODE; fKnownResponse = FALSE; break; } // Set hr m_hrResponse = hr; // Give to callback if (m_pCallback && m_fCommandLogging) m_pCallback->OnCommand(CMD_RESP, m_pszResponse, hr, SMTPTHISIXP); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::_HrFormatAddressString // -------------------------------------------------------------------------------- HRESULT CSMTPTransport::_HrFormatAddressString(LPCSTR pszEmail, LPCSTR pszExtra, LPSTR *ppszAddress) { // Locals HRESULT hr=S_OK; ULONG cchAlloc; // Invalid Arg Assert(pszEmail && ppszAddress); cchAlloc= lstrlen(pszEmail) + 3; // length of pszEmail plus <> and a null term if(pszExtra && pszExtra[0]) cchAlloc += lstrlen(pszExtra) + 1; // length of pszExtra plus a space // Allocate string CHECKALLOC(*ppszAddress = (LPSTR)g_pMalloc->Alloc(cchAlloc * sizeof((*ppszAddress)[0]))); // Format the String wnsprintf(*ppszAddress, cchAlloc, "<%s>", pszEmail); if(pszExtra && pszExtra[0]) { StrCatBuff(*ppszAddress, " ", cchAlloc); StrCatBuff(*ppszAddress, pszExtra, cchAlloc); } exit: // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandMAIL // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandMAIL(LPSTR pszEmailFrom) { // Locals HRESULT hr=S_OK; LPSTR pszAddress=NULL; CHAR szDSNData[128]; szDSNData[0]= '\0'; // Check params if (NULL == pszEmailFrom) return TrapError(E_INVALIDARG); // build DSN parameters if necessary if(m_fDSNAvail) { if(DSNRET_DEFAULT != m_rMessage.dsnRet) { StrCatBuff(szDSNData, g_szDSNRET, ARRAYSIZE(szDSNData)); if(m_rMessage.dsnRet == DSNRET_HDRS) StrCatBuff(szDSNData, g_szDSNHDRS, ARRAYSIZE(szDSNData)); else if(DSNRET_FULL == m_rMessage.dsnRet) StrCatBuff(szDSNData, g_szDSNFULL, ARRAYSIZE(szDSNData)); } if(m_rMessage.pszDSNENVID) { if(szDSNData[0]) StrCatBuff(szDSNData, " ", ARRAYSIZE(szDSNData)); StrCatBuff(szDSNData, g_szDSNENVID, ARRAYSIZE(szDSNData)); StrCatBuff(szDSNData, m_rMessage.pszDSNENVID, ARRAYSIZE(szDSNData)); } } // Put pszEmailFrom into CHECKHR(hr = _HrFormatAddressString(pszEmailFrom, szDSNData, &pszAddress)); // Send Command hr = HrSendCommand((LPSTR)SMTP_MAIL_STR, pszAddress, !m_fSendMessage); if (SUCCEEDED(hr)) { StrCpyN(m_szEmail, pszEmailFrom, ARRAYSIZE(m_szEmail)); m_command = SMTP_MAIL; } exit: // Cleanup SafeMemFree(pszAddress); // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandRCPT // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandRCPT(LPSTR pszEmailTo) { return CommandRCPT2(pszEmailTo, (INETADDRTYPE)0); } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandRCPT2 // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandRCPT2(LPSTR pszEmailTo, INETADDRTYPE atDSN) { // Locals HRESULT hr=S_OK; LPSTR pszAddress=NULL; CHAR szDSNData[32]; int iatDSN= atDSN; szDSNData[0]= '\0'; // Check params if (NULL == pszEmailTo) return TrapError(E_INVALIDARG); if ((atDSN & ~ADDR_DSN_MASK) || ((atDSN & ADDR_DSN_NEVER) && (atDSN & ~ADDR_DSN_NEVER))) return TrapError(E_INVALIDARG); // build DSN parameters if necessary if(m_fDSNAvail && atDSN) { StrCatBuff(szDSNData, g_szDSNNOTIFY, ARRAYSIZE(szDSNData)); if(atDSN & ADDR_DSN_NEVER) StrCatBuff(szDSNData, g_szDSNNEVER, ARRAYSIZE(szDSNData)); else { bool fPrev= false; if(atDSN & ADDR_DSN_SUCCESS) { StrCatBuff(szDSNData, g_szDSNSUCCESS, ARRAYSIZE(szDSNData)); fPrev= true; } if(atDSN & ADDR_DSN_FAILURE) { if(fPrev) StrCatBuff(szDSNData, ",", ARRAYSIZE(szDSNData)); StrCatBuff(szDSNData, g_szDSNFAILURE, ARRAYSIZE(szDSNData)); fPrev= true; } if(atDSN & ADDR_DSN_DELAY) { if(fPrev) StrCatBuff(szDSNData, ",", ARRAYSIZE(szDSNData)); StrCatBuff(szDSNData, g_szDSNDELAY, ARRAYSIZE(szDSNData)); } } } // Put pszEmailFrom into CHECKHR(hr = _HrFormatAddressString(pszEmailTo, szDSNData, &pszAddress)); // Send Command hr = HrSendCommand((LPSTR)SMTP_RCPT_STR, pszAddress, !m_fSendMessage); if (SUCCEEDED(hr)) { StrCpyN(m_szEmail, pszEmailTo, ARRAYSIZE(m_szEmail)); m_command = SMTP_RCPT; } exit: // Cleanup SafeMemFree(pszAddress); // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandEHLO // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandEHLO(void) { return _HrHELO_Or_EHLO(SMTP_EHLO_STR, SMTP_EHLO); } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandHELO // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandHELO(void) { return _HrHELO_Or_EHLO(SMTP_HELO_STR, SMTP_HELO); } // -------------------------------------------------------------------------------- // CSMTPTransport::_HrHELO_Or_EHLO // -------------------------------------------------------------------------------- HRESULT CSMTPTransport::_HrHELO_Or_EHLO(LPCSTR pszCommand, SMTPCOMMAND eNewCommand) { // Locals HRESULT hr=S_OK; // Use an IP address if (ISFLAGSET(m_rServer.dwFlags, ISF_SMTP_USEIPFORHELO)) { // Locals LPHOSTENT pHost=NULL; SOCKADDR_IN sa; // Get Host by name pHost = gethostbyname(SzGetLocalHostName()); // Cast ip sa.sin_addr.s_addr = (ULONG)(*(DWORD *)pHost->h_addr); // Send HELO, quit and die if it fails hr = HrSendCommand((LPSTR)pszCommand, inet_ntoa(sa.sin_addr), !m_fSendMessage && !m_fTLSNegotiation); if (SUCCEEDED(hr)) m_command = eNewCommand; } // Otherwise, this code uses a host name to do the ehlo or helo command else { // Locals CHAR szLocalHost[255]; LPSTR pszHost=SzGetLocalHostName(); // Get legal local host name #ifdef DEBUG StripIllegalHostChars("GTE/Athena", szLocalHost, ARRAYSIZE(szLocalHost)); StripIllegalHostChars("foobar.", szLocalHost, ARRAYSIZE(szLocalHost)); StripIllegalHostChars("127.256.34.23", szLocalHost, ARRAYSIZE(szLocalHost)); StripIllegalHostChars("¤56foo1", szLocalHost, ARRAYSIZE(szLocalHost)); #endif // Get legal local host name StripIllegalHostChars(pszHost, szLocalHost, ARRAYSIZE(szLocalHost)); // Send HELO, quit and die if it fails hr = HrSendCommand((LPSTR)pszCommand, szLocalHost, !m_fSendMessage && !m_fTLSNegotiation); if (SUCCEEDED(hr)) m_command = eNewCommand; } // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::DoQuit // -------------------------------------------------------------------------------- void CSMTPTransport::DoQuit(void) { CommandQUIT(); } // ------------------------------------------------------------------------------------ // CSMTPTransport::CommandAUTH // ------------------------------------------------------------------------------------ STDMETHODIMP CSMTPTransport::CommandAUTH(LPSTR pszAuthType) { // check params if (NULL == pszAuthType) return TrapError(E_INVALIDARG); // Do the command HRESULT hr = HrSendCommand((LPSTR)SMTP_AUTH_STR, pszAuthType, !m_fConnectAuth); if (SUCCEEDED(hr)) m_command = SMTP_AUTH; // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandQUIT // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandQUIT(void) { // Send QUIT OnStatus(IXP_DISCONNECTING); HRESULT hr = HrSendCommand((LPSTR)SMTP_QUIT_STR, NULL, !m_fSendMessage); if (SUCCEEDED(hr)) m_command = SMTP_QUIT; return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandRSET // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandRSET(void) { // Send Command HRESULT hr = HrSendCommand((LPSTR)SMTP_RSET_STR, NULL, !m_fSendMessage); if (SUCCEEDED(hr)) m_command = SMTP_RSET; return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandDATA // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandDATA(void) { // Send Command HRESULT hr = HrSendCommand((LPSTR)SMTP_DATA_STR, NULL, !m_fSendMessage); if (SUCCEEDED(hr)) m_command = SMTP_DATA; return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::CommandDOT // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::CommandDOT(void) { // Send Command HRESULT hr = HrSendCommand((LPSTR)SMTP_END_DATA_STR, NULL, !m_fSendMessage); if (SUCCEEDED(hr)) m_command = SMTP_DOT; return hr; } // ------------------------------------------------------------------------------------ // CSMTPTransport::CommandSTARTTLS // ------------------------------------------------------------------------------------ HRESULT CSMTPTransport::CommandSTARTTLS(void) { // Locals HRESULT hr=S_OK; // Is StartTLS supported? if(FALSE == m_fSTARTTLSAvail) { hr= IXP_E_SMTP_NO_STARTTLS_SUPPORT; goto exit; } // Do the command hr = HrSendCommand((LPSTR)SMTP_STARTTLS_STR, NULL, !m_fConnectAuth); if (SUCCEEDED(hr)) m_fTLSNegotiation = TRUE; // Done exit: return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::SendDataStream // -------------------------------------------------------------------------------- STDMETHODIMP CSMTPTransport::SendDataStream(IStream *pStream, ULONG cbSize) { // Locals HRESULT hr=S_OK; INT cb; // check params if (NULL == pStream) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Busy... if (m_fSendMessage == FALSE) { CHECKHR(hr = HrEnterBusy()); } // Save Total Size m_cbSent = 0; m_cbTotal = cbSize; // Send the stream, if it fails, move the the next message hr = m_pSocket->SendStream(pStream, &cb, TRUE); if (FAILED(hr)) { // If this is a blocking situation, enter SMTP_SEND_STREAM_RESP if (hr == IXP_E_WOULD_BLOCK) { m_command = SMTP_SEND_STREAM; SendStreamResponse(FALSE, S_OK, cb); hr =S_OK; goto exit; } // Otherwise, someother error else { hr = TrapError(IXP_E_SOCKET_WRITE_ERROR); goto exit; } } // Give send stream response SendStreamResponse(TRUE, S_OK, cb); // Not Busy if (FALSE == m_fSendMessage) LeaveBusy(); // Send DOT CHECKHR(hr = CommandDOT()); exit: // Failure if (FALSE == m_fSendMessage && FAILED(hr)) LeaveBusy(); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CSMTPTransport::SendStreamResponse // -------------------------------------------------------------------------------- void CSMTPTransport::SendStreamResponse(BOOL fDone, HRESULT hrResult, DWORD cbIncrement) { // Locals SMTPRESPONSE rResponse; // Increment Current m_cbSent += cbIncrement; // Set the HRESULT rResponse.command = SMTP_SEND_STREAM; rResponse.fDone = fDone; rResponse.rIxpResult.pszResponse = NULL; rResponse.rIxpResult.hrResult = hrResult; rResponse.rIxpResult.uiServerError = 0; rResponse.rIxpResult.hrServerError = S_OK; rResponse.rIxpResult.dwSocketError = m_pSocket->GetLastError(); rResponse.rIxpResult.pszProblem = NULL; rResponse.pTransport = this; rResponse.rStreamInfo.cbIncrement = cbIncrement; rResponse.rStreamInfo.cbCurrent = m_cbSent; rResponse.rStreamInfo.cbTotal = m_cbTotal; // Finished... if (fDone) { // No current command m_command = SMTP_NONE; // Leave Busy State LeaveBusy(); } // Give the Response to the client if (m_pCallback) ((ISMTPCallback *)m_pCallback)->OnResponse(&rResponse); } // -------------------------------------------------------------------------------- // CSMTPTransport::SendMAIL // -------------------------------------------------------------------------------- void CSMTPTransport::SendMessage_MAIL(void) { // Locals HRESULT hr=S_OK; ULONG i; LPINETADDR pInetAddress; // Loop address list for (i=0; iaddrtype & ADDR_TOFROM_MASK)) { // Save index of sender m_iAddress = 0; // Send Command hr = CommandMAIL(pInetAddress->szEmail); if (FAILED(hr)) SendMessage_DONE(hr); // Done return; } } // No Sender SendMessage_DONE(TrapError(IXP_E_SMTP_NO_SENDER)); } // -------------------------------------------------------------------------------- // CSMTPTransport::SendMessage_RCPT // -------------------------------------------------------------------------------- void CSMTPTransport::SendMessage_RCPT(void) { // Locals HRESULT hr=S_OK; ULONG i; LPINETADDR pInetAddress; // Find next ADDR_TO, starting with m_rCurrent.iRcptAddrList IxpAssert(m_iAddress <= m_rMessage.smtpMsg.rAddressList.cAddress); for(i=m_iAddress; iaddrtype & ADDR_TOFROM_MASK)) { // Count recipients m_cRecipients++; // Send Command hr = CommandRCPT2(pInetAddress->szEmail, (INETADDRTYPE)(pInetAddress->addrtype & ADDR_DSN_MASK)); if (FAILED(hr)) SendMessage_DONE(hr); else { m_iAddress = i + 1; m_cRecipients++; } // Done return; } } // If no recipients if (0 == m_cRecipients) SendMessage_DONE(TrapError(IXP_E_SMTP_NO_RECIPIENTS)); // Otherwise, were done with rcpt, lets send the message else { hr = CommandDATA(); if (FAILED(hr)) SendMessage_DONE(hr); } } // ------------------------------------------------------------------------------------ // CSMTPTransport::StartLogon // ------------------------------------------------------------------------------------ void CSMTPTransport::StartLogon(void) { // Locals HRESULT hr; // Progress OnStatus(IXP_AUTHORIZING); // Free current packages... if (NULL == m_rAuth.pPackages) { // If Not Using Sicily or its not installed, then send USER command SSPIGetPackages(&m_rAuth.pPackages, &m_rAuth.cPackages); } // ResponseAUTH TryNextAuthPackage(); // Done return; } // ------------------------------------------------------------------------------------ // CSMTPTransport::LogonRetry // ------------------------------------------------------------------------------------ void CSMTPTransport::LogonRetry(void) { // Locals HRESULT hr=S_OK; // Auth Retry OnStatus(IXP_AUTHRETRY); // Enter Auth Retry State m_pSocket->Close(); // Logon if (NULL == m_pCallback || m_pCallback->OnLogonPrompt(&m_rServer, SMTPTHISIXP) != S_OK) { // Go to terminal state, were done. OnDisconnected(); return; } // Finding Host Progress OnStatus(IXP_FINDINGHOST); // Connect to server hr = m_pSocket->Connect(); if (FAILED(hr)) { OnError(TrapError(IXP_E_SOCKET_CONNECT_ERROR)); OnDisconnected(); return; } // Reset the secured state m_fSecured = FALSE; // Start WatchDog m_pSocket->StartWatchDog(); } // ------------------------------------------------------------------------------------ // CSMTPTransport::TryNextAuthPackage // ------------------------------------------------------------------------------------ void CSMTPTransport::TryNextAuthPackage(void) { // Locals HRESULT hr=S_OK; BOOL fPackageInstalled; BOOL fLoginMethod=FALSE; ULONG i; // Set auth state m_rAuth.authstate = AUTH_NONE; // Loop through the auth tokens, and try to authenticate with each one in order for (;m_rAuth.iAuthToken < m_rAuth.cAuthToken; m_rAuth.iAuthToken++) { // Assume package is not installed fPackageInstalled = FALSE; // "LOGIN" if (lstrcmpi(m_rAuth.rgpszAuthTokens[m_rAuth.iAuthToken], "LOGIN") == 0) { fLoginMethod = TRUE; fPackageInstalled = TRUE; } // Loop through installed packages else { for (i=0; iStopWatchDog(); // I know how to do this if (lstrcmpi(m_rAuth.rgpszAuthTokens[m_rAuth.iAuthToken], "LOGIN") == 0) { // DoLogonAuth DoLoginAuth(hrResponse); } // Otherwise, we must have just tryed a package else if (m_rAuth.authstate == AUTH_TRYING_PACKAGE) { // DoPackageAuth DoPackageAuth(hrResponse); } // Otherwise, we got a response from a negotiation string else if (m_rAuth.authstate == AUTH_NEGO_RESP) { // DoAuthNegoResponse DoAuthNegoResponse(hrResponse); } // Otherwise, we got a response from a challenge response string else if (m_rAuth.authstate == AUTH_RESP_RESP) { // DoAuthRespResp DoAuthRespResponse(hrResponse); } // Auth was cancelled, try next package else if (m_rAuth.authstate == AUTH_CANCELED) { // Free Current Context SSPIFreeContext(&m_rAuth.rSicInfo); // Goto next package m_rAuth.iAuthToken++; // Try the next package TryNextAuthPackage(); } // Free Current Response SafeMemFree(m_pszResponse); m_hrResponse = S_OK; // Start the WatchDog m_pSocket->StartWatchDog(); } // ------------------------------------------------------------------------------------ // CSMTPTransport::DoLoginAuth // ------------------------------------------------------------------------------------ void CSMTPTransport::DoLoginAuth(HRESULT hrResponse) { // Locals SSPIBUFFER Buffer; // Failure, retry login if (FAILED(hrResponse)) { // I just issued the AUTH LOGIN command, this should not happen if (AUTH_SMTP_LOGIN == m_rAuth.authstate) { // Free Current Context SSPIFreeContext(&m_rAuth.rSicInfo); // Goto next package m_rAuth.iAuthToken++; // Try the next package TryNextAuthPackage(); } // Otherwise, I just issued the AUTH LOGIN USERNAME else if (AUTH_SMTP_LOGIN_USERNAME == m_rAuth.authstate || AUTH_SMTP_LOGIN_PASSWORD == m_rAuth.authstate) { // Retry the Logon LogonRetry(); } else Assert(FALSE); // Done goto exit; } // Should have a response Assert(m_pszResponse); // 334 if ((334 == m_uiResponse) && m_pszResponse) { // Set the Length SSPISetBuffer(m_pszResponse + 4, SSPI_STRING, 0, &Buffer); // Base64 Decode if (FAILED(SSPIDecodeBuffer(TRUE, &Buffer))) { OnError(E_FAIL); DropConnection(); goto exit; } // If the user name is empty, lets retry the login... if (FIsEmptyA(m_rServer.szUserName)) { // LogonRetry LogonRetry(); // Done goto exit; } // Handle Next STep if (StrCmpNI(Buffer.szBuffer, "username:", lstrlen("username:")) == 0) { // Set the Buffer SSPISetBuffer(m_rServer.szUserName, SSPI_STRING, 0, &Buffer); // Encode the User Name if (FAILED(SSPIEncodeBuffer(TRUE, &Buffer))) { OnError(E_FAIL); DropConnection(); goto exit; } // Send the user name if (FSendSicilyString(Buffer.szBuffer)) m_rAuth.authstate = AUTH_SMTP_LOGIN_USERNAME; } // Password else if (StrCmpNI(Buffer.szBuffer, "password:", lstrlen("password:")) == 0) { // Set the Buffer SSPISetBuffer(m_rServer.szPassword, SSPI_STRING, 0, &Buffer); // Encode the password if (FAILED(SSPIEncodeBuffer(TRUE, &Buffer))) { OnError(E_FAIL); DropConnection(); goto exit; } // Send the password if (FSendSicilyString(Buffer.szBuffer)) m_rAuth.authstate = AUTH_SMTP_LOGIN_PASSWORD; } // Bad response from the server else { OnError(E_FAIL); DropConnection(); goto exit; } } // Connected else if (235 == m_uiResponse) { // OnAuthorizied OnAuthorized(); } // Error Response ? else { OnError(E_FAIL); DropConnection(); goto exit; } exit: return; } // ------------------------------------------------------------------------------------ // CSMTPTransport::DoPackageAuth // ------------------------------------------------------------------------------------ void CSMTPTransport::DoPackageAuth(HRESULT hrResponse) { // Locals SSPIBUFFER Negotiate; // Failure, retry login if (FAILED(hrResponse)) { // Free Current Context SSPIFreeContext(&m_rAuth.rSicInfo); // Goto next package m_rAuth.iAuthToken++; // Try the next package TryNextAuthPackage(); // Done goto exit; } // Invalid Arg Assert(m_rAuth.iAuthToken < m_rAuth.cAuthToken); // Do Sicily Logon if (FAILED(SSPILogon(&m_rAuth.rSicInfo, m_rAuth.fRetryPackage, SSPI_BASE64, m_rAuth.rgpszAuthTokens[m_rAuth.iAuthToken], &m_rServer, m_pCallback))) { // Cancel Authentication CancelAuthInProg(); // Done goto exit; } // Retrying current package if (m_rAuth.fRetryPackage) { // Don't retry again m_rAuth.fRetryPackage = FALSE; } // Get negotiation string if (FAILED(SSPIGetNegotiate(&m_rAuth.rSicInfo, &Negotiate))) { // Cancel Authentication CancelAuthInProg(); // Done goto exit; } // Send AUTH Respons if (FSendSicilyString(Negotiate.szBuffer)) m_rAuth.authstate = AUTH_NEGO_RESP; exit: // Done return; } // ------------------------------------------------------------------------------------ // CSMTPTransport::DoAuthNegoResponse // ------------------------------------------------------------------------------------ void CSMTPTransport::DoAuthNegoResponse(HRESULT hrResponse) { // Locals HRESULT hr=S_OK; SSPIBUFFER Challenge; SSPIBUFFER Response; if (!m_pszResponse) { Assert(m_pszResponse); return; } // Invalid Arg Assert(m_rAuth.iAuthToken < m_rAuth.cAuthToken); // Failure, retry login if (FAILED(hrResponse) || (lstrlen(m_pszResponse) < 4)) { // RetryPackage RetryPackage(); // Done goto exit; } // Set Chal String - skip over "+ " SSPISetBuffer(m_pszResponse + 4, SSPI_STRING, 0, &Challenge); // Get response from challenge if (FAILED(SSPIResponseFromChallenge(&m_rAuth.rSicInfo, &Challenge, &Response))) { // Cancel Authentication CancelAuthInProg(); // Done goto exit; } // Send AUTH Respons if (FSendSicilyString(Response.szBuffer)) { // if we need to continue, we keep the state the same // else we transition to the AUTH_RESP_RESP state. if (!Response.fContinue) m_rAuth.authstate = AUTH_RESP_RESP; } exit: // Done return; } // ------------------------------------------------------------------------------------ // CSMTPTransport::DoAuthRespResponse // ------------------------------------------------------------------------------------ void CSMTPTransport::DoAuthRespResponse(HRESULT hrResponse) { // Failure if (FAILED(hrResponse)) { // RetryPackage RetryPackage(); // Done goto exit; } // We will free the context, but keep the credential handle SSPIReleaseContext(&m_rAuth.rSicInfo); // OnAuthorized OnAuthorized(); exit: // Done return; } // ------------------------------------------------------------------------------------ // CSMTPTransport::OnAuthorized // ------------------------------------------------------------------------------------ void CSMTPTransport::OnAuthorized(void) { // Connected (Authorized) state OnStatus(IXP_AUTHORIZED); // No more authorization m_fConnectAuth = FALSE; // Send command m_command = SMTP_CONNECTED; // Dispatch response DispatchResponse(S_OK, TRUE); } // ------------------------------------------------------------------------------------ // CSMTPTransport::RetryPackage // ------------------------------------------------------------------------------------ void CSMTPTransport::RetryPackage(void) { // retry current package, with prompt m_rAuth.fRetryPackage = TRUE; // Send the auth command again HRESULT hr = CommandAUTH(m_rAuth.rgpszAuthTokens[m_rAuth.iAuthToken]); if (FAILED(hr)) { OnError(hr); DropConnection(); goto exit; } // New State m_rAuth.authstate = AUTH_TRYING_PACKAGE; // Free current information SSPIFreeContext(&m_rAuth.rSicInfo); exit: // Done return; } // ------------------------------------------------------------------------------------ // CSMTPTransport::FSendSicilyString // ------------------------------------------------------------------------------------ BOOL CSMTPTransport::FSendSicilyString(LPSTR pszData) { // Locals LPSTR pszLine=NULL; HRESULT hr=S_OK; // Check Param Assert(pszData); // Allocate a line DWORD cchSize = (lstrlen(pszData) + 5); pszLine = PszAllocA(cchSize * sizeof(pszLine[0])); if (NULL == pszLine) { OnError(E_OUTOFMEMORY); DropConnection(); return FALSE; } // Make Line wnsprintf(pszLine, cchSize, "%s\r\n", pszData); // Send the lin hr = HrSendLine(pszLine); SafeMemFree(pszLine); // Failure if (FAILED(hr)) { OnError(hr); DropConnection(); return FALSE; } // Success return TRUE; } // ------------------------------------------------------------------------------------ // CSMTPTransport::CancelAuthInProg // ------------------------------------------------------------------------------------ void CSMTPTransport::CancelAuthInProg(void) { // Locals HRESULT hr; // Send *, quit and die if it fails hr = HrSendCommand((LPSTR)SMTP_AUTH_CANCEL_STR, NULL, FALSE); if (FAILED(hr)) { OnError(hr); DropConnection(); } else { // New state m_command = SMTP_AUTH; m_rAuth.authstate = AUTH_CANCELED; } } // ------------------------------------------------------------------------------------ // CSMTPTransport::StartTLS // ------------------------------------------------------------------------------------ void CSMTPTransport::StartTLS(void) { // Locals HRESULT hr; // Progress OnStatus(IXP_SECURING); hr = CommandSTARTTLS(); if (FAILED(hr)) { OnError(hr); DropConnection(); } return; } // ------------------------------------------------------------------------------------ // CSMTPTransport::TryNextSecurityPkg // ------------------------------------------------------------------------------------ void CSMTPTransport::TryNextSecurityPkg(void) { if (FALSE != FIsSecurityEnabled()) { m_pSocket->TryNextSecurityPkg(); } else { OnError(E_FAIL); DropConnection(); } return; } //*************************************************************************** // Function: SetWindow // // Purpose: // This function creates the current window handle for async winsock process. // // Returns: // HRESULT indicating success or failure. //*************************************************************************** STDMETHODIMP CSMTPTransport::SetWindow(void) { HRESULT hr; Assert(NULL != m_pSocket); if(m_pSocket) hr= m_pSocket->SetWindow(); else hr= E_UNEXPECTED; return hr; } //*************************************************************************** // Function: ResetWindow // // Purpose: // This function closes the current window handle for async winsock process. // // Returns: // HRESULT indicating success or failure. //*************************************************************************** STDMETHODIMP CSMTPTransport::ResetWindow(void) { HRESULT hr; Assert(NULL != m_pSocket); if(m_pSocket) hr= m_pSocket->ResetWindow(); else hr= E_UNEXPECTED; return hr; }