windows-nt/Source/XPSP1/NT/shell/ext/occache/cleanoc.cpp
2020-09-26 16:20:57 +08:00

1330 lines
42 KiB
C++

#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;
}