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

2400 lines
69 KiB
C++

#include <cdlpch.h>
#pragma hdrstop
#include "verp.h"
#include <mstask.h>
#include <pkgguid.h>
extern LCID g_lcidBrowser; // default to english
extern char g_szBrowserPrimaryLang[];
// global that encapsulates delay-loaded version.dll
CVersion g_versiondll;
extern BOOL g_bRunOnWin95;
HRESULT NeedForceLanguageCheck(HKEY hkeyCLSID, CLocalComponentInfo *plci);
BOOL SniffStringFileInfo( LPSTR szFileName, LPCTSTR lpszSubblock, DWORD *pdw = NULL );
HRESULT IsDistUnitLocallyInstalledSxS(LPCWSTR wszDistUnit, LPCWSTR wszClsid, DWORD dwFileVersionMS, DWORD dwFileVersionLS, CLocalComponentInfo * plci);
void ExtractVersion(char *pszDistUnit, DWORD *pdwVerMS, DWORD *pdwVerLS);
DWORD SearchPathA_Wrap(
LPCSTR lpPath, // search path
LPCSTR lpFileName, // file name
LPCSTR lpExtension, // file extension
DWORD nBufferLength, // size of buffer
LPSTR lpBuffer, // found file name buffer
LPSTR *lpFilePart // file component
)
{
DWORD dwRet = SearchPath(lpPath, lpFileName, lpExtension, nBufferLength, lpBuffer, lpFilePart);
//Trap the possible failures.
if (dwRet && (dwRet < nBufferLength))
{
ASSERT(lpFilePart); //All the current calls to this definitely pass this in.
if (! *lpFilePart)
{
//IE6 bug 19001
// If a dir is passed in in lpFileName terminating with a "/", then this happens ->
// to avoid the subsequent crash in MakeDestDir, make this point to the terminating
// NULL character. This return value would have made better sense from SearchPath, methinx.
*lpFilePart = lpBuffer+dwRet;
}
}
else
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
dwRet = 0; //simulate failure even for insufficient buffer. Unlikely with INTERNET_MAX_PATH,
// since we would have failed much before this in urlmon download code, but be safe
// and not assume success as the code does prior to this.
}
return dwRet;
}
// ---------------------------------------------------------------------------
// %%Function: CLocalComponentInfo::CLocalComponentInfo
// ---------------------------------------------------------------------------
CLocalComponentInfo::CLocalComponentInfo()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CLocalComponentInfo::CLocalComponentInfo",
"this=%#x",
this
));
szExistingFileName[0] = '\0';
pBaseExistingFileName = NULL;
lpDestDir = NULL;
dwLocFVMS = 0;
dwLocFVLS = 0;
ftLastModified.dwLowDateTime = 0;
ftLastModified.dwHighDateTime = 0;
lcid = g_lcidBrowser;
bForceLangGetLatest = FALSE;
pbEtag = NULL;
dwAvailMS = 0;
dwAvailLS = 0;
DEBUG_LEAVE(0);
} // CLocalComponentInfo
// ---------------------------------------------------------------------------
// %%Function: CLocalComponentInfo::~CLocalComponentInfo
// ---------------------------------------------------------------------------
CLocalComponentInfo::~CLocalComponentInfo()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CLocalComponentInfo::~CLocalComponentInfo",
"this=%#x",
this
));
SAFEDELETE(lpDestDir);
SAFEDELETE(pbEtag);
DEBUG_LEAVE(0);
} // ~CLocalComponentInfo
// ---------------------------------------------------------------------------
// %%Function: CLocalComponentInfo::MakeDestDir
// ---------------------------------------------------------------------------
HRESULT
CLocalComponentInfo::MakeDestDir()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CLocalComponentInfo::MakeDestDir",
"this=%#x",
this
));
HRESULT hr = S_OK;
ASSERT(pBaseExistingFileName);
if (szExistingFileName[0]) {
DWORD cbLen = (DWORD) (pBaseExistingFileName - szExistingFileName);
lpDestDir = new char[cbLen + 1];
if (lpDestDir) {
StrNCpy(lpDestDir, szExistingFileName, cbLen);
} else {
hr = E_OUTOFMEMORY;
}
}
DEBUG_LEAVE(hr);
return hr;
} // ~MakeDestDir
// ---------------------------------------------------------------------------
// %%Function: CModuleUsage::CModuleUsage
// CModuleUsage is the basic download obj.
// ---------------------------------------------------------------------------
CModuleUsage::CModuleUsage(LPCSTR szFileName, DWORD muflags, HRESULT *phr)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CModuleUsage::CModuleUsage",
"this=%#x, %.80q, %x, %#x",
this, szFileName, muflags, phr
));
m_szFileName = NULL;
if (szFileName) {
CHAR szCanonical[MAX_PATH];
if ( CDLGetLongPathNameA( szCanonical, szFileName, MAX_PATH ) != 0 )
m_szFileName = new char [lstrlen(szCanonical)+1];
if (m_szFileName) {
lstrcpy(m_szFileName, szCanonical);
}
else
{
// CDLGetLongPathName failed, so we are on a platform that
// doesn't support it, and we can't guess what the right long
// pathname is. Just use the short one
m_szFileName = new char [lstrlen(szFileName)+1];
if (m_szFileName) {
lstrcpy(m_szFileName, szFileName);
}
else {
*phr = E_OUTOFMEMORY;
}
}
}
m_dwFlags = muflags;
DEBUG_LEAVE(0);
} // CModuleUsage
// ---------------------------------------------------------------------------
// %%Function: CModuleUsage::~CModuleUsage
// ---------------------------------------------------------------------------
CModuleUsage::~CModuleUsage()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CModuleUsage::~CModuleUsage",
"this=%#x",
this
));
if (m_szFileName)
SAFEDELETE(m_szFileName);
DEBUG_LEAVE(0);
} // ~CModuleUsage
// ---------------------------------------------------------------------------
// %%Function: CModuleUsage::Update(LPCSTR lpClientName)
// ---------------------------------------------------------------------------
HRESULT
CModuleUsage::Update(LPCSTR lpClientName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CModuleUsage::Update",
"this=%#x, %.80q",
this, lpClientName
));
HRESULT hr = ::UpdateModuleUsage(m_szFileName,
lpClientName, NULL,
m_dwFlags);
DEBUG_LEAVE(hr);
return hr;
} // ~CModuleUsage
HRESULT
GetVersionHint(HKEY hkeyVersion, DWORD *pdwVersionMS, DWORD *pdwVersionLS)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"GetVersionHint",
"%#x, %#x, %#x",
hkeyVersion, pdwVersionMS, pdwVersionLS
));
DWORD Size = MAX_PATH;
char szVersionBuf[MAX_PATH];
DWORD dwType;
HRESULT hr = S_OK;
*pdwVersionMS = 0;
*pdwVersionLS = 0;
DWORD lResult = ::RegQueryValueEx(hkeyVersion, NULL, NULL, &dwType,
(unsigned char *)szVersionBuf, &Size);
if (lResult != ERROR_SUCCESS) {
// value not found, consider this is always with bad/low version
// of InstalledVersion
hr = S_FALSE;
goto Exit;
}
if ( FAILED(GetVersionFromString(szVersionBuf, pdwVersionMS, pdwVersionLS))){
hr = S_FALSE;
}
Exit:
DEBUG_LEAVE(hr);
return hr;
}
/*******************************************************************
NAME: CheckInstalledVersionHint
SYNOPSIS: Checks for key InstalledVersion under {...}
If no key then we fail
once key is present we check version numbers
S_OK: local version is good enough
S_FALSE: need update:
ERROR: not applicable, caller proceeds with InProcServer32
check.
[HKCR:clsid]
[{...}]
[InstalledVersion]
Deafult "1,0,0,1"
Path "c:\foo\foo.ocx"
The Path is optional, if find we will req update id
file pointed to by path is missing on client. This is a
way to robustify if user deletes the file on disk but
not the regsitry (unclean uninstall)
In this case update is needed
********************************************************************/
HRESULT
CheckInstalledVersionHint(
HKEY hKeyEmbedding,
CLocalComponentInfo *plci,
DWORD dwFileVersionMS,
DWORD dwFileVersionLS)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CheckInstalledVersionHint",
"%#x, %#x, %x, %x",
hKeyEmbedding, plci, dwFileVersionMS, dwFileVersionLS
));
HRESULT hr = S_OK;
DWORD Size = MAX_PATH;
DWORD dwType =0;
LONG lResult = ERROR_SUCCESS;
const static char * szInstalledVersion = "InstalledVersion";
const static char * szAvailableVersion = "AvailableVersion";
const static char * szPATH = "Path";
const static char * szLASTMODIFIED = "LastModified";
const static char * szETAG = "Etag";
char szVersionBuf[MAX_PATH];
char szFileName[MAX_PATH];
HKEY hKeyVersion = 0;
HKEY hkeyAvailableVersion = 0;
DWORD dwLocFVMS = 0;
DWORD dwLocFVLS = 0;
char szLastMod[INTERNET_RFC1123_BUFSIZE+1];
if (RegOpenKeyEx(hKeyEmbedding, szAvailableVersion, 0,
KEY_READ, &hkeyAvailableVersion) == ERROR_SUCCESS) {
DWORD dwAvailMS = 0;
DWORD dwAvailLS = 0;
if ( GetVersionHint(hkeyAvailableVersion, &dwAvailMS, &dwAvailLS) == S_OK){
plci->dwAvailMS = dwAvailMS;
plci->dwAvailLS = dwAvailLS;
}
}
lResult = ::RegOpenKeyEx(hKeyEmbedding, szInstalledVersion, 0,
KEY_READ, &hKeyVersion);
if (lResult != ERROR_SUCCESS) {
// key not found, consider this is regular ActiveX with no hint
// of InstalledVersion
hr = HRESULT_FROM_WIN32(lResult);
goto Exit;
}
if ( GetVersionHint(hKeyVersion, &dwLocFVMS, &dwLocFVLS) == S_FALSE){
hr = S_FALSE;
goto Exit;
}
plci->dwLocFVMS = dwLocFVMS;
plci->dwLocFVLS = dwLocFVLS;
Size = INTERNET_RFC1123_BUFSIZE + 1;
lResult = ::RegQueryValueEx(hKeyVersion, szLASTMODIFIED, NULL, &dwType,
(unsigned char *)szLastMod, &Size);
if (lResult == ERROR_SUCCESS) {
SYSTEMTIME st;
FILETIME ft;
// convert intenet time to file time
if (InternetTimeToSystemTimeA(szLastMod, &st, 0) &&
SystemTimeToFileTime(&st, &ft) ) {
memcpy(&(plci->ftLastModified), &ft, sizeof(FILETIME));
}
}
Size = 0;
lResult = ::RegQueryValueEx(hKeyVersion, szETAG, NULL, &dwType,
(unsigned char *)NULL, &Size);
if (lResult == ERROR_SUCCESS) {
char *pbEtag = new char [Size];
if (pbEtag) {
lResult = ::RegQueryValueEx(hKeyVersion, szETAG, NULL, &dwType,
(unsigned char *)pbEtag, &Size);
if (lResult == ERROR_SUCCESS)
plci->pbEtag = pbEtag;
else
delete pbEtag;
}
}
// check file versions
if ((dwFileVersionMS > dwLocFVMS) ||
((dwFileVersionMS == dwLocFVMS) &&
(dwFileVersionLS > dwLocFVLS)))
hr = S_FALSE;
if (hr == S_OK) {
// if we seem to have the right version
// check if the file physically exists on disk
// the software can specify this by having a PATH="c:\foo\foo.class"
// if present we will check for physical file existance
dwType = 0;
Size = MAX_PATH;
lResult = ::SHQueryValueEx(hKeyVersion, szPATH, NULL, &dwType,
(unsigned char *)szFileName, &Size);
if (lResult != ERROR_SUCCESS)
goto Exit;
// value present, check if file is present
if (GetFileAttributes(szFileName) == -1) {
// if file is not physically present then clear out our
// local file version. this comes in the way of doing
// get latest. (ie get latest will assume that if a local file
// is present then do If-Modified-Since
plci->dwLocFVMS = 0;
plci->dwLocFVLS = 0;
hr = S_FALSE;
}
}
Exit:
if (hKeyVersion)
::RegCloseKey(hKeyVersion);
if (hkeyAvailableVersion)
::RegCloseKey(hkeyAvailableVersion);
DEBUG_LEAVE(hr);
return hr;
}
HRESULT
CreateJavaPackageManager(IJavaPackageManager **ppPackageManager)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CreateJavaPackageManager",
"%#x",
ppPackageManager
));
HRESULT hr = S_OK;
Assert(ppPackageManager);
if (!(*ppPackageManager)) {
ICreateJavaPackageMgr *picjpm;
hr=CoCreateInstance(CLSID_JavaPackageManager,NULL,(CLSCTX_INPROC_SERVER),
IID_ICreateJavaPackageMgr,(LPVOID *) &picjpm);
if (SUCCEEDED(hr)) {
hr = picjpm->GetPackageManager(ppPackageManager);
picjpm->Release();
}
}
DEBUG_LEAVE(hr);
return hr;
}
HRESULT
IsPackageLocallyInstalled(IJavaPackageManager **ppPackageManager, LPCWSTR szPackageName,
LPCWSTR szNameSpace, DWORD dwVersionMS, DWORD dwVersionLS)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"IsPackageLocallyInstalled",
"%#x, %.80wq, %.80wq, %x, %x",
ppPackageManager, szPackageName, szNameSpace, dwVersionMS, dwVersionLS
));
HRESULT hr = S_OK; // assume Ok!
IJavaPackage *pJavaPkg = NULL;
DWORD dwLocMS = 0;
DWORD dwLocLS = 0;
Assert(ppPackageManager);
if (!(*ppPackageManager)) {
hr = CreateJavaPackageManager(ppPackageManager);
if (FAILED(hr))
goto Exit;
}
if (SUCCEEDED((*ppPackageManager)->GetPackage(szPackageName, szNameSpace, &pJavaPkg))) {
Assert(pJavaPkg);
pJavaPkg->GetVersion(&dwLocMS, &dwLocLS);
if ((dwVersionMS > dwLocMS) ||
((dwVersionMS == dwLocMS) &&
(dwVersionLS > dwLocLS)))
hr = S_FALSE;
BSTR bstrFileName = NULL;
if (SUCCEEDED(pJavaPkg->GetFilePath(&bstrFileName))) {
// check if file really exists
LPSTR szFileName = NULL;
if (SUCCEEDED(Unicode2Ansi(bstrFileName, &szFileName))) {
if (GetFileAttributes(szFileName) == -1)
hr = S_FALSE;
} else {
hr = S_FALSE;
}
} else {
hr = S_FALSE;
}
SAFESYSFREESTRING(bstrFileName);
SAFERELEASE(pJavaPkg);
} else {
hr = S_FALSE;
}
Exit:
DEBUG_LEAVE(hr);
return hr;
}
HRESULT
AreNameSpacePackagesIntact(HKEY hkeyJava, LPCWSTR lpwszNameSpace, IJavaPackageManager **ppPackageManager)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"AreNameSpacePackagesIntact",
"%#x, %.80wq, %#x",
hkeyJava, lpwszNameSpace, ppPackageManager
));
int iValue = 0;
DWORD dwType = REG_SZ;
DWORD dwValueSize = MAX_PATH;
char szPkgName[MAX_PATH];
HRESULT hr = S_OK;
while (
RegEnumValue(hkeyJava, iValue++, szPkgName, &dwValueSize, 0,
&dwType, NULL, NULL) == ERROR_SUCCESS) {
LPWSTR lpwszPkgName = NULL;
dwValueSize = MAX_PATH; // reset
if ( (Ansi2Unicode(szPkgName,&lpwszPkgName) == S_OK)
&& ((IsPackageLocallyInstalled
(ppPackageManager, lpwszPkgName, lpwszNameSpace, 0,0) != S_OK)) ) {
hr = S_FALSE;
SAFEDELETE(lpwszPkgName);
break;
}
SAFEDELETE(lpwszPkgName);
}
DEBUG_LEAVE(hr);
return hr;
}
HRESULT
ArePackagesIntact(HKEY hkeyContains)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"ArePackagesIntact",
"%#x",
hkeyContains
));
HRESULT hr = S_OK;
HKEY hkeyJava = 0;
const static char * szJava = "Java";
IJavaPackageManager *pPackageManager = NULL;
DWORD iSubKey = 0;
DWORD dwSize = MAX_PATH;
DWORD lResult;
char szNameSpace[MAX_PATH];
// first validate pkgs in the global namespace
if (RegOpenKeyEx( hkeyContains, szJava,
0, KEY_READ, &hkeyJava) == ERROR_SUCCESS) {
hr = AreNameSpacePackagesIntact(hkeyJava, NULL, &pPackageManager);
if (hr != S_OK)
goto Exit;
} else {
goto Exit;
}
// validate pkgs in each of other namespaces, if any
while ( (lResult = RegEnumKeyEx(hkeyJava, iSubKey++, szNameSpace, &dwSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS) {
dwSize = MAX_PATH;
HKEY hkeyNameSpace = 0;
if (RegOpenKeyEx( hkeyJava, szNameSpace,
0, KEY_READ, &hkeyNameSpace) == ERROR_SUCCESS) {
LPWSTR lpwszNameSpace = NULL;
if (Ansi2Unicode(szNameSpace,&lpwszNameSpace) == S_OK)
hr = AreNameSpacePackagesIntact(hkeyNameSpace, lpwszNameSpace,
&pPackageManager);
SAFEDELETE(lpwszNameSpace);
RegCloseKey(hkeyNameSpace);
if (hr != S_OK)
goto Exit;
}
}
if (lResult != ERROR_NO_MORE_ITEMS) {
hr = HRESULT_FROM_WIN32(lResult);
//FALLTHRU goto Exit;
}
Exit:
SAFERELEASE(pPackageManager);
if (hkeyJava)
RegCloseKey(hkeyJava);
DEBUG_LEAVE(hr);
return hr;
}
/*******************************************************************
NAME: IsDistUnitLocallyInstalled
SYNOPSIS: S_OK - distribution is installed correctly
S_FALSE - distribution unit entry exists, but not installed
or installed incorrectly
E_FAIL - distribution unit doesn't exit (no entry for it)
********************************************************************/
HRESULT
IsDistUnitLocallyInstalled(
LPCWSTR szDistUnit,
DWORD dwFileVersionMS,
DWORD dwFileVersionLS,
CLocalComponentInfo *plci,
LPSTR szDestDirHint,
LPBOOL pbParanoidCheck,
DWORD flags)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"IsDistUnitLocallyInstalled",
"%.80wq, %x, %x, %#x, %.80q, %#x, %x",
szDistUnit, dwFileVersionMS, dwFileVersionLS, plci, szDestDirHint, pbParanoidCheck, flags
));
LPSTR pszDist = NULL;
HRESULT hr = S_FALSE;
HKEY hkeyDist = 0, hkeyThisDist = 0;
HKEY hkeyContains = 0;
HKEY hkeyFiles = 0;
HKEY hkeyDepends = 0;
const static char * szContains = "Contains";
const static char * szFiles = "Files";
const static char * szDistUnitStr = "Distribution Units";
LONG lResult = ERROR_SUCCESS;
char szFileName[MAX_PATH];
ULONG cbSize = MAX_PATH;
DWORD dwType = REG_SZ;
if (pbParanoidCheck) {
*pbParanoidCheck = FALSE;
}
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS,
0, KEY_READ, &hkeyDist);
if (lResult != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(REGDB_E_KEYMISSING);
goto Exit;
}
if (FAILED((hr=::Unicode2Ansi(szDistUnit, &pszDist))))
{
goto Exit;
}
hr = S_FALSE; // reset to NOT found
// Open the key for this embedding:
lResult = ::RegOpenKeyEx(hkeyDist, pszDist, 0, KEY_READ,
&hkeyThisDist);
if (lResult == ERROR_SUCCESS) {
hr = CheckInstalledVersionHint( hkeyThisDist, plci,
dwFileVersionMS, dwFileVersionLS);
}
else
{
hr = HRESULT_FROM_WIN32(REGDB_E_KEYMISSING);
goto Exit;
}
if (hr == S_OK || (SUCCEEDED(hr) && dwFileVersionMS == -1 && dwFileVersionLS == -1)) {
if (RegOpenKeyEx( hkeyThisDist, szContains,
0, KEY_READ, &hkeyContains) == ERROR_SUCCESS) {
if (pbParanoidCheck) {
*pbParanoidCheck = TRUE;
}
// BUGBUG: only do if paranoid flag on?
// assert dependency state installed correctly on machine
// this is where we would have to walk the dependecy tree
// as well as make sure the contained packages and files
// are indeed availabel on the client machine
// BUGBUG: maybe we should only do this in paranoid mode
// instead of all the time.
if (RegOpenKeyEx( hkeyContains, szDistUnitStr,
0, KEY_READ, &hkeyDepends) == ERROR_SUCCESS ) {
int iSubKey = 0;
while (RegEnumValue(hkeyDepends, iSubKey++,
szFileName, &cbSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
CLocalComponentInfo lci;
LPWSTR wszFileName = 0;
CLSID clsidTemp;
if (FAILED(Ansi2Unicode(szFileName, &wszFileName)))
break;
clsidTemp = CLSID_NULL;
if (wszFileName)
CLSIDFromString(wszFileName, &clsidTemp);
// if above call fails DistUnit is not clsid
if (IsControlLocallyInstalled(NULL, &clsidTemp, wszFileName, 0,0, &lci, NULL) != S_OK) {
SAFEDELETE(wszFileName);
plci->dwLocFVMS = 0;
plci->dwLocFVLS = 0;
hr = S_FALSE;
goto Exit;
}
SAFEDELETE(wszFileName);
cbSize = MAX_PATH;
}
}
if (RegOpenKeyEx( hkeyContains, szFiles,
0, KEY_READ, &hkeyFiles) == ERROR_SUCCESS) {
int iValue = 0;
DWORD dwType = REG_SZ;
DWORD dwValueSize = MAX_PATH;
while (RegEnumValue(hkeyFiles, iValue++,
szFileName, &dwValueSize, 0, &dwType, NULL, NULL) == ERROR_SUCCESS) {
dwValueSize = MAX_PATH; // reset
if (GetFileAttributes(szFileName) == -1) {
// if file is not physically present then clear out our
// local file version. this comes in the way of doing
// get latest. (get latest will assume that if a local
// file is present then do If-Modified-Since
plci->dwLocFVMS = 0;
plci->dwLocFVLS = 0;
hr = S_FALSE;
goto Exit;
}
}
}
if (ArePackagesIntact(hkeyContains) == S_FALSE) {
plci->dwLocFVMS = 0;
plci->dwLocFVLS = 0;
hr = S_FALSE;
goto Exit;
}
}
} else {
hr = S_FALSE; // mark not present, don't error out
}
Exit:
if (pszDist)
delete pszDist;
if (hkeyDepends)
::RegCloseKey(hkeyDepends);
if (hkeyFiles)
::RegCloseKey(hkeyFiles);
if (hkeyContains)
::RegCloseKey(hkeyContains);
if (hkeyThisDist)
::RegCloseKey(hkeyThisDist);
if (hkeyDist)
::RegCloseKey(hkeyDist);
DEBUG_LEAVE(hr);
return hr;
}
HRESULT
IsFileLocallyInstalled(
LPSTR lpCurCode,
DWORD dwFileVersionMS,
DWORD dwFileVersionLS,
CLocalComponentInfo *plci,
LPSTR szDestDirHint,
BOOL bExactVersion
)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"IsFileLocallyInstalled",
"%.80q, %x, %x, %#x, %.80q, %B",
lpCurCode, dwFileVersionMS, dwFileVersionLS, plci, szDestDirHint, bExactVersion
));
HRESULT hr = S_FALSE;
// no clsid, but we have a file name
// first check for a file of the same name in DestDirHint.
// This is the directory that the main OCX file existed on
// and so should be checked first.
// In case this is a new install this will point to the
// suggested destination dir for the DLL.
if (szDestDirHint) {
if (SearchPathA_Wrap( szDestDirHint,
lpCurCode, NULL, MAX_PATH,
plci->szExistingFileName, &(plci->pBaseExistingFileName))) {
// check fileversion to see if update reqd.
hr = LocalVersionOK(0,plci,dwFileVersionMS, dwFileVersionLS, bExactVersion);
goto Exit;
}
}
// file not found in suggested destination. Look in system searchpath
// SearchPath for this filename
if (!(SearchPathA_Wrap( NULL, lpCurCode, NULL, MAX_PATH,
plci->szExistingFileName, &(plci->pBaseExistingFileName)))) {
hr = S_FALSE;
goto Exit;
}
// check fileversion to see if update reqd.
hr = LocalVersionOK(0,plci,dwFileVersionMS, dwFileVersionLS, bExactVersion);
Exit:
DEBUG_LEAVE(hr);
return hr;
}
BOOL GetEXEName(LPSTR szCmdLine)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"GetEXEName",
"%.80q",
szCmdLine
));
Assert(szCmdLine);
LPSTR pchStartBaseName = szCmdLine;
BOOL bFullyQualified = FALSE;
BOOL bHasSpaces = FALSE;
char *pch;
if (*szCmdLine == '"') {
szCmdLine++;
char *pszEnd = StrStrA(szCmdLine, "\"");
ASSERT(pszEnd);
*pszEnd = '\0';
if (GetFileAttributes(szCmdLine) != -1) {
//found the EXE name, but got to get rid of the first quote
szCmdLine--; // step back to the "
while (*szCmdLine) {
*szCmdLine = *(szCmdLine + 1);
szCmdLine++;
}
DEBUG_LEAVE(TRUE);
return TRUE;
}
szCmdLine--; // step back to the "
while (*szCmdLine) {
*szCmdLine = *(szCmdLine + 1);
szCmdLine++;
}
DEBUG_LEAVE(FALSE);
return FALSE;
}
// skip past the directory if fully qualified.
for (pch = szCmdLine; *pch; pch++) {
if (*pch == '\\')
pchStartBaseName = pch;
if ( (*pch == ' ') || (*pch == '\t') )
bHasSpaces = TRUE;
}
if (!bHasSpaces) {
if (GetFileAttributes(szCmdLine) != -1) {
//found the EXE name, it is already in szCmdLine
DEBUG_LEAVE(TRUE);
return TRUE;
}
DEBUG_LEAVE(FALSE);
return FALSE;
}
// pchStartBaseName now points at the last '\\' if any
if (*pchStartBaseName == '\\') {
pchStartBaseName++;
bFullyQualified = TRUE;
}
// pchStartBaseName no points at the base name of the EXE.
// Now look for spaces. When we find a space we will
// replace with a '\0' and check if the cmd line is valid.
// if valid, this must be the EXE name, return
// if not valid, march on to do the same. when we finish
// examining all of the cmd line, or no more spaces, likely the
// EXE is missing.
for (pch = pchStartBaseName; *pch != '\0'; pch++) {
if ( (*pch == ' ') || (*pch == '\t') ) {
char chTemp = *pch; // sacve the white spc char
*pch = '\0'; // stomp the spc with nul. now we have in szCmdLine
// what could be the full EXE name
if (bFullyQualified) {
if (GetFileAttributes(szCmdLine) != -1) {
//found the EXE name, it is already in szCmdLine
DEBUG_LEAVE(TRUE);
return TRUE;
}
} else {
char szBuf[MAX_PATH];
LPSTR pBaseFileName;
if (SearchPathA_Wrap( NULL, szCmdLine, NULL, MAX_PATH,
szBuf, &pBaseFileName)) {
//found the EXE name, it is already in szCmdLine
DEBUG_LEAVE(TRUE);
return TRUE;
}
}
*pch = chTemp; // restore the while spc and move past.
}
}
DEBUG_LEAVE(FALSE);
return FALSE;
}
BOOL
AdviseForceDownload(const LPCLSID lpclsid, DWORD dwClsContext)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"AdviseForceDownload",
"%#x, %x",
lpclsid, dwClsContext
));
HRESULT hr = S_OK;
BOOL bNullClsid = lpclsid?IsEqualGUID(*lpclsid , CLSID_NULL):TRUE;
HKEY hKeyClsid = 0;
HKEY hKeyEmbedding = 0;
HKEY hkeyDist = 0;
BOOL bForceDownload = TRUE;
LPOLESTR pwcsClsid = NULL;
DWORD dwType;
LONG lResult = ERROR_SUCCESS;
static char * szAppID = "AppID";
LPSTR pszClsid = NULL;
CLocalComponentInfo lci;
static char * szInprocServer32 = "InProcServer32";
static char * szLocalServer32 = "LocalServer32";
if (bNullClsid)
goto Exit;
// return if we can't get a valid string representation of the CLSID
if (FAILED((hr=StringFromCLSID(*lpclsid, &pwcsClsid))))
goto Exit;
Assert(pwcsClsid != NULL);
if (FAILED((hr=::Unicode2Ansi(pwcsClsid, &pszClsid))))
{
goto Exit;
}
// Open root HKEY_CLASSES_ROOT\CLSID key
lResult = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_READ, &hKeyClsid);
if (lResult == ERROR_SUCCESS)
{
// Open the key for this embedding:
lResult = ::RegOpenKeyEx(hKeyClsid, pszClsid, 0, KEY_READ,
&hKeyEmbedding);
if (lResult == ERROR_SUCCESS) {
// check for hint of FileVersion before actually getting FileVersion
// This way non-PE files, like Java, random data etc. can be
// accomodated with CODEBASE= with proper version checking.
// If they are not really COM object then they can't be
// instantiated by COM or us (if object needed). But if they
// use AsyncGetClassBits(no object instantiated) or from the
// browser give height=0 width=0, the browser will not
// report placeholders with errors, so they can work gracefully
// overloading the object tag to do version checking and
// random stuff
hr = CheckInstalledVersionHint( hKeyEmbedding, &lci,
0, 0);
// if the key is found and
// if the latest version is not available then
// return now with false, if right version is already
// present return. If the key is missing then we
// proceed with checks for InprocServer32/LocalServer32
if (SUCCEEDED(hr)) {
// if using installed version hint and the verdict is
// local good enough
if (hr == S_OK)
bForceDownload = FALSE;
goto Exit;
}
hr = S_OK; // reset
// ckeck if DCOM
HKEY hKeyAppID;
lResult = ::RegOpenKeyEx(hKeyEmbedding, szAppID, 0,
KEY_READ, &hKeyAppID);
if (lResult == ERROR_SUCCESS) {
// DCOM
// just assume that this is the latest version already
// we never attempt code download for dcom
bForceDownload = FALSE; // no force download for DCOM
RegCloseKey(hKeyAppID);
goto Exit;
}
HKEY hKeyInProc;
lResult = ::RegOpenKeyEx(hKeyEmbedding, szInprocServer32, 0,
KEY_READ, &hKeyInProc);
if (lResult != ERROR_SUCCESS) {
if (RegOpenKeyEx(hKeyEmbedding, szLocalServer32, 0,
KEY_READ, &hKeyInProc) == ERROR_SUCCESS) {
// specific look for vb doc obj hack where they just use
// the OBJECT tag to do code download, but not hosting
// no inproc but we have localserver
// we could have failed because we pass the wrong clsctx
RegCloseKey(hKeyInProc);
if (!(dwClsContext & CLSCTX_LOCAL_SERVER))
bForceDownload = FALSE;
}
} else {
RegCloseKey(hKeyInProc);
}
}
}
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_DIST_UNITS,
0, KEY_READ, &hkeyDist);
if (lResult == ERROR_SUCCESS)
{
// Open the key for this embedding:
HKEY hkeyThisDist = 0;
if (RegOpenKeyEx(hkeyDist, pszClsid, 0, KEY_READ,
&hkeyThisDist) == ERROR_SUCCESS) {
HKEY hkeyJava = 0;
if (RegOpenKeyEx(hkeyThisDist, "Contains\\Java", 0, KEY_READ,
&hkeyJava) == ERROR_SUCCESS) {
bForceDownload = FALSE;
RegCloseKey(hkeyJava);
}
RegCloseKey(hkeyThisDist);
}
RegCloseKey(hkeyDist);
}
Exit:
if (pwcsClsid)
delete pwcsClsid;
if (pszClsid)
delete pszClsid;
if (hKeyClsid)
::RegCloseKey(hKeyClsid);
if (hKeyEmbedding)
::RegCloseKey(hKeyEmbedding);
DEBUG_LEAVE(bForceDownload);
return bForceDownload;
}
/*******************************************************************
NAME: IsControlLocallyInstalled
SYNOPSIS: Indicates whether the provided CLSID represents an
OLE control.
If no clsid provided then checks to see if lpCurCode
exists in system and checks file version to verify if
update is needed
********************************************************************/
HRESULT
IsControlLocallyInstalled(LPSTR lpCurCode, const LPCLSID lpclsid,
LPCWSTR szDistUnit,
DWORD dwFileVersionMS, DWORD dwFileVersionLS,
CLocalComponentInfo *plci,
LPSTR szDestDirHint,
BOOL bExactVersion)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"IsControlLocallyInstalled",
"%.80q, %#x, %.80wq, %x, %x, %#x, %.80q, %B",
lpCurCode, lpclsid, szDistUnit, dwFileVersionMS, dwFileVersionLS, plci, szDestDirHint, bExactVersion
));
HRESULT hr1, hr2, hrResult, hr = S_FALSE;
BOOL bNullClsid = lpclsid?IsEqualGUID(*lpclsid , CLSID_NULL):TRUE;
BOOL bParanoidCheck = FALSE;
if ( bNullClsid && (lpCurCode == NULL) && (szDistUnit == NULL) ) {
hr = E_INVALIDARG;
goto Exit;
}
// hr1: HRESULT for whether the dist unit is installed
// hr2: HRESULT for whether the particular clsid is installed
// hrResult: HRESULT for whether the particular clsid is present (but not necessarily installed correctly)
if (szDistUnit) {
hr1 = IsDistUnitLocallyInstalled( szDistUnit, dwFileVersionMS, dwFileVersionLS, plci, szDestDirHint, &bParanoidCheck, 0);
} else {
if (bNullClsid)
{
hr1 = IsFileLocallyInstalled( lpCurCode, dwFileVersionMS, dwFileVersionLS, plci, szDestDirHint, bExactVersion);
// if no dist unit name or clsid and this is
// clearly just checking for dependent file then
// no need to fall thru and check the com br as well
hr = hr1;
goto Exit;
}
else
{
hr1 = E_FAIL;
}
}
hr2 = IsCLSIDLocallyInstalled( lpCurCode, lpclsid, szDistUnit, dwFileVersionMS, dwFileVersionLS, plci, szDestDirHint, &hrResult , bExactVersion);
if (hr2 != S_OK)
{
// if HKLM\CLSID\{CLSID} existed, but control wasn't there, we fail with that error
// otherwise we fail with hr1.
if (SUCCEEDED(hrResult))
{
hr = hr2;
}
else
{
// if DU check returned S_FALSE or S_OK we return that, otherwise return from CLSID check.
if (SUCCEEDED(hr1))
{
hr = hr1;
}
else
{
hr = hr2;
}
}
}
else
{
if (hr1 == S_FALSE)
{
// COM branch says we are OK, but Distribution unit says we are lacking.
// if we did paranoid checking and then failed the DU then
// really fail. But if we just looked at the InstalledVersion
// in the registry and concluded that our DU is no good then we
// should go by the COM br and succeed the call as the user
// could have obtained a newer version thru a mechanism other than
// code download and so we have no business trying to update this
// BUGBUG: do we at this point try to correct our registry
// record of the version? need to ship tomorrow!
if (bParanoidCheck)
hr = hr1;
else
hr = hr2;
}
else
{
hr = hr2;
}
}
Exit:
DEBUG_LEAVE(hr);
return hr;
}
/*******************************************************************
NAME: IsCLSIDLocallyInstalled
SYNOPSIS: Indicates whether the provided CLSID represents an
OLE control.
If no clsid provided then checks to see if lpCurCode
exists in system and checks file version to verify if
update is needed
********************************************************************/
HRESULT
IsCLSIDLocallyInstalled(LPSTR lpCurCode, const LPCLSID lpclsid,
LPCWSTR szDistUnit,
DWORD dwFileVersionMS, DWORD dwFileVersionLS,
CLocalComponentInfo *plci,
LPSTR szDestDirHint,
HRESULT *pHrExtra,
BOOL bExactVersion
)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"IsCLSIDLocallyInstalled",
"%.80q, %#x, %.80wq, %x, %x, %#x, %.80q, %#x, %B",
lpCurCode, lpclsid, szDistUnit, dwFileVersionMS, dwFileVersionLS, plci, szDestDirHint, pHrExtra, bExactVersion
));
LPSTR pszClsid = NULL;
LPOLESTR pwcsClsid = NULL;
HRESULT hr = S_FALSE;
DWORD dwType;
LONG lResult = ERROR_SUCCESS;
static char * szInprocServer32 = "InProcServer32";
static char * szLocalServer32 = "LocalServer32";
static char * szAppID = "AppID";
HKEY hKeyClsid = 0;
DWORD Size = MAX_PATH;
if (pHrExtra)
*pHrExtra = E_FAIL;
// return if we can't get a valid string representation of the CLSID
if (FAILED((hr=StringFromCLSID(*lpclsid, &pwcsClsid))))
goto Exit;
Assert(pwcsClsid != NULL);
// Open root HKEY_CLASSES_ROOT\CLSID key
lResult = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_READ, &hKeyClsid);
if (lResult == ERROR_SUCCESS)
{
if (FAILED((hr=::Unicode2Ansi(pwcsClsid, &pszClsid))))
{
goto Exit;
}
// Open the key for this embedding:
HKEY hKeyEmbedding;
HKEY hKeyInProc;
lResult = ::RegOpenKeyEx(hKeyClsid, pszClsid, 0, KEY_READ,
&hKeyEmbedding);
if (lResult == ERROR_SUCCESS) {
// check for hint of FileVersion before actually getting FileVersion
// This way non-PE files, like Java, random data etc. can be
// accomodated with CODEBASE= with proper version checking.
// If they are not really COM object then they can't be
// instantiated by COM or us (if object needed). But if they
// use AsyncGetClassBits(no object instantiated) or from the
// browser give height=0 width=0, the browser will not
// report placeholders with errors, so they can work gracefully
// overloading the object tag to do version checking and
// random stuff
if (pHrExtra)
{
// indicate that CLSID reg key exists, so any failures after this
// imply the control is not registered correctly
*pHrExtra = S_OK;
}
hr = CheckInstalledVersionHint( hKeyEmbedding, plci,
dwFileVersionMS, dwFileVersionLS);
// if the key is found and
// if the latest version is not available then
// return now with false, if right version is already
// present return. If the key is missing then we
// proceed with checks for InprocServer32/LocalServer32
if (SUCCEEDED(hr))
goto finish_all;
hr = S_OK; // reset
// ckeck if DCOM
HKEY hKeyAppID;
lResult = ::RegOpenKeyEx(hKeyEmbedding, szAppID, 0,
KEY_READ, &hKeyAppID);
if (lResult == ERROR_SUCCESS) {
// DCOM
// just assume that this is the latest version already
// we never attempt code download for dcom
::RegCloseKey(hKeyAppID);
goto finish_all;
}
lResult = ::RegOpenKeyEx(hKeyEmbedding, szInprocServer32, 0,
KEY_READ, &hKeyInProc);
if (lResult == ERROR_SUCCESS) {
Size = MAX_PATH;
lResult = ::SHQueryValueEx(hKeyInProc, NULL, NULL, &dwType,
(unsigned char *)plci->szExistingFileName, &Size);
if (lResult == ERROR_SUCCESS) {
if (!(SearchPathA_Wrap( NULL,
plci->szExistingFileName, NULL, MAX_PATH,
plci->szExistingFileName, &(plci->pBaseExistingFileName)))) {
hr = S_FALSE;
goto finish_verchecks;
}
// check fileversion to see if update reqd.
hr = LocalVersionOK(hKeyEmbedding, plci,
dwFileVersionMS, dwFileVersionLS, bExactVersion);
if (plci->bForceLangGetLatest) {
hr = NeedForceLanguageCheck(hKeyEmbedding, plci);
}
goto finish_verchecks;
} else {
hr = S_FALSE; // problem: can't locate file
goto finish_verchecks;
}
} else {
lResult = ::RegOpenKeyEx(hKeyEmbedding, szLocalServer32, 0,
KEY_READ, &hKeyInProc);
if (lResult != ERROR_SUCCESS) {
hr = S_FALSE; // problem :have a clsid but, can't locate it
goto finish_all;
}
Size = MAX_PATH;
lResult = ::SHQueryValueEx(hKeyInProc, NULL, NULL, &dwType,
(unsigned char *)plci->szExistingFileName, &Size);
if (lResult == ERROR_SUCCESS) {
// strip out args if any for this localserver32
// and extract only the EXE name
GetEXEName(plci->szExistingFileName);
if (!(SearchPathA_Wrap( NULL,
plci->szExistingFileName, NULL, MAX_PATH,
plci->szExistingFileName, &(plci->pBaseExistingFileName)))) {
hr = S_FALSE;
goto finish_verchecks;
}
// check fileversion to see if update reqd.
hr = LocalVersionOK(hKeyEmbedding, plci, dwFileVersionMS, dwFileVersionLS, bExactVersion);
if (plci->bForceLangGetLatest)
hr = NeedForceLanguageCheck(hKeyEmbedding, plci);
goto finish_verchecks;
} else {
hr = S_FALSE; // problem: can't locate file
goto finish_verchecks;
}
}
finish_verchecks:
::RegCloseKey(hKeyInProc);
finish_all:
::RegCloseKey(hKeyEmbedding);
} else {
// here if we could not find the embedding in HKCR\CLSID
hr = S_FALSE;
}
} else
hr = S_FALSE;
Exit:
// release the string allocated by StringFromCLSID
if (pwcsClsid)
delete pwcsClsid;
if (pszClsid)
delete pszClsid;
if (hKeyClsid)
::RegCloseKey(hKeyClsid);
DEBUG_LEAVE(hr);
return hr;
}
BOOL SupportsSelfRegister(LPSTR szFileName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"SupportsSelfRegister",
"%.80q",
szFileName
));
HRESULT hr = SniffStringFileInfo( szFileName, TEXT("OLESelfRegister") );
DEBUG_LEAVE(hr);
return hr;
}
BOOL WantsAutoExpire(LPSTR szFileName, DWORD *pnExpireDays)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"WantsAutoExpire",
"%.80q, %#x",
szFileName, pnExpireDays
));
HRESULT hr = SniffStringFileInfo( szFileName, TEXT("Expire"), pnExpireDays );
DEBUG_LEAVE(hr);
return hr;
}
HRESULT GetFileVersion(CLocalComponentInfo *plci, LPDWORD pdwFileVersionMS, LPDWORD pdwFileVersionLS)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"GetFileVersion",
"%#x, %#x, %#x",
plci, pdwFileVersionMS, pdwFileVersionLS
));
DWORD handle;
UINT uiInfoSize;
UINT uiVerSize ;
UINT uiSize ;
BYTE* pbData = NULL ;
VS_FIXEDFILEINFO *lpVSInfo;;
HRESULT hr = S_OK;
LPVOID lpVerBuffer = NULL;
#ifdef UNIX
// We don't have version.dll
DebugBreak();
DEBUG_LEAVE(E_INVALIDARG);
return E_INVALIDARG;
#endif
if (!pdwFileVersionMS || !pdwFileVersionLS) {
hr = E_INVALIDARG;
goto Exit;
}
*pdwFileVersionMS = 0;
*pdwFileVersionLS = 0;
// Get the size of the version information.
uiInfoSize = g_versiondll.GetFileVersionInfoSize( (char *)plci->szExistingFileName, &handle);
if (uiInfoSize == 0) {
hr = S_FALSE;
goto Exit;
}
// Allocate a buffer for the version information.
pbData = new BYTE[uiInfoSize] ;
if (!pbData)
{
DEBUG_LEAVE(E_OUTOFMEMORY);
return E_OUTOFMEMORY;
}
// Fill the buffer with the version information.
if (!g_versiondll.GetFileVersionInfo((char *)plci->szExistingFileName, handle, uiInfoSize, pbData)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit ;
}
// Get the translation information.
if (!g_versiondll.VerQueryValue( pbData, "\\", (void**)&lpVSInfo, &uiVerSize)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit ;
}
if (!uiVerSize) {
hr = E_FAIL;
goto Exit ;
}
*pdwFileVersionMS = lpVSInfo->dwFileVersionMS;
*pdwFileVersionLS = lpVSInfo->dwFileVersionLS;
// Get the translation information.
if (!g_versiondll.VerQueryValue( pbData, "\\VarFileInfo\\Translation", &lpVerBuffer, &uiVerSize)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit ;
}
if (!uiVerSize) {
hr = E_FAIL;
goto Exit ;
}
plci->lcid = LOWORD(*((DWORD *) lpVerBuffer)); // Language ID
Exit:
if (pbData)
delete [] pbData ;
DEBUG_LEAVE(hr);
return hr;
}
DWORD
GetLanguageCheckInterval(HKEY hkeyCheckPeriod)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Dword,
"GetLanguageCheckInterval",
"%#x",
hkeyCheckPeriod
));
DWORD dwMagicDays = 30;
DWORD dwType = REG_DWORD;
DWORD dwSize = sizeof(DWORD);
char szLangString[MAX_PATH];
LCID lcidPriOverride = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale()));
LCID lcidPriBrowser = PRIMARYLANGID(LANGIDFROMLCID(g_lcidBrowser));
if (SUCCEEDED(GetLangString(lcidPriOverride, szLangString, sizeof(szLangString)))) {
if (RegQueryValueEx(hkeyCheckPeriod, szLangString, NULL, &dwType,
(unsigned char *)&dwMagicDays, &dwSize) == ERROR_SUCCESS) {
DEBUG_LEAVE(dwMagicDays);
return dwMagicDays;
}
}
if ( (lcidPriOverride != lcidPriBrowser) &&
SUCCEEDED(GetLangString(lcidPriBrowser, szLangString, sizeof(szLangString)))) {
if (RegQueryValueEx(hkeyCheckPeriod, szLangString, NULL, &dwType,
(unsigned char *)&dwMagicDays, &dwSize) == ERROR_SUCCESS) {
DEBUG_LEAVE(dwMagicDays);
return dwMagicDays;
}
}
DEBUG_LEAVE(dwMagicDays);
return dwMagicDays;
}
// returns:
// S_OK: local version OK or local version lang check not reqd now
// S_FALSE: localversion not of right lang force lang check now
// ERROR: fail
HRESULT NeedForceLanguageCheck(HKEY hkeyCLSID, CLocalComponentInfo *plci)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"NeedForceLanguageCheck",
"%#x, %#x",
hkeyCLSID, plci
));
HRESULT hr = S_OK;
DWORD lResult;
const char *szCHECKPERIOD = "LanguageCheckPeriod";
const char *szLASTCHECKEDHI = "LastCheckedHi";
DWORD dwMagicPerDay = 201;
DWORD dwMagicDays;
DWORD dwType;
DWORD dwSize;
FILETIME ftlast, ftnow;
SYSTEMTIME st;
HKEY hkeyCheckPeriod = 0;
char szLangEnable[MAX_PATH];
if (!plci->bForceLangGetLatest)
{
DEBUG_LEAVE(hr);
return hr;
}
// lang is mismatched for this browser
// check when was the last time we checked for the right lang
if ((lResult = RegOpenKeyEx( hkeyCLSID, szCHECKPERIOD,
0, KEY_READ, &hkeyCheckPeriod)) != ERROR_SUCCESS) {
plci->bForceLangGetLatest = FALSE;
goto Exit;
}
szLangEnable[0] = '\0';
dwType = REG_SZ;
dwSize = MAX_PATH;
if ( (RegQueryValueEx(hkeyCheckPeriod, NULL, NULL, &dwType,
(unsigned char *)szLangEnable, &dwSize) != ERROR_SUCCESS) ||
lstrcmpi(szLangEnable, "Enabled") != 0 ) {
plci->bForceLangGetLatest = FALSE;
goto Exit;
}
// see if lang check interval is specified for this lang
dwMagicDays = GetLanguageCheckInterval(hkeyCheckPeriod);
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ftnow);
ftnow.dwLowDateTime = 0;
memset(&ftlast, 0, sizeof(FILETIME));
dwType = REG_DWORD;
dwSize = sizeof(DWORD);
if (RegQueryValueEx(hkeyCheckPeriod, szLASTCHECKEDHI, NULL, &dwType,
(unsigned char *)&ftlast.dwHighDateTime, &dwSize) == ERROR_SUCCESS) {
ftlast.dwHighDateTime += (dwMagicPerDay * dwMagicDays);
}
if (CompareFileTime(&ftlast, &ftnow) > 0) {
plci->bForceLangGetLatest = FALSE;
}
Exit:
SAFEREGCLOSEKEY(hkeyCheckPeriod);
if (FAILED(hr))
plci->bForceLangGetLatest = FALSE;
hr = plci->bForceLangGetLatest?S_FALSE:S_OK;
DEBUG_LEAVE(hr);
return hr;
}
HRESULT IsRightLanguageLocallyInstalled(CLocalComponentInfo *plci)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"IsRightLanguageLocallyInstalled",
"%#x",
plci
));
HRESULT hr = S_OK;
LCID lcidLocalVersion;
LCID lcidNeeded;
if (!plci->lcid) // lang neutral?
goto Exit;
// make sure that the browser locale and lang strings are
// initialized at this point
hr = InitBrowserLangStrings();
if (FAILED(hr))
goto Exit;
// BUGBUG: we are using threadlocale here instead of the
// bindopts from the bindctx passed in
if (plci->lcid == GetThreadLocale()) // full match with override?
goto Exit;
if (plci->lcid == g_lcidBrowser) // full match with browser?
goto Exit;
// get primary lang of local version
lcidLocalVersion = PRIMARYLANGID(LANGIDFROMLCID(plci->lcid));
// check with primary language of override
lcidNeeded = PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale()));
if (lcidLocalVersion == lcidNeeded) // same primary lang?
goto Exit;
// check with primary language of browser
lcidNeeded = PRIMARYLANGID(LANGIDFROMLCID(g_lcidBrowser));
if (lcidLocalVersion == lcidNeeded) // same primary lang?
goto Exit;
// BUGBUG: how to detect language neutral or multiligual OCX
// we have a mismatch
// check when was the last time we check and if past the
// interval to check for new language availability
// force a download now.
hr = S_FALSE;
plci->bForceLangGetLatest = TRUE;
Exit:
DEBUG_LEAVE(hr);
return hr;
}
//
HRESULT LocalVersionOK(HKEY hkeyCLSID, CLocalComponentInfo *plci, DWORD dwFileVersionMS,
DWORD dwFileVersionLS, BOOL bExactVersion)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"LocalVersionOK",
"%#x, %#x, %x, %x, %B",
hkeyCLSID, plci, dwFileVersionMS, dwFileVersionLS, bExactVersion
));
DWORD handle;
HRESULT hr = S_OK; // assume local version OK.
DWORD dwLocFVMS = 0;
DWORD dwLocFVLS = 0;
HKEY hkeyCheckPeriod = 0;
const char *szCHECKPERIOD = "LanguageCheckPeriod";
if (FAILED(hr = plci->MakeDestDir()) ) {
goto Exit;
}
#ifdef UNIX
DEBUG_LEAVE(S_OK);
return S_OK;
#endif
if ((dwFileVersionMS == 0) && (dwFileVersionLS == 0)) {
// for dlls that don't require lang support and have no version req
// don't check version numbers
// this is to boost perf on system/IE dlls
// One can also avoid such checks
// by adding a [InstalledVersion] key under the clsid
if (!hkeyCLSID || RegOpenKeyEx( hkeyCLSID, szCHECKPERIOD,
0, KEY_READ, &hkeyCheckPeriod) != ERROR_SUCCESS) {
goto Exit;
}
}
hr = GetFileVersion( plci, &dwLocFVMS, &dwLocFVLS);
if (hr == S_OK) {
plci->dwLocFVMS = dwLocFVMS;
plci->dwLocFVLS = dwLocFVLS;
if (bExactVersion) {
if (dwFileVersionMS != dwLocFVMS || dwFileVersionLS != dwLocFVLS) {
hr = S_FALSE;
} else {
// check language
// sets the plci->bForcelangGetLatest if reqd
IsRightLanguageLocallyInstalled(plci);
}
}
else {
if ((dwFileVersionMS > dwLocFVMS) ||
((dwFileVersionMS == dwLocFVMS) &&
(dwFileVersionLS > dwLocFVLS))) {
hr = S_FALSE;
} else {
// check language
// sets the plci->bForcelangGetLatest if reqd
IsRightLanguageLocallyInstalled(plci);
}
}
}
if ((dwFileVersionMS == 0) && (dwFileVersionLS == 0)) {
hr = S_OK;
}
if ((dwFileVersionMS == -1) && (dwFileVersionLS == -1)) {
hr = S_FALSE;
}
Exit:
if (hkeyCheckPeriod)
RegCloseKey(hkeyCheckPeriod);
DEBUG_LEAVE(hr);
return hr;
}
/*
*
* UpdateSharedDlls
*
* the SharedDlls section looks like this
*
* [SharedDlls]
* C:\Windows\System\foo.ocx = <ref count>
*
* Parameters:
*
* szFileName full file name of module we want to use
*
* Returns:
*
* S_OK incremented tge shared dlls ref count.
*
* Error the error encountered
*/
HRESULT
UpdateSharedDlls( LPCSTR szFileName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"UpdateSharedDlls",
"%.80q",
szFileName
));
HKEY hKeySD = NULL;
HRESULT hr = S_OK;
DWORD dwType;
DWORD dwRef = 1;
DWORD dwSize = sizeof(DWORD);
LONG lResult;
// get the main SHAREDDLLS key ready; this is never freed!
if ((lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_SHAREDDLLS,
0, KEY_ALL_ACCESS, &hKeySD)) != ERROR_SUCCESS) {
if ((lResult = RegCreateKey( HKEY_LOCAL_MACHINE,
REGSTR_PATH_SHAREDDLLS, &hKeySD)) != ERROR_SUCCESS) {
hKeySD = NULL;
hr = HRESULT_FROM_WIN32(lResult);
goto Exit;
}
}
// now look for szFileName
lResult = SHQueryValueEx(hKeySD, szFileName, NULL, &dwType,
(unsigned char *)&dwRef, &dwSize);
if (lResult == ERROR_SUCCESS)
dwRef++;
// does not exist. Create one and initialize to 1
if ((lResult = RegSetValueEx (hKeySD, szFileName, 0, REG_DWORD,
(unsigned char *)&dwRef,
sizeof(DWORD))) != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(lResult);
goto Exit;
}
Exit:
if (hKeySD)
RegCloseKey(hKeySD);
DEBUG_LEAVE(hr);
return hr;
}
/*
*
* UpdateModuleUsage
*
* the module usage section in the regitry looks like this
*
* [ModuleUsage]
* [c:/windows/occache/foo.ocx]
* Owner = Internet Code Downloader
* FileVersion = <optional text version of 64-bit fileversion of ocx
* [clients]
* Internet Code Downloader = <this client's ref count>
* To allow for full path names without using the backslash we convert
* backslahes to forward slashes.
*
*
* Parameters:
*
* szFileName full file name of module we want to use
*
* szClientName name of or stringfromclsid of client.
*
* szClientPath optional (if present we can detect if client is gone)
*
* muFlags:
* MU_CLIENT mark ourselves client
* MU_OWNER mark ourselves owner
*
* Returns:
* S_OK we updated the module usage section,
* and if that was previously absent then we also upped the
* shared dlls count.
*
* Error the error encountered
*/
HRESULT
UpdateModuleUsage(
LPCSTR szFileName,
LPCSTR szClientName,
LPCSTR szClientPath,
LONG muFlags)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"UpdateModuleUsage",
"%.80q, %.80q, %.80q, %x",
szFileName, szClientName, szClientPath, muFlags
));
HRESULT hr = S_OK;
LONG lResult = 0;
BOOL fUpdateSharedDlls = TRUE;
DWORD dwType;
HKEY hKeyMod = NULL;
DWORD dwSize = MAX_PATH;
char szBuf[MAX_PATH];
const char *pchSrc;
char *pchDest;
static const LPCSTR szCLIENTPATHDEFAULT = "";
LPCSTR lpClientPath = (szClientPath)?szClientPath:szCLIENTPATHDEFAULT;
HKEY hKeyMU = NULL;
static const char szOWNER[] = ".Owner";
static const char szUNKNOWN[] = "Unknown Owner";
Assert(szClientName);
DWORD cbClientName = lstrlen(szClientName);
DWORD cbUnknown = sizeof(szUNKNOWN);
char szShortFileName[MAX_PATH];
#ifdef SHORTEN
if (!GetShortPathName(szFileName, szShortFileName, MAX_PATH)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
#else
StrNCpy(szShortFileName, szFileName, sizeof(szShortFileName));
#endif
if (g_bRunOnWin95) {
char szCharFileName[MAX_PATH];
OemToCharBuff(szShortFileName, szCharFileName, sizeof(szCharFileName) / sizeof(szCharFileName[0]));
StrNCpy(szShortFileName, szCharFileName, sizeof(szShortFileName));
}
// get the main MODULEUSAGE key ready; this is never freed!
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_MODULE_USAGE,
0, KEY_ALL_ACCESS, &hKeyMU) != ERROR_SUCCESS)
if ((lResult = RegCreateKey( HKEY_LOCAL_MACHINE,
REGSTR_PATH_MODULE_USAGE, &hKeyMU)) != ERROR_SUCCESS) {
hKeyMU = NULL;
hr = HRESULT_FROM_WIN32(lResult);
goto Exit;
}
// check if Usage section is present for this dll
// open the file's section we are concerned with
// if absent create it
// BUGBUG: win95 registry bug does not allow keys to be > 255
// MAX_PATH for filename is 260
pchDest = szBuf;
for (pchSrc = szShortFileName; *pchSrc != '\0'; pchSrc++, pchDest++) {
if ((*pchDest = *pchSrc) == '\\')
*pchDest = '/';
}
*pchDest = '\0'; // null terminate
szBuf[256] = '\0'; // truncate if longer than 255 ude to win95 registry bug
if (RegOpenKeyEx( hKeyMU, szBuf,
0, KEY_ALL_ACCESS, &hKeyMod) != ERROR_SUCCESS) {
if ((lResult = RegCreateKey( hKeyMU,
szBuf, &hKeyMod)) != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(lResult);
goto Exit;
}
}
// now look for '.Owner='
dwSize = MAX_PATH;
szBuf[0] = '\0';
if (RegQueryValueEx(hKeyMod, szOWNER, NULL, &dwType,
(unsigned char *)szBuf, &dwSize) == ERROR_SUCCESS) {
if ((lstrcmpi(szBuf, szClientName) != 0) && (muFlags & MU_OWNER)) {
// if we are the not the owner we can't make ourselves the owner
hr = E_INVALIDARG;
goto Exit;
}
} else {
// '.Owner =' does not exist. Create one and initialize to us
// if muFlags & MU_OWNER
if (((lResult = RegSetValueEx (hKeyMod, szOWNER, 0, REG_SZ,
(UCHAR *)((muFlags & MU_OWNER)?szClientName:szUNKNOWN),
((muFlags & MU_OWNER)?cbClientName:cbUnknown)))) != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(lResult);
goto Exit;
}
}
// look for szClientName already marked as a client
dwSize = MAX_PATH;
if (SHQueryValueEx(hKeyMod, szClientName, NULL, &dwType,
(unsigned char *)szBuf, &dwSize) == ERROR_SUCCESS) {
// signal that we have already registered as a
// client and so don't up ref count in shareddlls
fUpdateSharedDlls = FALSE;
} else {
// add ourselves as a client
if ((lResult =RegSetValueEx(hKeyMod, szClientName, 0, REG_SZ,
(unsigned char *)lpClientPath, lstrlen(lpClientPath)+1 )) != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(lResult);
goto Exit;
}
}
Exit:
if (hKeyMod)
RegCloseKey(hKeyMod);
if (hKeyMU)
RegCloseKey(hKeyMU);
// Update ref count in SharedDlls only if usage section was not
// already updated. This will ensure that the code downloader has
// just one ref count represented in SharedDlls
if ( fUpdateSharedDlls)
hr = UpdateSharedDlls(szShortFileName);
DEBUG_LEAVE(hr);
return hr;
}
// Name: SniffStringFileInfo
// Parameters:
// szFileName - full path to file whose StringFileInfo we want to sniff
// lpszSubblock - sub block of the StringFileInfo to look for
// pdw - place to put the string, interpreted as a number
// Returns:
// TRUE - if the subblock is present.
// FALSE - if the subblock is absent.
// Notes:
// This function implements stuff common to SupportsSelfRegister and WantsAutoExpire
BOOL SniffStringFileInfo( LPSTR szFileName, LPCTSTR lpszSubblock, DWORD *pdw )
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"SniffStringFileInfo",
"%.80q, %#x, %#x",
szFileName, lpszSubblock, pdw
));
BOOL bResult = FALSE;
DWORD handle;
UINT uiInfoSize;
UINT uiVerSize ;
UINT uiSize ;
BYTE* pbData = NULL ;
DWORD* lpBuffer;
TCHAR szName[512] ;
LPTSTR szExpire;
if ( pdw )
*pdw = 0;
#ifdef UNIX
// Don't have version.dll
DebugBreak();
DEBUG_LEAVE(FALSE);
return FALSE;
#endif
// Get the size of the version information.
uiInfoSize = g_versiondll.GetFileVersionInfoSize( szFileName, &handle);
if (uiInfoSize == 0)
{
DEBUG_LEAVE(FALSE);
return FALSE ;
}
// Allocate a buffer for the version information.
pbData = new BYTE[uiInfoSize] ;
if (!pbData)
{
DEBUG_LEAVE(TRUE);
return TRUE; // nothing nasty, just quirky
}
// Fill the buffer with the version information.
bResult = g_versiondll.GetFileVersionInfo( szFileName, handle, uiInfoSize, pbData);
if (!bResult) goto Exit ;
// Get the translation information.
bResult = g_versiondll.VerQueryValue( pbData, "\\VarFileInfo\\Translation",
(void**)&lpBuffer, &uiVerSize);
if (!bResult) goto Exit ;
if (!uiVerSize) goto Exit ;
// Build the path to the OLESelfRegister key
// using the translation information.
wnsprintf( szName, ARRAY_ELEMENTS(szName)-1, "\\StringFileInfo\\%04hX%04hX\\%s",
LOWORD(*lpBuffer), HIWORD(*lpBuffer), lpszSubblock) ;
// Search for the key.
bResult = g_versiondll.VerQueryValue( pbData, szName, (void**)&szExpire, &uiSize);
// If there's a string there, we need to convert it to a count of days.
if ( bResult && pdw && uiSize )
{
DWORD dwExpire = 0;
for ( ; *szExpire; szExpire++ )
{
if ( (*szExpire >= TEXT('0') && *szExpire <= TEXT('9')) )
dwExpire = dwExpire * 10 + *szExpire - TEXT('0');
else
break;
}
if (dwExpire > MAX_EXPIRE_DAYS)
dwExpire = MAX_EXPIRE_DAYS;
*pdw = dwExpire;
}
Exit:
delete [] pbData ;
DEBUG_LEAVE(bResult);
return bResult ;
}
void ExtractVersion(char *pszDistUnit, DWORD *pdwVerMS, DWORD *pdwVerLS)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"ExtractVersion",
"%.80q, %#x, %#x",
pszDistUnit, pdwVerMS, pdwVerLS
));
char *pszCopy = NULL;
char *pszPtr = NULL;
int iLen = 0;
if (!pszDistUnit)
{
DEBUG_LEAVE(0);
return;
}
iLen = lstrlen(pszDistUnit) + 1;
pszCopy = new char[iLen];
if (!pszCopy) {
DEBUG_LEAVE(0);
return;
}
StrNCpy(pszCopy, pszDistUnit, iLen);
pszPtr = pszCopy;
// Convert _ to , for GetVersionFromString()
while (*pszPtr)
{
if (*pszPtr == '_')
{
*pszPtr = ',';
}
pszPtr++;
}
pszPtr = StrStrA(pszCopy, "!");
if (pszPtr) {
pszPtr++;
GetVersionFromString(pszPtr, pdwVerMS, pdwVerLS);
}
SAFEDELETE(pszCopy);
DEBUG_LEAVE(0);
}