|
|
// --------------------------------------------------------------------------------
// 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 <shlwapi.h>
#include <demand.h>
// --------------------------------------------------------------------------------
// 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 <pszEmailFrom>
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 <pszEmailFrom>
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; i<m_rMessage.smtpMsg.rAddressList.cAddress; i++) { // Readability
pInetAddress = &m_rMessage.smtpMsg.rAddressList.prgAddress[i];
// From...
if (ADDR_FROM == (pInetAddress->addrtype & 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; i<m_rMessage.smtpMsg.rAddressList.cAddress; i++) { // Readability
pInetAddress = &m_rMessage.smtpMsg.rAddressList.prgAddress[i];
// Recipient
if (ADDR_TO == (pInetAddress->addrtype & 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; i<m_rAuth.cPackages; i++) { // Null Package ??
if (!m_rAuth.pPackages[i].pszName) continue;
// Is this the package I am looking for
if (lstrcmpi(m_rAuth.pPackages[i].pszName, m_rAuth.rgpszAuthTokens[m_rAuth.iAuthToken]) == 0) { fPackageInstalled = TRUE; break; } } }
// Package not installed ?
if (!fPackageInstalled) continue;
// We are not retrying the current package
m_rAuth.fRetryPackage = FALSE;
// Otherwise, send AUTH enumpacks command
hr = CommandAUTH(m_rAuth.rgpszAuthTokens[m_rAuth.iAuthToken]); if (FAILED(hr)) { OnError(hr); DropConnection(); return; }
// We are in the TRYING_PACKAGE state
m_rAuth.authstate = fLoginMethod ? AUTH_SMTP_LOGIN : AUTH_TRYING_PACKAGE;
// Done
break; }
// If auth state is none, try HELO command
if (AUTH_NONE == m_rAuth.authstate) { // Were authenticated
m_fAuthenticated = TRUE;
// Authorized
OnAuthorized(); } }
// ------------------------------------------------------------------------------------
// CSMTPTransport::ResponseAUTH
// ------------------------------------------------------------------------------------
void CSMTPTransport::ResponseAUTH(HRESULT hrResponse) { // Stop the WatchDog
m_pSocket->StopWatchDog();
// 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; }
|