|
|
#include "ParseInf.h"
#include "general.h"
#include <shlwapi.h>
#include <wininet.h>
//#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; }
|