2322 lines
68 KiB
C++
2322 lines
68 KiB
C++
// --------------------------------------------------------------------------------
|
||
// 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';
|
||
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));
|
||
lstrcpyn(m_rMessage.pszDSNENVID, pMessage->pszDSNENVID, 101);
|
||
}
|
||
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[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 cbAlloc;
|
||
|
||
// Invalid Arg
|
||
Assert(pszEmail && ppszAddress);
|
||
|
||
cbAlloc= lstrlen(pszEmail) + 3; // length of pszEmail plus <> and a null term
|
||
if(pszExtra && pszExtra[0])
|
||
cbAlloc += lstrlen(pszExtra) + 1; // length of pszExtra plus a space
|
||
|
||
// Allocate string
|
||
CHECKALLOC(*ppszAddress = (LPSTR)g_pMalloc->Alloc(cbAlloc));
|
||
|
||
// Format the String
|
||
wsprintf(*ppszAddress, "<%s>", pszEmail);
|
||
if(pszExtra && pszExtra[0])
|
||
{
|
||
strcat(*ppszAddress, " ");
|
||
strcat(*ppszAddress, pszExtra);
|
||
}
|
||
|
||
exit:
|
||
// Done
|
||
return hr;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------
|
||
// CSMTPTransport::CommandMAIL
|
||
// --------------------------------------------------------------------------------
|
||
STDMETHODIMP CSMTPTransport::CommandMAIL(LPSTR pszEmailFrom)
|
||
{
|
||
// Locals
|
||
HRESULT hr=S_OK;
|
||
LPSTR pszAddress=NULL;
|
||
CHAR pszDSNData[128];
|
||
|
||
pszDSNData[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)
|
||
{
|
||
strcat(pszDSNData, g_szDSNRET);
|
||
|
||
if(m_rMessage.dsnRet == DSNRET_HDRS)
|
||
strcat(pszDSNData, g_szDSNHDRS);
|
||
else if(DSNRET_FULL == m_rMessage.dsnRet)
|
||
strcat(pszDSNData, g_szDSNFULL);
|
||
|
||
}
|
||
|
||
if(m_rMessage.pszDSNENVID)
|
||
{
|
||
if(pszDSNData[0])
|
||
strcat(pszDSNData, " ");
|
||
|
||
strcat(pszDSNData, g_szDSNENVID);
|
||
strcat(pszDSNData, m_rMessage.pszDSNENVID);
|
||
}
|
||
}
|
||
|
||
// Put pszEmailFrom into <pszEmailFrom>
|
||
CHECKHR(hr = _HrFormatAddressString(pszEmailFrom, pszDSNData, &pszAddress));
|
||
|
||
// Send Command
|
||
hr = HrSendCommand((LPSTR)SMTP_MAIL_STR, pszAddress, !m_fSendMessage);
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
lstrcpyn(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 pszDSNData[32];
|
||
int iatDSN= atDSN;
|
||
|
||
pszDSNData[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)
|
||
{
|
||
strcat(pszDSNData, g_szDSNNOTIFY);
|
||
|
||
if(atDSN & ADDR_DSN_NEVER)
|
||
strcat(pszDSNData, g_szDSNNEVER);
|
||
else
|
||
{
|
||
bool fPrev= false;
|
||
|
||
if(atDSN & ADDR_DSN_SUCCESS)
|
||
{
|
||
strcat(pszDSNData, g_szDSNSUCCESS);
|
||
fPrev= true;
|
||
}
|
||
if(atDSN & ADDR_DSN_FAILURE)
|
||
{
|
||
if(fPrev)
|
||
strcat(pszDSNData, ",");
|
||
strcat(pszDSNData, g_szDSNFAILURE);
|
||
fPrev= true;
|
||
}
|
||
if(atDSN & ADDR_DSN_DELAY)
|
||
{
|
||
if(fPrev)
|
||
strcat(pszDSNData, ",");
|
||
strcat(pszDSNData, g_szDSNDELAY);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Put pszEmailFrom into <pszEmailFrom>
|
||
CHECKHR(hr = _HrFormatAddressString(pszEmailTo, pszDSNData, &pszAddress));
|
||
|
||
// Send Command
|
||
hr = HrSendCommand((LPSTR)SMTP_RCPT_STR, pszAddress, !m_fSendMessage);
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
lstrcpyn(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);
|
||
StripIllegalHostChars("foobar.", szLocalHost);
|
||
StripIllegalHostChars("127.256.34.23", szLocalHost);
|
||
StripIllegalHostChars("<EFBFBD>56foo1", szLocalHost);
|
||
#endif
|
||
// Get legal local host name
|
||
StripIllegalHostChars(pszHost, 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)
|
||
{
|
||
// 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;
|
||
|
||
// 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
|
||
pszLine = PszAllocA(lstrlen(pszData) + 5);
|
||
if (NULL == pszLine)
|
||
{
|
||
OnError(E_OUTOFMEMORY);
|
||
DropConnection();
|
||
return FALSE;
|
||
}
|
||
|
||
// Make Line
|
||
wsprintf(pszLine, "%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;
|
||
}
|
||
|