#include "ParseInf.h" #include "general.h" #include #include //#define USE_SHORT_PATH_NAME 1 #define REG_PATH_IE_CACHE_LIST TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\ActiveX Cache") #define cCachePathsMax 5 // maximum number of legacy caches + the current cache du jour struct OCCFindData { LPCLSIDLIST_ITEM m_pcliHead; LPCLSIDLIST_ITEM m_pcliTail; struct { TCHAR m_sz[MAX_PATH]; DWORD m_cch; } m_aCachePath[cCachePathsMax]; OCCFindData(); ~OCCFindData(); BOOL IsCachePath( LPCTSTR szPath ); // Control List operations HRESULT AddListItem( LPCTSTR szFileName, LPCTSTR szCLSID, DWORD dwIsDistUnit ); LPCLSIDLIST_ITEM TakeFirstItem(void); }; DWORD CCacheLegacyControl::s_dwType = 1; DWORD CCacheDistUnit::s_dwType = 2; HRESULT CCacheLegacyControl::Init( HKEY hkeyCLSID, LPCTSTR szFile, LPCTSTR szCLSID ) { HRESULT hr = S_OK; lstrcpyn(m_szFile, szFile, ARRAYSIZE(m_szFile)); lstrcpyn(m_szCLSID, szCLSID, ARRAYSIZE(m_szCLSID)); // Get full user type name m_szName[0] = '\0'; DWORD dw = sizeof(m_szName); LRESULT lResult = RegQueryValue(hkeyCLSID, m_szCLSID, m_szName, (LONG*)&dw); // if the fails, we should get a resource string (seanf 5/9/97 ) // Get type lib id TCHAR szTypeLibValName[MAX_PATH]; CatPathStrN( szTypeLibValName, szCLSID, HKCR_TYPELIB, ARRAYSIZE(szTypeLibValName) ); dw = sizeof(m_szTypeLibID); lResult = RegQueryValue( hkeyCLSID, szTypeLibValName, m_szTypeLibID, (LONG*)&dw); if (lResult != ERROR_SUCCESS) m_szTypeLibID[0] = TEXT('\0'); // Set Codebase m_szCodeBase[0] = '\0'; m_szVersion[0] = '\0'; hr = DoParse( m_szFile, m_szCLSID ); return hr; } HRESULT CCacheDistUnit::Init( HKEY hkeyCLSID, LPCTSTR szFile, LPCTSTR szCLSID, HKEY hkeyDist, LPCTSTR szDU ) { HRESULT hr = S_OK; HKEY hkeyDU; HKEY hkeyDLInfo; // DownloadInformation subkey HKEY hkeyVers; // InstalledVersion subkey HKEY hkeyCOM; // subkey of HKCR\CLSID, used if outside of cache dir LRESULT lResult = ERROR_SUCCESS; DWORD dw; TCHAR szNameT[MAX_PATH]; UINT uiVerSize = 0; DWORD dwVerSize = 0; DWORD dwHandle = 0; BYTE *pbBuffer = NULL; HANDLE hFile; FILETIME ftLastAccess; BOOL bRunOnNT5 = FALSE; OSVERSIONINFO osvi; VS_FIXEDFILEINFO *lpVSInfo = NULL; if ( szFile[0] == '\0' && RegOpenKeyEx( hkeyCLSID, szCLSID, 0, KEY_READ, &hkeyCOM ) == ERROR_SUCCESS ) { LONG lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, INPROCSERVER, szNameT, &lcb ); if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, INPROCSERVER32, szNameT, &lcb ); } if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, INPROCSERVERX86, szNameT, &lcb ); } if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, LOCALSERVER, szNameT, &lcb ); } if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, LOCALSERVER32, szNameT, &lcb ); } if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, LOCALSERVERX86, szNameT, &lcb ); } RegCloseKey( hkeyCOM ); } else lstrcpyn( szNameT, szFile, ARRAYSIZE(szNameT)); if ( lResult != ERROR_SUCCESS ) // needed to find file path but couldn't szNameT[0] = '\0'; hr = CCacheLegacyControl::Init( hkeyCLSID, szNameT, szCLSID ); if ( FAILED(hr) ) return hr; lResult = RegOpenKeyEx(hkeyDist, szDU, 0, KEY_READ, &hkeyDU); if (lResult != ERROR_SUCCESS) return E_FAIL; // Get CLSID lstrcpyn(m_szCLSID, szDU, MAX_DIST_UNIT_NAME_LEN); // Get full user type name - only override the control name if DU name is not empty dw = sizeof(szNameT); lResult = RegQueryValue(hkeyDU, NULL, szNameT, (LONG*)&dw); if ( lResult == ERROR_SUCCESS && szNameT[0] != '\0' ) { lstrcpyn( m_szName, szNameT, ARRAYSIZE(m_szName) ); } else if ( *m_szName == '\0' ) // worst case, if we still don't have a name, a GUID will suffice lstrcpyn( m_szName, szDU, ARRAYSIZE(m_szName) ); // Get type lib id // Get type lib id TCHAR szTypeLibValName[MAX_PATH]; CatPathStrN(szTypeLibValName, m_szCLSID, HKCR_TYPELIB, ARRAYSIZE(szTypeLibValName)); dw = sizeof(m_szTypeLibID); lResult = RegQueryValue( hkeyCLSID, szTypeLibValName, m_szTypeLibID, (LONG*)&dw); if (lResult != ERROR_SUCCESS) (m_szTypeLibID)[0] = TEXT('\0'); m_szCodeBase[0] ='\0'; lResult = RegOpenKeyEx(hkeyDU, REGSTR_DOWNLOAD_INFORMATION, 0, KEY_READ, &hkeyDLInfo); if (lResult == ERROR_SUCCESS) { dw = sizeof(m_szCodeBase); HRESULT hrErr = RegQueryValueEx(hkeyDLInfo, REGSTR_DLINFO_CODEBASE, NULL, NULL, (unsigned char *)m_szCodeBase, &dw); RegCloseKey( hkeyDLInfo ); } // Get Version from DU branch m_szVersion[0] ='\0'; lResult = RegOpenKeyEx(hkeyDU, REGSTR_INSTALLED_VERSION, 0, KEY_READ, &hkeyVers); if (lResult == ERROR_SUCCESS) { dw = sizeof(m_szVersion); RegQueryValueEx(hkeyVers, NULL, NULL, NULL, (LPBYTE)m_szVersion, &dw); RegCloseKey(hkeyVers); } // The version specified in the COM branch is the definitive word on // what the version is. If a key exists in the COM branch, use the version // that is found inside the InProcServer/LocalServer. if (RegOpenKeyEx( hkeyCLSID, szCLSID, 0, KEY_READ, &hkeyCOM ) == ERROR_SUCCESS) { LONG lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, INPROCSERVER32, szNameT, &lcb ); if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, INPROCSERVER, szNameT, &lcb ); } if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, INPROCSERVERX86, szNameT, &lcb ); } if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, LOCALSERVER32, szNameT, &lcb ); } if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, LOCALSERVER, szNameT, &lcb ); } if ( lResult != ERROR_SUCCESS ) { lcb = sizeof(szNameT); lResult = RegQueryValue( hkeyCOM, LOCALSERVERX86, szNameT, &lcb ); } RegCloseKey( hkeyCOM ); // HACK! GetFileVersionInfoSize and GetFileVersionInfo modify // the last access time of the file under NT5! This causes us // to retrieve the wrong last access time when removing expired // controls. This hack gets the last access time before the // GetFileVersionInfo calls, and sets it back afterwards. // See IE5 RAID #56927 for details. This code should be removed // when NT5 fixes this bug. osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion == 5) { bRunOnNT5 = TRUE; } if (bRunOnNT5) { hFile = CreateFile(szNameT, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { GetFileTime(hFile, NULL, &ftLastAccess, NULL); CloseHandle(hFile); } } dwVerSize = GetFileVersionInfoSize((char *)szNameT, &dwHandle); pbBuffer = new BYTE[dwVerSize]; if (!pbBuffer) { return E_OUTOFMEMORY; } if (GetFileVersionInfo((char *)szNameT, 0, dwVerSize, pbBuffer)) { if (VerQueryValue(pbBuffer, "\\", (void **)&lpVSInfo, &uiVerSize)) { wsprintf(m_szVersion, "%d,%d,%d,%d", (lpVSInfo->dwFileVersionMS >> 16) & 0xFFFF , lpVSInfo->dwFileVersionMS & 0xFFFF , (lpVSInfo->dwFileVersionLS >> 16) & 0xFFFF , lpVSInfo->dwFileVersionLS & 0xFFFF); } } delete [] pbBuffer; if (bRunOnNT5) { hFile = CreateFile(szNameT, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { SetFileTime(hFile, NULL, &ftLastAccess, NULL); CloseHandle(hFile); } } } RegCloseKey( hkeyDU ); return DoParseDU( m_szFile, m_szCLSID); } HRESULT MakeCacheItemFromControlList( HKEY hkeyClass, // HKCR\CLSID HKEY hkeyDist, // HKLM\SOFTWARE\MICROSOFT\Code Store Database\Distribution Units LPCLSIDLIST_ITEM pcli, CCacheItem **ppci ) { HRESULT hr = E_FAIL; *ppci = NULL; if ( pcli->bIsDistUnit ) { CCacheDistUnit *pcdu = new CCacheDistUnit(); if ( pcdu != NULL && SUCCEEDED(hr = pcdu->Init( hkeyClass, pcli->szFile, pcli->szCLSID, hkeyDist, pcli->szCLSID)) ) *ppci = pcdu; else hr = E_OUTOFMEMORY; } else { CCacheLegacyControl *pclc = new CCacheLegacyControl(); if ( pclc != NULL && SUCCEEDED(hr = pclc->Init( hkeyClass, pcli->szFile, pcli->szCLSID )) ) *ppci = pclc; else hr = E_OUTOFMEMORY; } return hr; } OCCFindData::OCCFindData() : m_pcliHead(NULL), m_pcliTail(NULL) { LONG lResult; HKEY hkeyCacheList; for ( int i = 0; i < cCachePathsMax; i++ ) { m_aCachePath[i].m_cch = 0; m_aCachePath[i].m_sz[0] = '\0'; } // Unhook occache as a shell extension for the cache folders. lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_PATH_IE_CACHE_LIST, 0x0, KEY_READ, &hkeyCacheList ); if ( lResult == ERROR_SUCCESS ) { DWORD dwIndex; TCHAR szName[MAX_PATH]; DWORD cbName; DWORD cbValue; for ( dwIndex = 0, cbName = sizeof(szName), cbValue = MAX_PATH * sizeof(TCHAR); dwIndex < cCachePathsMax; dwIndex++, cbName = sizeof(szName), cbValue = MAX_PATH * sizeof(TCHAR) ) { lResult = RegEnumValue( hkeyCacheList, dwIndex, szName, &cbName, NULL, NULL, (LPBYTE)m_aCachePath[dwIndex].m_sz, &cbValue ); m_aCachePath[dwIndex].m_cch = lstrlen( m_aCachePath[dwIndex].m_sz ); } // We leave this key in place because it is the only record we have of the // cache folders and would be useful to future installations of IE RegCloseKey( hkeyCacheList ); } } OCCFindData::~OCCFindData() { if ( m_pcliHead ) RemoveList(m_pcliHead); } BOOL OCCFindData::IsCachePath( LPCTSTR szPath ) { BOOL fMatch = FALSE; for ( int i = 0; i < cCachePathsMax && !fMatch; i++ ) fMatch = m_aCachePath[i].m_cch != 0 && LStrNICmp( szPath, m_aCachePath[i].m_sz, m_aCachePath[i].m_cch ) == 0; return fMatch; } HRESULT OCCFindData::AddListItem( LPCTSTR szFile, LPCTSTR szCLSID, DWORD dwIsDistUnit ) { HRESULT hr = S_OK; if ( m_pcliTail == NULL ) { m_pcliTail = new CLSIDLIST_ITEM; if (m_pcliHead == NULL) m_pcliHead = m_pcliTail; } else { m_pcliTail->pNext = new CLSIDLIST_ITEM; m_pcliTail = m_pcliTail->pNext; } if ( m_pcliTail != NULL ) { m_pcliTail->pNext = NULL; lstrcpyn(m_pcliTail->szFile, szFile, MAX_PATH); lstrcpyn(m_pcliTail->szCLSID, szCLSID, MAX_DIST_UNIT_NAME_LEN); m_pcliTail->bIsDistUnit = dwIsDistUnit; } else hr = E_OUTOFMEMORY; return hr; } LPCLSIDLIST_ITEM OCCFindData::TakeFirstItem(void) { LPCLSIDLIST_ITEM pcli = m_pcliHead; if (m_pcliHead != NULL) { m_pcliHead = m_pcliHead; m_pcliHead = m_pcliHead->pNext; if ( m_pcliHead == NULL ) m_pcliTail = NULL; } return pcli; } BOOL IsDUDisplayable(HKEY hkeyDU) { BOOL bRet = FALSE; if (hkeyDU) { if (IsShowAllFilesEnabled()) { bRet = TRUE; } else { DWORD dwType = 0, dwSystem = 0, dwSize = sizeof(dwSystem); long lResult = RegQueryValueEx(hkeyDU, VALUE_SYSTEM, NULL, &dwType, (LPBYTE)&dwSystem, &dwSize); bRet = (lResult == ERROR_SUCCESS && dwSystem == TRUE) ? (FALSE) : (TRUE); } } return bRet; } BOOL IsShowAllFilesEnabled() { HKEY hkey = 0; BOOL bRet = FALSE; DWORD dwShowAll = 0; DWORD lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_READ, &hkey); if (lResult == ERROR_SUCCESS) { DWORD dwType, dwSize = sizeof(dwShowAll); lResult = RegQueryValueEx(hkey, REGSTR_SHOW_ALL_FILES, NULL, &dwType, (LPBYTE)&dwShowAll, &dwSize); if (lResult == ERROR_SUCCESS) { bRet = (dwShowAll != 0); } RegCloseKey(hkey); } return bRet; } void ToggleShowAllFiles() { DWORD dwShowAll = !IsShowAllFilesEnabled(); HKEY hkey = 0; DWORD lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_ALL_ACCESS, &hkey); if (lResult == ERROR_SUCCESS) { RegSetValueEx(hkey, REGSTR_SHOW_ALL_FILES, 0, REG_DWORD, (CONST BYTE *)&dwShowAll, sizeof(dwShowAll)); RegCloseKey(hkey); } } LONG WINAPI FindFirstControl(HANDLE& hFindHandle, HANDLE& hControlHandle, LPCTSTR pszCachePath) { LONG lResult = ERROR_SUCCESS; HRESULT hr = S_OK; DWORD dw = 0; HKEY hKeyClass = NULL; HKEY hKeyMod = NULL; HKEY hKeyDist = NULL; TCHAR szT[MAX_PATH]; // scratch buffer int cEnum = 0; CCacheItem *pci = NULL; LPCLSIDLIST_ITEM pcli = NULL; TCHAR szDUName[MAX_DIST_UNIT_NAME_LEN]; OCCFindData *poccfd = new OCCFindData(); if ( poccfd == NULL ) { lResult = ERROR_NOT_ENOUGH_MEMORY; goto EXIT_FINDFIRSTCONTROL; } // Open up the HKCR\CLSID key. lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, HKCR_CLSID, 0, KEY_READ, &hKeyClass); if (ERROR_SUCCESS != lResult) goto EXIT_FINDFIRSTCONTROL; // Search for legacy controls found in the COM branch lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_MODULE_USAGE, 0, KEY_READ, &hKeyMod); if (ERROR_SUCCESS != lResult) goto EXIT_FINDFIRSTCONTROL; // Enumerate the known modules and build up a list of the owners. // This is a search for legacy controls. while ((lResult = RegEnumKey(hKeyMod, cEnum++, szT, ARRAYSIZE(szT))) == ERROR_SUCCESS) { TCHAR szClient[MAX_CLIENT_LEN]; HKEY hKeyClsid = NULL; HKEY hkeyMUEntry = NULL; lResult = RegOpenKeyEx( hKeyMod, szT, 0, KEY_READ, &hkeyMUEntry ); if (ERROR_SUCCESS != lResult) continue; // Fetch the module owner. // If the module owner is in the COM branch AND // ( the owner lives in the cache OR it has an INF in the cache ) // Then add the _owner_ to our list of legacy controls. // In the INF case, we may be looking at a control that was re-registered // outside of the cache. // If it doesn't have these properties, then it is either a DU module or // was installed by something other than MSICD. In either case, we'll skip it // at least for now. dw = sizeof(szClient); lResult = RegQueryValueEx(hkeyMUEntry, VALUE_OWNER, NULL, NULL, (LPBYTE)szClient, &dw); if (ERROR_SUCCESS != lResult) continue; lResult = RegOpenKeyEx(hKeyClass, szClient, 0, KEY_READ, &hKeyClsid); if (ERROR_SUCCESS == lResult) { TCHAR szCLocation[MAX_PATH]; // Canonical path of control TCHAR szLocation[MAX_PATH]; // Location in COM CLSID reg tree. // Look for InprocServer[32] or LocalServer[32] key dw = sizeof(szLocation); lResult = RegQueryValue(hKeyClsid, INPROCSERVER32, szLocation, (PLONG)&dw); if (lResult != ERROR_SUCCESS) { dw = sizeof(szLocation); lResult = RegQueryValue(hKeyClsid, LOCALSERVER32, szLocation, (PLONG)&dw); } RegCloseKey(hKeyClsid); hKeyClsid = NULL; if ( lResult == ERROR_SUCCESS ) { BOOL bAddOwner; // see if we've already got an entry for this one. for ( pcli = poccfd->m_pcliHead; pcli != NULL && lstrcmp( szClient, pcli->szCLSID ) != 0; pcli = pcli->pNext ); if ( pcli == NULL ) // not found - possibly add new item { // Canonicalize the path for use in comparisons with cache dirs if ( OCCGetLongPathName(szCLocation, szLocation, MAX_PATH) == 0 ) lstrcpyn( szCLocation, szLocation, MAX_PATH ); // Is the owner in our cache? bAddOwner = poccfd->IsCachePath( szCLocation ); if ( !bAddOwner ) { // does it have an INF in our cache(s)? // We'll appropriate szDCachePath for ( int i = 0; i < cCachePathsMax && !bAddOwner; i++ ) { if ( poccfd->m_aCachePath[i].m_sz != '\0' ) { CatPathStrN( szT, poccfd->m_aCachePath[i].m_sz, PathFindFileName( szCLocation ), MAX_PATH); // Note if another copy of the owner exists within the cache(s). // This would be a case of re-registration. if ( PathFileExists( szT ) ) { // add our version of the control. lstrcpyn( szCLocation, szT, MAX_PATH ); bAddOwner = TRUE; } else bAddOwner = PathRenameExtension( szT, INF_EXTENSION ) && PathFileExists( szT ); } // if cache path } // for each cache directory } // if check for cached INF if ( bAddOwner ) { HKEY hkeyDUCheck = 0; char achBuf[MAX_REGPATH_LEN]; wnsprintfA(achBuf, MAX_REGPATH_LEN, "%s\\%s", REGSTR_PATH_DIST_UNITS, szClient); lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, achBuf, 0, KEY_READ, &hkeyDUCheck); if (lResult != ERROR_SUCCESS) { // This is a legacy control with no corresponding DU poccfd->AddListItem( szCLocation, szClient, FALSE ); } else { if (IsDUDisplayable(hkeyDUCheck)) { // Legacy control w/ DU keys that is displayable poccfd->AddListItem( szCLocation, szClient, FALSE ); } RegCloseKey(hkeyDUCheck); } } } // if owner we haven't seen before } // if owner has local or inproc server } // if owner has COM entry RegCloseKey( hkeyMUEntry ); } // while enumerating Module Usage // we're finished with module usage RegCloseKey(hKeyMod); // Now search distribution units // Check for duplicates - distribution units for controls we detected above lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_READ, &hKeyDist); if (lResult == ERROR_SUCCESS) { cEnum = 0; // Enumerate distribution units and queue them up in the list while ((lResult = RegEnumKey(hKeyDist, cEnum++, szDUName, ARRAYSIZE(szDUName))) == ERROR_SUCCESS) { // We should only display DU's installed by code download. HKEY hkeyDU; DWORD dwType; lResult = RegOpenKeyEx( hKeyDist, szDUName, 0, KEY_READ, &hkeyDU ); Assert( lResult == ERROR_SUCCESS ); if ((ERROR_SUCCESS != lResult) || !IsDUDisplayable(hkeyDU)) { continue; } szT[0] = '\0'; DWORD cb = sizeof(szT); lResult = RegQueryValueEx( hkeyDU, DU_INSTALLER_VALUE, NULL, &dwType, (LPBYTE)szT, &cb ); Assert( lResult == ERROR_SUCCESS ); // properly-formed DU will have this Assert( dwType == REG_SZ ); // properly-formed DU's have a string here // Check for an installed version. We might just have a DU that has an AvailableVersion // but hasn't been installed yet. lResult = RegQueryValue( hkeyDU, REGSTR_INSTALLED_VERSION, NULL, NULL ); RegCloseKey( hkeyDU ); if ( lstrcmpi( szT, CDL_INSTALLER ) == 0 && lResult == ERROR_SUCCESS ) // from InstalledVersion RegQueryValue { // If we can convert the unique name to a GUID, then this DU // may have already been added on the first pass through the // COM branch. CLSID clsidDummy = CLSID_NULL; WORD szDummyStr[MAX_CTRL_NAME_SIZE]; BOOL bFoundDuplicate = FALSE; MultiByteToWideChar(CP_ACP, 0, szDUName, -1, szDummyStr, ARRAYSIZE(szDummyStr)); if ((CLSIDFromString(szDummyStr, &clsidDummy) == S_OK)) { for (pcli = poccfd->m_pcliHead; pcli; pcli = pcli->pNext) { if (!lstrcmpi(szDUName, pcli->szCLSID)) { // Duplicate found. Use dist unit information to // fill in additional fields if it is the first // entry in the list bFoundDuplicate = TRUE; pcli->bIsDistUnit = TRUE; break; } } } if (!bFoundDuplicate) { // Okay we're looking at some sort of Java scenario. We have a distribution unit, but // no corresponding entry in the COM branch. This generally means we've got a DU that // consists of java packages. It can also mean that we're dealing with a java/code download // backdoor introduced in IE3. In this case, an Object tag gets a CAB downloaded that // installs Java classes and sets of a CLSID that invokes MSJava.dll on the class ( ESPN's // sportszone control/applet works this way ). In the first case, we get the name // squared-away when we parse the DU. In the latter case, we need to try and pick the name // up from the COM branch. hr = poccfd->AddListItem( "", szDUName, TRUE ); if ( FAILED(hr) ) { lResult = ERROR_NOT_ENOUGH_MEMORY; goto EXIT_FINDFIRSTCONTROL; } } // if no duplicate - add DU to the list } // if installed by MSICD } // while enumerating DU's } // if we can open the DU key. else lResult = ERROR_NO_MORE_ITEMS; // if no DU's then make due with our legacy controls, if any pcli = poccfd->TakeFirstItem(); if (pcli) { hr = MakeCacheItemFromControlList(hKeyClass, hKeyDist, pcli, &pci); delete pcli; if ( FAILED(hr) ) lResult = hr; } if (hKeyDist) { RegCloseKey(hKeyDist); hKeyDist = 0; } // Clean up if (lResult != ERROR_NO_MORE_ITEMS) goto EXIT_FINDFIRSTCONTROL; if (pci == NULL) lResult = ERROR_NO_MORE_ITEMS; else { lResult = ERROR_SUCCESS; } hFindHandle = (HANDLE)poccfd; hControlHandle = (HANDLE)pci; EXIT_FINDFIRSTCONTROL: if (hKeyDist) RegCloseKey(hKeyDist); if (hKeyClass) RegCloseKey(hKeyClass); if (lResult != ERROR_SUCCESS) { if ( pci != NULL ) delete pci; if ( poccfd != NULL ) delete poccfd; hFindHandle = INVALID_HANDLE_VALUE; hControlHandle = INVALID_HANDLE_VALUE; } return lResult; } LONG WINAPI FindNextControl(HANDLE& hFindHandle, HANDLE& hControlHandle) { LONG lResult = ERROR_SUCCESS; HRESULT hr = S_OK; HKEY hKeyClass = NULL; CCacheItem *pci = NULL; OCCFindData *poccfd = (OCCFindData *)hFindHandle; LPCLSIDLIST_ITEM pcli = poccfd->TakeFirstItem(); hControlHandle = INVALID_HANDLE_VALUE; if (pcli == NULL) { lResult = ERROR_NO_MORE_ITEMS; goto EXIT_FINDNEXTCONTROL; } if ((lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, HKCR_CLSID, 0, KEY_READ, &hKeyClass)) != ERROR_SUCCESS) goto EXIT_FINDNEXTCONTROL; if ( pcli->bIsDistUnit ) { HKEY hKeyDist; lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS, 0, KEY_READ, &hKeyDist); if ( lResult == ERROR_SUCCESS ) { hr = MakeCacheItemFromControlList( hKeyClass, hKeyDist, pcli, &pci ); if ( FAILED(hr) ) lResult = hr; RegCloseKey( hKeyDist ); } } else { // This is not a distribution unit. Fill in CCachItem information // from the COM branch. hr = MakeCacheItemFromControlList(hKeyClass, NULL, pcli, &pci ); if ( FAILED(hr) ) lResult = hr; } hControlHandle = (HANDLE)pci; EXIT_FINDNEXTCONTROL: if (hKeyClass) RegCloseKey(hKeyClass); if (pcli != NULL) { delete pcli; } return lResult; } void WINAPI FindControlClose(HANDLE hFindHandle) { if (hFindHandle == INVALID_HANDLE_VALUE || hFindHandle == (HANDLE)0) return; delete (OCCFindData*)hFindHandle; } void WINAPI ReleaseControlHandle(HANDLE hControlHandle) { if (hControlHandle == INVALID_HANDLE_VALUE || hControlHandle == (HANDLE)0) return; delete (CCacheItem *)hControlHandle; } HRESULT WINAPI RemoveControlByHandle(HANDLE hControlHandle, BOOL bForceRemove /* = FALSE */) { return RemoveControlByHandle2( hControlHandle, bForceRemove, FALSE ); } HRESULT WINAPI RemoveControlByName(LPCTSTR lpszFile, LPCTSTR lpszCLSID, LPCTSTR lpszTypeLibID, BOOL bForceRemove, /* = FALSE */ DWORD dwIsDistUnit /* = FALSE */) { return RemoveControlByName2( lpszFile, lpszCLSID, lpszTypeLibID, bForceRemove, dwIsDistUnit, FALSE); } LONG WINAPI GetControlDependentFile(int iFile, HANDLE hControlHandle, LPTSTR lpszFile, LPDWORD lpdwSize, BOOL bToUpper /* = FALSE */) { CCacheItem *pci = (CCacheItem *)hControlHandle; if (iFile < 0 || lpszFile == NULL || lpdwSize == NULL) return ERROR_BAD_ARGUMENTS; // loop through the list of files to find the one indicated // by the given index. // this way is dumb but since a control does not depend on // too many files, it's ok CFileNode *pFileNode = pci->GetFirstFile(); for (int i = 0; i < iFile && pFileNode != NULL; i++) pFileNode = pci->GetNextFile(); if (pFileNode == NULL) { lpszFile[0] = TEXT('\0'); lpdwSize = 0; return ERROR_NO_MORE_FILES; } // Make a fully qualified filename if (pFileNode->GetPath() != NULL) { CatPathStrN( lpszFile, pFileNode->GetPath(), pFileNode->GetName(), MAX_PATH); } else { lstrcpy(lpszFile, pFileNode->GetName()); } if (FAILED(GetSizeOfFile(lpszFile, lpdwSize))) *lpdwSize = 0; // to upper case if required if (bToUpper) CharUpper(lpszFile); return ERROR_SUCCESS; } // determine if a control or one of its associated files can be removed // by reading its SharedDlls count BOOL WINAPI IsModuleRemovable(LPCTSTR lpszFile) { TCHAR szFile[MAX_PATH]; TCHAR szT[MAX_PATH]; if (lpszFile == NULL) return FALSE; if ( OCCGetLongPathName(szFile, lpszFile, MAX_PATH) == 0 ) lstrcpyn( szFile, lpszFile, MAX_PATH ); // Don't ever pull something out of the system directory. // This is a "safe" course of action because it is not reasonable // to expect the user to judge whether yanking this file damage other // software installations or the system itself. GetSystemDirectory(szT, MAX_PATH); if (StrStrI(szFile, szT)) return FALSE; // check moduleusage if a control is safe to remove if (LookUpModuleUsage(szFile, NULL, szT, MAX_PATH) != S_OK) return FALSE; // if we don't know who the owner of the module is, it's not // safe to remove if (lstrcmpi(szT, UNKNOWNOWNER) == 0) return FALSE; else { // check shareddlls if a control is safe to remove LONG cRef; HRESULT hr = SetSharedDllsCount( szFile, -1, &cRef ); return cRef == 1; } } BOOL WINAPI GetControlInfo(HANDLE hControlHandle, UINT nFlag, DWORD *pdwData, LPTSTR pszData, int nBufLen) { if (hControlHandle == 0 || hControlHandle == INVALID_HANDLE_VALUE) return FALSE; BOOL bResult = TRUE; LPCTSTR pStr = NULL; DWORD dw = 0; switch (nFlag) { case GCI_NAME: // get friend name of control pStr = ((CCacheItem *)hControlHandle)->m_szName; break; case GCI_FILE: // get filename of control (with full path) pStr = ((CCacheItem *)hControlHandle)->m_szFile; // if there is no file, but there is a package list, fake it // with the path to the first package's ZIP file. if ( *pStr == '\0' ) { CPNode *ppn = ((CCacheItem *)hControlHandle)->GetFirstPackage(); if (ppn) { pStr = ppn->GetPath(); if (!pStr) { return FALSE; // this means hControlHandle is an invalid arg } } } if ( pStr && *pStr == TEXT('\0') ) { CPNode *pfn = ((CCacheItem *)hControlHandle)->GetFirstFile(); if ( pfn != NULL ) pStr = pfn->GetPath(); } break; case GCI_DIST_UNIT_VERSION: pStr = ((CCacheItem *)hControlHandle)->m_szVersion; break; case GCI_CLSID: // get CLSID of control pStr = ((CCacheItem *)hControlHandle)->m_szCLSID; break; case GCI_TYPELIBID: // get TYPELIB id of control pStr = ((CCacheItem *)hControlHandle)->m_szTypeLibID; break; case GCI_TOTALSIZE: // get total size in bytes dw = ((CCacheItem *)hControlHandle)->GetTotalFileSize(); break; case GCI_SIZESAVED: // get total size restored if control is removed dw = ((CCacheItem *)hControlHandle)->GetTotalSizeSaved(); break; case GCI_TOTALFILES: // get total number of files related to control dw = (DWORD)(((CCacheItem *)hControlHandle)->GetTotalFiles()); break; case GCI_CODEBASE: // get CodeBase for control pStr = ((CCacheItem *)hControlHandle)->m_szCodeBase; break; case GCI_ISDISTUNIT: dw = ((CCacheItem *)hControlHandle)->ItemType() == CCacheDistUnit::s_dwType; break; case GCI_STATUS: dw = ((CCacheItem *)hControlHandle)->GetStatus(); break; case GCI_HAS_ACTIVEX: dw = ((CCacheItem *)hControlHandle)->GetHasActiveX(); break; case GCI_HAS_JAVA: dw = ((CCacheItem *)hControlHandle)->GetHasJava(); break; } if (nFlag == GCI_TOTALSIZE || nFlag == GCI_SIZESAVED || nFlag == GCI_TOTALFILES || nFlag == GCI_ISDISTUNIT || nFlag == GCI_STATUS || nFlag == GCI_HAS_ACTIVEX || nFlag == GCI_HAS_JAVA) { bResult = pdwData != NULL; if (bResult) *pdwData = dw; } else { bResult = pszData && pStr; if (bResult) lstrcpyn(pszData, pStr, nBufLen); } return bResult; } /////////////////////////////////////////////////////////////////////////////// // API to be called by Advpack.dll // Define list node to be used in a linked list of control struct tagHANDLENODE; typedef struct tagHANDLENODE HANDLENODE; typedef HANDLENODE* LPHANDLENODE; struct tagHANDLENODE { HANDLE hControl; struct tagHANDLENODE* pNext; }; // Given a handle to a control, get the control's last access time // Result is stored in a FILETIME struct HRESULT GetLastAccessTime(HANDLE hControl, FILETIME *pLastAccess) { Assert(hControl != NULL && hControl != INVALID_HANDLE_VALUE); Assert(pLastAccess != NULL); HRESULT hr = S_OK; WIN32_FIND_DATA fdata; HANDLE h = INVALID_HANDLE_VALUE; LPCTSTR lpszFile = NULL; CCacheItem *pci = (CCacheItem *)hControl; CPNode *ppn; if (pci->m_szFile[0] != 0) lpszFile = pci->m_szFile; else if ( (ppn = pci->GetFirstPackage()) != NULL ) lpszFile = ppn->GetPath(); else if ( (ppn = pci->GetFirstFile()) != NULL ) lpszFile = ppn->GetPath(); if ( lpszFile ) h = FindFirstFile(lpszFile, &fdata); if (h == INVALID_HANDLE_VALUE) { SYSTEMTIME stNow; GetLocalTime(&stNow); SystemTimeToFileTime(&stNow, pLastAccess); hr = HRESULT_FROM_WIN32(GetLastError()); } else { // Convert file time to local file time, then file time to // system time. Set those fields to be ignored to 0, and // set system time back to file time. // FILETIME struct is used because API for time comparison // only works on FILETIME. // SYSTEMTIME sysTime; FindClose(h); FileTimeToLocalFileTime(&(fdata.ftLastAccessTime), pLastAccess); } return hr; } HRESULT WINAPI SweepControlsByLastAccessDate( SYSTEMTIME *pLastAccessTime /* = NULL */, PFNDOBEFOREREMOVAL pfnDoBefore /* = NULL */, PFNDOAFTERREMOVAL pfnDoAfter /* = NULL */, DWORD dwSizeLimit /* = 0 */ ) { LONG lResult = ERROR_SUCCESS; HRESULT hr = S_FALSE; DWORD dwSize = 0, dwTotalSize = 0; HANDLE hFind = NULL, hControl = NULL; LPHANDLENODE pHead = NULL, pCur = NULL; FILETIME timeLastAccess, timeRemovePrior; UINT cCnt = 0; TCHAR szFile[MAX_PATH]; // ignore all fields except wYear, wMonth and wDay if (pLastAccessTime != NULL) { pLastAccessTime->wDayOfWeek = 0; pLastAccessTime->wHour = 0; pLastAccessTime->wMinute = 0; pLastAccessTime->wSecond = 0; pLastAccessTime->wMilliseconds = 0; } // loop through all controls and put in a list the // ones that are accessed before the given date and // are safe to uninstall lResult = FindFirstControl(hFind, hControl); for (;lResult == ERROR_SUCCESS; lResult = FindNextControl(hFind, hControl)) { // check last access time if (pLastAccessTime != NULL) { GetLastAccessTime(hControl, &timeLastAccess); SystemTimeToFileTime(pLastAccessTime, &timeRemovePrior); if (CompareFileTime(&timeLastAccess, &timeRemovePrior) > 0) { ReleaseControlHandle(hControl); continue; } } // check if control is safe to remove GetControlInfo(hControl, GCI_FILE, NULL, szFile, MAX_PATH); if (!IsModuleRemovable(szFile)) { ReleaseControlHandle(hControl); continue; } // put control in a list if (pHead == NULL) { pHead = new HANDLENODE; pCur = pHead; } else { pCur->pNext = new HANDLENODE; pCur = pCur->pNext; } if (pCur == NULL) { hr = E_OUTOFMEMORY; goto EXIT_REMOVECONTROLBYLASTACCESSDATE; } pCur->pNext = NULL; pCur->hControl = hControl; cCnt += 1; // calculate total size GetControlInfo(pCur->hControl, GCI_SIZESAVED, &dwSize, NULL, NULL); dwTotalSize += dwSize; } // quit if total size restored is less than the given amount if (dwTotalSize < dwSizeLimit) goto EXIT_REMOVECONTROLBYLASTACCESSDATE; // traverse the list and remove each control for (pCur = pHead; pCur != NULL; cCnt--) { hr = S_OK; pHead = pHead->pNext; // call callback function before removing a control if (pfnDoBefore == NULL || SUCCEEDED(pfnDoBefore(pCur->hControl, cCnt))) { hr = RemoveControlByHandle(pCur->hControl); // call callback function after removing a control, passing it the // result of the removal if (pfnDoAfter != NULL && FAILED(pfnDoAfter(hr, cCnt - 1))) { pHead = pCur; // set pHead back to head of list goto EXIT_REMOVECONTROLBYLASTACCESSDATE; } } // release memory used by the control handle ReleaseControlHandle(pCur->hControl); delete pCur; pCur = pHead; } EXIT_REMOVECONTROLBYLASTACCESSDATE: FindControlClose(hFind); // release memory taken up by the list for (pCur = pHead; pCur != NULL; pCur = pHead) { pHead = pHead->pNext; ReleaseControlHandle(pCur->hControl); delete pCur; } return hr; } HRESULT WINAPI RemoveExpiredControls(DWORD dwFlags, DWORD dwReserved) { LONG lResult = ERROR_SUCCESS; HRESULT hr = S_FALSE; HANDLE hFind = NULL, hControl = NULL; LPHANDLENODE pHead = NULL, pCur = NULL; FILETIME ftNow, ftMinLastAccess, ftLastAccess; LARGE_INTEGER liMinLastAccess; SYSTEMTIME stNow; UINT cCnt = 0; GetLocalTime( &stNow ); SystemTimeToFileTime(&stNow, &ftNow); // loop through all controls and put in a list the // ones that are accessed before the given date and // are safe to uninstall lResult = FindFirstControl(hFind, hControl); for (;lResult == ERROR_SUCCESS; lResult = FindNextControl(hFind, hControl)) { CCacheItem *pci = (CCacheItem *)hControl; // Controls must have a last access time of at least ftMinLastAccess or they will // expire by default. If they have the Office Auto-expire set, then they may // have to pass a higher bar. liMinLastAccess.LowPart = ftNow.dwLowDateTime; liMinLastAccess.HighPart = ftNow.dwHighDateTime; // We add one to GetExpireDays to deal with bug 17151. The last access time // returned by the file system is truncated down to 12AM, so we need to // expand the expire interval to ensure that this truncation does not cause // the control to expire prematurely. liMinLastAccess.QuadPart -= ((pci->GetExpireDays()+1) * 864000000000L); //24*3600*10^7 ftMinLastAccess.dwLowDateTime = liMinLastAccess.LowPart; ftMinLastAccess.dwHighDateTime = liMinLastAccess.HighPart; GetLastAccessTime(hControl, &ftLastAccess); // ftLastAccess is a local file time if (CompareFileTime(&ftLastAccess, &ftMinLastAccess) >= 0) { ReleaseControlHandle(hControl); continue; } // put control in a list if (pHead == NULL) { pHead = new HANDLENODE; pCur = pHead; } else { pCur->pNext = new HANDLENODE; pCur = pCur->pNext; } if (pCur == NULL) { hr = E_OUTOFMEMORY; goto cleanup; } pCur->pNext = NULL; pCur->hControl = hControl; cCnt += 1; } // traverse the list and remove each control for (pCur = pHead; pCur != NULL; cCnt--) { hr = S_OK; pHead = pHead->pNext; hr = RemoveControlByHandle2(pCur->hControl, FALSE, TRUE); // release memory used by the control handle ReleaseControlHandle(pCur->hControl); delete pCur; pCur = pHead; } cleanup: FindControlClose(hFind); // release memory taken up by the list, if any left for (pCur = pHead; pCur != NULL; pCur = pHead) { pHead = pHead->pNext; ReleaseControlHandle(pCur->hControl); delete pCur; } return hr; }