You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2329 lines
69 KiB
2329 lines
69 KiB
// --------------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
|