Windows2003-3790/inetcore/urlmon/download/dl.cxx
2020-09-30 16:53:55 +02:00

3354 lines
96 KiB
C++

// ===========================================================================
// File: DL.CXX
// implements CDownload, CBindStatusCallback classes
//
#include <cdlpch.h>
// SILENT MODE
#include <winineti.h>
#include <shlwapi.h>
#include <shlwapip.h>
#include <comcat.h>
#include <initguid.h> // office antivirus goo
#define AVVENDOR // don't look at unwanted office defs
#include <msoav.h>
extern char g_szOCXTempDir[MAX_PATH];
extern IEnumFORMATETC *g_pEFmtETC;
extern FORMATETC g_rgfmtetc[];
// ---------------------------------------------------------------------------
// %%Function: CDownload::CDownload
// CDownload is the basic download obj.
// ---------------------------------------------------------------------------
CDownload::CDownload(LPCWSTR szURL, FILEXTN extn, HRESULT *phr)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CDownload::CDownload",
"this=%#x, %.80wq, %#x, %#x",
this, szURL, extn, phr
));
DllAddRef();
DWORD len = lstrlenW(szURL); // make private copy
m_url = new WCHAR [len + 1];
if (m_url)
StrCpyW(m_url, szURL);
else
*phr = E_OUTOFMEMORY;
m_pmk = 0;
m_pbc = 0;
m_pbsc = 0;
m_pdlnext= NULL;
m_ParentCDL.RemoveAll();
m_extn = extn;
m_pFileName = NULL; // we don't know the dest filename
// till we create a temp file in the first
// notification of OnDataAvailable
// This is guaranteed to get set before
// OnStopBinding
m_ulProgress = 0;
m_ulProgressMax = 0;
m_state = DLSTATE_INIT;
m_psess = NULL;
m_pFilesToExtract = NULL;
m_pSetuphead = NULL;
m_hPostData = NULL;
m_cbPostData = 0;
m_hrOSB = S_OK;
m_hrStatus = S_OK;
m_hrResponseHdr = S_OK;
m_bCompleteSignalled = FALSE;
m_flags = DL_FLAGS_INIT;
m_SetupHooks.RemoveAll();
m_JavaSetupList.RemoveAll();
m_pUnkForCacheFileRelease = NULL;
m_pbJavaTrust = NULL;
m_wszDistUnit = NULL;
m_pcbhList = NULL;
m_ppmkContext = NULL;
m_grfBINDF = 0;
m_bExactVersion = FALSE;
DEBUG_LEAVE(0);
} // CDownload
// ---------------------------------------------------------------------------
// %%Function: CDownload::~CDownload
// ---------------------------------------------------------------------------
CDownload::~CDownload()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CDownload::~CDownload",
"this=%#x",
this
));
int i;
CCodeBaseHold *pcbh = NULL;
LISTPOSITION lpos = 0;
CleanUp();
LISTPOSITION pos = m_ParentCDL.GetHeadPosition();
int iNum = m_ParentCDL.GetCount();
Assert(iNum == 0);
for (i=0; i < iNum; i++) {
CParentCDL *pParentCDL = m_ParentCDL.GetNext(pos); // pass ref!
delete pParentCDL;
}
m_ParentCDL.RemoveAll();
if (m_pcbhList != NULL) {
lpos = m_pcbhList->GetHeadPosition();
while (lpos) {
pcbh = m_pcbhList->GetNext(lpos);
delete pcbh;
}
m_pcbhList->RemoveAll();
}
SAFEDELETE(m_pcbhList);
SAFEDELETE(m_wszDistUnit);
pos = m_SetupHooks.GetHeadPosition();
iNum = m_SetupHooks.GetCount();
for (i=0; i < iNum; i++) {
CSetupHook *pSetupHook = m_SetupHooks.GetNext(pos); // pass ref!
delete pSetupHook;
}
m_SetupHooks.RemoveAll();
pos = m_JavaSetupList.GetHeadPosition();
iNum = m_JavaSetupList.GetCount();
for (i=0; i < iNum; i++) {
CJavaSetup *pJavaSetup = m_JavaSetupList.GetNext(pos); // pass ref!
delete pJavaSetup;
}
m_JavaSetupList.RemoveAll();
DllRelease();
DEBUG_LEAVE(0);
} // ~CDownload
// ---------------------------------------------------------------------------
// %%Function: CDownload::HasJavaPermissions
// ---------------------------------------------------------------------------
BOOL
CDownload::HasJavaPermissions()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"CDownload::HasJavaPermissions",
"this=%#x",
this
));
if (m_pbJavaTrust) {
// new jaavcypt > 2151 succeeds even if one of activex/java
// is allowed
DEBUG_LEAVE((m_pbJavaTrust->pbJavaPermissions != NULL));
return (m_pbJavaTrust->pbJavaPermissions != NULL);
}
DEBUG_LEAVE(FALSE);
return FALSE;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::HasAllActiveXPermissions
// ---------------------------------------------------------------------------
BOOL
CDownload::HasAllActiveXPermissions()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"CDownload::HasAllActiveXPermissions",
"this=%#x",
this
));
PJAVA_TRUST pbJavaTrust = NULL;
if (m_pbJavaTrust) {
// new jaavcypt > 2151 succeeds even if one of activex/java
// is allowed
DEBUG_LEAVE(m_pbJavaTrust->fAllActiveXPermissions);
return m_pbJavaTrust->fAllActiveXPermissions;
}
else {
pbJavaTrust = GetCodeDownload()->GetJavaTrust();
if (pbJavaTrust) {
DEBUG_LEAVE(pbJavaTrust->fAllActiveXPermissions);
return pbJavaTrust->fAllActiveXPermissions;
}
}
DEBUG_LEAVE(FALSE);
return FALSE;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::CompleteSignal
// ---------------------------------------------------------------------------
HRESULT
CDownload::CompleteSignal(HRESULT hrOSB, HRESULT hrStatus, HRESULT hrResponseHdr, LPCWSTR szError)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::CompleteSignal",
"this=%#x, %#x, %#x, %#x, %.80wq",
this, hrOSB, hrStatus, hrResponseHdr, szError
));
int i, iNum;
LISTPOSITION pos;
m_hrOSB = hrOSB;
m_hrStatus = hrStatus;
m_hrResponseHdr = hrResponseHdr;
m_bCompleteSignalled = TRUE;
restart:
iNum = m_ParentCDL.GetCount();
Assert(iNum);
pos = m_ParentCDL.GetHeadPosition();
for (i=0; i < iNum; i++) {
CParentCDL *pParentCDL = m_ParentCDL.GetNext(pos); // pass ref!
if (!pParentCDL->m_bCompleteSignalled) {
// unsignalled code download
pParentCDL->m_bCompleteSignalled = TRUE;
pParentCDL->m_pcdl->CompleteOne( this ,hrOSB, hrStatus, hrResponseHdr, szError);
if (iNum > 1) {
// failed complete reports could cause CodeDownloads to release
// us and thus change the list
goto restart;
}
}
}
DEBUG_LEAVE(S_OK);
return S_OK;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::AddParent(CCodeDownload *pcdl)
// ---------------------------------------------------------------------------
HRESULT
CDownload::AddParent(CCodeDownload *pcdl)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::AddParent",
"this=%#x, %#x",
this, pcdl
));
HRESULT hr = S_OK;
CParentCDL *pParentCDL = new CParentCDL(pcdl);
if (pParentCDL)
m_ParentCDL.AddTail(pParentCDL);
else
hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) {
if (m_bCompleteSignalled) {
pParentCDL->m_bCompleteSignalled = TRUE;
pcdl->CompleteOne(this ,m_hrOSB, m_hrStatus, m_hrResponseHdr, NULL);
} else {
hr = FAILED(m_hrOSB)?m_hrOSB:(FAILED(m_hrStatus)?m_hrStatus:(FAILED(m_hrResponseHdr)?m_hrResponseHdr:S_OK));
}
}
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::IsSignalled(CCodeDownload *pcdl)
// ---------------------------------------------------------------------------
BOOL
CDownload::IsSignalled(CCodeDownload *pcdl)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"CDownload::IsSignalled",
"this=%#x, %#x",
this, pcdl
));
CParentCDL *pParentCDL = NULL;
BOOL bRet = FALSE;
LISTPOSITION pos = 0;
DLSTATE dls;
int iNum = 0;
int i;
dls = GetDLState();
if (dls == DLSTATE_DONE || dls == DLSTATE_READY_TO_SETUP) {
bRet = TRUE;
goto Exit;
}
iNum = m_ParentCDL.GetCount();
Assert(iNum);
pos = m_ParentCDL.GetHeadPosition();
for (i=0; i < iNum; i++) {
pParentCDL = m_ParentCDL.GetNext(pos); // pass ref!
if (pParentCDL->m_pcdl == pcdl && pParentCDL->m_bCompleteSignalled) {
bRet = TRUE;
break;
}
}
Exit:
DEBUG_LEAVE(bRet);
return bRet;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::Abort(CCodeDownload *pcdl)
// ---------------------------------------------------------------------------
HRESULT
CDownload::Abort(CCodeDownload *pcdl)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::Abort",
"this=%#x, %#x",
this, pcdl
));
int i;
CParentCDL *pThisParentCDL = NULL;
BOOL bDelinkParent = FALSE;
BOOL fWaitForAbortCompletion = FALSE;
HRESULT hr = S_OK;
int iNum = m_ParentCDL.GetCount();
Assert(iNum);
LISTPOSITION pos = m_ParentCDL.GetHeadPosition();
for (i=0; i < iNum; i++) {
CParentCDL *pParentCDL = m_ParentCDL.GetNext(pos); // pass ref!
if (pParentCDL->m_pcdl == pcdl)
pThisParentCDL = pParentCDL;
else if ( !pParentCDL->m_bCompleteSignalled )
bDelinkParent = TRUE;
}
Assert(pThisParentCDL);
if (!pThisParentCDL)
{
DEBUG_LEAVE(E_FAIL);
return E_FAIL;
}
if (bDelinkParent) {
// multiple code downloads interested in this
// delink this parent, by marking as complete signalled
pThisParentCDL->m_bCompleteSignalled = TRUE;
DEBUG_LEAVE(S_OK);
return S_OK;
}
switch ( GetDLState()) {
case DLSTATE_BINDING:
GetBSC()->GetBinding()->Abort();
break;
case DLSTATE_ABORT:
// have aborted this but the OSB has not been recieved yet
// so wait for that to come to us before we complteall
// or post the setup packet to completeall
fWaitForAbortCompletion = TRUE;
break;
case DLSTATE_DONE:
case DLSTATE_READY_TO_SETUP:
break;
default:
// packet processing pending for this state. we will check for
// DLSTATE_ABORT in each packet processing state and if true
// it will call CompleteOne(us), which marks each piece DLSTATE_DONE
SetDLState(DLSTATE_ABORT);
fWaitForAbortCompletion = TRUE;
}
if (fWaitForAbortCompletion) {
hr = S_FALSE;
}
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::ReleaseParent(CCodeDownload *pcdl)
// ---------------------------------------------------------------------------
HRESULT
CDownload::ReleaseParent(CCodeDownload *pcdl)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::ReleaseParent",
"this=%#x, %#x",
this, pcdl
));
int iNum = m_ParentCDL.GetCount();
Assert(iNum);
LISTPOSITION pos = m_ParentCDL.GetHeadPosition();
for (int i=0; i < iNum; i++) {
CParentCDL *pParentCDL = m_ParentCDL.GetNext(pos); // pass ref!
if (pParentCDL->m_pcdl == pcdl) {
// found the item
// getnext would have stepped past the position
pos = m_ParentCDL.Find(pParentCDL);
m_ParentCDL.RemoveAt(pos);
iNum = m_ParentCDL.GetCount();
if (iNum == 0) {
CleanupFiles();
delete this;
}
DEBUG_LEAVE(S_OK);
return S_OK;
}
}
// not found in list
Assert(TRUE);
DEBUG_LEAVE(E_FAIL);
return E_FAIL;
}
// ---------------------------------------------------------------------------
// HRESULT CDownload::IsDownloadedVersionRequired()
// returns S_OK if downloaded version is required
// error if local version is OK and new verison is not required
// ---------------------------------------------------------------------------
HRESULT CDownload::IsDownloadedVersionRequired()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::IsDownloadedVersionRequired",
"this=%#x",
this
));
HRESULT hr = S_OK;
char szFullURL[INTERNET_MAX_URL_LENGTH];
DWORD dwLen = INTERNET_MAX_URL_LENGTH;
FILETIME *pftLastMod = GetCodeDownload()->GetLastModifiedTime();
HANDLE hf = INVALID_HANDLE_VALUE;
if (!GetCodeDownload()->LocalVersionPresent()) {
// if no prev version always download
DEBUG_LEAVE(hr);
return hr;
} else {
// if prev version exists, but we are not doing Get Latest
// then accept the download.
if (!GetCodeDownload()->NeedLatestVersion())
{
DEBUG_LEAVE(hr);
return hr;
}
}
if (GetMoniker() != GetCodeDownload()->GetContextMoniker()){
// if we are not the context (or the main moniker then
// -1 does not apply (to secondary CABs)
DEBUG_LEAVE(hr);
return hr;
}
dwLen = WideCharToMultiByte(CP_ACP, 0, GetURL(), -1,
szFullURL, INTERNET_MAX_URL_LENGTH, NULL, NULL);
Assert(dwLen);
if (!dwLen) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
if (StrCmpNI(szFullURL, "file:", 5) == 0) {
WIN32_FIND_DATA fd;
hf = FindFirstFile(GetFileName(), &fd);
if (hf == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
// BUGBUG: Defend against Bug#40696 - Vatsan should check to see if this is the _right_ defense.
if ( pftLastMod != NULL &&
CompareFileTime(pftLastMod, &(fd.ftLastWriteTime)) >= 0) {
// if the file needs no upgrade then fail!
// if we succeed then an update will take place.
hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
goto Exit;
}
}
Exit:
if ( hf != INVALID_HANDLE_VALUE)
FindClose(hf);
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::GetFriendlyName
// ---------------------------------------------------------------------------
HRESULT CDownload::GetFriendlyName(LPSTR szUrlPath, LPSTR *ppBaseFileName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::GetFriendlyName",
"this=%#x, %.80q, %#x",
this, szUrlPath, ppBaseFileName
));
HRESULT hr = S_OK;
char szFullURL[INTERNET_MAX_URL_LENGTH];
DWORD dwLen = INTERNET_MAX_URL_LENGTH;
URL_COMPONENTS UrlComponents;
char *pBaseFileName = NULL;
dwLen = WideCharToMultiByte(CP_ACP, 0, GetURL(), -1,
szFullURL, INTERNET_MAX_URL_LENGTH, NULL, NULL);
memset(&UrlComponents, 0, sizeof(URL_COMPONENTS));
UrlComponents.dwStructSize = sizeof(URL_COMPONENTS);
UrlComponents.lpszUrlPath = szUrlPath;
UrlComponents.dwUrlPathLength = INTERNET_MAX_URL_LENGTH;
if (!InternetCrackUrl( szFullURL, 0,
ICU_DECODE, &UrlComponents)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
Assert(UrlComponents.lpszUrlPath);
Assert(UrlComponents.dwUrlPathLength);
if ( !UrlComponents.dwUrlPathLength ||
!UrlComponents.lpszUrlPath ) {
hr = E_UNEXPECTED;
goto Exit;
}
if (ppBaseFileName)
GetExtnAndBaseFileName(szUrlPath, ppBaseFileName);
Exit:
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::SniffType
// ---------------------------------------------------------------------------
HRESULT CDownload::SniffType()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::SniffType",
"this=%#x",
this
));
HANDLE hFile = INVALID_HANDLE_VALUE;
HRESULT hr = S_OK;
DWORD dwSignature;
DWORD dwBytesRead = 0;
#define CAB_SIG 0x4643534d
if (GetExtn() != FILEXTN_CAB) {
if ( (hFile = CreateFile(GetFileName(), GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
if ((ReadFile(hFile, &dwSignature, sizeof(DWORD), &dwBytesRead, NULL))
&& (dwSignature == CAB_SIG)) {
SetURLAndExtn(NULL, FILEXTN_CAB);
} else {
// here if its not a CAB
// check if of compatible type
hr = IsCompatibleFile(GetFileName());
}
}
Exit:
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::VerifyTrust
// ---------------------------------------------------------------------------
VOID CDownload::VerifyTrust()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CDownload::VerifyTrust",
"this=%#x",
this
));
HANDLE hFile = INVALID_HANDLE_VALUE;
HRESULT hr = S_OK;
HWND hWnd = GetCodeDownload()->GetClientBinding()->GetHWND();
WCHAR szDisplayUrl[INTERNET_MAX_URL_LENGTH];
DWORD cchDisplayUrl = INTERNET_MAX_URL_LENGTH;
LPSTR szCatalogFile = NULL;
CUrlMkTls tls(hr); // hr passed by reference!
if (FAILED(hr)) // if tls ctor failed above
goto Exit;
if ( GetDLState() == DLSTATE_ABORT) {
hr = E_ABORT;
goto Exit;
}
// sniff file for detecting CAB extensions
// and if not CAB, assume PE and check if of compatible type
// before calling trust on it. The reason we presniff for
// compat is because it will make for better user experience to
// fail if not of correct binary before we present trust dialogs
hr = SniffType();
if (FAILED(hr))
goto Exit;
Assert(tls->pTrustCookie);
// need to serialize all trust verification on this thread
// grab the trust cookie
hr = tls->pTrustCookie->Acquire(this);
if (hr != S_OK) {
Assert(!tls->pTrustCookie->IsFree());
Assert(!tls->pTrustCookie->IsOwner(this));
DEBUG_LEAVE(0);
return; // wait till we get posted a message when the current owner
// relinquishes the cookie
}
// have the cookie
Assert(tls->pTrustCookie->IsOwner(this));
if ( (hFile = CreateFile(GetFileName(), GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
// form friendly displayable URL (ie. decode) in browser mode
hr = UrlUnescapeW((LPWSTR)GetURL(), szDisplayUrl, &cchDisplayUrl, 0);
Assert(!GetCodeDownload()->IsSilentMode() || hWnd == INVALID_HANDLE_VALUE);
// Try to get a catalog file
szCatalogFile = GetCodeDownload()->GetCatalogFile();
hr = m_wvt.VerifyTrust(hFile, hWnd, &m_pbJavaTrust, szDisplayUrl,
GetCodeDownload()->GetClientBinding()->GetHostSecurityManager(),
(char *)GetFileName(), szCatalogFile, this);
if(SUCCEEDED(hr)) {
SetTrustVerified();
} else {
// trust failed on this file. Delete it from the cache for
// added safety.
// remove entry from cache only if we're not in silent mode.
// or we are in silent mode and the hr != TRUST_E_SUBJECT_NOT_TRUSTED
// when ui choice is NONE, WVT reurns the special error code to
// mean that all was OK but could not trust because we did not
// allow them to put up confirmation UI.
if (!GetCodeDownload()->IsSilentMode())
{
CHAR szURL[INTERNET_MAX_URL_LENGTH];
DWORD cchURL = INTERNET_MAX_URL_LENGTH;
WideCharToMultiByte(CP_ACP, 0, GetURL(), -1, szURL,
INTERNET_MAX_URL_LENGTH, 0,0);
// If we still have the file open when we call DeleteUrlCacheEntry, then
// WinInet won't be able to delete it. Having untrusted bits in the cache
// is dangerous.
if ( hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
DeleteUrlCacheEntryA(szURL);
}
else
GetCodeDownload()->SetTrustSomeFailed();
}
Exit:
// reset status to resume the rest of download if we're in
// silent mode
if (GetCodeDownload()->IsSilentMode() && FAILED(hr) && (hr != E_ABORT))
hr = S_OK;
if (tls->pTrustCookie->IsOwner(this)) {
tls->pTrustCookie->Relinquish(this);
}
if (SUCCEEDED(hr)) {
CCDLPacket *pPkt= new CCDLPacket(CODE_DOWNLOAD_PROCESS_PIECE, this, 0);
if (pPkt) {
hr = pPkt->Post();
} else {
hr = E_OUTOFMEMORY;
}
}
if (FAILED(hr)) {
// does all the master state analysis
CompleteSignal(hr, S_OK, S_OK, NULL);
}
if ( hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
DEBUG_LEAVE(0);
return;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::SetCdlProtocol
// ---------------------------------------------------------------------------
HRESULT
CDownload::SetUsingCdlProtocol(LPWSTR wszDistUnit)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::SetUsingCdlProtocol",
"this=%#x, %.80wq",
this, wszDistUnit
));
HRESULT hr = S_OK;
m_wszDistUnit = new WCHAR [lstrlenW(wszDistUnit) + 1];
if (m_wszDistUnit)
StrCpyW(m_wszDistUnit, wszDistUnit);
else
hr = E_OUTOFMEMORY;
m_flags |= DL_FLAGS_CDL_PROTOCOL;
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::ExtractManifest()
// ---------------------------------------------------------------------------
HRESULT
CDownload::ExtractManifest(FILEXTN extn, LPSTR szFileName, LPSTR& pBaseFileName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::ExtractManifest",
"this=%#x, %#x, %.80q, %#x",
this, extn, szFileName, &pBaseFileName
));
CCodeDownload *pcdl = GetCodeDownload();
char * pFileName;
PSESSION psess = NULL;
PFNAME pf = NULL;
HRESULT hr = S_FALSE; // assume not found
Assert(m_psess);
for (pf = m_psess->pFileList; pf != NULL; pf = pf->pNextName) {
FILEXTN curextn = ::GetExtnAndBaseFileName(pf->pszFilename,
&pBaseFileName);
if (( curextn == extn) &&
((szFileName[0] == '\0') ||
(lstrcmpi(szFileName, pBaseFileName) == 0))) {
FNAME fne;
memset(&fne, 0, sizeof(FNAME));
fne.pszFilename = pf->pszFilename;
// INF present in CAB, extract it and process it
m_psess->pFilesToExtract = &fne;
m_psess->flags &= ~SESSION_FLAG_ENUMERATE; // already enumerated
if (FAILED((hr=ExtractFromCabinet(m_psess, GetFileName()))))
goto Exit;
// side effect!
// if extract succeeds we also have set the return hr to S_OK.
// hr = S_OK;
m_psess->pFilesToExtract = NULL;
if (!catDirAndFile(szFileName, MAX_PATH, m_psess->achLocation,
pf->pszFilename)) {
hr = E_UNEXPECTED;
}
goto Exit;
}
}
Exit:
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::ProcessPiece
// CBSC::OnStopBinding calls us as soon as a piece gets downloaded
// and trust is verified.
// ie. this CDownload obj has completed binding
// Depending on the Content type we will process further
// This triggers a change state in our state machine. Depending on the
// obj we have downloaded (a CAB or INF or DLL/OCX/EXE) we:
//
// OCX:
// Csetup for this download is usually previously created
// mark this download as done and
// call into main CodeDownload::CompleteOne (state analyser)
//
// CAB:
// if we don't have an INF already we look for one in the CAB
// if INF in CAB
// process INF (may trigger further extractions/downloads/Csetup)
// else
// look for primary OCX in CAB and create CSetup or it.
//
// INF:
// Process INF
//
// ---------------------------------------------------------------------------
VOID
CDownload::ProcessPiece()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CDownload::ProcessPiece",
"this=%#x",
this
));
CCodeDownload *pcdl = GetCodeDownload();
char * pFileName;
char *pBaseFileName;
PSESSION psess = NULL;
PFNAME pf = NULL;
HRESULT hr = S_OK; // assume all OK
CSetup *pSetup;
char szBuf[INTERNET_MAX_URL_LENGTH];
char szCatalogBuf[INTERNET_MAX_URL_LENGTH];
FILEXTN extn = m_extn;
if ( GetDLState() == DLSTATE_ABORT) {
hr = E_ABORT;
goto Exit;
}
//REVIEW: Virus scanning a CAB or INF file may not be a bright thing to do since
// they are not executable.
if (FAILED(hr = PerformVirusScan((char *)GetFileName()))) {
goto Exit;
}
switch (extn) {
case FILEXTN_EXE:
case FILEXTN_OCX:
case FILEXTN_DLL:
case FILEXTN_NONE:
case FILEXTN_UNKNOWN:
pSetup = GetSetupHead();
if (pSetup) {
// If a CSetup exists for this m_pdl then initialize its
// m_pSrcFileName using the m_pFileName
Assert(pSetup->GetNext() == NULL);
pSetup->SetSrcFileName(GetFileName());
} else {
if (!HasAllActiveXPermissions()) {
if (GetCodeDownload()->IsSilentMode())
{
GetCodeDownload()->SetBitsInCache();
}
hr = TRUST_E_FAIL;
goto Exit;
}
// If no CSetup exists then make one and attach it to the m_pdl
// initiated at top level
hr = GetFriendlyName(szBuf, &pBaseFileName);
if (pBaseFileName[0] == '\0') {
hr = E_UNEXPECTED; // No filename to setup!
goto Exit;
}
if (FAILED(hr)) {
goto Exit;
}
pSetup = new CSetup(GetFileName(), pBaseFileName, extn,
pcdl->GetDestDirHint(), &hr);
if (!pSetup) {
hr = E_OUTOFMEMORY;
goto Exit;
} else if (FAILED(hr)) {
delete pSetup;
goto Exit;
}
AddSetupToList(pSetup);
}
break;
case FILEXTN_CAB:
// if CAB then make SESSION for this CDownload
Assert(!(GetSession()));
psess = new SESSION;
if (!psess) {
hr = E_OUTOFMEMORY;
goto Exit;
}
SetSession(psess);
// Initialize the structure
psess->pFileList = NULL;
psess->cFiles = 0;
psess->cbCabSize = 0;
psess->flags = SESSION_FLAG_ENUMERATE;
// extract files in a CAB into a unique dir so that
// parallel downloads of CABs containing same name
// components can go on without conflict.
// By serailizing the setup phase we make sure the right
// latest version is finally left on client machine.
hr = MakeUniqueTempDirectory(g_szOCXTempDir, psess->achLocation, sizeof(psess->achLocation));
if (FAILED(hr)) {
goto Exit;
}
psess->pFilesToExtract = NULL; // just enumerate first
if (!pcdl->HaveManifest() || NeedToExtractAllFiles()) {
psess->flags |= SESSION_FLAG_EXTRACT_ALL;
}
// enumerate the files of the CAB
if (FAILED((hr = ExtractFromCabinet(psess, GetFileName()))))
goto Exit;
if (!pcdl->HaveManifest() || NeedToExtractAllFiles()) {
psess->flags |= SESSION_FLAG_EXTRACTED_ALL;
}
// if we don't have INF already look for one
if (!pcdl->HaveManifest()) {
// Extract catalog file
szCatalogBuf[0] = '\0';
if (ExtractManifest(FILEXTN_CAT, szCatalogBuf, pBaseFileName) == S_OK) {
if (FAILED(pcdl->SetCatalogFile(szCatalogBuf))) {
goto Exit;
}
}
szBuf[0] = '\0';
hr = ExtractManifest(FILEXTN_OSD , szBuf, pBaseFileName);
if (FAILED(hr))
goto Exit;
if (hr == S_FALSE) {
szBuf[0] = '\0';
// if no dist unit profile, process old INF
hr = ExtractManifest(FILEXTN_INF , szBuf, pBaseFileName);
if (FAILED(hr))
goto Exit;
if (hr == S_OK) {
hr=pcdl->SetupInf(szBuf, pBaseFileName, this);
goto Exit;
}
} else {
// process dist unit profile
hr=pcdl->ParseOSD(szBuf, pBaseFileName, this);
goto Exit;
}
if (!pcdl->HaveManifest()) { // still don't have an INF?
if (!HasAllActiveXPermissions()) {
if (GetCodeDownload()->IsSilentMode())
{
GetCodeDownload()->SetBitsInCache();
}
hr = TRUST_E_FAIL;
goto Exit;
}
// only valid case at this point is
// case where we have a CAB file with ONE file in it
if (psess->cFiles != 1) {
hr = E_INVALIDARG;
goto Exit;
}
pf = psess->pFilesToExtract = psess->pFileList;
psess->flags &= ~SESSION_FLAG_ENUMERATE; // already enumerated
if (FAILED((hr=ExtractFromCabinet(psess, GetFileName()))))
goto Exit;
extn = GetExtnAndBaseFileName(pf->pszFilename, &pBaseFileName);
if (!catDirAndFile(szBuf, MAX_PATH, psess->achLocation,
pf->pszFilename)) {
hr = E_UNEXPECTED;
goto Exit;
}
psess->pFilesToExtract = NULL;
pSetup = new CSetup(szBuf, pBaseFileName, extn,
pcdl->GetDestDirHint(), &hr);
if (!pSetup) {
hr = E_OUTOFMEMORY;
goto Exit;
} else if (FAILED(hr)) {
delete pSetup;
goto Exit;
}
AddSetupToList(pSetup);
}
} else { /* have inf */
// INF processor would have made Csetup already
psess->pFilesToExtract = GetFilesToExtract();
if (!psess->pFilesToExtract) {
// no files to extract, means there was only a hook
// in this CAB and no other particular files
// the general code downloader is looking for
// so no setup work either.
Assert(NeedToExtractAllFiles());
break;
}
psess->flags &= ~SESSION_FLAG_ENUMERATE; // already enumerated
CSetup *pSetupCur = m_pSetuphead;
Assert(m_pSetuphead);
// set fully qual names for all of these
for (; pSetupCur; pSetupCur = pSetupCur->GetNext()) {
if (!catDirAndFile(szBuf, MAX_PATH, m_psess->achLocation,
(LPSTR)pSetupCur->GetSrcFileName())) {
hr = E_UNEXPECTED;
goto Exit;
}
pSetupCur->SetSrcFileName(szBuf);
}
if (FAILED((hr=ExtractFromCabinet(psess, GetFileName()))))
goto Exit;
}
break;
case FILEXTN_INF:
if(pcdl->HaveManifest()) {
hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
goto Exit;
}
hr = GetFriendlyName(szBuf, &pBaseFileName);
if (FAILED(hr)) {
goto Exit;
}
// get friendly name for INF from URL
hr=pcdl->SetupInf(GetFileName(), pBaseFileName, this);
if (FAILED(hr)) {
SetDLState(DLSTATE_DONE);
}
goto Exit;
case FILEXTN_OSD:
if(pcdl->HaveManifest()) {
hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
goto Exit;
}
hr = GetFriendlyName(szBuf, &pBaseFileName);
if (FAILED(hr)) {
goto Exit;
}
// get friendly name for OSD from URL
hr=pcdl->ParseOSD(GetFileName(), pBaseFileName, this);
goto Exit;
} /* end switch (extn) */
// done with this CDownload. Mark it ready for setup
SetDLState(DLSTATE_READY_TO_SETUP);
Assert(SUCCEEDED(hr));
Exit:
if ( (FAILED(hr)) || (GetDLState() == DLSTATE_READY_TO_SETUP)) {
CompleteSignal(hr, S_OK, S_OK, NULL);
}
// if success setupInf would have dispatched a msg for
// INF processing and only when that completes this
// CDownload is deemed completed/ready_to_setup
DEBUG_LEAVE(0);
return;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::SetURLAndExnt(LPCWSTR szURL, FILEXTN extn);
// ---------------------------------------------------------------------------
HRESULT
CDownload::SetURLAndExtn(LPCWSTR szURL, FILEXTN extn)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::ProcessPiece",
"this=%#x, %.80wq, %#x",
this, szURL, extn
));
m_extn = extn;
if (!szURL)
{
DEBUG_LEAVE(S_OK);
return S_OK;
}
DWORD len = lstrlenW(szURL); // make private copy
LPWSTR lpch = new WCHAR [len + 1];
if (!lpch)
{
DEBUG_LEAVE(E_OUTOFMEMORY);
return E_OUTOFMEMORY;
}
StrCpyW(lpch, szURL);
if (m_url)
SAFEDELETE(m_url);
m_url = lpch;
DEBUG_LEAVE(S_OK);
return S_OK;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::CheckForNameCollision(LPCSTR szCacheDir);
// for each in list CSetup::CheckForNameCollision
// ---------------------------------------------------------------------------
HRESULT
CDownload::CheckForNameCollision(LPCSTR szCacheDir)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::CheckForNameCollision",
"this=%#x, %.80q",
this, szCacheDir
));
CSetup *pSetupCur = m_pSetuphead;
HRESULT hr = S_OK;
for (pSetupCur = m_pSetuphead; pSetupCur; pSetupCur =pSetupCur->GetNext()) {
if ((hr=pSetupCur->CheckForNameCollision(GetCodeDownload(), szCacheDir)) == S_FALSE)
break;
}
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::FindJavaSetup
// ---------------------------------------------------------------------------
CJavaSetup*
CDownload::FindJavaSetup(LPCWSTR szPackageName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Pointer,
"CDownload::FindJavaSetup",
"this=%#x, %.80wq",
this, szPackageName
));
HRESULT hr = S_OK;
CJavaSetup *pjs = NULL;
if (!szPackageName)
{
DEBUG_LEAVE(NULL);
return NULL;
}
int iNumJavaSetup = m_JavaSetupList.GetCount();
LISTPOSITION curpos = m_JavaSetupList.GetHeadPosition();
for (int i=0; i < iNumJavaSetup; i++) {
pjs = m_JavaSetupList.GetNext(curpos);
if (pjs->GetPackageName() && (StrCmpIW(szPackageName, pjs->GetPackageName()) == 0)) {
DEBUG_LEAVE(pjs);
return pjs;
}
}
DEBUG_LEAVE(NULL);
return NULL;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::FindHook
// ---------------------------------------------------------------------------
CSetupHook*
CDownload::FindHook(LPCSTR szHook)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Pointer,
"CDownload::FindHook",
"this=%#x, %.80q",
this, szHook
));
HRESULT hr = S_OK;
CSetupHook *psh = NULL;
if (!szHook)
{
DEBUG_LEAVE(NULL);
return NULL;
}
int iNumHooks = m_SetupHooks.GetCount();
LISTPOSITION curpos = m_SetupHooks.GetHeadPosition();
for (int i=0; i < iNumHooks; i++) {
psh = m_SetupHooks.GetNext(curpos);
if (psh->GetHookName() && (lstrcmpi(szHook, psh->GetHookName()) == 0)) {
DEBUG_LEAVE(psh);
return psh;
}
}
DEBUG_LEAVE(NULL);
return NULL;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::DoSetup
// ---------------------------------------------------------------------------
HRESULT
CDownload::DoSetup()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::DoSetup",
"this=%#x",
this
));
CSetup *pSetupCur = m_pSetuphead;
HRESULT hr = S_OK;
int nSetupsPerCall = 0;
int iNumHooks,i;
POSITION curpos;
SetDLState(DLSTATE_SETUP);
// SILENT MODE
// determine if we're in silent mode
if (GetCodeDownload()->IsSilentMode() && !GetCodeDownload()->IsAllTrusted())
{
SetDLState(DLSTATE_DONE);
DEBUG_LEAVE(hr);
return hr;
}
if (m_JavaSetupList.GetCount() != 0) {
CJavaSetup *pjs = m_JavaSetupList.GetHead();
curpos = m_JavaSetupList.GetHeadPosition();
BOOL bInstallReqd = FALSE;
if (pjs != NULL) {
for (int i=0; i< m_JavaSetupList.GetCount(); i++) {
CJavaSetup *pjs = m_JavaSetupList.GetNext(curpos);
Assert(pjs != NULL);
if (pjs->GetState() != INSTALL_DONE) {
bInstallReqd = TRUE;
break;
}
}
if (bInstallReqd) {
Assert(HasJavaPermissions());
// the below check is our final security test
// we should never need to test this in retail
// but, we do anyway
if (HasJavaPermissions())
hr = pjs->DoSetup();
else
hr = TRUST_E_FAIL;
if (FAILED(hr))
goto Exit;
else
{
DEBUG_LEAVE(S_OK);
return S_OK;
}
}
}
}
// done processing Java Setups
// process all hooks
iNumHooks = m_SetupHooks.GetCount();
curpos = m_SetupHooks.GetHeadPosition();
for (i=0; i < iNumHooks; i++) {
CSetupHook *psh = m_SetupHooks.GetNext(curpos);
if (psh->GetState() == INSTALL_DONE)
continue;
if (nSetupsPerCall++) {
// here if we have already done 1 hook and there's more to
// do in this CDownload
// we don't set DLState to DONE and just return
DEBUG_LEAVE(S_OK);
return S_OK;
}
Assert(HasAllActiveXPermissions());
// the below check is our final security test
// we should never need to test this in retail
// but, we do anyway
if (HasAllActiveXPermissions())
hr=psh->Run();
else
hr = TRUST_E_FAIL;
if (FAILED(hr))
goto Exit;
if (psh->GetState() != INSTALL_DONE) {
// more work left in this setup hook
// wait for next msg, don't mark ourselves done yet.
DEBUG_LEAVE(S_OK);
return S_OK;
}
}
// processed all Java Setups, hooks, now run setups
for (pSetupCur = m_pSetuphead; pSetupCur; pSetupCur = pSetupCur->GetNext()) {
if (pSetupCur->GetState() == INSTALL_DONE)
continue;
if (nSetupsPerCall++) {
// here if we have already done 1 setup and there's more to
// do in this CDownload
// we don't set DLState to DONE and just return
DEBUG_LEAVE(S_OK);
return S_OK;
}
if (m_bExactVersion) {
pSetupCur->SetExactVersion(TRUE);
}
if (pSetupCur->GetExtn() == FILEXTN_OSD) {
hr=pSetupCur->DoSetup(GetCodeDownload(), this);
} else {
Assert(HasAllActiveXPermissions());
// the below check is our final security test
// we should never need to test this in retail
// but, we do anyway
if (HasAllActiveXPermissions())
hr=pSetupCur->DoSetup(GetCodeDownload(), this);
else
hr = TRUST_E_FAIL;
}
if (FAILED(hr))
break;
if (pSetupCur->GetState() != INSTALL_DONE) {
// more work left in this CSetup (pSetupCur)
// wait for next msg, don't mark ourselves done yet.
DEBUG_LEAVE(S_OK);
return S_OK;
}
} /* for each CSetup */
Exit:
SetDLState(DLSTATE_DONE);
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::AddJavaSetup
//
// create and add a new JavaSetup to the list of setup hooks in this cab
// ---------------------------------------------------------------------------
HRESULT
CDownload::AddJavaSetup(
LPCWSTR szPackageName,
LPCWSTR szNameSpace,
IXMLElement *pPackage,
DWORD dwVersionMS,
DWORD dwVersionLS,
DWORD flags)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::AddJavaSetup",
"this=%#x, %.80wq, %.80wq, %#x, %#x, %#x, %#x",
this, szPackageName, szNameSpace, pPackage, dwVersionMS, dwVersionLS, flags
));
HRESULT hr = S_OK;
CJavaSetup *pJavaSetup = NULL;
if (GetCodeDownload()->IsDuplicateJavaSetup(szPackageName) == S_OK) {
goto Exit;
}
// create a CJavaSetup OBJ and add it to the CDownload obj
pJavaSetup = new CJavaSetup(this, szPackageName, szNameSpace, pPackage, dwVersionMS, dwVersionLS, flags, &hr);
if(!pJavaSetup) {
hr = E_OUTOFMEMORY;
}
if (FAILED(hr)) {
SAFEDELETE(pJavaSetup);
goto Exit;
}
m_JavaSetupList.AddTail(pJavaSetup);
Exit:
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::AddHook
//
// create and add a new hook to the list of setup hooks in this cab
// ---------------------------------------------------------------------------
HRESULT
CDownload::AddHook(
LPCSTR szHook,
LPCSTR szInf,
LPCSTR szInfSection,
DWORD flags)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::AddHook",
"this=%#x, %.80wq, %.80wq, %.80wq, %#x",
this, szHook, szInf, szInfSection, flags
));
HRESULT hr = S_OK;
CSetupHook *psh;
Assert(m_state != DLSTATE_EXTRACTING);
if (GetCodeDownload()->IsDuplicateHook(szHook) == S_OK) {
goto Exit;
}
if (m_extn == FILEXTN_CAB) { // if a CAB
if (m_state > DLSTATE_DOWNLOADED) {
// this CAB is ready, extract this code first
// BUGBUG: multi-threading issue: we are relying on
// not being re-enterant in our extraction
Assert(m_psess);
if (!m_psess) {
hr = E_UNEXPECTED;
goto Exit;
}
if (!(m_psess->flags & SESSION_FLAG_EXTRACTED_ALL)) {
m_psess->pFilesToExtract = NULL;
m_psess->flags &= ~SESSION_FLAG_ENUMERATE; // already enumerated
m_psess->flags |= SESSION_FLAG_EXTRACT_ALL;
if (FAILED((hr = ExtractFromCabinet(m_psess, m_pFileName)))) {
goto Exit;
}
m_psess->flags |= SESSION_FLAG_EXTRACTED_ALL;
}
} else {
// newly initiated download, mark CDownload as extract all.
SetNeedToExtractAll();
}
}
psh = new CSetupHook(this, szHook, szInf, szInfSection, flags, &hr);
if (psh && SUCCEEDED(hr)) {
m_SetupHooks.AddTail(psh);
} else {
if (psh)
delete psh;
hr = E_OUTOFMEMORY;
}
Exit:
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::AddSetupToExistingCAB
// if CAB is already downloaded
// extract file; create CSetup to install it (piggy back to pdl)
// else if some other CAB that has been set for download
// attach file to be extracted to pFilesToExtract
// attach a CSetup for this file
// else
// ---------------------------------------------------------------------------
HRESULT
CDownload::AddSetupToExistingCAB(char * lpCode, const char * szDestDir, DESTINATION_DIR dest, DWORD dwRegisterServer, DWORD dwCopyFlags)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::AddSetupToExistingCAB",
"this=%#x, %.80q, %.80q, %#x, %#x, %#x",
this, lpCode, szDestDir, dest, dwRegisterServer, dwCopyFlags
));
char *pBaseFileName = lpCode;
FILEXTN extn;
HRESULT hr = NO_ERROR;
CSetup* pSetup = NULL;
char szBuf[MAX_PATH];
Assert(lpCode);
if (!lpCode) {
hr = E_INVALIDARG;
goto Exit;
}
if (IsDuplicateSetup(lpCode))
goto Exit;
// assumes that both CAB extraction and download
// are into same temp dir
// make a name for extraction ie: tempdir\curcode
extn = GetExtnAndBaseFileName( lpCode, &pBaseFileName);
// this check is totally legit : ie no race condition here
// we are on the main wininet thread and all onstopbindgs get
// posted on this thread. So a newly initialted download could not
// have completed, and even if so CAB extraction could not have started
Assert(m_state != DLSTATE_EXTRACTING);
Assert(m_state != DLSTATE_SETUP);
Assert(m_state != DLSTATE_DONE);
if (m_state > DLSTATE_DOWNLOADED) {
// part of CAB that the INF is in,
// or part of a CAB of some other code download that matches our spec.
// extract this code first
Assert(m_psess);
if (!m_psess) {
hr = E_UNEXPECTED;
goto Exit;
}
FNAME fname;
fname.pszFilename = pBaseFileName;
fname.pNextName = NULL;
fname.status = SFNAME_INIT;
m_psess->pFilesToExtract = &fname;
m_psess->flags &= ~SESSION_FLAG_ENUMERATE; // already enumerated
if (FAILED((hr = ExtractFromCabinet(m_psess, m_pFileName)))) {
goto Exit;
}
m_psess->pFilesToExtract = NULL;
} else {
// newly initiated download, piggy back to end of extraction list
PFNAME pf = new FNAME;
if (!pf) {
hr = E_OUTOFMEMORY;
goto Exit;
}
pf->pszFilename = new char [lstrlen(pBaseFileName)+1];
if (!pf->pszFilename) {
delete pf;
hr = E_OUTOFMEMORY;
goto Exit;
}
lstrcpy(pf->pszFilename, pBaseFileName);
pf->status = SFNAME_INIT;
pf->pNextName = m_pFilesToExtract; // add to list
m_pFilesToExtract = pf;
}
if (!catDirAndFile(szBuf, MAX_PATH,
(m_psess)?m_psess->achLocation:NULL, pBaseFileName)) {
hr = E_UNEXPECTED;
goto Exit;
}
// create a CSetup OBJ and add it to us
pSetup = new CSetup(szBuf, pBaseFileName, extn, szDestDir, &hr, dest);
if (!pSetup) {
hr = E_OUTOFMEMORY;
goto Exit;
} else if (FAILED(hr)) {
delete pSetup;
goto Exit;
}
AddSetupToList(pSetup);
pSetup->SetCopyFlags (dwCopyFlags);
if (dwRegisterServer) {
pSetup->SetUserOverrideRegisterServer(dwRegisterServer&CST_FLAG_REGISTERSERVER);
}
Exit:
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::IsDuplicateSetup
// ---------------------------------------------------------------------------
BOOL
CDownload::IsDuplicateSetup(LPCSTR pBaseFileName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"CDownload::IsDuplicateSetup",
"this=%#x, %.80q",
this, pBaseFileName
));
CSetup *pSetupCur = m_pSetuphead;
for (pSetupCur = m_pSetuphead; pSetupCur; pSetupCur=pSetupCur->GetNext()) {
if (lstrcmpi(pBaseFileName, pSetupCur->GetBaseFileName()) == 0)
{
DEBUG_LEAVE(TRUE);
return TRUE;
}
}
DEBUG_LEAVE(FALSE);
return FALSE;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::AddSetupToList
// ---------------------------------------------------------------------------
VOID
CDownload::AddSetupToList(CSetup *pSetup)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CDownload::AddSetupToList",
"this=%#x, %#x",
this, pSetup
));
pSetup->SetNext(m_pSetuphead);
m_pSetuphead = pSetup;
DEBUG_LEAVE(0);
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::RemoveSetupFromList
// ---------------------------------------------------------------------------
HRESULT
CDownload::RemoveSetupFromList(CSetup *pSetup)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::RemoveSetupFromList",
"this=%#x, %#x",
this, pSetup
));
CSetup *pSetupCur = m_pSetuphead;
HRESULT hr = HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
Assert(pSetup);
Assert(pSetupCur); // empty list?
if (pSetupCur == pSetup) {
m_pSetuphead = pSetup->GetNext();
hr = S_OK;
goto Exit;
}
do {
if (pSetupCur->GetNext() == pSetup) {
pSetupCur->SetNext(pSetup->GetNext());
hr = S_OK;
goto Exit;
}
} while ( (pSetupCur = pSetupCur->GetNext()));
Exit:
DEBUG_LEAVE(hr);
return hr; // not found in list!
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::CleanupFiles
// ---------------------------------------------------------------------------
HRESULT
CDownload::CleanupFiles()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::CleanupFiles",
"this=%#x",
this
));
if (m_psess) { // CAB?
DeleteExtractedFiles(m_psess);
RemoveDirectoryAndChildren(m_psess->achLocation);
SAFEDELETE(m_psess);
}
if (!m_pSetuphead) {
if (m_pFileName) {
delete (LPSTR)m_pFileName;
m_pFileName = NULL;
}
} else {
CSetup *pSetupCur = m_pSetuphead;
CSetup *pSetupNext;
for (pSetupCur = m_pSetuphead; pSetupCur;
pSetupCur = pSetupNext) {
pSetupNext = pSetupCur->GetNext();
SAFEDELETE(pSetupCur);
}
}
if (m_pUnkForCacheFileRelease)
SAFERELEASE(m_pUnkForCacheFileRelease);
DEBUG_LEAVE(S_OK);
return S_OK;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::DoDownload
// CDownload is the basic download obj. It's action entry point is DoDownload
// Here it creates a URL moniker for the given m_url and a bind ctx to go
// with it and then calls pmk->BindToStorage to get the bits. Note how we
// use URL mon's services to get the bits even as URLmon is our client for
// the Code Download. We are its client for individual downloads. CDownload
// has a BSC implementation to track progress and completion. This BSC is
// where the magic of taking us from one state to next occurs.
//
// ---------------------------------------------------------------------------
HRESULT
CDownload::DoDownload(LPMONIKER *ppmkContext, DWORD grfBINDF,
CList<CCodeBaseHold *, CCodeBaseHold *> *pcbhList)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::DoDownload",
"this=%#x, %#x, %#x, %#x",
this, ppmkContext, grfBINDF, pcbhList
));
HRESULT hr = NOERROR;
IBindHost *pBindHost = NULL;
m_pcbhList = pcbhList;
m_ppmkContext = ppmkContext;
m_grfBINDF = grfBINDF;
pBindHost = GetCodeDownload()->GetClientBinding()->GetIBindHost();
hr = CreateBindCtx(0, &m_pbc);
if (FAILED(hr)) {
goto Exit;
}
// register the format enumerator with the bind ctx if one exists
if (g_pEFmtETC) {
hr = RegisterFormatEnumerator(m_pbc, g_pEFmtETC, 0);
}
if( SUCCEEDED(hr) ) {
m_pbsc = new CBindStatusCallback(this, grfBINDF);
if (m_pbsc == NULL)
hr = E_OUTOFMEMORY;
if (!pBindHost)
if (SUCCEEDED(hr))
hr = RegisterBindStatusCallback(m_pbc, m_pbsc, 0, 0);
}
if (FAILED(hr)) {
goto Exit;
}
if (pBindHost) {
IMoniker *pmk;
hr = pBindHost->CreateMoniker(m_url, m_pbc, &pmk, 0);
if (FAILED(hr)) {
goto Exit;
}
if (*ppmkContext == NULL) { // no context moniker yet?
m_pmk = pmk;
m_ppmkContext = &pmk;
} else {
hr = (*ppmkContext)->ComposeWith(pmk, FALSE, &m_pmk);
pmk->Release();
}
} else {
hr = CreateURLMoniker(*ppmkContext, m_url, &m_pmk);
}
if( SUCCEEDED(hr) ) {
// store away the full URL
SAFEDELETE(m_url);
hr = m_pmk->GetDisplayName(m_pbc, NULL, &m_url);
if (FAILED(hr))
goto Exit;
// everything succeeded
if (*ppmkContext == NULL) { // no context moniker yet?
// make this the context moniker
*ppmkContext = m_pmk;
}
IUnknown *pUnk = NULL;
if (pBindHost) {
hr = pBindHost->MonikerBindToStorage(m_pmk, m_pbc, m_pbsc,
IID_IUnknown, (void **)&pUnk);
} else {
hr = m_pmk->BindToStorage(m_pbc, 0, IID_IUnknown, (void**)&pUnk);
}
// m_pbc will get the onstopbinding, ondatavailable, and onprogress
// messages and pass them on to m_pbsc; wait asynchronously
if (pUnk) {
pUnk->Release();
}
}
Exit:
if (FAILED(hr) && hr != E_PENDING) {
// real failure!
m_hrOSB = hr;
SetDLState(DLSTATE_DONE);
if (*ppmkContext == m_pmk)
*ppmkContext = NULL;
}
else {
/*
// everything succeeded
if (*ppmkContext == NULL) { // no context moniker yet?
// make this the context moniker
*ppmkContext = m_pmk;
}
*/
hr = MK_S_ASYNCHRONOUS;
}
DEBUG_LEAVE(hr);
return hr;
} // CDownload::DoDownload
// ---------------------------------------------------------------------------
// %%Function: CDownload::PerformVirusScan
// S_OK : continue with operation
// S_FALSE : cancel operation.
// ---------------------------------------------------------------------------
HRESULT CDownload::PerformVirusScan(LPSTR szFileName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::PerformVirusScan",
"this=%#x, %.80q",
this, szFileName
));
HRESULT hr = S_OK, hrReturn = S_OK;
ICatInformation * pci = NULL; // category manager
IEnumCLSID * peclsid = NULL; // enum of av objects
IOfficeAntiVirus * poav = NULL; // current av interface
CLSID clsidCurrent; // current av clsid
ULONG pcFetched;
MSOAVINFO msavi; // antivirus struct
BOOL fInitStruct = FALSE;
//
// Get COM category manager and get an enumerator for our virus
// scanner category
//
// If something goes wrong finding AV objects, proceed as normal.
//
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL,
CLSCTX_INPROC_SERVER, IID_ICatInformation, (void **)&pci);
if(FAILED(hr))
{
DEBUG_LEAVE(NOERROR);
return NOERROR;
}
hr = pci->EnumClassesOfCategories(1, (GUID *)&CATID_MSOfficeAntiVirus, 0, NULL, &peclsid);
pci->Release();
if(FAILED(hr))
{
DEBUG_LEAVE(NOERROR);
return NOERROR;
}
//
// Call all scanners. If any fail, return E_FAIL.
//
hr = peclsid->Next(1, &clsidCurrent, &pcFetched);
while(SUCCEEDED(hr) && pcFetched > 0)
{
if(FALSE == fInitStruct)
{
if (FAILED(Ansi2Unicode(szFileName,&msavi.u.pwzFullPath)))
{
break;
}
msavi.cbsize = sizeof(msavi);
msavi.fPath = TRUE;
msavi.fHttpDownload = TRUE;
msavi.fReadOnlyRequest = FALSE;
msavi.fInstalled = FALSE;
msavi.hwnd = GetCodeDownload()->GetClientBinding()->GetHWND();
msavi.pwzOrigURL = (LPWSTR)GetURL();
// per office spec, this is only meant as a method for the scanner
// to differentiate the caller. Not localized.
msavi.pwzHostName = L"Urlmon";
fInitStruct = TRUE;
}
// have clsid of av component
hr = CoCreateInstance(clsidCurrent, NULL, CLSCTX_INPROC_SERVER,
IID_IOfficeAntiVirus, (void **)&poav);
if(SUCCEEDED(hr))
{
// call scan method
hr = poav->Scan(&msavi);
poav->Release();
if(hr == E_FAIL)
{
// file could not be cleaned
hrReturn = E_FAIL;
}
}
hr = peclsid->Next(1, &clsidCurrent, &pcFetched);
}
//
// clean up
//
peclsid->Release();
if(fInitStruct)
{
SAFEDELETE(msavi.u.pwzFullPath);
}
DEBUG_LEAVE(hrReturn);
return hrReturn;
}
// ---------------------------------------------------------------------------
// %%Function: CDownload::DownloadRedundantCodeBase()
// Returns S_OK if starting next download, or S_FALSE if no redundant
// codebases remaining to try.
// ---------------------------------------------------------------------------
STDMETHODIMP CDownload::DownloadRedundantCodeBase()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CDownload::DownloadRedundantCodeBase",
"this=%#x",
this
));
HRESULT hr = S_FALSE;
LISTPOSITION lpos = 0;
CCodeBaseHold *pcbh = NULL;
if (m_pcbhList == NULL) {
goto Exit;
}
lpos = m_pcbhList->GetHeadPosition();
while (lpos) {
pcbh = m_pcbhList->GetNext(lpos);
if (!(pcbh->dwFlags & CBH_FLAGS_DOWNLOADED)) {
RevokeBindStatusCallback(GetBindCtx(), GetBSC());
CleanUp();
m_url = new WCHAR[lstrlenW(pcbh->wszCodeBase) + 1];
if (m_url == NULL) {
hr = E_OUTOFMEMORY;
goto Exit;
}
StrCpyW(m_url, pcbh->wszCodeBase);
SetResponseHeaderStatus(S_OK);
pcbh->dwFlags |= CBH_FLAGS_DOWNLOADED;
// Try another download
hr = DoDownload(m_ppmkContext, m_grfBINDF, m_pcbhList);
break;
}
}
Exit:
if (hr == S_FALSE) {
GetCodeDownload()->CodeDownloadDebugOut(DEB_CODEDL, FALSE, ID_CDLDBG_DL_REDUNDANT_FAILED);
}
else {
LPSTR szUrl = NULL;
Unicode2Ansi(m_url, &szUrl);
GetCodeDownload()->CodeDownloadDebugOut(DEB_CODEDL, FALSE, ID_CDLDBG_DL_REDUNDANT, (szUrl == NULL) ? ("") : (szUrl), hr);
delete szUrl;
}
DEBUG_LEAVE(hr);
return hr;
}
void CDownload::CleanUp()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CDownload::CleanUp",
"this=%#x",
this
));
LISTPOSITION pos = 0;
int i, iNum;
SAFERELEASE(m_pmk);
SAFERELEASE(m_pbc);
SAFERELEASE(m_pbsc);
SAFEDELETE(m_url);
if (m_pFilesToExtract) {
PFNAME pf = m_pFilesToExtract;
PFNAME pfnext;
for (;pf != NULL; pf = pfnext) {
delete pf->pszFilename;
pfnext = pf->pNextName;
delete pf;
}
}
m_pFilesToExtract = NULL;
if (m_hPostData)
GlobalFree(m_hPostData);
SAFERELEASE(m_pUnkForCacheFileRelease);
SAFEDELETE(m_pbJavaTrust);
DEBUG_LEAVE(0);
}
HRESULT CDownload::SetMainCABJavaTrustPermissions(PJAVA_TRUST pbJavaTrust)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CDownload::SetMainCABJavaTrustPermissions",
"this=%#x, %#x",
this, pbJavaTrust
));
HRESULT hr = GetCodeDownload()->SetMainCABJavaTrustPermissions(pbJavaTrust);
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::CBindStatusCallback
// The BSC implementation for CDownload to track progress of indiv dwlds
// ---------------------------------------------------------------------------
CBindStatusCallback::CBindStatusCallback(CDownload *pdl, DWORD grfBINDF)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CBindStatusCallback::CBindStatusCallback",
"this=%#x, %#x, %#x",
this, pdl, grfBINDF
));
DllAddRef();
m_pbinding = NULL;
m_cRef = 1; // equ of internal addref
m_pdl = pdl;
m_grfBINDF = grfBINDF;
DEBUG_LEAVE(0);
} // CBindStatusCallback
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::~CBindStatusCallback
// ---------------------------------------------------------------------------
CBindStatusCallback::~CBindStatusCallback()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CBindStatusCallback::~CBindStatusCallback",
"this=%#x",
this
));
SAFERELEASE(m_pbinding);
DllRelease();
DEBUG_LEAVE(0);
} // ~CBindStatusCallback
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::AddRef
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CBindStatusCallback::AddRef()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Dword,
"CBindStatusCallback::IUnknown::AddRef",
"this=%#x",
this
));
ULONG ulRet = m_cRef++;
DEBUG_LEAVE(ulRet);
return ulRet;
}
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::Release
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CBindStatusCallback::Release()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Dword,
"CBindStatusCallback::IUnknown::Release",
"this=%#x",
this
));
if (--m_cRef == 0) {
delete this;
DEBUG_LEAVE(0);
return 0;
}
DEBUG_LEAVE(m_cRef);
return m_cRef;
}
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::QueryInterface
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::QueryInterface(REFIID riid, void** ppv)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IUnknown::QueryInterface",
"this=%#x, %#x, %#x",
this, &riid, ppv
));
*ppv = NULL;
if (riid==IID_IUnknown || riid==IID_IBindStatusCallback)
*ppv = (IBindStatusCallback *)this;
if (riid==IID_IHttpNegotiate)
*ppv = (IHttpNegotiate *)this;
if (riid==IID_IWindowForBindingUI)
*ppv = (IWindowForBindingUI*)this;
if (riid==IID_IServiceProvider)
*ppv = (IServiceProvider *)this;
if (riid==IID_ICatalogFileInfo)
*ppv = (ICatalogFileInfo *)this;
if (*ppv == NULL)
{
DEBUG_LEAVE(E_NOINTERFACE);
return E_NOINTERFACE;
}
AddRef();
DEBUG_LEAVE(S_OK);
return S_OK;
} // CBindStatusCallback::QueryInterface
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetWindow
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::GetWindow(REFGUID rguidreason, HWND *phWnd)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IWindowForBindingUI::GetWindow",
"this=%#x, %#x, %#x",
this, &rguidreason, phWnd
));
HRESULT hr = S_OK;
CCodeDownload *pcdl = m_pdl->GetCodeDownload();
HWND hWnd = pcdl->GetClientBinding()->GetHWND(rguidreason);
if (hWnd == INVALID_HANDLE_VALUE)
hr = S_FALSE;
*phWnd = hWnd;
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::QueryService
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::QueryService(REFGUID guidService, REFIID riid, LPVOID *ppv)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IServiceProvider::QueryService",
"this=%#x, %#x, %#x, %#x",
this, &guidService, &riid, ppv
));
IBindStatusCallback *pbsc = m_pdl->GetCodeDownload()->GetClientBSC();
IServiceProvider *psp = NULL;
HRESULT hr = E_NOINTERFACE;
ASSERT(pbsc);
if (pbsc && SUCCEEDED(pbsc->QueryInterface(IID_IServiceProvider, (void **)&psp)) && psp) {
hr = psp->QueryService(guidService, riid, ppv);
SAFERELEASE(psp);
}
// Since this is QueryService we can QI on our client's BSC object too.
if (FAILED(hr)) {
// This is special case we handle so we can bind to client's ultimate IBindHost
// if one exists. BUG BUG: Support other interfaces here, in general?
// BUG BUG: Rearrange order of comparisons for performance.
if (IsEqualGUID(guidService, riid) &&
(IsEqualGUID(riid, IID_IBindHost) ||
IsEqualGUID(riid, IID_IWindowForBindingUI) ||
IsEqualGUID(riid, IID_ICodeInstall) ||
IsEqualGUID(riid, IID_ICatalogFileInfo) ||
IsEqualGUID(riid, IID_IInternetHostSecurityManager))) {
hr = pbsc->QueryInterface(riid, (void **)ppv);
}
}
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetBindInfo
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IBindingStatusCallback::GetBindInfo",
"this=%#x, %#x, %#x",
this, pgrfBINDF, pbindInfo
));
if ((pgrfBINDF == NULL) || (pbindInfo == NULL) || (pbindInfo->cbSize == 0))
{
DEBUG_LEAVE(E_INVALIDARG);
return E_INVALIDARG;
}
*pgrfBINDF = m_grfBINDF;
// clear BINDINFO but keep its size
DWORD cbSize = pbindInfo->cbSize;
ZeroMemory( pbindInfo, cbSize );
pbindInfo->cbSize = cbSize;
// use IE5's utf-8 policy
pbindInfo->dwOptions |= BINDINFO_OPTIONS_USE_IE_ENCODING;
if (m_pdl->DoPost()) {
pbindInfo->dwBindVerb = BINDVERB_POST;
pbindInfo->stgmedData.tymed = TYMED_HGLOBAL;
pbindInfo->stgmedData.hGlobal = m_pdl->GetPostData(&(pbindInfo->cbstgmedData));
pbindInfo->stgmedData.pUnkForRelease = (IUnknown *) (IBindStatusCallback *) this;
AddRef(); // AddRef ourselves so we stick around; caller must release!
}
DWORD grfBINDF = 0;
BINDINFO bindInfo;
memset(&bindInfo, 0, sizeof(BINDINFO));
bindInfo.cbSize = sizeof(BINDINFO);
CCodeDownload *pcdl = m_pdl->GetCodeDownload();
pcdl->GetClientBSC()->GetBindInfo(&grfBINDF, &bindInfo);
if (grfBINDF & BINDF_SILENTOPERATION)
{
*pgrfBINDF |= BINDF_SILENTOPERATION;
pcdl->SetSilentMode();
}
if (grfBINDF & BINDF_OFFLINEOPERATION)
*pgrfBINDF |= BINDF_OFFLINEOPERATION;
if (grfBINDF & BINDF_GETNEWESTVERSION)
*pgrfBINDF |= BINDF_GETNEWESTVERSION;
if (grfBINDF & BINDF_RESYNCHRONIZE)
*pgrfBINDF |= BINDF_RESYNCHRONIZE;
// or should we always insist on this regardless of what client wants?
if (grfBINDF & BINDF_PREFERDEFAULTHANDLER)
*pgrfBINDF |= BINDF_PREFERDEFAULTHANDLER;
if (grfBINDF & BINDF_ENFORCERESTRICTED)
*pgrfBINDF |= BINDF_ENFORCERESTRICTED;
// To make sure the file winds up on disk even for SSL connections, we need to add
*pgrfBINDF |= BINDF_NEEDFILE;
// BINDINFO_FIX(LaszloG 8/15/97)
ReleaseBindInfo(&bindInfo);
DEBUG_LEAVE(S_OK);
return S_OK;
} // CBindStatusCallback::GetBindInfo
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStartBinding
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnStartBinding(DWORD grfBSCOPTION,IBinding* pbinding)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IBindingStatusCallback::OnStartBinding",
"this=%#x, %#x, %#x",
this, grfBSCOPTION, pbinding
));
CCodeDownload *pcdl = m_pdl->GetCodeDownload();
Assert(pbinding);
if (m_pbinding != NULL)
SAFERELEASE(m_pbinding);
m_pbinding = pbinding;
if (m_pbinding != NULL)
m_pbinding->AddRef();
m_pdl->SetDLState(DLSTATE_BINDING);
// call the client BSC::OnStartBinding if not already done
CClBinding *pClientBinding = pcdl->GetClientBinding();
if(pClientBinding->GetState() == CDL_NoOperation){
Assert(pClientBinding->GetAssBSC() == pcdl->GetClientBSC());
pClientBinding->SetState(CDL_Downloading);
pcdl->AddRef();
pcdl->GetClientBSC()->OnStartBinding(grfBSCOPTION, pClientBinding);
}
DEBUG_LEAVE(S_OK);
return S_OK;
} // CBindStatusCallback::OnStartBinding
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetPriority
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::GetPriority(LONG* pnPriority)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IBindingStatusCallback::GetPriority",
"this=%#x, %#x",
this, pnPriority
));
DEBUG_LEAVE(E_NOTIMPL);
return E_NOTIMPL;
} // CBindStatusCallback::GetPriority
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnProgress
// Here we get the master CodeDownload obj to collate progress and report
// cumulative code download progress to client BSC::OnProgress.
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IBindingStatusCallback::GetPriority",
"this=%#x, %#x, %#x, %#x, %.80wq",
this, ulProgress, ulProgressMax, ulStatusCode, szStatusText
));
IBindStatusCallback *pClientBSC = m_pdl->GetCodeDownload()->GetClientBSC();
char szURL[INTERNET_MAX_URL_LENGTH];
FILEXTN extn;
char *pBaseFileName;
HRESULT hr = S_OK;
IMoniker *pmk = NULL;
CCodeDownload *pcdl = m_pdl->GetCodeDownload();
// if this a redirect set the context appropriately
// also use this URL to get the extension and base dest name for this
// component, if its a POST (Search Path)
if (m_pdl->DoPost() && (ulStatusCode == BINDSTATUS_REDIRECTING)) {
WideCharToMultiByte(CP_ACP, 0, szStatusText, -1, szURL,
INTERNET_MAX_URL_LENGTH, 0,0);
// BUGBUG: use mime type in response header to determine extn
extn = GetExtnAndBaseFileName( szURL, &pBaseFileName);
hr = m_pdl->SetURLAndExtn( szStatusText, extn);
if (SUCCEEDED(hr)) {
IBindHost *pBH = pcdl->GetClientBinding()->GetIBindHost();
if (pBH) {
hr = pBH->CreateMoniker((LPOLESTR)szStatusText, m_pdl->GetBindCtx(), &pmk, 0);
} else {
hr = CreateURLMoniker(NULL, szStatusText, &pmk);
}
if (SUCCEEDED(hr)) {
pcdl->SetContextMoniker(pmk);
pcdl->MarkNewContextMoniker();
}
}
if (FAILED(hr))
m_pdl->SetResponseHeaderStatus( hr );
}
// we are only interested in cumulative numbers for "downloading" status
// for all others progress is usually: "connecting: 0 of 0", so we
// pass these as is to our client
if ((ulStatusCode != BINDSTATUS_DOWNLOADINGDATA ) &&
(ulStatusCode != BINDSTATUS_ENDDOWNLOADDATA )) {
// pass on progress as is to our client
pClientBSC->OnProgress(ulProgress, ulProgressMax, ulStatusCode,
szStatusText);
DEBUG_LEAVE(S_OK);
return S_OK;
}
// here if Downloading Data progress
m_pdl->SetProgress(ulProgress, ulProgressMax); // update my dl-object's prog
// now summate stats and report to client
CDownload *pdl = m_pdl->GetCodeDownload()->GetDownloadHead();
ULONG ulSum = 0;
ULONG ulSumMax = 0;
// walk each dl object and make a sum of all ulProgress and ulProgressMax
do {
pdl->SumProgress(&ulSum, &ulSumMax);
} while ((pdl = pdl->GetNext()) != NULL);
// pass on cumulative downloading progress to our client
pClientBSC->OnProgress(ulSum, ulSumMax, BINDSTATUS_DOWNLOADINGDATA,
m_pdl->GetCodeDownload()->GetMainURL());
if (ulStatusCode == BINDSTATUS_ENDDOWNLOADDATA ) {
// pass on progress as is to our client
pClientBSC->OnProgress(ulProgress, ulProgressMax, ulStatusCode,
szStatusText);
}
DEBUG_LEAVE(NOERROR);
return(NOERROR);
} // CBindStatusCallback
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnDataAvailable
// At the last notification we get the filename URLmon has downloaded the
// m_url data to and rename it to a file in the temp dir.
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnDataAvailable(DWORD grfBSC, DWORD dwSize, FORMATETC *pFmtetc, STGMEDIUM __RPC_FAR *pstgmed)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IBindingStatusCallback::OnDataAvailable",
"this=%#x, %#x, %#x, %#x, %#x",
this, grfBSC, dwSize, pFmtetc, pstgmed
));
HRESULT hr = NO_ERROR;
// never forward OnDataAvailable to code download's client BSC
if (grfBSC & BSCF_LASTDATANOTIFICATION)
{
// if this is the final notification then get the data and display it
// we asked for IUnknown, we should get back a filename
Assert((pFmtetc->tymed & TYMED_FILE));
if (pFmtetc->tymed & TYMED_FILE) {
char szFile[MAX_PATH];
DWORD dwLen = 0;
if (!(dwLen = WideCharToMultiByte(CP_ACP, 0 , pstgmed->lpszFileName , -1 , szFile, MAX_PATH, NULL, NULL))) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
} else {
LPSTR lpFileName = new char[dwLen + 1];
if (!lpFileName) {
hr = E_OUTOFMEMORY;
goto Exit;
} else {
lstrcpy(lpFileName, szFile);
m_pdl->SetFileName(lpFileName);
}
}
// check last modified date for file: URLs
// maybe we don't need the file
HRESULT hr1 = m_pdl->IsDownloadedVersionRequired();
if (FAILED(hr1)) {
m_pdl->SetResponseHeaderStatus(hr1);
goto Exit;
}
// ref count on the cache
// file.
pstgmed->pUnkForRelease->AddRef();
m_pdl->SetUnkForCacheFileRelease(pstgmed->pUnkForRelease);
}
}
Exit:
DEBUG_LEAVE(hr);
return hr;
} // CBindStatusCallback::OnDataAvailable
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnObjectAvailable
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnObjectAvailable( REFIID riid, IUnknown* punk)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IBindingStatusCallback::OnObjectAvailable",
"this=%#x, %#x, %#x",
this, &riid, punk
));
// Not applicable: we call pmk->BTS not BTO
DEBUG_LEAVE(E_NOTIMPL);
return E_NOTIMPL;
} // CBindStatusCallback::OnObjectAvailable
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnLowResource
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnLowResource(DWORD dwReserved)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IBindingStatusCallback::OnObjectAvailable",
"this=%#x, %#x",
this, dwReserved
));
DEBUG_LEAVE(E_NOTIMPL);
return E_NOTIMPL;
} // CBindStatusCallback::OnLoadResource
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStopBinding
//
// we get here when we have fully downloaded 'this'.
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnStopBinding(HRESULT hrStatus, LPCWSTR szError)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IBindingStatusCallback::OnStopBinding",
"this=%#x, %#x, %.80wq",
this, hrStatus, szError
));
CCodeDownload *pcdl = m_pdl->GetCodeDownload();
HRESULT hrResponseHdr = m_pdl->GetResponseHeaderStatus();
IBindHost *pBindHost = NULL;
HRESULT hr = S_OK; // assume all OK
if (pcdl) {
pcdl->CodeDownloadDebugOut(DEB_CODEDL, FALSE, ID_CDLDBG_DL_ON_STOP_BINDING, hrStatus, hrResponseHdr);
}
if ((FAILED(hrStatus) && (SCODE_FACILITY(hrStatus) == FACILITY_INTERNET)) ||
FAILED(hrResponseHdr) || SCODE_CODE(hrStatus) == ERROR_MOD_NOT_FOUND) {
hr = m_pdl->DownloadRedundantCodeBase();
if (hr == E_PENDING || hr == MK_S_ASYNCHRONOUS) {
goto Exit;
}
}
m_pdl->SetDLState(DLSTATE_DOWNLOADED);
pBindHost = pcdl->GetClientBinding()->GetIBindHost();
SAFERELEASE(m_pbinding);
if (!pBindHost) {
hr = RevokeBindStatusCallback(m_pdl->GetBindCtx(), m_pdl->GetBSC());
}
if (FAILED(hr)) {
goto OSB_Complete;
}
// if URLMON failed the download or if the response hdr indicated
// a failure that URLMON failed to detect properly
// pass the problem to pcdl->CompleteOne(). This will determine if it
// will query for the clsid with more urls in the CodeSearchPath
// in the registry.
if (FAILED(hrStatus) || FAILED(hrResponseHdr)) {
goto OSB_Complete;
}
// BUGBUG: also check here for Last Modified Date on the Cache Entry
// versus Last Modified if a previous version exists and we are doung
// GetLatest. If data is in the cache then wininet ignores our
// if-modified-since and so we will end up re-installing even though
// there is no version change.
if (m_pdl->GetFileName() != NULL) { // should be set by OnDataAvailable
// This takes us to the next state. VerifyTrust moves us when
// complete to the next state of processing the ProcessPiece.
CCDLPacket *pPkt= new CCDLPacket(CODE_DOWNLOAD_TRUST_PIECE, m_pdl, 0);
if (pPkt) {
hr = pPkt->Post();
} else {
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
goto Exit;
// else fall thru to OSB_Complete
} else if (!m_pdl->UsingCdlProtocol()) {
// In case of CDL protocol handler we don't need OnDataAvailable or
// Trust Verification done here.
// BindToStorage may have not detected the error
if (m_pdl->DoPost())
hrResponseHdr = HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
else
hr = HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
}
OSB_Complete:
// does all the master state analysis
m_pdl->CompleteSignal(hr, hrStatus, hrResponseHdr, szError);
// This very BSC may already have been deleted if all done.
// Don't access any members. Just return !!!
Exit:
DEBUG_LEAVE(S_OK);
return S_OK; // always succeed to url mon.
} // CBindStatusCallback::OnStopBinding
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::BeginningTransaction
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::BeginningTransaction(
LPCWSTR szURL,
LPCWSTR szHeaders,
DWORD dwReserved,
LPWSTR *pszAdditionalHeaders)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IHttpNegotiate::BeginningTransaction",
"this=%#x, %.80wq, %.80wq, %#x, %#x",
this, szURL, szHeaders, dwReserved, pszAdditionalHeaders
));
HRESULT hr = S_OK;
char szHttpDate[INTERNET_RFC1123_BUFSIZE+1];
DWORD dwLen = 0;
LPWSTR szAHdrs = NULL;
static const char cszHeaderFmt[] = "%s %s\r\n";
static const char szIfMod[] = "If-Modified-Since:";
static const char szNONEMATCH[] = "If-None-Match:";
static const WCHAR szFORM[] = L"Content-Type: application/x-www-form-urlencoded\r\n";
static const char szAcceptLanguageFmt[] = "Accept-Language: %s\r\n";
char szBuf[MAX_PATH];
WCHAR szAcceptLanguage[MAX_PATH];
char szLangBuf[10];
char *pszNoneMatch = NULL;
CCodeDownload *pcdl = m_pdl->GetCodeDownload();
LCID lcid = pcdl->GetLCID();
// BUGBUG: we currently only support primary lang or default
// it should really be "en-us, en", instead of just "en"
// waiting for note from TonyCi about some servers like Apache
// broken by this
lcid = MAKELCID(MAKELANGID(PRIMARYLANGID(LANGIDFROMLCID(lcid)), SUBLANG_DEFAULT), SORT_DEFAULT);
DEBUG_PRINT(DOWNLOAD,
INFO,
("this=%#x, m_lcid: %d (%#x), lcid: %d (%#x)\n",
this, pcdl->GetLCID(), pcdl->GetLCID(), lcid, lcid
));
*szAcceptLanguage = L'\0';
if (pcdl->GetLangInfo()->GetAcceptLanguageString(lcid, szLangBuf, sizeof(szLangBuf))
&& (*szLangBuf != '\0'))
{
wnsprintf(szBuf, sizeof(szBuf)-1, szAcceptLanguageFmt, szLangBuf);
dwLen = MultiByteToWideChar(CP_ACP, 0, szBuf, -1, szAcceptLanguage, MAX_PATH);
}
Assert((pszAdditionalHeaders != NULL));
FILETIME *pftLastMod = pcdl->GetLastModifiedTime();
SYSTEMTIME sSysTime;
BOOL bSendNoneMatch = !pcdl->ForceDownload() && ( pcdl->LocalVersionPresent() && (pcdl->GetLocalVersionEtag()) ) && pcdl->NeedLatestVersion();
BOOL bSendLastMod = !bSendNoneMatch && (!pcdl->ForceDownload() && ( pcdl->LocalVersionPresent() && (pftLastMod) ) && pcdl->NeedLatestVersion());
if ( bSendLastMod) {
Assert( (pftLastMod != NULL) ); // Check for bug#40696
// need to send If-Modified-Since
if (!FileTimeToSystemTime(pftLastMod, &sSysTime)) {
m_pdl->SetResponseHeaderStatus( HRESULT_FROM_WIN32(GetLastError()));
goto Exit;
}
if (!InternetTimeFromSystemTimeA(&sSysTime, INTERNET_RFC1123_FORMAT,
szHttpDate, INTERNET_RFC1123_BUFSIZE)) {
m_pdl->SetResponseHeaderStatus( HRESULT_FROM_WIN32(GetLastError()));
goto Exit;
}
dwLen += (INTERNET_RFC1123_BUFSIZE + 1 + sizeof(szIfMod) +
sizeof(cszHeaderFmt));
}
if (bSendNoneMatch) {
DWORD dwNoneMatch = lstrlen(pcdl->GetLocalVersionEtag()) + sizeof(szNONEMATCH) + sizeof(cszHeaderFmt);
pszNoneMatch = new char [dwNoneMatch+1];
wsprintf(pszNoneMatch, cszHeaderFmt, szNONEMATCH, pcdl->GetLocalVersionEtag());
dwLen += dwNoneMatch;
}
if (m_pdl->DoPost()) {
dwLen += sizeof(szFORM);
}
if (dwLen) {
szAHdrs = new WCHAR [dwLen + 1];
if (!szAHdrs) {
m_pdl->SetResponseHeaderStatus( E_OUTOFMEMORY );
// BUGBUG: Clean all this up to never return right away, and
// goto exit to cleanup
SAFEDELETE(pszNoneMatch);
DEBUG_LEAVE(hr);
return hr;
}
szAHdrs[0] = '\0';
}
if (bSendLastMod) {
char *szTemp = new char [dwLen + 1];
if (!szTemp) {
hr = E_OUTOFMEMORY;
delete szAHdrs;
goto Exit;
}
wsprintf(szTemp, cszHeaderFmt, szIfMod, szHttpDate);
MultiByteToWideChar(CP_ACP, 0, szTemp, -1, szAHdrs, dwLen);
delete szTemp;
}
if (bSendNoneMatch) {
MultiByteToWideChar(CP_ACP, 0, pszNoneMatch, -1, szAHdrs, dwLen);
}
if (m_pdl->DoPost()) {
StrCatW(szAHdrs, szFORM);
}
if (*szAcceptLanguage != L'\0')
{
StrCatW(szAHdrs, szAcceptLanguage);
}
Exit:
SAFEDELETE(pszNoneMatch);
*pszAdditionalHeaders = szAHdrs;
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnResponse
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnResponse(
DWORD dwResponseCode,
LPCWSTR szResponseHeaders,
LPCWSTR szRequestHeaders,
LPWSTR *pszAdditionalRequestHeaders)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::IHttpNegotiate::OnResponse",
"this=%#x, %#x, %.80wq, %.80wq, %#x",
this, dwResponseCode, szResponseHeaders, szRequestHeaders, pszAdditionalRequestHeaders
));
HRESULT hr = S_OK;
// propogate errors here to CSBC::OnStopBinding
// we need this as urlmon might just convert any error returned here
// as user_cancelled
if (dwResponseCode != HTTP_STATUS_OK) {
if (dwResponseCode == HTTP_STATUS_NOT_MODIFIED) {
hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
} else {
hr = HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
}
m_pdl->SetResponseHeaderStatus( hr );
}
if (m_pdl->DoPost() ||
(m_pdl->GetMoniker() == m_pdl->GetCodeDownload()->GetContextMoniker())){
// Get the HttpQueryInfo wrapper object.
IWinInetHttpInfo *pHttpInfo = NULL;
HRESULT hr = GetBinding()->QueryInterface
(IID_IWinInetHttpInfo, (void **) &pHttpInfo);
if (SUCCEEDED(hr)) {
DWORD cbLen = INTERNET_RFC1123_BUFSIZE + 1;
char szHttpDate[INTERNET_RFC1123_BUFSIZE+1];
if ((pHttpInfo->QueryInfo (HTTP_QUERY_LAST_MODIFIED,
(LPVOID)szHttpDate, &cbLen, NULL, 0) == S_OK) && cbLen)
m_pdl->GetCodeDownload()->SetLastModifiedTime(szHttpDate);
cbLen = 0; // reset
if ( (pHttpInfo->QueryInfo (HTTP_QUERY_ETAG,
(LPVOID)NULL, &cbLen, NULL, 0) == S_OK) && cbLen) {
char *pbEtag = new char [cbLen +1];
if (pbEtag)
{
*pbEtag = '\0'; // clr
pHttpInfo->QueryInfo (HTTP_QUERY_ETAG,
(LPVOID)pbEtag, &cbLen, NULL, 0);
if (*pbEtag)
m_pdl->GetCodeDownload()->SetEtag(pbEtag);
}
}
pHttpInfo->Release();
}
}
DEBUG_LEAVE(S_OK);
return S_OK;
}
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetCatalogFile
// ---------------------------------------------------------------------------
STDMETHODIMP CBindStatusCallback::GetCatalogFile(LPSTR *ppszCatalogFile)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::ICatalogFileInfo::GetCatalogFile",
"this=%#x, %#x",
this, ppszCatalogFile
));
HRESULT hr = S_OK;
LPSTR pszCatFile = NULL;
if (ppszCatalogFile) {
pszCatFile = m_pdl->GetCodeDownload()->GetCatalogFile();
if (pszCatFile) {
*ppszCatalogFile = new char[lstrlen(pszCatFile) + 1];
if (*ppszCatalogFile == NULL) {
hr = E_OUTOFMEMORY;
}
else {
lstrcpy(*ppszCatalogFile, pszCatFile);
}
}
else {
*ppszCatalogFile = NULL;
}
}
else {
hr = E_INVALIDARG;
}
DEBUG_LEAVE(hr);
return hr;
}
// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetJavaTrust
// ---------------------------------------------------------------------------
STDMETHODIMP CBindStatusCallback::GetJavaTrust(void **ppJavaTrust)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBindStatusCallback::ICatalogFileInfo::GetJavaTrust",
"this=%#x, %#x",
this, ppJavaTrust
));
HRESULT hr = S_OK;
if (ppJavaTrust) {
*ppJavaTrust = (void *)m_pdl->GetCodeDownload()->GetJavaTrust();
}
else {
hr = E_INVALIDARG;
}
DEBUG_LEAVE(hr);
return hr;
}