// =========================================================================== // File: DL.CXX // implements CDownload, CBindStatusCallback classes // #include // SILENT MODE #include #include #include #include #include // office antivirus goo #define AVVENDOR // don't look at unwanted office defs #include 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 *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; }