Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1557 lines
41 KiB

/******************************************************************************
Copyright (c) 1999 Microsoft Corporation
Module Name:
ProtocolRoot.cpp
Abstract:
This file contains the implementation of the CPAData class, which is
used to specify a single problem area
Revision History:
Davide Massarenti (Dmassare) 07/05/99
created
******************************************************************************/
#include "stdafx.h"
#include <Debug.h>
/////////////////////////////////////////////////////////////////////////////
// BINDSTATUS_FINDINGRESOURCE 01
// BINDSTATUS_CONNECTING 02
// BINDSTATUS_REDIRECTING 03
// BINDSTATUS_BEGINDOWNLOADDATA 04
// BINDSTATUS_DOWNLOADINGDATA 05
// BINDSTATUS_ENDDOWNLOADDATA 06
// BINDSTATUS_BEGINDOWNLOADCOMPONENTS 07
// BINDSTATUS_INSTALLINGCOMPONENTS 08
// BINDSTATUS_ENDDOWNLOADCOMPONENTS 09
// BINDSTATUS_USINGCACHEDCOPY 10
// BINDSTATUS_SENDINGREQUEST 11
// BINDSTATUS_CLASSIDAVAILABLE 12
// BINDSTATUS_MIMETYPEAVAILABLE 13
// BINDSTATUS_CACHEFILENAMEAVAILABLE 14
// BINDSTATUS_BEGINSYNCOPERATION 15
// BINDSTATUS_ENDSYNCOPERATION 16
// BINDSTATUS_BEGINUPLOADDATA 17
// BINDSTATUS_UPLOADINGDATA 18
// BINDSTATUS_ENDUPLOADDATA 19
// BINDSTATUS_PROTOCOLCLASSID 20
// BINDSTATUS_ENCODING 21
// BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE 22
// BINDSTATUS_CLASSINSTALLLOCATION 23
// BINDSTATUS_DECODING 24
// BINDSTATUS_LOADINGMIMEHANDLER 25
// BINDSTATUS_CONTENTDISPOSITIONATTACH 26
// BINDSTATUS_FILTERREPORTMIMETYPE 27
// BINDSTATUS_CLSIDCANINSTANTIATE 28
// BINDSTATUS_DLLNAMEAVAILABLE 29
// BINDSTATUS_DIRECTBIND 30
// BINDSTATUS_RAWMIMETYPE 31
// BINDSTATUS_PROXYDETECTING 32
/////////////////////////////////////////////////////////////////////////////
static const WCHAR c_szContent [] = L"Content Type";
static const WCHAR c_szSystem [] = L"hcp://system/";
static const WCHAR c_szHelp [] = L"hcp://help/";
static const WCHAR c_szRoot [] = L"hcp://";
static const WCHAR c_szSharedCSS[] = L"hcp://system/css/shared.css";
static const WCHAR c_szSystemDir [] = HC_HELPSET_SUB_SYSTEM L"\\";
static const WCHAR c_szSystemOEMDir[] = HC_HELPSET_SUB_SYSTEM_OEM L"\\";
static const WCHAR c_szVendorDir [] = HC_HELPSET_SUB_VENDORS L"\\";
typedef struct
{
LPCWSTR szPrefix;
int iPrefix;
LPCWSTR szRealSubDir;
bool fRelocate;
bool fCSS;
bool fSkipIfMissing;
} Lookup_Virtual_To_Real;
static const Lookup_Virtual_To_Real c_lookup[] =
{
{ c_szSharedCSS, MAXSTRLEN(c_szSharedCSS), NULL , false, true , true },
{ c_szHelp , MAXSTRLEN(c_szHelp ), NULL , true , false, false },
///////////////////////////////////////////////////////////////////////////////////
{ c_szSystem , MAXSTRLEN(c_szSystem ), c_szSystemOEMDir, true , false, true }, // First try the OEM directory...
{ c_szSystem , MAXSTRLEN(c_szSystem ), c_szSystemDir , true , false, true },
{ c_szRoot , MAXSTRLEN(c_szRoot ), c_szVendorDir , true , false, true },
///////////////////////////////////////////////////////////////////////////////////
{ c_szSystem , MAXSTRLEN(c_szSystem ), c_szSystemOEMDir, false, false, true }, // First try the OEM directory...
{ c_szSystem , MAXSTRLEN(c_szSystem ), c_szSystemDir , false, false, false },
{ c_szRoot , MAXSTRLEN(c_szRoot ), c_szVendorDir , false, false, false }
};
typedef struct
{
LPCWSTR szExt;
LPCWSTR szMIME;
} Lookup_Ext_To_Mime;
static const Lookup_Ext_To_Mime c_lookupMIME[] =
{
{ L".htm" , L"text/html" },
{ L".html", L"text/html" },
{ L".css" , L"text/css" },
{ L".xml" , L"text/xml" },
{ L".js" , L"application/x-javascript" },
{ L".gif" , L"image/gif" },
{ L".jpg" , L"image/jpeg" },
{ L".bmp" , L"image/bmp" },
};
/////////////////////////////////////////////////////////////////////////////
HRESULT GetMimeFromExt( LPCWSTR pszExt ,
LPWSTR pszMime ,
DWORD cbMime )
{
__HCP_FUNC_ENTRY("::GetMimeFromExt");
HRESULT hr;
MPC::wstring szMime;
bool fFound;
hr = MPC::RegKey_Value_Read( szMime, fFound, pszExt, c_szContent, HKEY_CLASSES_ROOT );
if(SUCCEEDED(hr) && fFound)
{
wcsncpy( pszMime, szMime.c_str(), cbMime );
}
else
{
pszMime[0] = L'\0';
for(int i=0; i<ARRAYSIZE(c_lookupMIME); i++)
{
if(!MPC::StrICmp( c_lookupMIME[i].szExt, pszExt ))
{
wcsncpy( pszMime, c_lookupMIME[i].szMIME, cbMime );
break;
}
}
}
__HCP_FUNC_EXIT(S_OK);
}
static LPCWSTR UnescapeFileName( CComBSTR& bstrFile ,
LPCWSTR szUrl )
{
WCHAR* rgTmpLarge;
WCHAR rgTmpSmall[MAX_PATH+1];
DWORD dwSize = MAX_PATH;
if(::InternetCanonicalizeUrlW( szUrl, rgTmpSmall, &dwSize, ICU_DECODE | ICU_NO_ENCODE ))
{
bstrFile = rgTmpSmall;
}
else
{
rgTmpLarge = new WCHAR[dwSize+1];
if(rgTmpLarge)
{
if(::InternetCanonicalizeUrlW( szUrl, rgTmpLarge, &dwSize, ICU_DECODE | ICU_NO_ENCODE ))
{
bstrFile = rgTmpLarge;
}
delete [] rgTmpLarge;
}
}
return bstrFile;
}
/////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG_PROTOCOLLEAK
#include <Debug.h>
DEBUG_ProtocolLeak::DEBUG_ProtocolLeak()
{
m_num = 0;
m_numOut = 0;
m_numStart = 0;
m_numComplete = 0;
}
DEBUG_ProtocolLeak::~DEBUG_ProtocolLeak()
{
Iter it;
for(it=m_set.begin(); it != m_set.end(); it++)
{
CHCPProtocol* ptr = *it;
bool fGot = m_setStart .count( ptr ) != 0;
bool fDone = m_setComplete.count( ptr ) != 0;
DebugLog( L"Protocol Leakage: %08x %s %s %s\n", ptr, fGot ? L"STARTED" : L"NOT STARTED", fDone ? L"DONE" : L"RECEIVING", ptr->m_bstrUrlComplete );
}
}
void DEBUG_ProtocolLeak::Add( CHCPProtocol* ptr )
{
DebugLog( L"Protocol Leakage: %08x CREATED %s\n", ptr, ptr->m_bstrUrlComplete );
if(m_set.count( ptr ) != 0)
{
DebugBreak();
}
m_set.insert( ptr ); m_numOut++; m_num++;
}
void DEBUG_ProtocolLeak::Del( CHCPProtocol* ptr )
{
DebugLog( L"Protocol Leakage: %08x RELEASED %s\n", ptr, ptr->m_bstrUrlComplete );
if(m_setStart.erase( ptr ) == 1)
{
m_numStart += 0x10000;
}
if(m_setComplete.erase( ptr ) == 1)
{
m_numComplete += 0x10000;
}
if(m_set.erase( ptr ) == 1)
{
m_numOut--;
}
else
{
DebugBreak();
}
}
void DEBUG_ProtocolLeak::CheckStart( CHCPProtocol* ptr )
{
DebugLog( L"Protocol Leakage: %08x STARTED %s\n", ptr, ptr->m_bstrUrlComplete );
if(m_setStart.count( ptr ) != 0)
{
DebugBreak();
}
m_setStart.insert( ptr ); m_numStart++;
}
void DEBUG_ProtocolLeak::Completed( CHCPProtocol* ptr )
{
DebugLog( L"Protocol Leakage: %08x DONE %s\n", ptr, ptr->m_bstrUrlComplete );
m_setComplete.insert( ptr ); m_numComplete++;
}
static DEBUG_ProtocolLeak leaker;
#endif
CHCPProtocol::CHCPProtocol()
{
#ifdef DEBUG_PROTOCOLLEAK
leaker.Add( this );
#endif
__HCP_FUNC_ENTRY("CHCPProtocol::CHCPProtocol");
m_fDone = false; // bool m_fDone;
m_fReportResult = false; // bool m_fReportResult;
//
m_cbAvailableSize = 0; // DWORD m_cbAvailableSize;
m_cbTotalSize = 0; // DWORD m_cbTotalSize;
//
// CComPtr<IStream> m_pstrmRead;
// CComPtr<IStream> m_pstrmWrite;
//
// CComPtr<IInternetProtocolSink> m_pIProtSink;
// CComPtr<IInternetBindInfo> m_pIBindInfo;
m_grfSTI = 0; // DWORD m_grfSTI;
// BINDINFO m_bindinfo;
m_bindf = 0; // DWORD m_bindf;
//
// CComBSTR m_bstrUrlComplete;
// CComBSTR m_bstrUrlRedirected;
m_pDownloader = NULL; // InnerDownloader* m_pDownloader;
//
m_fRedirected = false; // bool m_fRedirected;
m_fCSS = false; // bool m_fCSS;
m_fBypass = false; // bool m_fBypass;
//
// CComPtr<IInternetProtocol> m_ipiBypass;
//
// CComBSTR m_bstrMimeType;
m_dwContentLength = 0; // DWORD m_dwContentLength;
//
m_hCache = NULL; // HANDLE m_hCache;
m_szCacheFileName[0] = 0; // WCHAR m_szCacheFileName[MAX_PATH];
memset( &m_bindinfo, 0, sizeof( m_bindinfo ) );
m_bindinfo.cbSize = sizeof( m_bindinfo );
}
CHCPProtocol::~CHCPProtocol()
{
#ifdef DEBUG_PROTOCOLLEAK
leaker.Del( this );
#endif
__HCP_FUNC_ENTRY("CHCPProtocol::~CHCPProtocol");
Shutdown();
}
////////////////////////////////////////////////////////////////////////////////
bool CHCPProtocol::OpenCacheEntry()
{
__HCP_FUNC_ENTRY("CHCPProtocol::OpenCacheEntry");
bool fRes = false;
LPCWSTR szUrl = m_bstrUrlComplete;
LPCWSTR szExt;
if((szExt = wcsrchr( szUrl, '.' ))) szExt++;
CloseCacheEntry( true );
if(::CreateUrlCacheEntryW( szUrl, 0, szExt, m_szCacheFileName, 0) )
{
if(m_szCacheFileName[0])
{
m_hCache = ::CreateFileW( m_szCacheFileName ,
GENERIC_WRITE ,
FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
CREATE_ALWAYS ,
FILE_ATTRIBUTE_NORMAL , NULL);
if(m_hCache == INVALID_HANDLE_VALUE)
{
m_hCache = NULL;
}
else
{
fRes = true;
}
}
}
__HCP_FUNC_EXIT(fRes);
}
void CHCPProtocol::WriteCacheEntry( /*[in]*/ void *pv ,
/*[in]*/ ULONG cbRead )
{
if(m_hCache && cbRead)
{
DWORD cbWritten;
if(::WriteFile( m_hCache, pv, cbRead, &cbWritten, NULL ) == FALSE || cbRead != cbWritten)
{
CloseCacheEntry( true );
}
}
}
void CHCPProtocol::CloseCacheEntry( /*[in]*/ bool fDelete )
{
if(m_hCache)
{
::CloseHandle( m_hCache ); m_hCache = NULL;
if(fDelete)
{
::DeleteUrlCacheEntryW( m_bstrUrlComplete );
}
else
{
WCHAR szHeader[256];
FILETIME ftZero = { 0, 0 };
swprintf( szHeader, L"HTTP/1.0 200 OK \r\n Content-Length: %d \r\n Content-Type: %s \r\n\r\n", m_dwContentLength, (BSTR)m_bstrMimeType );
::CommitUrlCacheEntryW( m_bstrUrlComplete, m_szCacheFileName,
ftZero, ftZero, NORMAL_CACHE_ENTRY,
szHeader, wcslen( szHeader ), NULL, 0 );
}
}
}
////////////////////////////////////////////////////////////////////////////////
HRESULT CHCPProtocol::InnerReportProgress( /*[in]*/ ULONG ulStatusCode ,
/*[in]*/ LPCWSTR szStatusText )
{
__HCP_FUNC_ENTRY("CHCPProtocol::InnerReportProgress");
HRESULT hr;
if(m_pIProtSink)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, m_pIProtSink->ReportProgress( ulStatusCode, szStatusText ));
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::InnerReportData( /*[in]*/ DWORD grfBSCF ,
/*[in]*/ ULONG ulProgress ,
/*[in]*/ ULONG ulProgressMax )
{
__HCP_FUNC_ENTRY("CHCPProtocol::InnerReportData");
HRESULT hr;
if(m_pIProtSink)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, m_pIProtSink->ReportData( grfBSCF, ulProgress, ulProgressMax ));
}
//
// On the last data notification, also send a ReportResult.
//
if(grfBSCF & BSCF_LASTDATANOTIFICATION)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportResult( S_OK, 0, 0 ));
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::InnerReportResult( /*[in]*/ HRESULT hrResult ,
/*[in]*/ DWORD dwError ,
/*[in]*/ LPCWSTR szResult )
{
__HCP_FUNC_ENTRY("CHCPProtocol::InnerReportResult");
HRESULT hr;
if(m_fReportResult == false)
{
m_fReportResult = true;
#ifdef DEBUG_PROTOCOLLEAK
leaker.Completed( this );
#endif
DEBUG_AppendPerf( DEBUG_PERF_PROTOCOL, L"CHCPProtocol::InnerReportResult : %s", SAFEBSTR( m_bstrUrlComplete ) );
if(m_pIProtSink)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, m_pIProtSink->ReportResult( hrResult, dwError, szResult ));
}
//
// Release the references to the ProtSink and BindInfo objects, but not the references to the streams.
//
Shutdown( false );
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
////////////////////////////////////////////////////////////////////////////////
void CHCPProtocol::Shutdown( /*[in]*/ bool fAll )
{
__HCP_FUNC_ENTRY("CHCPProtocol::Shutdown");
m_pIBindInfo.Release();
m_pIProtSink.Release();
CloseCacheEntry( true );
if(m_pDownloader)
{
m_pDownloader->Release();
m_pDownloader = NULL;
}
if(fAll)
{
m_pstrmRead .Release();
m_pstrmWrite.Release();
// Release BINDINFO contents
::ReleaseBindInfo( &m_bindinfo );
}
}
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CHCPProtocol::Start( /*[in]*/ LPCWSTR szUrl ,
/*[in]*/ IInternetProtocolSink *pIProtSink ,
/*[in]*/ IInternetBindInfo *pIBindInfo ,
/*[in]*/ DWORD grfSTI ,
/*[in]*/ HANDLE_PTR dwReserved )
{
#ifdef DEBUG_PROTOCOLLEAK
leaker.CheckStart( this );
#endif
__HCP_FUNC_ENTRY("CHCPProtocol::Start");
HRESULT hr;
DEBUG_AppendPerf( DEBUG_PERF_PROTOCOL, L"CHCPProtocol::Start : %s", szUrl );
//
// Initialize variables for new download.
//
Shutdown();
m_fDone = false;
m_cbAvailableSize = 0;
m_cbTotalSize = 0;
m_pIProtSink = pIProtSink;
m_pIBindInfo = pIBindInfo;
m_grfSTI = grfSTI;
m_bstrUrlComplete = (LPCOLESTR)NULL;
m_bstrUrlRedirected = (LPCOLESTR)NULL;
//
// Get URLMoniker BINDINFO structure from IInternetBindInfo
//
m_bindinfo.cbSize = sizeof( m_bindinfo );
if(pIBindInfo)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, pIBindInfo->GetBindInfo( &m_bindf, &m_bindinfo ));
}
// Parse URL and store results inside
hr = DoParse( szUrl );
if(grfSTI & PI_PARSE_URL)
{
if(FAILED(hr))
{
__MPC_SET_ERROR_AND_EXIT(hr, S_FALSE);
}
else
{
__MPC_SET_ERROR_AND_EXIT(hr, S_OK);
}
}
if(FAILED(hr)) __MPC_FUNC_LEAVE;
// TODO: We could always spawn a worker thread to be more truly asynchronous.
// Rather than complicate this code as multithreading scenarios tend to do,
// we do everything on the main apartment thread and only pretend to be
// working on a secondary thread if we get PI_FORCE_ASYNC
if(!(grfSTI & PI_FORCE_ASYNC))
{
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind());
}
else // Wait for Continue to DoBind()
{
PROTOCOLDATA protdata;
hr = E_PENDING;
protdata.grfFlags = PI_FORCE_ASYNC;
protdata.dwState = 1;
protdata.pData = NULL;
protdata.cbData = 0;
// TODO: Actually, we should spawn a new worker thread and have it do the
// bind process, then when done, it could use Switch / Continue to
// pass data back to the apartment thread
if(m_pIProtSink)
{
m_pIProtSink->Switch( &protdata );
}
else
{
__MPC_SET_ERROR_AND_EXIT(hr, E_INVALIDARG);
}
}
__HCP_FUNC_CLEANUP;
if(FAILED(hr))
{
(void)InnerReportResult( hr, 0, 0 );
}
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::Continue( /*[in]*/ PROTOCOLDATA *pStateInfo )
{
__HCP_FUNC_ENTRY("CHCPProtocol::Continue");
HRESULT hr;
if(m_fBypass)
{
__MPC_SET_ERROR_AND_EXIT(hr, m_ipiBypass->Continue( pStateInfo ));
}
// We're faking what it would be like to have a worker thread
// communicating with the apartment thread
// If we really did spawn off a worker thread, we should do the
// bind there, and just use Switch/Continue to echo UI data back
// to this thread
if(pStateInfo->dwState == 1)
{
__MPC_SET_ERROR_AND_EXIT(hr, DoBind());
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::Abort( /*[in]*/ HRESULT hrReason ,
/*[in]*/ DWORD dwOptions )
{
__HCP_FUNC_ENTRY("CHCPProtocol::Abort");
HRESULT hr = E_FAIL;
if(m_fBypass)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, m_ipiBypass->Abort( hrReason, dwOptions ));
}
// Stop our own internal download process
// TODO: If we call Abort too early on the Binding object,
// this won't abort the download. (Too early is OnStartBinding
// or before.) We won't bother checking, though, for clarity.
// TODO: Make sure we set m_pDownloader to NULL when the
// downloader object is destructed or finished.
if(m_pDownloader)
{
m_pDownloader->Abort();
}
if(SUCCEEDED(hrReason)) // Possibly Abort could get called with 0?
{
hrReason = E_ABORT;
}
// Notify Sink of abort
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportResult( hrReason, 0, 0 ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::Terminate( /*[in]*/ DWORD dwOptions )
{
__HCP_FUNC_ENTRY("CHCPProtocol::Terminate");
HRESULT hr;
if(m_fBypass)
{
(void)m_ipiBypass->Terminate( dwOptions );
}
hr = S_OK;
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::Suspend()
{
__HCP_FUNC_ENTRY("CHCPProtocol::Suspend");
if(m_fBypass)
{
(void)m_ipiBypass->Suspend();
}
__HCP_FUNC_EXIT(E_NOTIMPL);
}
STDMETHODIMP CHCPProtocol::Resume()
{
__HCP_FUNC_ENTRY("CHCPProtocol::Resume");
if(m_fBypass)
{
(void)m_ipiBypass->Resume();
}
__HCP_FUNC_EXIT(E_NOTIMPL);
}
// IInternetProtocol methods
STDMETHODIMP CHCPProtocol::Read( /*[in] */ void *pv ,
/*[in] */ ULONG cb ,
/*[out]*/ ULONG *pcbRead )
{
__HCP_FUNC_ENTRY("CHCPProtocol::Read");
HRESULT hr = S_OK;
DEBUG_AppendPerf( DEBUG_PERF_PROTOCOL_READ, L"CHCPProtocolRoot::Read : Enter %s %d", SAFEBSTR( m_bstrUrlComplete ), (int)cb );
if(m_fBypass)
{
__MPC_SET_ERROR_AND_EXIT(hr, m_ipiBypass->Read( pv, cb, pcbRead ));
}
if(m_pstrmRead == 0)
{
__MPC_SET_ERROR_AND_EXIT(hr, S_FALSE); // We've hit the end of the road, jack
}
// One might expect URLMON to Read only the amount of data that we specified we have.
// However, it actually reads in blocks and will go far beyond the data we have
// specified unless we slap it around a little.
// We must only return S_FALSE when we have hit the absolute end of the stream
// If we think there is more data coming down the wire, then we return E_PENDING
// here. Even if we return S_OK and no data, URLMON will still think we've hit
// the end of the stream.
// ASSERTION: End of data means we've received BSCF_LASTDATANOTIFICATION
__MPC_EXIT_IF_METHOD_FAILS(hr, m_pstrmRead->Read( pv, cb, pcbRead ));
if(hr == S_FALSE)
{
CloseCacheEntry( false );
__MPC_SET_ERROR_AND_EXIT(hr, S_FALSE); // We've hit the end of the road, jack
}
else if(*pcbRead == 0)
{
if(m_fDone)
{
CloseCacheEntry( false );
__MPC_SET_ERROR_AND_EXIT(hr, S_FALSE); // We've hit the end of the road, jack
}
else
{
__MPC_SET_ERROR_AND_EXIT(hr, E_PENDING);
}
}
else
{
WriteCacheEntry( pv, *pcbRead );
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::Seek( /*[in] */ LARGE_INTEGER dlibMove ,
/*[in] */ DWORD dwOrigin ,
/*[out]*/ ULARGE_INTEGER *plibNewPosition )
{
__HCP_FUNC_ENTRY("CHCPProtocol::Seek");
if(m_fBypass)
{
(void)m_ipiBypass->Seek( dlibMove, dwOrigin, plibNewPosition );
}
__HCP_FUNC_EXIT(E_NOTIMPL);
}
STDMETHODIMP CHCPProtocol::LockRequest( /*[in]*/ DWORD dwOptions )
{
__HCP_FUNC_ENTRY("CHCPProtocol::LockRequest");
if(m_fBypass)
{
(void)m_ipiBypass->LockRequest( dwOptions );
}
__HCP_FUNC_EXIT(S_OK);
}
STDMETHODIMP CHCPProtocol::UnlockRequest()
{
__HCP_FUNC_ENTRY("CHCPProtocol::UnlockRequest");
if(m_fBypass)
{
(void)m_ipiBypass->UnlockRequest();
}
//
// Release all the pointers to objects.
//
Shutdown();
__HCP_FUNC_EXIT(S_OK);
}
////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CHCPProtocol::QueryOption( DWORD dwOption, LPVOID pBuffer, DWORD *pcbBuf )
{
__HCP_FUNC_ENTRY( "CHCPProtocol::QueryOption" );
HRESULT hr;
if(dwOption == INTERNET_OPTION_REQUEST_FLAGS && *pcbBuf == sizeof(DWORD))
{
*((DWORD*)pBuffer) = INTERNET_REQFLAG_FROM_CACHE;
hr = S_OK;
}
else
{
hr = E_NOTIMPL;
}
__HCP_FUNC_EXIT(hr);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CHCPProtocol::DoParse( /*[in]*/ LPCWSTR szURL )
{
__HCP_FUNC_ENTRY("CHCPProtocol::DoParse");
HRESULT hr;
CComBSTR bstrURLCopy;
LPCWSTR szURLCopy;
LPWSTR szQuery;
LPCWSTR szRedirect;
bool fHCP;
m_bstrUrlComplete = szURL;
m_bstrUrlRedirected = (LPCOLESTR)NULL;
fHCP = CHCPProtocolInfo::LookForHCP( szURL, m_fRedirected, szRedirect );
m_fRedirected = false; // redirection should never happen here
if(m_fRedirected)
{
m_bstrUrlRedirected = szRedirect;
}
else
{
const Lookup_Virtual_To_Real* ptr;
int i;
MPC::wstring strDir;
LPOLESTR szTmp;
szURLCopy = ::UnescapeFileName( bstrURLCopy, szURL ); if(!szURLCopy) __MPC_SET_ERROR_AND_EXIT(hr, E_OUTOFMEMORY);
//
// Remove the query part of the URL.
//
if(szQuery = wcschr( szURLCopy, L'?' ))
{
szQuery[0] = 0;
}
//
// Do the mapping between virtual paths and real ones.
//
for(ptr=c_lookup, i=0; i<ARRAYSIZE(c_lookup); i++, ptr++)
{
if(!_wcsnicmp( szURLCopy, ptr->szPrefix, ptr->iPrefix ))
{
if(ptr->fCSS)
{
m_bstrUrlRedirected = szURL;
m_fCSS = true;
break;
}
if(!ptr->szRealSubDir)
{
strDir = ptr->fRelocate ? CHCPProtocolEnvironment::s_GLOBAL->HelpLocation() : HC_HELPSVC_HELPFILES_DEFAULT;
strDir += L"\\";
}
else
{
strDir = ptr->fRelocate ? CHCPProtocolEnvironment::s_GLOBAL->System() : HC_HELPSET_ROOT;
strDir += ptr->szRealSubDir;
}
MPC::SubstituteEnvVariables( strDir );
m_bstrUrlRedirected = strDir.c_str();
m_bstrUrlRedirected += &szURLCopy[ ptr->iPrefix ];
//
// Convert the slashes to backslashes.
//
while((szTmp = wcschr( m_bstrUrlRedirected, L'/' ))) szTmp[0] = L'\\';
//
// Remove any trailing slash.
//
while((szTmp = wcsrchr( m_bstrUrlRedirected, L'/' )) && szTmp[1] == 0) szTmp[0] = 0;
while((szTmp = wcsrchr( m_bstrUrlRedirected, L'\\' )) && szTmp[1] == 0) szTmp[0] = 0;
CHCPProtocolEnvironment::s_GLOBAL->ReformatURL( m_bstrUrlRedirected );
if(ptr->fSkipIfMissing && MPC::FileSystemObject::IsFile( m_bstrUrlRedirected ) == false) continue;
break;
}
}
}
if(!m_bstrUrlRedirected) __MPC_SET_ERROR_AND_EXIT(hr, E_OUTOFMEMORY);
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::DoBind()
{
__HCP_FUNC_ENTRY("CHCPProtocol::DoBind");
HRESULT hr;
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportProgress( BINDSTATUS_FINDINGRESOURCE, SAFEBSTR( m_bstrUrlRedirected ) ));
if(m_fRedirected)
{
if(MPC::MSITS::IsCHM( SAFEBSTR( m_bstrUrlRedirected ) ))
{
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_Redirect_MSITS());
}
else
{
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_Redirect_UrlMoniker());
}
}
else if(m_fCSS)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_CSS());
}
else
{
MPC::wstring szPage = SAFEBSTR(m_bstrUrlRedirected);
MPC::FileSystemObject fso = szPage.c_str();
bool fFound;
bool fIsAFile;
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_Exists( fso, fFound, fIsAFile ));
if(fFound && fIsAFile)
{
//
// The file exists, so load its content.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_File());
}
else
{
//
// The file, as is, doesn't exist, so try to find a .chm on the path.
//
while(1)
{
MPC::wstring szParent;
MPC::wstring szCHM;
__MPC_EXIT_IF_METHOD_FAILS(hr, fso.get_Parent( szParent ));
if(szParent.length() == 0)
{
//
// No parent, so exit with error.
//
__MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
}
//
// Point the FileSystemObject to its parent.
//
fso = szParent.c_str();
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_Exists( fso, fFound, fIsAFile ));
//
// Parent exists, so it cannot exist a .CHM file. Exit with error.
//
if(fFound)
{
__MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
}
//
// Add the .CHM extension and look for it.
//
szCHM = szParent; szCHM.append( L".chm" );
fso = szCHM.c_str();
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_Exists( fso, fFound, fIsAFile ));
//
// No .CHM file, recurse up to the root.
//
if(fFound == false)
{
continue;
}
//
// The .CHM is not a file, exit with error.
//
if(fIsAFile == false)
{
__MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
}
//
// Found, so redirect to the proper protocol.
//
szCHM = L"ms-its:";
szCHM.append( szParent );
szCHM.append( L".chm" );
if(szParent.length() < szPage.length())
{
LPWSTR szBuf = new WCHAR[szPage.length()+1];
if(szBuf)
{
LPWSTR szTmp;
wcscpy( szBuf, szPage.c_str() );
//
// Convert the backslashes to slashes.
//
while(szTmp = wcschr( szBuf, L'\\' )) szTmp[0] = L'/';
szCHM.append( L"::" );
szCHM.append( &szBuf[szParent.length()] );
delete [] szBuf;
}
}
m_bstrUrlRedirected = szCHM.c_str();
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_Redirect_MSITS());
break;
}
}
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::DoBind_Exists( /*[in] */ MPC::FileSystemObject& fso ,
/*[out]*/ bool& fFound ,
/*[out]*/ bool& fIsAFile )
{
__HCP_FUNC_ENTRY("CHCPProtocol::DoBind_Exists");
HRESULT hr;
if(fso.Exists())
{
fFound = true;
fIsAFile = fso.IsFile();
}
else
{
fFound = false;
fIsAFile = false;
}
hr = S_OK;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::DoBind_Redirect_UrlMoniker()
{
__HCP_FUNC_ENTRY("CHCPProtocol::DoBind_Redirect_UrlMoniker");
HRESULT hr;
//
// Create the stream used to receive downloaded data.
//
::CreateStreamOnHGlobal( NULL, TRUE, &m_pstrmWrite );
if(m_pstrmWrite == NULL)
{
__MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
}
//
// Create the downloader object.
//
if(SUCCEEDED(hr = m_pDownloader->CreateInstance( &m_pDownloader )))
{
m_pDownloader->AddRef();
if(FAILED(hr = m_pDownloader->StartAsyncDownload( this, m_bstrUrlRedirected, NULL, FALSE )))
{
if(hr != E_PENDING) __MPC_FUNC_LEAVE;
}
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::DoBind_Redirect_MSITS()
{
__HCP_FUNC_ENTRY("CHCPProtocol::DoBind_Redirect_MSITS");
HRESULT hr;
CComBSTR bstrStorageName;
CComBSTR bstrFilePath;
LPCWSTR szExt;
WCHAR rgMime[MAX_PATH];
if(MPC::MSITS::IsCHM( SAFEBSTR( m_bstrUrlRedirected ), &bstrStorageName, &bstrFilePath ) == false)
{
__MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
}
//
// Try to find the Mime Type for this file.
//
if((szExt = wcsrchr( bstrFilePath, L'.' )))
{
::GetMimeFromExt( szExt, rgMime, MAX_PATH-1 );
}
else
{
rgMime[0] = 0;
}
//
// Extract the file from the CHM.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::MSITS::OpenAsStream( bstrStorageName, bstrFilePath, &m_pstrmRead ));
//
// Signal the Protocol Sink that data is available.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_ReturnData( /*fCloneStream*/false, szExt ? rgMime : NULL ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::DoBind_CSS()
{
__HCP_FUNC_ENTRY("CHCPProtocol::DoBind_CSS");
HRESULT hr;
LPCWSTR szExt;
WCHAR rgMime[256];
//
// Try to find the Mime Type for this file.
//
if((szExt = wcsrchr( m_bstrUrlComplete, L'.' )))
{
::GetMimeFromExt( szExt, rgMime, 255 );
}
__MPC_EXIT_IF_METHOD_FAILS(hr, CHCPProtocolEnvironment::s_GLOBAL->GetCSS( m_pstrmRead ));
//
// Signal the Protocol Sink that data is available.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_ReturnData( /*fCloneStream*/false, szExt ? rgMime : NULL ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::DoBind_File()
{
__HCP_FUNC_ENTRY("CHCPProtocol::DoBind_File");
HRESULT hr;
CComPtr<MPC::FileStream> pStm;
LPCWSTR szFile = m_bstrUrlRedirected;
LPCWSTR szExt;
WCHAR rgMime[256];
//
// Try to find the Mime Type for this file.
//
if((szExt = wcsrchr( szFile, L'.' )))
{
::GetMimeFromExt( szExt, rgMime, 255 );
}
//
// Create the file stream.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &pStm ));
__MPC_EXIT_IF_METHOD_FAILS(hr, pStm->InitForRead ( szFile ));
__MPC_EXIT_IF_METHOD_FAILS(hr, pStm->QueryInterface( IID_IStream, (void**)&m_pstrmRead ));
//
// Signal the Protocol Sink that data is available.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, DoBind_ReturnData( /*fCloneStream*/false, szExt ? rgMime : NULL ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
HRESULT CHCPProtocol::DoBind_ReturnData( /*[in]*/ bool fCloneStream ,
/*[in]*/ LPCWSTR szMimeType )
{
__HCP_FUNC_ENTRY("CHCPProtocol::DoBind_ReturnData");
HRESULT hr;
STATSTG statstg;
m_fDone = true;
if(fCloneStream)
{
LARGE_INTEGER li;
//
// Clone the stream, so that we can hand it back to the ProtSink for data reading.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, m_pstrmWrite->Clone( &m_pstrmRead ));
//
// Reset stream to beginning.
//
li.LowPart = 0;
li.HighPart = 0;
__MPC_EXIT_IF_METHOD_FAILS(hr, m_pstrmRead->Seek( li, STREAM_SEEK_SET, NULL ));
}
(void)m_pstrmRead->Stat( &statstg, STATFLAG_NONAME );
m_bstrMimeType = szMimeType;
m_dwContentLength = statstg.cbSize.LowPart;
//
// Create an entry in the cache, if required.
//
if(m_bindf & BINDF_NEEDFILE)
{
(void)OpenCacheEntry();
}
if(szMimeType)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportProgress( BINDSTATUS_MIMETYPEAVAILABLE, szMimeType ));
}
if(m_hCache)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportProgress( BINDSTATUS_CACHEFILENAMEAVAILABLE, m_szCacheFileName ));
}
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportData( BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE,
statstg.cbSize.LowPart ,
statstg.cbSize.LowPart ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
// Implementation of the ISimpleBindStatusCallback interface.
//
STDMETHODIMP CHCPProtocol::ForwardQueryInterface( /*[in] */ REFIID riid ,
/*[out]*/ void** ppv )
{
__HCP_FUNC_ENTRY("CHCPProtocol::ForwardQueryInterface");
HRESULT hr = E_NOINTERFACE;
*ppv = NULL;
if(IsEqualIID( riid, IID_IHttpNegotiate))
{
CComQIPtr<IServiceProvider> pProv;
pProv = m_pIProtSink;
if(pProv)
{
if(SUCCEEDED(pProv->QueryService( riid, riid, ppv )))
{
__MPC_SET_ERROR_AND_EXIT(hr, S_OK);
}
}
pProv = m_pIBindInfo;
if(pProv)
{
if(SUCCEEDED(pProv->QueryService( riid, riid, ppv )))
{
__MPC_SET_ERROR_AND_EXIT(hr, S_OK);
}
}
}
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::GetBindInfo( /*[out]*/ BINDINFO *pbindInfo )
{
__HCP_FUNC_ENTRY("CHCPProtocol::GetBindInfo");
HRESULT hr = ::CopyBindInfo( &m_bindinfo, pbindInfo );
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::PreBindMoniker( /*[in]*/ IBindCtx* pBindCtx ,
/*[in]*/ IMoniker* pMoniker )
{
__HCP_FUNC_ENTRY("CHCPProtocol::PreBindMoniker");
__HCP_FUNC_EXIT(S_OK);
}
STDMETHODIMP CHCPProtocol::OnProgress( /*[in]*/ ULONG ulProgress ,
/*[in]*/ ULONG ulProgressMax,
/*[in]*/ ULONG ulStatusCode ,
/*[in]*/ LPCWSTR szStatusText )
{
__HCP_FUNC_ENTRY("CHCPProtocol::OnProgress");
HRESULT hr;
switch(ulStatusCode)
{
case BINDSTATUS_BEGINDOWNLOADDATA:
// ulProgressMax represents the total size of the download
// When talking HTTP, this is determined by the CONTENT_LENGTH header
// If this header is missing or wrong, we're missing or wrong
m_cbTotalSize = ulProgressMax;
break;
case BINDSTATUS_MIMETYPEAVAILABLE :
case BINDSTATUS_FINDINGRESOURCE :
case BINDSTATUS_CONNECTING :
case BINDSTATUS_SENDINGREQUEST :
case BINDSTATUS_CACHEFILENAMEAVAILABLE:
case BINDSTATUS_REDIRECTING :
case BINDSTATUS_USINGCACHEDCOPY :
case BINDSTATUS_CLASSIDAVAILABLE :
case BINDSTATUS_LOADINGMIMEHANDLER :
// only pass on these notifications:
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportProgress( ulStatusCode, szStatusText ));
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::OnData( /*[in]*/ CHCPBindStatusCallback* pbsc ,
/*[in]*/ BYTE* pBytes ,
/*[in]*/ DWORD dwSize ,
/*[in]*/ DWORD grfBSCF ,
/*[in]*/ FORMATETC* pformatetc ,
/*[in]*/ STGMEDIUM* pstgmed )
{
__HCP_FUNC_ENTRY("CHCPProtocol::OnData");
HRESULT hr;
ULONG cbWritten;
//
// To handle an error, we just report result that we failed and terminate the download object
//
if(FAILED(hr = m_pstrmWrite->Write( pBytes, dwSize, &cbWritten )))
{
// Our own Abort handles this just nicely
Abort( hr, 0 ); __MPC_FUNC_LEAVE;
}
m_cbAvailableSize += cbWritten;
if(grfBSCF & BSCF_FIRSTDATANOTIFICATION)
{
LARGE_INTEGER li;
// We need two concurrent seek pointers to the same stream
// because we'll be writing to the stream at the end while
// we're trying to read from the beginning
__MPC_EXIT_IF_METHOD_FAILS(hr, m_pstrmWrite->Clone( &m_pstrmRead ));
// reset stream to beginning
li.LowPart = 0;
li.HighPart = 0;
__MPC_EXIT_IF_METHOD_FAILS(hr, m_pstrmRead->Seek( li, STREAM_SEEK_SET, NULL ));
}
// We've got all the data, signal complete
if(grfBSCF & BSCF_LASTDATANOTIFICATION)
{
// We need to remember if we've received LASTDATANOTIFICATION yet
m_fDone = true;
// We only need to do ReportResult if we fail somehow -
// DATAFULLYAVAILABLE is signal enough that we succeeded
// NOT NEEDED: m_pIProtSink->ReportResult(S_OK, 0, NULL);
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportData( BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE,
m_cbAvailableSize ,
m_cbAvailableSize ));
}
else
{
// Report our progress accurately using our byte count
// of what we've read versus the total known download size
// We know the total amount to read, the total read so far, and
// the total written. The problem is that we can't know the total
// amount that will be written in the end. So we estimate at
// 1.5 * Total size and if we overrun, we just start adding some
// extra to the end
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportData( grfBSCF, m_cbAvailableSize, m_cbTotalSize ));
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
STDMETHODIMP CHCPProtocol::OnBindingFailure( /*[in]*/ HRESULT hr ,
/*[in]*/ LPCWSTR szError )
{
__HCP_FUNC_ENTRY("CHCPProtocol::OnBindingFailure");
//
// Inform protocol-sink that we've failed to download the data for some reason.
//
__MPC_EXIT_IF_METHOD_FAILS(hr, InnerReportResult( hr, 0, szError ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}