Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2320 lines
68 KiB

// --------------------------------------------------------------------------------
// Ixppop3.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// Steven J. Bailey
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "dllmain.h"
#include "ixppop3.h"
#include "asynconn.h"
#include "ixputil.h"
#include "strconst.h"
#include <shlwapi.h>
#include <ntverp.h>
#include "demand.h"
// --------------------------------------------------------------------------------
// Usefule C++ pointer casting
// --------------------------------------------------------------------------------
#define POP3THISIXP ((IPOP3Transport *)(CIxpBase *)this)
#define STR_HOTMAILAUTH "Outlook Express V" VER_PRODUCTVERSION_STR
// --------------------------------------------------------------------------------
// FreeAuthInfo
// --------------------------------------------------------------------------------
void FreeAuthInfo(LPAUTHINFO pAuth)
{
for (UINT i=0; i<pAuth->cAuthToken; i++)
{
ZeroMemory(pAuth->rgpszAuthTokens[i], sizeof(pAuth->rgpszAuthTokens[i][0]) * lstrlen(pAuth->rgpszAuthTokens[i]));
SafeMemFree(pAuth->rgpszAuthTokens[i]);
}
pAuth->iAuthToken = pAuth->cAuthToken = 0;
if (pAuth->pPackages && pAuth->cPackages)
{
SSPIFreePackages(&pAuth->pPackages, pAuth->cPackages);
pAuth->pPackages = NULL;
pAuth->cPackages = 0;
}
SSPIFreeContext(&pAuth->rSicInfo);
ZeroMemory(pAuth, sizeof(*pAuth));
}
// --------------------------------------------------------------------------------
// CPOP3Transport::CPOP3Transport
// --------------------------------------------------------------------------------
CPOP3Transport::CPOP3Transport(void) : CIxpBase(IXP_POP3)
{
DllAddRef();
ZeroMemory(&m_rInfo, sizeof(POP3INFO));
m_rInfo.rAuth.authstate = AUTH_NONE;
m_command = POP3_NONE;
m_fHotmail = FALSE;
}
// --------------------------------------------------------------------------------
// CPOP3Transport::~CPOP3Transport
// --------------------------------------------------------------------------------
CPOP3Transport::~CPOP3Transport(void)
{
ResetBase();
DllRelease();
}
// --------------------------------------------------------------------------------
// CPOP3Transport::ResetBase
// --------------------------------------------------------------------------------
void CPOP3Transport::ResetBase(void)
{
EnterCriticalSection(&m_cs);
FreeAuthInfo(&m_rInfo.rAuth);
SafeMemFree(m_rInfo.prgMarked);
ZeroMemory(&m_rInfo, sizeof(m_rInfo));
m_command = POP3_NONE;
LeaveCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::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 *)(IPOP3Transport *)this);
// IID_IInternetTransport
else if (IID_IInternetTransport == riid)
*ppv = ((IInternetTransport *)(CIxpBase *)this);
// IID_IPOP3Transport
else if (IID_IPOP3Transport == riid)
*ppv = (IPOP3Transport *)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;
}
// --------------------------------------------------------------------------------
// CPOP3Transport::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CPOP3Transport::AddRef(void)
{
return ++m_cRef;
}
// --------------------------------------------------------------------------------
// CPOP3Transport::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CPOP3Transport::Release(void)
{
if (0 != --m_cRef)
return m_cRef;
delete this;
return 0;
}
// --------------------------------------------------------------------------------
// CPOP3Transport::InitNew
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::InitNew(LPSTR pszLogFilePath, IPOP3Callback *pCallback)
{
// Call Base Class
return CIxpBase::OnInitNew("POP3", pszLogFilePath, FILE_SHARE_READ | FILE_SHARE_WRITE,
(ITransportCallback *)pCallback);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::HandsOffCallback
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::HandsOffCallback(void)
{
return CIxpBase::HandsOffCallback();
}
// --------------------------------------------------------------------------------
// CPOP3Transport::GetStatus
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::GetStatus(IXPSTATUS *pCurrentStatus)
{
return CIxpBase::GetStatus(pCurrentStatus);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::InetServerFromAccount
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::InetServerFromAccount(IImnAccount *pAccount, LPINETSERVER pInetServer)
{
return CIxpBase::InetServerFromAccount(pAccount, pInetServer);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::Connect
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::Connect(LPINETSERVER pInetServer, boolean fAuthenticate, boolean fCommandLogging)
{
// Does user want us to always prompt for his password? Prompt him here to avoid
// inactivity timeouts while the prompt is up, unless a password was supplied
if (ISFLAGSET(pInetServer->dwFlags, ISF_ALWAYSPROMPTFORPASSWORD) &&
'\0' == pInetServer->szPassword[0])
{
HRESULT hr;
if (NULL != m_pCallback)
hr = m_pCallback->OnLogonPrompt(pInetServer, POP3THISIXP);
if (NULL == m_pCallback || S_OK != hr)
return IXP_E_USER_CANCEL;
}
return CIxpBase::Connect(pInetServer, fAuthenticate, fCommandLogging);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::DropConnection
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::DropConnection(void)
{
return CIxpBase::DropConnection();
}
// --------------------------------------------------------------------------------
// CPOP3Transport::Disconnect
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::Disconnect(void)
{
return CIxpBase::Disconnect();
}
// --------------------------------------------------------------------------------
// CPOP3Transport::IsState
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::IsState(IXPISSTATE isstate)
{
return CIxpBase::IsState(isstate);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::GetServerInfo
// --------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::GetServerInfo(LPINETSERVER pInetServer)
{
return CIxpBase::GetServerInfo(pInetServer);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::GetIXPType
// --------------------------------------------------------------------------------
STDMETHODIMP_(IXPTYPE) CPOP3Transport::GetIXPType(void)
{
return CIxpBase::GetIXPType();
}
// --------------------------------------------------------------------------------
// CPOP3Transport::OnEnterBusy
// --------------------------------------------------------------------------------
void CPOP3Transport::OnEnterBusy(void)
{
IxpAssert(m_command == POP3_NONE);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::OnLeaveBusy
// --------------------------------------------------------------------------------
void CPOP3Transport::OnLeaveBusy(void)
{
m_command = POP3_NONE;
}
// --------------------------------------------------------------------------------
// CPOP3Transport::OnConnected
// --------------------------------------------------------------------------------
void CPOP3Transport::OnConnected(void)
{
m_command = POP3_BANNER;
CIxpBase::OnConnected();
}
// --------------------------------------------------------------------------------
// CPOP3Transport::OnDisconnect
// --------------------------------------------------------------------------------
void CPOP3Transport::OnDisconnected(void)
{
ResetBase();
CIxpBase::OnDisconnected();
}
// --------------------------------------------------------------------------------
// CPOP3Transport::OnNotify
// --------------------------------------------------------------------------------
void CPOP3Transport::OnNotify(ASYNCSTATE asOld, ASYNCSTATE asNew, ASYNCEVENT ae)
{
// Enter Critical Section
EnterCriticalSection(&m_cs);
// Handle Event
switch(ae)
{
// --------------------------------------------------------------------------------
case AE_RECV:
OnSocketReceive();
break;
// --------------------------------------------------------------------------------
default:
CIxpBase::OnNotify(asOld, asNew, ae);
break;
}
// Leave Critical Section
LeaveCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CPOP3Transport::OnSocketReceive
// --------------------------------------------------------------------------------
void CPOP3Transport::OnSocketReceive(void)
{
// Locals
HRESULT hr;
// Enter Critical Section
EnterCriticalSection(&m_cs);
// Handle Current pop3 state
switch(m_command)
{
// --------------------------------------------------------------------------------
case POP3_BANNER:
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Detect if the banner had the word hotmail in it
Assert(m_pszResponse);
m_fHotmail = (NULL == m_pszResponse || NULL == StrStrIA(m_pszResponse, "hotmail")) ? FALSE : TRUE;
// Dispatch the response
DispatchResponse(hr);
// Authorizing
if (m_fConnectAuth)
StartLogon();
// Ohterwise were connected
else
{
m_command = POP3_CONNECTED;
DispatchResponse(S_OK);
}
// Not yet auth'ed
m_fAuthenticated = FALSE;
break;
// --------------------------------------------------------------------------------
case POP3_USER:
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Dispatch the response
DispatchResponse(FAILED(hr) ? IXP_E_POP3_INVALID_USER_NAME : S_OK);
// Authorizing
if (m_fConnectAuth)
{
// Retry logon
if (FAILED(hr))
LogonRetry(IXP_E_POP3_INVALID_USER_NAME);
// otherwise send the password
else
{
hr = CommandPASS(m_rServer.szPassword);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
}
}
}
break;
// --------------------------------------------------------------------------------
case POP3_PASS:
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Dispatch the response
DispatchResponse(FAILED(hr) ? IXP_E_POP3_INVALID_PASSWORD : S_OK);
// Authorizing
if (m_fConnectAuth)
{
// Retry if failed
if (FAILED(hr))
LogonRetry(IXP_E_POP3_INVALID_PASSWORD);
// Otherwise, we're authorized
else
{
OnStatus(IXP_AUTHORIZED);
m_fConnectAuth = FALSE;
m_command = POP3_CONNECTED;
DispatchResponse(S_OK);
}
}
break;
// --------------------------------------------------------------------------------
case POP3_AUTH:
// If hotmail, then, we've identified ourselves, so lets send the user command
if (m_fHotmail)
{
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Issue the user command
hr = CommandUSER(m_rServer.szUserName);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
}
}
// Otherwise, lets continue DPA auth
else if (m_rInfo.rAuth.authstate != AUTH_ENUMPACKS_DATA)
{
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Authenticating
if (m_fConnectAuth)
{
ResponseAUTH(hr);
}
else
{
// Dispatch the response
DispatchResponse(hr);
}
}
// Otherwise, handle resposne
else
{
// no HrGetResponse() because we are getting list data
ResponseAUTH(0);
}
break;
// --------------------------------------------------------------------------------
case POP3_STAT:
ResponseSTAT();
break;
// --------------------------------------------------------------------------------
case POP3_NOOP:
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Dispatch the response
DispatchResponse(hr, TRUE);
break;
// --------------------------------------------------------------------------------
case POP3_UIDL:
case POP3_LIST:
ResponseGenericList();
break;
// --------------------------------------------------------------------------------
case POP3_DELE:
ResponseDELE();
break;
// --------------------------------------------------------------------------------
case POP3_RETR:
case POP3_TOP:
ResponseGenericRetrieve();
break;
// --------------------------------------------------------------------------------
case POP3_QUIT:
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Dispatch the response
DispatchResponse(hr, FALSE);
// Drop the socket
m_pSocket->Close();
break;
}
exit:
// Done
LeaveCriticalSection(&m_cs);
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::DispatchResponse
// ------------------------------------------------------------------------------------
void CPOP3Transport::DispatchResponse(HRESULT hrResult, BOOL fDone, LPPOP3RESPONSE pResponse)
{
// Locals
POP3RESPONSE rResponse;
// If a response was passed in, use it...
if (pResponse)
CopyMemory(&rResponse, pResponse, sizeof(POP3RESPONSE));
else
ZeroMemory(&rResponse, sizeof(POP3RESPONSE));
// Set the HRESULT
rResponse.command = m_command;
rResponse.rIxpResult.hrResult = hrResult;
rResponse.rIxpResult.pszResponse = m_pszResponse;
rResponse.rIxpResult.uiServerError = m_uiResponse;
rResponse.rIxpResult.hrServerError = m_hrResponse;
rResponse.rIxpResult.dwSocketError = m_pSocket->GetLastError();
rResponse.rIxpResult.pszProblem = NULL;
rResponse.fDone = fDone;
rResponse.pTransport = this;
// If Done...
if (fDone)
{
// No current command
m_command = POP3_NONE;
// Leave Busy State
LeaveBusy();
}
// Give the Response to the client
if (m_pCallback)
((IPOP3Callback *)m_pCallback)->OnResponse(&rResponse);
// Reset Last Response
SafeMemFree(m_pszResponse);
m_hrResponse = S_OK;
m_uiResponse = 0;
}
// --------------------------------------------------------------------------------
// CPOP3Transport::HrGetResponse
// --------------------------------------------------------------------------------
HRESULT CPOP3Transport::HrGetResponse(void)
{
// Locals
INT cbLine;
BOOL fComplete;
// Clear current response
IxpAssert(m_pszResponse == NULL && m_hrResponse == S_OK);
// Set m_hrResponse
m_hrResponse = S_OK;
// Read Line
m_hrResponse = HrReadLine(&m_pszResponse, &cbLine, &fComplete);
if (FAILED(m_hrResponse))
goto exit;
// If not complete
if (!fComplete)
goto exit;
// - Response
if ('+' != m_pszResponse[0])
{
m_hrResponse = TrapError(IXP_E_POP3_RESPONSE_ERROR);
if (m_pCallback && m_fCommandLogging)
m_pCallback->OnCommand(CMD_RESP, m_pszResponse, m_hrResponse, POP3THISIXP);
goto exit;
}
// Don't log UIDL or LIST response lines...
else if (m_pCallback && m_fCommandLogging)
m_pCallback->OnCommand(CMD_RESP, m_pszResponse, S_OK, POP3THISIXP);
exit:
// Exit
return m_hrResponse;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::StartLogon
// ------------------------------------------------------------------------------------
void CPOP3Transport::StartLogon(void)
{
// Locals
HRESULT hr;
// Progress
OnStatus(IXP_AUTHORIZING);
// If Not Using Sicily or its not installed, then send USER command
if (FALSE == m_rServer.fTrySicily || FALSE == FIsSicilyInstalled())
{
// If Hotmail, send the AUTH OutlookExpress command
if (m_fHotmail)
{
// Otherwise, send AUTH enumpacks command
hr = CommandAUTH(STR_HOTMAILAUTH);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
}
}
// Otherwise
else
{
// Issue the user command
hr = CommandUSER(m_rServer.szUserName);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
}
}
// Done
return;
}
// Turn Off HOtmail
m_fHotmail = FALSE;
// Otherwise, send AUTH enumpacks command
hr = CommandAUTH((LPSTR)"");
if (FAILED(hr))
{
OnError(hr);
DropConnection();
}
// Otherwise, set the state
m_rInfo.rAuth.authstate = AUTH_ENUMPACKS;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::LogonRetry
// ------------------------------------------------------------------------------------
void CPOP3Transport::LogonRetry(HRESULT hrLogon)
{
// Locals
HRESULT hr=S_OK;
// Give logon failed status
// OnError(hrLogon);
// Auth Retry
OnStatus(IXP_AUTHRETRY);
// Enter Auth Retry State
m_pSocket->Close();
// Logon
if (NULL == m_pCallback || m_pCallback->OnLogonPrompt(&m_rServer, POP3THISIXP) != 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;
}
// Start WatchDog
m_pSocket->StartWatchDog();
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::ResponseAUTH
// ------------------------------------------------------------------------------------
void CPOP3Transport::ResponseAUTH(HRESULT hrResponse)
{
// Locals
HRESULT hr;
BOOL fPackageInstalled;
SSPIBUFFER Negotiate;
SSPIBUFFER Challenge;
SSPIBUFFER Response;
ULONG i;
// We better be in sicily
Assert(FIsSicilyInstalled());
// If we just started enumerating packages...
if (m_rInfo.rAuth.authstate == AUTH_ENUMPACKS)
{
// Free old tokens
for (i=0; i<m_rInfo.rAuth.cAuthToken; i++)
{
ZeroMemory(m_rInfo.rAuth.rgpszAuthTokens[i], sizeof(m_rInfo.rAuth.rgpszAuthTokens[i][0]) * lstrlen(m_rInfo.rAuth.rgpszAuthTokens[i]));
SafeMemFree(m_rInfo.rAuth.rgpszAuthTokens[i]);
}
m_rInfo.rAuth.iAuthToken = m_rInfo.rAuth.cAuthToken = 0;
if (SUCCEEDED(hrResponse))
{
m_rInfo.rAuth.authstate = AUTH_ENUMPACKS_DATA;
goto EnumData;
}
OnError(IXP_E_SICILY_LOGON_FAILED);
hr = CommandQUIT();
if (FAILED(hr))
DropConnection();
return;
}
else if (m_rInfo.rAuth.authstate == AUTH_ENUMPACKS_DATA)
{
EnumData:
int cbLine;
BOOL fComplete;
// Clear Response
SafeMemFree(m_pszResponse);
m_uiResponse = 0;
m_hrResponse = S_OK;
// Read a blob of lines
while (m_rInfo.rAuth.cAuthToken < MAX_AUTH_TOKENS)
{
// Read the line
hr = HrReadLine(&m_pszResponse, &cbLine, &fComplete);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
}
// If not complete
if (!fComplete)
return;
// Add Detail
if (m_pCallback && m_fCommandLogging)
m_pCallback->OnCommand(CMD_RESP, m_pszResponse, S_OK, POP3THISIXP);
// StripCRLF
StripCRLF(m_pszResponse, (ULONG *)&cbLine);
// If its a dot, were done
if (*m_pszResponse == '.')
break;
m_rInfo.rAuth.rgpszAuthTokens[m_rInfo.rAuth.cAuthToken++] = m_pszResponse;
}
if (!m_rInfo.rAuth.cAuthToken)
{
OnError(IXP_E_SICILY_LOGON_FAILED);
hr = CommandQUIT();
if (FAILED(hr))
DropConnection();
return;
}
// Free current packages...
if (m_rInfo.rAuth.pPackages && m_rInfo.rAuth.cPackages)
{
SSPIFreePackages(&m_rInfo.rAuth.pPackages, m_rInfo.rAuth.cPackages);
m_rInfo.rAuth.pPackages = NULL;
m_rInfo.rAuth.cPackages = 0;
}
// Get installed security packages
if (FAILED(SSPIGetPackages(&m_rInfo.rAuth.pPackages, &m_rInfo.rAuth.cPackages)))
{
OnError(IXP_E_LOAD_SICILY_FAILED);
hr = CommandQUIT();
if (FAILED(hr))
DropConnection();
return;
}
}
// Otherwise, we must have just tryed a package
else if (m_rInfo.rAuth.authstate == AUTH_TRYING_PACKAGE)
{
// Stop the WatchDog
m_pSocket->StopWatchDog();
// If Success Response
if (SUCCEEDED(hrResponse))
{
// Do Sicily Logon
Assert(m_rInfo.rAuth.iAuthToken < m_rInfo.rAuth.cAuthToken);
if (SUCCEEDED(SSPILogon(&m_rInfo.rAuth.rSicInfo, m_rInfo.rAuth.fRetryPackage, SSPI_BASE64, m_rInfo.rAuth.rgpszAuthTokens[m_rInfo.rAuth.iAuthToken], &m_rServer, m_pCallback)))
{
if (m_rInfo.rAuth.fRetryPackage)
{
// Don't retry again
m_rInfo.rAuth.fRetryPackage = FALSE;
}
// Get negotiation string
if (SUCCEEDED(SSPIGetNegotiate(&m_rInfo.rAuth.rSicInfo, &Negotiate)))
{
// Send AUTH Respons
if (SUCCEEDED(HrSendSicilyString(Negotiate.szBuffer)))
{
m_rInfo.rAuth.authstate = AUTH_NEGO_RESP;
}
}
else
{
HrCancelAuthInProg();
}
}
else
{
HrCancelAuthInProg();
}
// Start the WatchDog
m_pSocket->StartWatchDog();
// Done
return;
}
// That failed, free sicinfo and go on with life
SSPIFreeContext(&m_rInfo.rAuth.rSicInfo);
// Goto Next Package
m_rInfo.rAuth.iAuthToken++;
}
// Otherwise, we got a response from a negotiation string
else if (m_rInfo.rAuth.authstate == AUTH_NEGO_RESP)
{
// Start the WatchDog
m_pSocket->StopWatchDog();
// Succeeded Response
if (SUCCEEDED(hrResponse))
{
// Set Chal String - skip over "+ "
SSPISetBuffer(m_pszResponse + 2, SSPI_STRING, 0, &Challenge);
// Get response from challenge
if (SUCCEEDED(SSPIResponseFromChallenge(&m_rInfo.rAuth.rSicInfo, &Challenge, &Response)))
{
// Send AUTH Respons
if (SUCCEEDED(HrSendSicilyString(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_rInfo.rAuth.authstate = AUTH_RESP_RESP;
}
}
else
{
HrCancelAuthInProg();
}
}
else
{
// retry current package, with prompt
m_rInfo.rAuth.fRetryPackage = TRUE;
Assert(m_rInfo.rAuth.iAuthToken < m_rInfo.rAuth.cAuthToken);
hr = CommandAUTH(m_rInfo.rAuth.rgpszAuthTokens[m_rInfo.rAuth.iAuthToken]);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
return;
}
// We are in the TRYING_PACKAGE state
m_rInfo.rAuth.authstate = AUTH_TRYING_PACKAGE;
SSPIFreeContext(&m_rInfo.rAuth.rSicInfo);
}
// Start the WatchDog
m_pSocket->StartWatchDog();
// Done
return;
}
// Otherwise, we got a response from a challenge response string
else if (m_rInfo.rAuth.authstate == AUTH_RESP_RESP)
{
// If that succeeded
if (SUCCEEDED(hrResponse))
{
// We will free the context, but keep the credential handle
SSPIReleaseContext(&m_rInfo.rAuth.rSicInfo);
// Connected (Authorized) state
OnStatus(IXP_AUTHORIZED);
m_fConnectAuth = FALSE;
m_command = POP3_CONNECTED;
DispatchResponse(S_OK);
}
else
{
// retry current package, with prompt
m_rInfo.rAuth.fRetryPackage = TRUE;
Assert(m_rInfo.rAuth.iAuthToken < m_rInfo.rAuth.cAuthToken);
hr = CommandAUTH(m_rInfo.rAuth.rgpszAuthTokens[m_rInfo.rAuth.iAuthToken]);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
return;
}
// We are in the TRYING_PACKAGE state
m_rInfo.rAuth.authstate = AUTH_TRYING_PACKAGE;
SSPIFreeContext(&m_rInfo.rAuth.rSicInfo);
}
return;
}
else if (m_rInfo.rAuth.authstate == AUTH_CANCELED)
{
SSPIFreeContext(&m_rInfo.rAuth.rSicInfo);
// Goto Next Package
m_rInfo.rAuth.iAuthToken++;
}
// Loop through the auth tokens, and try to authenticate with each one in order
while(m_rInfo.rAuth.iAuthToken < m_rInfo.rAuth.cAuthToken)
{
// We will handle basic authentication
if (lstrcmpi(m_rInfo.rAuth.rgpszAuthTokens[m_rInfo.rAuth.iAuthToken], "BASIC") != 0)
{
// Package not installed ?
fPackageInstalled=FALSE;
for (i=0; i<m_rInfo.rAuth.cPackages; i++)
{
// Null Package ??
if (!m_rInfo.rAuth.pPackages[i].pszName)
continue;
// Is this the package I am looking for
if (lstrcmpi(m_rInfo.rAuth.pPackages[i].pszName, m_rInfo.rAuth.rgpszAuthTokens[m_rInfo.rAuth.iAuthToken]) == 0)
{
fPackageInstalled = TRUE;
break;
}
}
// Package not installed ?
if (fPackageInstalled)
{
m_rInfo.rAuth.fRetryPackage = FALSE;
// If the package has a realm, send digest, otherwise, send normal
hr = CommandAUTH(m_rInfo.rAuth.rgpszAuthTokens[m_rInfo.rAuth.iAuthToken]);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
return;
}
// We are in the TRYING_PACKAGE state
m_rInfo.rAuth.authstate = AUTH_TRYING_PACKAGE;
// Done
return;
}
}
// Goto Next Package String
m_rInfo.rAuth.iAuthToken++;
}
// If we make it here, we have exhausted all packages, so it is time
// to report an error and drop the connection
OnError(IXP_E_SICILY_LOGON_FAILED);
hr = CommandQUIT();
if (FAILED(hr))
DropConnection();
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::HrSendSicilyString
// ------------------------------------------------------------------------------------
HRESULT CPOP3Transport::HrSendSicilyString(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)
{
hr = TrapError(E_OUTOFMEMORY);
return hr;
}
// Make Line
wnsprintf(pszLine, cchSize, "%s\r\n", pszData);
// Send the lin
hr = HrSendLine(pszLine);
ZeroMemory(pszLine, cchSize * sizeof(pszLine[0]));
SafeMemFree(pszLine);
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandAUTH
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandAUTH(LPSTR pszAuthType)
{
// check params
if (NULL == pszAuthType)
return TrapError(E_INVALIDARG);
// Do the command
HRESULT hr = HrSendCommand((LPSTR)POP3_AUTH_STR, pszAuthType, !m_fConnectAuth);
if (SUCCEEDED(hr))
m_command = POP3_AUTH;
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::HrCancelAuthInProg
// ------------------------------------------------------------------------------------
HRESULT CPOP3Transport::HrCancelAuthInProg()
{
// Locals
HRESULT hr;
// Send *, quit and die if it fails
hr = HrSendCommand((LPSTR)POP3_AUTH_CANCEL_STR, NULL, FALSE);
if (FAILED(hr))
{
OnError(hr);
DropConnection();
}
else
{
// New state
m_command = POP3_AUTH;
m_rInfo.rAuth.authstate = AUTH_CANCELED;
}
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandUSER
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandUSER(LPSTR pszUserName)
{
// check params
if (NULL == pszUserName)
return TrapError(E_INVALIDARG);
// Do the command
HRESULT hr = HrSendCommand((LPSTR)POP3_USER_STR, pszUserName);
if (SUCCEEDED(hr))
m_command = POP3_USER;
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandPASS
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandPASS(LPSTR pszPassword)
{
// check params
if (NULL == pszPassword)
return TrapError(E_INVALIDARG);
// Do the command
HRESULT hr = HrSendCommand((LPSTR)POP3_PASS_STR, pszPassword);
if (SUCCEEDED(hr))
m_command = POP3_PASS;
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandSTAT
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandSTAT(void)
{
// Send Command
HRESULT hr = HrSendCommand((LPSTR)POP3_STAT_STR, NULL);
if (SUCCEEDED(hr))
m_command = POP3_STAT;
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::DoQuit
// ------------------------------------------------------------------------------------
void CPOP3Transport::DoQuit(void)
{
CommandQUIT();
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandQUIT
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandQUIT(void)
{
// Send Command
OnStatus(IXP_DISCONNECTING);
HRESULT hr = HrSendCommand((LPSTR)POP3_QUIT_STR, NULL);
if (SUCCEEDED(hr))
m_command = POP3_QUIT;
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandRSET
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandRSET(void)
{
// Send Command
HRESULT hr = HrSendCommand((LPSTR)POP3_RSET_STR, NULL);
if (SUCCEEDED(hr))
m_command = POP3_RSET;
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandNOOP
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandNOOP(void)
{
// Locals
HRESULT hr = S_OK;
SYSTEMTIME stNow;
FILETIME ftNow;
static FILETIME ftNext = { 0, 0 };
LARGE_INTEGER liNext;
// Thread Safety
EnterCriticalSection(&m_cs);
// Checks for need for NOOP
GetSystemTime (&stNow);
SystemTimeToFileTime (&stNow, &ftNow);
if (CompareFileTime (&ftNow, &ftNext) < 0)
goto exit;
// Sets the next NOOP time (+60 seconds)
liNext.HighPart = ftNow.dwHighDateTime;
liNext.LowPart = ftNow.dwLowDateTime;
liNext.QuadPart += 600000000i64;
ftNext.dwHighDateTime = liNext.HighPart;
ftNext.dwLowDateTime = liNext.LowPart;
// Send Command
hr = HrSendCommand((LPSTR)POP3_NOOP_STR, NULL);
if (SUCCEEDED(hr))
m_command = POP3_NOOP;
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandLIST
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandLIST(POP3CMDTYPE cmdtype, DWORD dwPopId)
{
// Issue complex command
return HrComplexCommand(POP3_LIST, cmdtype, dwPopId, 0);
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandTOP
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandTOP (POP3CMDTYPE cmdtype, DWORD dwPopId, DWORD cPreviewLines)
{
// Issue complex command
return HrComplexCommand(POP3_TOP, cmdtype, dwPopId, cPreviewLines);
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandUIDL
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandUIDL(POP3CMDTYPE cmdtype, DWORD dwPopId)
{
// Issue complex command
return HrComplexCommand(POP3_UIDL, cmdtype, dwPopId, 0);
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandDELE
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandDELE(POP3CMDTYPE cmdtype, DWORD dwPopId)
{
// Issue complex command
return HrComplexCommand(POP3_DELE, cmdtype, dwPopId, 0);
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CommandRETR
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::CommandRETR(POP3CMDTYPE cmdtype, DWORD dwPopId)
{
// Issue complex command
return HrComplexCommand(POP3_RETR, cmdtype, dwPopId, 0);
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::MarkItem
// ------------------------------------------------------------------------------------
STDMETHODIMP CPOP3Transport::MarkItem(POP3MARKTYPE marktype, DWORD dwPopId, boolean fMarked)
{
// Locals
HRESULT hr=S_OK;
ULONG i;
// Thread Safety
EnterCriticalSection(&m_cs);
// No stat yet...
if (FALSE == m_rInfo.fStatDone)
{
hr = TrapError(IXP_E_POP3_NEED_STAT);
goto exit;
}
// No Messages...
if (0 == m_rInfo.cMarked || NULL == m_rInfo.prgMarked)
{
hr = TrapError(IXP_E_POP3_NO_MESSAGES);
goto exit;
}
// Bad PopId
if (0 == dwPopId || dwPopId > m_rInfo.cMarked)
{
hr = TrapError(E_INVALIDARG);
goto exit;
}
// Message Index
i = dwPopId - 1;
// Handle Mark Type
switch(marktype)
{
// Mark for Top
case POP3_MARK_FOR_TOP:
if (fMarked)
FLAGSET(m_rInfo.prgMarked[i], POP3_MARK_FOR_TOP);
else
FLAGCLEAR(m_rInfo.prgMarked[i], POP3_MARK_FOR_TOP);
break;
// Mark for Retrieval
case POP3_MARK_FOR_RETR:
if (fMarked)
FLAGSET(m_rInfo.prgMarked[i], POP3_MARK_FOR_RETR);
else
FLAGCLEAR(m_rInfo.prgMarked[i], POP3_MARK_FOR_RETR);
break;
// Mark for Delete
case POP3_MARK_FOR_DELE:
if (fMarked)
FLAGSET(m_rInfo.prgMarked[i], POP3_MARK_FOR_DELE);
else
FLAGCLEAR(m_rInfo.prgMarked[i], POP3_MARK_FOR_DELE);
break;
// Mark for UIDL
case POP3_MARK_FOR_UIDL:
if (fMarked)
FLAGSET(m_rInfo.prgMarked[i], POP3_MARK_FOR_UIDL);
else
FLAGCLEAR(m_rInfo.prgMarked[i], POP3_MARK_FOR_UIDL);
break;
// Mark for List
case POP3_MARK_FOR_LIST:
if (fMarked)
FLAGSET(m_rInfo.prgMarked[i], POP3_MARK_FOR_LIST);
else
FLAGCLEAR(m_rInfo.prgMarked[i], POP3_MARK_FOR_LIST);
break;
// E_INVALIDARG
default:
hr = TrapError(E_INVALIDARG);
goto exit;
}
exit:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::HrComplexCommand
// ------------------------------------------------------------------------------------
HRESULT CPOP3Transport::HrComplexCommand(POP3COMMAND command, POP3CMDTYPE cmdtype, DWORD dwPopId, ULONG cPreviewLines)
{
// Locals
HRESULT hr=S_OK;
ULONG cMarked;
BOOL fDone;
// Thread Safety
EnterCriticalSection(&m_cs);
// go Busy
CHECKHR(hr = HrEnterBusy());
// Save top preview lines
m_rInfo.cPreviewLines = cPreviewLines;
// Save command type
m_rInfo.cmdtype = cmdtype;
// Locals
switch(cmdtype)
{
// Single command
case POP3CMD_GET_POPID:
// Bad PopId
if (0 == dwPopId)
{
hr = TrapError(IXP_E_POP3_POPID_OUT_OF_RANGE);
goto exit;
}
// Have we done a stat command
if (m_rInfo.fStatDone && dwPopId > m_rInfo.cMarked)
{
hr = TrapError(IXP_E_POP3_POPID_OUT_OF_RANGE);
goto exit;
}
// Save as Current
m_rInfo.dwPopIdCurrent = dwPopId;
// Do the command
CHECKHR(hr = HrCommandGetPopId(command, dwPopId));
// Done
break;
// Get marked items
case POP3CMD_GET_MARKED:
// No stat yet...
if (FALSE == m_rInfo.fStatDone)
{
hr = TrapError(IXP_E_POP3_NEED_STAT);
goto exit;
}
// No Messages...
if (0 == m_rInfo.cMarked || NULL == m_rInfo.prgMarked)
{
hr = TrapError(IXP_E_POP3_NO_MESSAGES);
goto exit;
}
// Are there any messages mared for this command...
cMarked = CountMarked(command);
if (0 == cMarked)
{
hr = TrapError(IXP_E_POP3_NO_MARKED_MESSAGES);
goto exit;
}
// Init Marked State
m_rInfo.dwPopIdCurrent = 0;
// Do next marked...
CHECKHR(hr = HrCommandGetNext(command, &fDone));
IxpAssert(fDone == FALSE);
// Done
break;
// Multiple commands or a list operation
case POP3CMD_GET_ALL:
// Do the command
CHECKHR(hr = HrCommandGetAll(command));
// done
break;
// E_INVALIDARG
default:
hr = TrapError(E_INVALIDARG);
goto exit;
}
exit:
// Failure
if (FAILED(hr))
LeaveBusy();
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::HrCommandGetPopId
// ------------------------------------------------------------------------------------
HRESULT CPOP3Transport::HrCommandGetPopId(POP3COMMAND command, DWORD dwPopId)
{
// Locals
HRESULT hr=S_OK;
CHAR szPopId[30];
// Handle command type
IxpAssert(dwPopId == m_rInfo.dwPopIdCurrent);
switch(command)
{
case POP3_DELE:
wnsprintf(szPopId, ARRAYSIZE(szPopId), "%d", dwPopId);
CHECKHR(hr = HrSendCommand((LPSTR)POP3_DELE_STR, szPopId, FALSE));
m_command = POP3_DELE;
break;
case POP3_RETR:
ZeroMemory(&m_rInfo.rFetch, sizeof(FETCHINFO));
wnsprintf(szPopId, ARRAYSIZE(szPopId), "%d", dwPopId);
CHECKHR(hr = HrSendCommand((LPSTR)POP3_RETR_STR, szPopId, FALSE));
m_command = POP3_RETR;
break;
case POP3_TOP:
ZeroMemory(&m_rInfo.rFetch, sizeof(FETCHINFO));
wnsprintf(szPopId, ARRAYSIZE(szPopId), "%d %d", dwPopId, m_rInfo.cPreviewLines);
CHECKHR(hr = HrSendCommand((LPSTR)POP3_TOP_STR, szPopId, FALSE));
m_command = POP3_TOP;
break;
case POP3_LIST:
m_rInfo.cList = 0;
wnsprintf(szPopId, ARRAYSIZE(szPopId), "%d", dwPopId);
CHECKHR(hr = HrSendCommand((LPSTR)POP3_LIST_STR, szPopId, FALSE));
m_command = POP3_LIST;
break;
case POP3_UIDL:
m_rInfo.cList = 0;
wnsprintf(szPopId, ARRAYSIZE(szPopId), "%d", dwPopId);
CHECKHR(hr = HrSendCommand((LPSTR)POP3_UIDL_STR, szPopId, FALSE));
m_command = POP3_UIDL;
break;
default:
hr = TrapError(E_INVALIDARG);
goto exit;
}
exit:
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::DwGetCommandMarkedFlag
// ------------------------------------------------------------------------------------
DWORD CPOP3Transport::DwGetCommandMarkedFlag(POP3COMMAND command)
{
DWORD dw;
switch(command)
{
case POP3_LIST:
dw = POP3_MARK_FOR_LIST;
break;
case POP3_DELE:
dw = POP3_MARK_FOR_DELE;
break;
case POP3_RETR:
dw = POP3_MARK_FOR_RETR;
break;
case POP3_TOP:
dw = POP3_MARK_FOR_TOP;
break;
case POP3_UIDL:
dw = POP3_MARK_FOR_UIDL;
break;
default:
IxpAssert(FALSE);
dw = 0;
break;
}
return dw;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::CountMarked
// ------------------------------------------------------------------------------------
ULONG CPOP3Transport::CountMarked(POP3COMMAND command)
{
// Locals
DWORD dw = 0;
ULONG c=0,
i;
// Check some stuff
IxpAssert(m_rInfo.cMarked && m_rInfo.prgMarked);
// Handle Command type
dw = DwGetCommandMarkedFlag(command);
if (0 == dw)
return 0;
// Count
for (i=0; i<m_rInfo.cMarked; i++)
if (dw & m_rInfo.prgMarked[i])
c++;
// Done
return c;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::HrCommandGetNext
// ------------------------------------------------------------------------------------
HRESULT CPOP3Transport::HrCommandGetNext(POP3COMMAND command, BOOL *pfDone)
{
// Locals
HRESULT hr=S_OK;
CHAR szPopId[30];
DWORD dw;
ULONG i;
// check params
IxpAssert(pfDone && m_rInfo.dwPopIdCurrent <= m_rInfo.cMarked);
// Init - Assume were done
*pfDone = TRUE;
// Doing all
if (POP3CMD_GET_ALL == m_rInfo.cmdtype)
{
// Done
IxpAssert(m_rInfo.fStatDone == TRUE);
if (m_rInfo.dwPopIdCurrent == m_rInfo.cMarked)
goto exit;
// Next Message..
m_rInfo.dwPopIdCurrent++;
*pfDone = FALSE;
CHECKHR(hr = HrCommandGetPopId(command, m_rInfo.dwPopIdCurrent));
}
// Doing Marked
else
{
// Check Parms
IxpAssert(POP3CMD_GET_MARKED == m_rInfo.cmdtype);
// Get marked flag
dw = DwGetCommandMarkedFlag(command);
if (0 == dw)
{
hr = TrapError(E_INVALIDARG);
goto exit;
}
// Step Over Last Marked Item
m_rInfo.dwPopIdCurrent++;
// Start comparing at iCurrent
for (i=m_rInfo.dwPopIdCurrent-1; i<m_rInfo.cMarked; i++)
{
// Is this item marked...
if (dw & m_rInfo.prgMarked[i])
{
// Send Command
m_rInfo.dwPopIdCurrent = i + 1;
*pfDone = FALSE;
CHECKHR(hr = HrCommandGetPopId(command, m_rInfo.dwPopIdCurrent));
break;
}
}
}
exit:
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::HrCommandGetAll
// ------------------------------------------------------------------------------------
HRESULT CPOP3Transport::HrCommandGetAll(POP3COMMAND command)
{
// Locals
HRESULT hr=S_OK;
CHAR szPopId[30];
BOOL fDone;
// Init current
m_rInfo.dwPopIdCurrent = 0;
// POP3_LIST
if (POP3_LIST == command)
{
m_rInfo.cList = 0;
CHECKHR(hr = HrSendCommand((LPSTR)POP3_LIST_ALL_STR, NULL, FALSE));
m_command = POP3_LIST;
}
// POP3_UIDL
else if (POP3_UIDL == command)
{
m_rInfo.cList = 0;
CHECKHR(hr = HrSendCommand((LPSTR)POP3_UIDL_ALL_STR, NULL, FALSE));
m_command = POP3_UIDL;
}
// Otherwise, we better have done the stat command
else
{
// No stat yet...
if (FALSE == m_rInfo.fStatDone)
{
hr = TrapError(IXP_E_POP3_NEED_STAT);
goto exit;
}
// No Messages...
if (0 == m_rInfo.cMarked || NULL == m_rInfo.prgMarked)
{
hr = TrapError(IXP_E_POP3_NO_MESSAGES);
goto exit;
}
// Next Command
CHECKHR(hr = HrCommandGetNext(command, &fDone));
IxpAssert(fDone == FALSE);
}
exit:
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::ResponseSTAT
// ------------------------------------------------------------------------------------
void CPOP3Transport::ResponseSTAT(void)
{
// Locals
HRESULT hr=S_OK;
DWORD cMessages=0,
cbMessages=0;
LPSTR pszPart1=NULL,
pszPart2=NULL;
POP3RESPONSE rResponse;
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
return;
// Init Response
ZeroMemory(&rResponse, sizeof(POP3RESPONSE));
// Parse the response
CHECKHR(hr = HrSplitPop3Response(m_pszResponse, &pszPart1, &pszPart2));
// Convert
IxpAssert(pszPart1 && pszPart2);
cMessages = StrToInt(pszPart1);
cbMessages = StrToInt(pszPart2);
// Are there messages
if (FALSE == m_rInfo.fStatDone && cMessages > 0)
{
// Set Number of messages
IxpAssert(m_rInfo.prgMarked == NULL);
m_rInfo.cMarked = cMessages;
// Allocate message array
CHECKHR(hr = HrAlloc((LPVOID *)&m_rInfo.prgMarked, sizeof(DWORD) * m_rInfo.cMarked));
// Zero
ZeroMemory(m_rInfo.prgMarked, sizeof(DWORD) * m_rInfo.cMarked);
}
// Success
m_rInfo.fStatDone = TRUE;
exit:
// Cleanup
SafeMemFree(pszPart1);
SafeMemFree(pszPart2);
// Build Response
rResponse.fValidInfo = TRUE;
rResponse.rStatInfo.cMessages = cMessages;
rResponse.rStatInfo.cbMessages = cbMessages;
DispatchResponse(hr, TRUE, &rResponse);
// Done
return;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::HrSplitPop3Response
// ------------------------------------------------------------------------------------
HRESULT CPOP3Transport::HrSplitPop3Response(LPSTR pszLine, LPSTR *ppszPart1, LPSTR *ppszPart2)
{
// Locals
LPSTR psz,
pszStart;
CHAR ch;
HRESULT hr=IXP_E_POP3_PARSE_FAILURE;
// No Response...
IxpAssert(pszLine && pszLine[0] != '-' && ppszPart1 && ppszPart2);
if (NULL == pszLine)
goto exit;
// Parse: '+OK' 432 1234
psz = PszSkipWhiteA(pszLine);
if ('\0' == *psz)
goto exit;
// Parse response token
pszStart = psz;
if ('+' == *pszLine)
{
// Parse: '+OK' 432 1234
psz = PszScanToWhiteA(psz);
if ('\0' == *psz)
goto exit;
#ifdef DEBUG
IxpAssert(' ' == *psz);
*psz = '\0';
IxpAssert(lstrcmpi(pszStart, "+OK") == 0);
*psz = ' ';
#endif
// Parse: +OK '432' 1234
psz = PszSkipWhiteA(psz);
if ('\0' == *psz)
goto exit;
}
// Parse: +OK '432' 1234
pszStart = psz;
psz = PszScanToWhiteA(psz);
if ('\0' == *psz)
goto exit;
// Get Message Count
*psz = '\0';
*ppszPart1 = PszDupA(pszStart);
*psz = ' ';
// Parse: +OK 432 '1234'
psz = PszSkipWhiteA(psz);
if ('\0' == *psz)
{
// Raid 28435 - Outlook needs INETCOMM to accept empty UIDL responses
*ppszPart2 = PszDupA(c_szEmpty);
hr = S_OK;
goto exit;
}
// Parse: +OK 432 1234
pszStart = psz;
psz = PszScanToWhiteA(psz);
// Get Message Count
ch = *psz;
*psz = '\0';
*ppszPart2 = PszDupA(pszStart);
*psz = ch;
// Success
hr = S_OK;
exit:
// Done
return hr;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::ResponseGenericList
// ------------------------------------------------------------------------------------
void CPOP3Transport::ResponseGenericList(void)
{
// Locals
HRESULT hr;
INT cbLine;
BOOL fDone,
fComplete;
LPSTR pszPart1=NULL,
pszPart2=NULL;
POP3RESPONSE rResponse;
// Same response as single LIST x command, but then get next
if (POP3CMD_GET_MARKED == m_rInfo.cmdtype || POP3CMD_GET_POPID == m_rInfo.cmdtype)
{
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Otherwise, if failure...
else if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// Get the two parts from the line
hr = HrSplitPop3Response(m_pszResponse, &pszPart1, &pszPart2);
if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// Init Response
ZeroMemory(&rResponse, sizeof(POP3RESPONSE));
// POP3_LIST
if (POP3_LIST == m_command)
{
rResponse.fValidInfo = TRUE;
rResponse.rListInfo.dwPopId = StrToInt(pszPart1);
rResponse.rListInfo.cbSize = StrToInt(pszPart2);
IxpAssert(rResponse.rListInfo.dwPopId == m_rInfo.dwPopIdCurrent);
}
// POP3_UIDL
else
{
rResponse.fValidInfo = TRUE;
rResponse.rUidlInfo.dwPopId = StrToInt(pszPart1);
rResponse.rUidlInfo.pszUidl = pszPart2;
IxpAssert(rResponse.rUidlInfo.dwPopId == m_rInfo.dwPopIdCurrent);
}
// Do Next
if (POP3CMD_GET_MARKED == m_rInfo.cmdtype)
{
// Give the response
DispatchResponse(S_OK, FALSE, &rResponse);
// Do the next marked list item
hr = HrCommandGetNext(m_command, &fDone);
if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// Done Response
if (fDone)
DispatchResponse(S_OK, TRUE);
}
// Dispatch Done or single item response
else
DispatchResponse(S_OK, TRUE, &rResponse);
}
// Full LIST response
else if (POP3CMD_GET_ALL == m_rInfo.cmdtype)
{
// First call...
if (m_rInfo.dwPopIdCurrent == 0)
{
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Otherwise, if failure...
else if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// Current
m_rInfo.dwPopIdCurrent = 1;
}
// Clear Response
SafeMemFree(m_pszResponse);
m_uiResponse = 0;
m_hrResponse = S_OK;
// Read a blob of lines
while(1)
{
// Read Line
hr = HrReadLine(&m_pszResponse, &cbLine, &fComplete);
if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// If not complete
if (!fComplete)
goto exit;
// Add Detail
if (m_pCallback && m_fCommandLogging)
m_pCallback->OnCommand(CMD_RESP, m_pszResponse, S_OK, POP3THISIXP);
// If its a dot, were done
if (*m_pszResponse == '.')
{
// If we haven't done a stat yet, we can use these totals...
IxpAssert(m_rInfo.fStatDone ? m_rInfo.cList == m_rInfo.cMarked : TRUE);
if (FALSE == m_rInfo.fStatDone && m_rInfo.cList > 0)
{
// Have I build my internal array of messages yet...
IxpAssert(m_rInfo.prgMarked == NULL);
m_rInfo.cMarked = m_rInfo.cList;
// Allocate message array
CHECKHR(hr = HrAlloc((LPVOID *)&m_rInfo.prgMarked, sizeof(DWORD) * m_rInfo.cMarked));
// Zero
ZeroMemory(m_rInfo.prgMarked, sizeof(DWORD) * m_rInfo.cMarked);
}
// Were Done
DispatchResponse(S_OK, TRUE);
// Stat Done
m_rInfo.fStatDone = TRUE;
// Done
break;
}
// Get the two parts from the line
hr = HrSplitPop3Response(m_pszResponse, &pszPart1, &pszPart2);
if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// Init Response
ZeroMemory(&rResponse, sizeof(POP3RESPONSE));
// POP3_LIST
if (POP3_LIST == m_command)
{
rResponse.fValidInfo = TRUE;
rResponse.rListInfo.dwPopId = StrToInt(pszPart1);
rResponse.rListInfo.cbSize = StrToInt(pszPart2);
}
// POP3_UIDL
else
{
rResponse.fValidInfo = TRUE;
rResponse.rUidlInfo.dwPopId = StrToInt(pszPart1);
rResponse.rUidlInfo.pszUidl = pszPart2;
IxpAssert(rResponse.rUidlInfo.dwPopId == m_rInfo.dwPopIdCurrent);
}
// Count the number of messages
m_rInfo.cList++;
// Dispatch the response
DispatchResponse(S_OK, FALSE, &rResponse);
m_rInfo.dwPopIdCurrent++;
// Cleanup
SafeMemFree(pszPart1);
SafeMemFree(pszPart2);
// Clear Response
SafeMemFree(m_pszResponse);
m_uiResponse = 0;
m_hrResponse = S_OK;
}
}
// Otherwise failure...
else
{
IxpAssert(FALSE);
goto exit;
}
exit:
// Cleanup
SafeMemFree(pszPart1);
SafeMemFree(pszPart2);
// Done
return;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::FEndRetrRecvHeader
// ------------------------------------------------------------------------------------
BOOL CPOP3Transport::FEndRetrRecvHeader(LPSTR pszLines, ULONG cbRead)
{
// If we see CRLFCRLF
if (StrStr(pszLines, "\r\n\r\n"))
return TRUE;
// Otherwise, did last block end with a CRLF and this block begins with a crlf
else if (cbRead >= 2 &&
m_rInfo.rFetch.fLastLineCRLF &&
pszLines[0] == '\r' &&
pszLines[1] == '\n')
return TRUE;
// Header is not done
return FALSE;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::ResponseGenericRetrieve
// ------------------------------------------------------------------------------------
void CPOP3Transport::ResponseGenericRetrieve(void)
{
// Locals
HRESULT hr=S_OK;
LPSTR pszLines=NULL;
INT cbRead,
cLines;
ULONG cbSubtract;
BOOL fDone,
fMessageDone;
POP3RESPONSE rResponse;
// First call...
if (FALSE == m_rInfo.rFetch.fGotResponse)
{
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Otherwise, if failure...
else if (FAILED(hr))
{
FillRetrieveResponse(&rResponse, NULL, 0, &fMessageDone);
DispatchResponse(hr, TRUE, &rResponse);
goto exit;
}
// Current
m_rInfo.rFetch.fGotResponse = TRUE;
}
// While there are lines to read...
hr = m_pSocket->ReadLines(&pszLines, &cbRead, &cLines);
// Incomplete data available...
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Or if we failed...
else if (FAILED(hr))
{
FillRetrieveResponse(&rResponse, NULL, 0, &fMessageDone);
DispatchResponse(hr, TRUE, &rResponse);
goto exit;
}
// Are we receiving the header...
if (FALSE == m_rInfo.rFetch.fHeader)
{
// Test for end of header found
if (FEndRetrRecvHeader(pszLines, cbRead))
m_rInfo.rFetch.fHeader = TRUE;
// $$BUG$$ Our good buddies on Exchange produced the following message:
//
// To: XXXXXXXXXXXXXXXXXXX
// From: XXXXXXXXXXXXXXXXX
// Subject: XXXXXXXXXXXXXX
// .
//
// As you can see there is not CRLFCRLF following the last header line which is very
// illegal. This message caused us to hange because we never saw the end of the header.
// So this is why I also test for the end of the body...
else if (FEndRetrRecvBody(pszLines, cbRead, &cbSubtract))
{
cbRead -= cbSubtract;
m_rInfo.rFetch.fHeader = TRUE;
m_rInfo.rFetch.fBody = TRUE;
}
// Otherwise, did this block end with a crlf
else if (cbRead >= 2 && pszLines[cbRead - 1] == '\n' && pszLines[cbRead - 2] == '\r')
m_rInfo.rFetch.fLastLineCRLF = TRUE;
else
m_rInfo.rFetch.fLastLineCRLF = FALSE;
}
// Also check to see if body was received in same set of lines
if (TRUE == m_rInfo.rFetch.fHeader)
{
// Test for end of header found
if (FEndRetrRecvBody(pszLines, cbRead, &cbSubtract))
{
cbRead -= cbSubtract;
m_rInfo.rFetch.fBody = TRUE;
}
// Otherwise, check for line ending with crlf
else if (cbRead >= 2 && pszLines[cbRead - 1] == '\n' && pszLines[cbRead - 2] == '\r')
m_rInfo.rFetch.fLastLineCRLF = TRUE;
else
m_rInfo.rFetch.fLastLineCRLF = FALSE;
}
// Count bytes downloaded on this fetch
m_rInfo.rFetch.cbSoFar += cbRead;
// UnStuff
UnStuffDotsFromLines(pszLines, &cbRead);
// Fill the response
FillRetrieveResponse(&rResponse, pszLines, cbRead, &fMessageDone);
// Dispatch This Resposne...
if (POP3CMD_GET_POPID == m_rInfo.cmdtype)
DispatchResponse(S_OK, fMessageDone, &rResponse);
// Otherwise
else
{
// Check command type
IxpAssert(POP3CMD_GET_MARKED == m_rInfo.cmdtype || POP3CMD_GET_ALL == m_rInfo.cmdtype);
// Dispatch current response
DispatchResponse(S_OK, FALSE, &rResponse);
// If done with current message...
if (fMessageDone)
{
// Get Next
hr = HrCommandGetNext(m_command, &fDone);
if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// If Done
if (fDone)
DispatchResponse(S_OK, TRUE);
}
}
exit:
// Cleanup
SafeMemFree(pszLines);
// Done
return;
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::FillRetrieveResponse
// ------------------------------------------------------------------------------------
void CPOP3Transport::FillRetrieveResponse(LPPOP3RESPONSE pResponse, LPSTR pszLines, ULONG cbRead,
BOOL *pfMessageDone)
{
// Clear Response
ZeroMemory(pResponse, sizeof(POP3RESPONSE));
// POP3_TOP
if (POP3_TOP == m_command)
{
// Build Response
pResponse->fValidInfo = TRUE;
pResponse->rTopInfo.dwPopId = m_rInfo.dwPopIdCurrent;
pResponse->rTopInfo.cPreviewLines = m_rInfo.cPreviewLines;
pResponse->rTopInfo.cbSoFar = m_rInfo.rFetch.cbSoFar;
pResponse->rTopInfo.pszLines = pszLines;
pResponse->rTopInfo.cbLines = cbRead;
pResponse->rTopInfo.fHeader = m_rInfo.rFetch.fHeader;
pResponse->rTopInfo.fBody = m_rInfo.rFetch.fBody;
*pfMessageDone = (m_rInfo.rFetch.fHeader && m_rInfo.rFetch.fBody);
}
// POP3_RETR
else
{
IxpAssert(POP3_RETR == m_command);
pResponse->fValidInfo = TRUE;
pResponse->rRetrInfo.fHeader = m_rInfo.rFetch.fHeader;
pResponse->rRetrInfo.fBody = m_rInfo.rFetch.fBody;
pResponse->rRetrInfo.dwPopId = m_rInfo.dwPopIdCurrent;
pResponse->rRetrInfo.cbSoFar = m_rInfo.rFetch.cbSoFar;
pResponse->rRetrInfo.pszLines = pszLines;
pResponse->rRetrInfo.cbLines = cbRead;
*pfMessageDone = (m_rInfo.rFetch.fHeader && m_rInfo.rFetch.fBody);
}
}
// ------------------------------------------------------------------------------------
// CPOP3Transport::ResponseDELE
// ------------------------------------------------------------------------------------
void CPOP3Transport::ResponseDELE(void)
{
// Locals
HRESULT hr=S_OK;
BOOL fDone;
POP3RESPONSE rResponse;
// Read Server Response...
hr = HrGetResponse();
if (IXP_E_INCOMPLETE == hr)
goto exit;
// Otherwise, if failure...
else if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// Clear Response
ZeroMemory(&rResponse, sizeof(POP3RESPONSE));
rResponse.fValidInfo = TRUE;
rResponse.dwPopId = m_rInfo.dwPopIdCurrent;
// Dispatch This Resposne...
if (POP3CMD_GET_POPID == m_rInfo.cmdtype)
DispatchResponse(S_OK, TRUE, &rResponse);
// Otherwise
else
{
// Check command type
IxpAssert(POP3CMD_GET_MARKED == m_rInfo.cmdtype || POP3CMD_GET_ALL == m_rInfo.cmdtype);
// Dispatch current response
DispatchResponse(S_OK, FALSE, &rResponse);
// Get Next
hr = HrCommandGetNext(m_command, &fDone);
if (FAILED(hr))
{
DispatchResponse(hr, TRUE);
goto exit;
}
// If Done
if (fDone)
DispatchResponse(S_OK, TRUE);
}
exit:
// Done
return;
}