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

2883 lines
88 KiB
C++

// ===========================================================================
// File: CDLAPI.CXX
// The main code downloader api file.
//
#include <cdlpch.h>
#include <mshtmhst.h>
#include <shlwapi.h>
#include <inseng.h>
// globals for code downloader
CMutexSem g_mxsCodeDownloadGlobals;
LCID g_lcidBrowser = 0x409; // default to english
char g_szBrowserLang[20];
int g_lenBrowserLang = 20;
char g_szBrowserPrimaryLang[20];
int g_lenBrowserPrimaryLang = 20;
// Use SetupApi by default!
DWORD g_dwCodeDownloadSetupFlags = CDSF_USE_SETUPAPI;
char g_szOCXCacheDir[MAX_PATH];
char g_szOCXTempDir[MAX_PATH];
int g_OCXCacheDirSize = 0;
static BOOL g_fHaveCacheDir = FALSE;
BOOL g_OSInfoInitialized = FALSE;
int g_CPUType = PROCESSOR_ARCHITECTURE_UNKNOWN; // default
BOOL g_fRunningOnNT = FALSE;
BOOL g_bRunOnWin95 = FALSE;
#ifdef WX86
BOOL g_fWx86Present; // Set if running on RISC and Wx86 is installed
#endif
BOOL g_bLockedDown = FALSE;
// New default name for ActiveX cache folder! We only use this if occache fails to set up the cache folder.
// const static char *g_szOCXDir = "OCCACHE";
const char *g_szOCXDir = "Downloaded Program Files";
const char *g_szActiveXCache = "ActiveXCache";
const char *g_szRegKeyActiveXCache = "ActiveX Cache";
// This is the client platform specific location name that we look for in INF
// and is built by concatenating g_szProcessorTypes at end of g_szLocPrefix
char g_szPlatform[17]; // sizeof("file-win32-alpha"), (longest possible)
static char *g_szLocPrefix="file-win32-";
// directly indexed into with PROCESSOR_ARCH* returned by GetSystemInfo..
// The "" ones are those we don't support.
char *g_szProcessorTypes[] = {
"x86",
"",
"",
"",
"",
"",
"ia64",
"",
"",
"amd64"
};
// used for Accept Types doing http based platform independence
// use alpha as the longest of possible g_szProcessorTypes strings
static char *g_szCABAcceptPrefix = "application/x-cabinet-win32-";
static char *g_szPEAcceptPrefix = "application/x-pe-win32-";
// Name of dll that implements ActiveX control cache shell extension
const char *g_szActiveXCacheDll = "occache.dll";
// list that goes into accept types
// this first 2 left null to substitue with strings for this platform (cab, pe)
// the number of media types are are registering as Accept types for
// HTTP based contente negotiation
#ifdef WX86
#define CDL_NUM_TYPES 7
#else
#define CDL_NUM_TYPES 5
#endif
LPSTR g_rgszMediaStr[CDL_NUM_TYPES] =
{
CFSTR_MIME_NULL,
CFSTR_MIME_NULL,
CFSTR_MIME_RAWDATASTRM,
"application/x-setupscript",
"*/*",
#ifdef WX86
CFSTR_MIME_NULL,
CFSTR_MIME_NULL,
#endif
};
CLIPFORMAT g_rgclFormat[CDL_NUM_TYPES];
FORMATETC g_rgfmtetc[CDL_NUM_TYPES];
IEnumFORMATETC *g_pEFmtETC;
extern COleAutDll g_OleAutDll;
typedef HRESULT (STDAPICALLTYPE *PFNCOINSTALL)(
IBindCtx *pbc,
DWORD dwFlags,
uCLSSPEC *pClassSpec,
QUERYCONTEXT *pQuery,
LPWSTR pszCodeBase);
PFNCOINSTALL g_pfnCoInstall=NULL;
BOOL g_bCheckedForCoInstall=FALSE;
HMODULE g_hOLE32 = 0;
BOOL g_bUseOLECoInstall = FALSE;
HRESULT GetClassFromExt(LPSTR pszExt, CLSID *pclsid);
STDAPI
WrapCoInstall (
REFCLSID rCLASSID, // CLSID of object (may be NULL)
LPCWSTR szCODE, // URL to code (may be NULL)
DWORD dwFileVersionMS, // Version of primary object
DWORD dwFileVersionLS, // Version of primary object
LPCWSTR szTYPE, // MIME type (may be NULL)
LPBINDCTX pBindCtx, // Bind ctx
DWORD dwClsContext, // CLSCTX flags
LPVOID pvReserved, // Must be NULL
REFIID riid, // Usually IID_IClassFactory
DWORD flags
);
#define WRAP_OLE32_COINSTALL
STDAPI PrivateCoInstall(
IBindCtx *pbc,
DWORD dwFlags,
uCLSSPEC *pClassSpec,
QUERYCONTEXT *pQuery,
LPWSTR pszCodeBase);
/*
* RegisterNewActiveXCacheFolder
* change default ActiveX Cache path to lpszNewPath and properly
* updates all registry entries
*/
/*
LONG RegisterNewActiveXCacheFolder(LPCTSTR lpszNewPath, DWORD dwPathLen)
{
Assert(lpszNewPath != NULL && lpszNewPath[0] != '\0');
if (lpszNewPath == NULL || lpszNewPath[0] == '\0')
return ERROR_BAD_ARGUMENTS;
LONG lResult = ERROR_SUCCESS;
HKEY hkeyIntSetting = NULL;
HKEY hkeyActiveXCache = NULL;
char szOldPath[MAX_PATH];
char szPath[MAX_PATH];
char szSubKey[MAX_PATH];
DWORD dwLen = MAX_PATH;
DWORD dwKeyLen = MAX_PATH;
DWORD dwIndex = 0;
BOOL fOldFound = FALSE;
BOOL fNewFound = FALSE;
BOOL fEqual = FALSE;
LONG lValueIndex = -1;
lResult = RegOpenKeyEx(
HKEY_LOCAL_MACHINE, REGSTR_PATH_IE_SETTINGS,
0, KEY_ALL_ACCESS, &hkeyIntSetting);
if (lResult != ERROR_SUCCESS)
goto Exit;
// read ActiveXCache
lResult = RegQueryValueEx(
hkeyIntSetting, g_szActiveXCache, NULL, NULL,
(LPBYTE)szOldPath, &dwLen);
if (lResult != ERROR_SUCCESS)
{
fOldFound = TRUE;
szOldPath[0] = '\0';
}
fEqual = (lstrcmpi(lpszNewPath, szOldPath) == 0);
lResult = RegSetValueEx(
hkeyIntSetting, g_szActiveXCache, 0, REG_SZ,
(LPBYTE)lpszNewPath, dwPathLen);
if (lResult != ERROR_SUCCESS)
goto Exit;
// Check to see if new path already exists in the list of paths under
// HKLM\...\Windows\CurrentVersion\Internet Settings\ActiveX Cache\Paths.
// If not, add it.
lResult = RegCreateKey(
hkeyIntSetting, g_szRegKeyActiveXCache,
&hkeyActiveXCache);
if (lResult != ERROR_SUCCESS)
goto Exit;
for (dwLen = dwKeyLen = MAX_PATH;
lResult == ERROR_SUCCESS;
dwLen = dwKeyLen = MAX_PATH)
{
lResult = RegEnumValue(
hkeyActiveXCache, dwIndex++, szSubKey, &dwKeyLen,
NULL, NULL, (LPBYTE)szPath, &dwLen);
if (lResult == ERROR_SUCCESS)
{
// for find new unique value name later.
lValueIndex = max(lValueIndex, atol(szSubKey));
if (!fNewFound)
fNewFound = (lstrcmpi(lpszNewPath, szPath) == 0);
if (!fOldFound)
fOldFound = (lstrcmpi(szOldPath, szPath) == 0);
}
}
// something goes wrong!
if (lResult != ERROR_NO_MORE_ITEMS)
goto Exit;
// Add lpszNewPath to the list of paths
lResult = ERROR_SUCCESS;
// keep registry intact
if (!fOldFound && szOldPath[0] != '\0')
{
wsprintf(szSubKey, "%i", ++lValueIndex);
lResult = RegSetValueEx(
hkeyActiveXCache, szSubKey, 0, REG_SZ,
(LPBYTE)szOldPath, lstrlen(szOldPath) + 1);
}
// add new path to list of paths
if (lResult == ERROR_SUCCESS && !fNewFound && !fEqual)
{
wsprintf(szSubKey, "%i", ++lValueIndex);
lResult = RegSetValueEx(
hkeyActiveXCache, szSubKey, 0, REG_SZ,
(LPBYTE)lpszNewPath, dwPathLen);
}
Exit:
// restore old state if failed
if (lResult != ERROR_SUCCESS)
{
if (szOldPath[0] == '\0')
RegDeleteValue(hkeyIntSetting, g_szActiveXCache);
else
RegSetValueEx(
hkeyIntSetting, g_szActiveXCache, 0, REG_SZ,
(LPBYTE)lpszNewPath, dwPathLen);
}
if (hkeyActiveXCache != NULL)
RegCloseKey(hkeyActiveXCache);
if (hkeyIntSetting != NULL)
RegCloseKey(hkeyIntSetting);
return lResult;
}
LONG SwitchToNewCachePath(LPCSTR lpszNewPath, DWORD dwPathLen)
{
Assert(lpszNewPath != NULL);
if (lpszNewPath == NULL)
return ERROR_BAD_ARGUMENTS;
LONG lResult = ERROR_SUCCESS;
char szKey[MAX_PATH];
char szPath[MAX_PATH];
char *pCh = NULL;
HKEY hkey = NULL;
DWORD dwIndex = 0;
DWORD dwLen = MAX_PATH;
wsprintf(szKey, "%s\\%s", REGSTR_PATH_IE_SETTINGS, g_szRegKeyActiveXCache);
lResult = RegOpenKeyEx(
HKEY_LOCAL_MACHINE, szKey,
0, KEY_ALL_ACCESS, &hkey);
for (; lResult == ERROR_SUCCESS; dwLen = MAX_PATH)
{
lResult = RegEnumValue(
hkey, dwIndex++, szKey, &dwLen,
NULL, NULL, (LPBYTE)szPath, &dwLen);
if (lResult == ERROR_SUCCESS && lstrcmpi(szPath, lpszNewPath) == 0)
break;
}
RegCloseKey(hkey);
if (lResult != ERROR_SUCCESS)
return RegisterNewActiveXCacheFolder(lpszNewPath, dwPathLen);
// return fail to indicate that path has not been changed to
// Downloaded ActiveX Controls
return HRESULT_CODE(E_FAIL);
}
*/
HRESULT
SetCoInstall()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"SetCoInstall",
NULL
));
HKEY hKeyIESettings = 0;
//execute entire function under critical section
CLock lck(g_mxsCodeDownloadGlobals);
if (!g_bCheckedForCoInstall && !g_pfnCoInstall)
{
// So we don't keep rechecking for this api.
g_bCheckedForCoInstall = TRUE;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_IE_SETTINGS, 0,
KEY_READ, &hKeyIESettings) == ERROR_SUCCESS) {
if (RegQueryValueEx(hKeyIESettings, REGVAL_USE_COINSTALL, NULL,
NULL, NULL, 0) != ERROR_SUCCESS) {
g_bUseOLECoInstall = FALSE;
g_pfnCoInstall = NULL;
RegCloseKey(hKeyIESettings);
DEBUG_LEAVE(S_OK);
return S_OK;
}
RegCloseKey(hKeyIESettings);
}
// find CoInstall entry point in OLE32
if (!g_hOLE32)
{
// BUGBUG: We never FreeLibrary on this. Realisticly, someone else will probably already be using this
// and it will be in use throughout the life of the process, so we won't worry too much.
g_hOLE32 = LoadLibraryA("ole32.dll");
}
if(g_hOLE32 != 0)
{
void *pfn = GetProcAddress(g_hOLE32, "CoInstall");
if(pfn != NULL)
{
g_bUseOLECoInstall = TRUE;
g_pfnCoInstall = (PFNCOINSTALL) pfn;
}
}
}
DEBUG_LEAVE(S_OK);
return S_OK;
}
VOID
DetermineOSAndCPUVersion()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"DetermineOSAndCPUVersion",
NULL
));
OSVERSIONINFO osvi;
SYSTEM_INFO sysinfo;
//execute entire function under critical section
CLock lck(g_mxsCodeDownloadGlobals);
if (g_OSInfoInitialized) {
DEBUG_LEAVE(0);
return;
}
// Determine which version of NT we're running on
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
g_fRunningOnNT = (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId);
if (osvi.dwPlatformId & VER_PLATFORM_WIN32_WINDOWS &&
osvi.dwMinorVersion == 0) {
g_bRunOnWin95 = TRUE;
}
else {
g_bRunOnWin95 = FALSE;
}
#ifdef WX86
if (g_bNT5OrGreater) {
//
// The pre-alpha Wx86 that runs on NT 4.0 does not support
// x86 ActiveX controls inside a RISC host. Only call Wx86
// on NT 5.0 machines.
//
HKEY hKey;
LONG Error;
// Temp hack: allow users to disable Wx86 support in URLMON if this
// key is present.
// bugbug: probably rip this before ship.
Error = ::RegOpenKeyW(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Control\\Wx86\\DisableCDL",
&hKey);
if (Error == ERROR_SUCCESS) {
::RegCloseKey(hKey);
} else {
g_fWx86Present = TRUE;
}
}
#endif // WX86
GetSystemInfo(&sysinfo);
switch(sysinfo.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_INTEL:
case PROCESSOR_ARCHITECTURE_AMD64:
case PROCESSOR_ARCHITECTURE_IA64:
g_CPUType = sysinfo.wProcessorArchitecture;
break;
case PROCESSOR_ARCHITECTURE_UNKNOWN:
default:
g_CPUType = PROCESSOR_ARCHITECTURE_UNKNOWN;
break;
}
g_OSInfoInitialized = TRUE;
DEBUG_LEAVE(0);
}
/*
* SetGlobals
* set all the globals for the Code Downloader
* called from AsyncGetClassBits. executes under a crit section
*/
HRESULT
SetGlobals()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"SetGlobals",
NULL
));
SYSTEM_INFO sysinfo;
int nCPUType;
HRESULT hr = S_OK;
DWORD lResult;
// static const char szActiveXCache[] = "ActiveXCache";
BOOL fRegActiveXCacheDll = FALSE; // do we need to register occache.dll?
//execute entire function under critical section
CLock lck(g_mxsCodeDownloadGlobals);
if (!g_fHaveCacheDir) {
// do some init: get ocx ccahe dir
DWORD Size = MAX_PATH;
DWORD dwType;
HKEY hKeyIESettings = 0;
char szOldCacheDir[MAX_PATH];
int len = 0;
int CdlNumTypes;
lResult = ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_IE_SETTINGS, 0,
KEY_READ, &hKeyIESettings );
if (lResult != ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(lResult);
goto Exit;
}
// Get Code Download flags
dwType = REG_DWORD;
Size = sizeof(DWORD);
lResult = ::RegQueryValueEx(hKeyIESettings, "CodeDownloadFlags",
NULL, &dwType, (unsigned char *)&g_dwCodeDownloadSetupFlags, &Size);
Size = MAX_PATH;
dwType = REG_SZ;
// Get existing cache path from registry
lResult = ::RegQueryValueEx(hKeyIESettings, g_szActiveXCache,
NULL, &dwType, (unsigned char *)g_szOCXCacheDir, &Size);
if (lResult != ERROR_SUCCESS ||
!PathFileExists( g_szOCXCacheDir ) )
{
// OC Cache didn't set up the registry for us...
// Load it to make sure it has done if regsvring work...
HRESULT hr = E_FAIL;
HINSTANCE h = LoadLibrary(g_szActiveXCacheDll);
HRESULT (STDAPICALLTYPE *pfnReg)(void);
HRESULT (STDAPICALLTYPE *pfnInst)(BOOL bInstall, LPCWSTR pszCmdLine);
if (h != NULL)
{
(FARPROC&)pfnReg = GetProcAddress(h, "DllRegisterServer");
if (pfnReg != NULL)
hr = (*pfnReg)();
// also need to call DllInstall to complete the wiring of the shell extension
(FARPROC&)pfnInst = GetProcAddress(h, "DllInstall");
if (pfnInst != NULL)
hr = (*pfnInst)( TRUE, L"");
FreeLibrary(h);
}
// Retry now that we're sure OCCACHE has had a shot at setting things up for us
if ( SUCCEEDED(hr) )
lResult = ::RegQueryValueEx(hKeyIESettings, g_szActiveXCache,
NULL, &dwType, (unsigned char *)g_szOCXCacheDir, &Size);
if ( lResult != ERROR_SUCCESS ) {
// OC Cache is having a bad day. Don't let this stop code download.
// Compose the default path and see if we need to change
// old cache path to the new default path.
if(!(len = GetWindowsDirectory(g_szOCXCacheDir, MAX_PATH)))
g_szOCXCacheDir[0] = '\0';
else
{
Assert(len <= MAX_PATH);
if (g_szOCXCacheDir[len-1] != '\\')
lstrcat(g_szOCXCacheDir, "\\");
}
StrNCat(g_szOCXCacheDir, g_szOCXDir,MAX_PATH-len-2);
// OC Cache or not, remember our directory
HKEY hkeyWriteIESettings;
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGSTR_PATH_IE_SETTINGS,0,
KEY_ALL_ACCESS, &hkeyWriteIESettings ) == ERROR_SUCCESS) {
RegSetValueEx(hkeyWriteIESettings,g_szActiveXCache,0,REG_SZ,
(LPBYTE)g_szOCXCacheDir, lstrlen(g_szOCXCacheDir) + 1);
RegCloseKey(hkeyWriteIESettings);
}
}
// load oleaut32.dll
lResult = g_OleAutDll.Init();
} // if reg key not set up.
if (hKeyIESettings) {
RegCloseKey(hKeyIESettings);
hKeyIESettings = 0;
}
DWORD dwAttr = GetFileAttributes(g_szOCXCacheDir);
if (dwAttr == -1) {
if (!CreateDirectory(g_szOCXCacheDir, NULL)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
// if cache dir has just been created, we also need
// to register occache shell extension
fRegActiveXCacheDll = TRUE;
} else {
if ( (!(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) ||
(dwAttr & FILE_ATTRIBUTE_READONLY)) {
hr = OLE_E_NOCACHE; // closest we can get to!
goto Exit;
}
}
if (!GetTempPath(MAX_PATH, g_szOCXTempDir) ) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
// Set up g_bRunningOnNT, g_CpuType, and g_fWx86Present
DetermineOSAndCPUVersion();
// Record client architecture to make proper accept types
// and to use in INF for platform independent CODE=URL
LPSTR pBaseFileName = NULL;
InitBrowserLangStrings();
nCPUType = g_CPUType;
if (g_CPUType == PROCESSOR_ARCHITECTURE_UNKNOWN) {
nCPUType = PROCESSOR_ARCHITECTURE_INTEL;
}
lstrcpy(g_szPlatform, g_szLocPrefix);
lstrcat(g_szPlatform, g_szProcessorTypes[nCPUType]);
// Register the Media types
// first build up the table of eight types we send out
char szCABStr[MAX_PATH];
char szPEStr[MAX_PATH];
lstrcpy(szCABStr, g_szCABAcceptPrefix);
lstrcat(szCABStr, g_szProcessorTypes[nCPUType]);
lstrcpy(szPEStr, g_szPEAcceptPrefix);
lstrcat(szPEStr, g_szProcessorTypes[nCPUType]);
g_rgszMediaStr[0] = szCABStr;
g_rgszMediaStr[1] = szPEStr;
#ifdef WX86
char szCABStrX86[MAX_PATH];
char szPEStrX86[MAX_PATH];
if (g_fWx86Present) {
g_rgszMediaStr[6] = g_rgszMediaStr[4]; // move "*/*" to the end of the list
lstrcpy(szCABStrX86, g_szCABAcceptPrefix);
lstrcat(szCABStrX86, g_szProcessorTypes[PROCESSOR_ARCHITECTURE_INTEL]);
lstrcpy(szPEStrX86, g_szPEAcceptPrefix);
lstrcat(szPEStrX86, g_szProcessorTypes[PROCESSOR_ARCHITECTURE_INTEL]);
g_rgszMediaStr[4] = szCABStrX86;
g_rgszMediaStr[5] = szPEStrX86;
CdlNumTypes = CDL_NUM_TYPES;
} else {
CdlNumTypes = CDL_NUM_TYPES-2;
}
#else
CdlNumTypes = CDL_NUM_TYPES;
#endif
hr = RegisterMediaTypes(CdlNumTypes, (const LPCSTR *)g_rgszMediaStr, g_rgclFormat);
if (FAILED(hr))
goto Exit;
// initialize the formatetc array
for (int i=0; i< CdlNumTypes; i++) {
g_rgfmtetc[i].cfFormat = g_rgclFormat[i];
g_rgfmtetc[i].tymed = TYMED_NULL;
g_rgfmtetc[i].dwAspect = DVASPECT_CONTENT;
g_rgfmtetc[i].ptd = NULL;
}
// BUGBUG: leaked!
hr = CreateFormatEnumerator(CdlNumTypes, g_rgfmtetc, &g_pEFmtETC);
if (FAILED(hr))
goto Exit;
if (g_bNT5OrGreater) {
HKEY hkeyLockedDown = 0;
// Test for lock-down. If we cannot write to HKLM, then we are in
// a locked-down environment, and should abort right away.
if (RegCreateKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_NT5_LOCKDOWN_TEST,
&hkeyLockedDown) != ERROR_SUCCESS) {
// We are in lock-down mode; abort.
g_bLockedDown = TRUE;
}
else {
// Not locked-down. Delete the key, and continue
RegCloseKey(hkeyLockedDown);
RegDeleteKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_NT5_LOCKDOWN_TEST);
g_bLockedDown = FALSE;
}
}
g_fHaveCacheDir = TRUE;
}
Exit:
if (FAILED(hr))
{
UrlMkDebugOut((DEB_CODEDL, "ERR CodeDownload failed to initialize: hr(%lx)\n", hr));
}
DEBUG_LEAVE(hr);
return hr;
}
#define CLSID_ActiveXPlugin \
{0x06DD38D3L,0xD187,0x11CF, \
{0xA8,0x0D,0x00,0xC0,0x4F,0xD7,0x4A,0xD8}}
//
// FindPlugin - delegates this call to the plugin OCX
//
BOOL FindPlugin(char *szFileExt, char *szName, char *szMime)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Bool,
"FindPlugin",
"%.80q, %.80q, %.80q",
szFileExt, szName, szMime
));
typedef BOOL (WINAPI *LPFINDPLUGIN_API)(char *ext, char *name, char *mime);
LPFINDPLUGIN_API pfnFindPlugin;
BOOL fRet = FALSE;
HMODULE hLib = LoadLibrary("plugin.ocx");
if (hLib == NULL) {
goto Exit;
}
pfnFindPlugin = (LPFINDPLUGIN_API)GetProcAddress(hLib, "FindPluginA");
if (pfnFindPlugin == NULL) {
goto Exit;
}
fRet = pfnFindPlugin(szFileExt, szName, szMime);
Exit:
if (hLib)
FreeLibrary(hLib);
DEBUG_LEAVE(fRet);
return fRet;
}
// GetClsidFromExtOrMime
// fills up clsidout with rCLASSID if not CLSID_NULL
// or gets clsid from passed in ext or mime type
// returns:
// S_OK
// S_FALSE: couldn't convert to a clsid
HRESULT
GetClsidFromExtOrMime(
REFCLSID rclsid,
CLSID &clsidout,
LPCWSTR szExt,
LPCWSTR szTYPE,
LPSTR *ppFileName)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"GetClsidFromExtOrMime",
"%#x, %#x, %.80wq, %.80wq, %#x",
&rclsid, &clsidout, szExt, szTYPE, ppFileName
));
BOOL fNullClsid;
HRESULT hr = S_OK;
char szTypeA[MAX_PATH];
char szExtA[MAX_PATH];
LPSTR lpName = NULL;
memcpy(&clsidout, &rclsid, sizeof(GUID));
if ((fNullClsid = IsEqualGUID(rclsid , CLSID_NULL))) {
if (!szTYPE && !szExt) {
hr = S_FALSE;
goto Exit;
}
if (szTYPE) {
if (!WideCharToMultiByte(CP_ACP, 0 , szTYPE ,
-1 ,szTypeA, MAX_PATH, NULL, NULL)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
// convert the mime/type into a clsid
if (SUCCEEDED((GetClassMime(szTypeA, &clsidout)))) {
fNullClsid = FALSE;
}
} else { // szExt
if (!WideCharToMultiByte(CP_ACP, 0 , szExt ,
-1 ,szExtA, MAX_PATH, NULL, NULL)) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
if (SUCCEEDED(GetClassFromExt(szExtA, &clsidout))) {
fNullClsid = FALSE;
}
}
}
Exit:
if ((hr == S_OK) && fNullClsid) {
// still no clsid
char szName[MAX_PATH];
BOOL bRet = FindPlugin(szExtA, szName, szTypeA);
if (bRet) {
// found a plugin, instantiate the plugin ocx
CLSID plug = CLSID_ActiveXPlugin;
clsidout = plug;
if (ppFileName) {
lpName = new char [lstrlen(szName) + 1];
if (lpName)
lstrcpy(lpName, szName);
else
hr = E_OUTOFMEMORY;
}
} else {
// not a plugin either!
hr = S_FALSE;
}
}
if (ppFileName) {
*ppFileName = lpName;
}
DEBUG_LEAVE(hr);
return hr;
}
//BUGBUG this must be defined in a public header. Is currently in ole32\ih\ole2com.h.
#if defined(_X86_)
#define DEFAULT_ARCHITECTURE PROCESSOR_ARCHITECTURE_INTEL
#elif defined(_AMD64_)
#define DEFAULT_ARCHITECTURE PROCESSOR_ARCHITECTURE_AMD64
#elif defined(_IA64_)
#define DEFAULT_ARCHITECTURE PROCESSOR_ARCHITECTURE_IA64
#else
#define DEFAULT_ARCHITECTURE PROCESSOR_ARCHITECTURE_UNKNOWN
#endif
//+-------------------------------------------------------------------------
//
// Function: GetDefaultPlatform (internal)
//
// Synopsis: Gets the current platform
//
// Returns: none
//
//--------------------------------------------------------------------------
void
GetDefaultPlatform(CSPLATFORM *pPlatform)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"GetDefaultPlatform",
"%#x",
pPlatform
));
OSVERSIONINFO VersionInformation;
VersionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&VersionInformation);
pPlatform->dwPlatformId = VersionInformation.dwPlatformId;
pPlatform->dwVersionHi = VersionInformation.dwMajorVersion;
pPlatform->dwVersionLo = VersionInformation.dwMinorVersion;
pPlatform->dwProcessorArch = DEFAULT_ARCHITECTURE;
DEBUG_LEAVE(0);
}
//+-------------------------------------------------------------------------
//
// Function: GetActiveXSafetyProvider (internal)
//
// Synopsis: Gets the ActiveXSafetyProvider, if there is one installed.
//
// Returns: none
//
//--------------------------------------------------------------------------
HRESULT
GetActiveXSafetyProvider(IActiveXSafetyProvider **ppProvider)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"GetActiveXSafetyProvider",
"%#x",
ppProvider
));
HRESULT hr;
LONG l;
HKEY hKey;
//
// See if an IActiveXSafetyProvider is present by peeking into the
// registry.
//
l = RegOpenKeyA(HKEY_CLASSES_ROOT,
"CLSID\\{aaf8c6ce-f972-11d0-97eb-00aa00615333}",
&hKey
);
if (l != ERROR_SUCCESS) {
//
// No ActiveXSafetyProvider installed.
//
*ppProvider = NULL;
DEBUG_LEAVE(S_OK);
return S_OK;
}
RegCloseKey(hKey);
//
// Call OLE to instantiate the ActiveXSafetyProvider.
//
hr = CoCreateInstance(CLSID_IActiveXSafetyProvider,
NULL,
CLSCTX_INPROC_SERVER,
IID_IActiveXSafetyProvider,
(void **)ppProvider
);
DEBUG_LEAVE(hr);
return hr;
}
#ifdef WRAP_OLE32_COINSTALL
// This currently breaks trident for unknown reasons.
// For full class store integration (Darwin packages) this will need to be enabled!!!
// ===========================================================================
// CBSCCreateObject Implementation
// ===========================================================================
class CBSCCreateObject : public IServiceProvider,
public IBindStatusCallback
{
private:
DWORD m_cRef;
CLSID m_clsid;
LPWSTR m_szExt;
LPWSTR m_szType;
DWORD m_dwClsContext;
IID m_riid;
IBindStatusCallback* m_pclientbsc;
//IBindCtx* m_pbc;
CRITICAL_SECTION m_sect;
LPVOID m_pvReserved;
BOOL m_bObjectAvailableCalled;
DWORD m_dwFlags;
public:
CBSCCreateObject(REFCLSID rclsid, LPCWSTR szType, LPCWSTR szExt, DWORD dwClsContext,
LPVOID pvReserved, REFIID riid, IBindCtx* pbc, DWORD dwFlags, HRESULT &hr)
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CBSCCreateObject::CBSCCreateObject",
"this=%#x, %#x, %.80wq, %.80wq, %#x, %#x, %#x, %#x, %#x, %#x",
this, &rclsid, szType, szExt, dwClsContext, pvReserved, &riid, pbc, dwFlags, &hr
));
InitializeCriticalSection(&m_sect);
m_cRef=1;
m_clsid=rclsid;
m_dwClsContext=dwClsContext;
m_pvReserved=pvReserved;
m_riid=riid;
//m_pbc=NULL;
m_pclientbsc=NULL;
m_bObjectAvailableCalled=FALSE;
m_dwFlags = dwFlags;
if ( szType != NULL )
{
m_szType = new WCHAR[lstrlenW(szType) + 1];
if ( m_szType != NULL )
StrCpyW( m_szType, szType );
else
hr = E_OUTOFMEMORY;
}
else
m_szType = NULL;
if ( szExt != NULL )
{
m_szExt = new WCHAR[lstrlenW(szExt) + 1];
if ( m_szExt != NULL )
StrCpyW( m_szExt, szExt );
else
hr = E_OUTOFMEMORY;
}
else
m_szExt = NULL;
if ( SUCCEEDED(hr) )
{
// Get client's BSC and store away for delegation
hr=RegisterBindStatusCallback(pbc, (IBindStatusCallback*) this, &m_pclientbsc, NULL);
if (SUCCEEDED(hr))
{
if (m_pclientbsc)
{
//m_pbc=pbc;
//m_pbc->AddRef();
}
else
{
hr=E_INVALIDARG; // need BSC in bind context!
}
}
}
DEBUG_LEAVE(0);
}
~CBSCCreateObject()
{
DEBUG_ENTER((DBG_DOWNLOAD,
None,
"CBSCCreateObject::~CBSCCreateObject",
"this=%#x",
this
));
//RevokeFromBC();
if ( m_szType != NULL )
delete m_szType;
if ( m_szExt != NULL )
delete m_szExt;
if (m_pclientbsc)
{
IBindStatusCallback* pclientbsc=m_pclientbsc;
m_pclientbsc=NULL;
pclientbsc->Release();
}
DeleteCriticalSection(&m_sect);
DEBUG_LEAVE(0);
}
IBindStatusCallback* GetClientBSC()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Pointer,
"CBSCCreateObject::GetClientBSC",
"this=%#x",
this
));
EnterCriticalSection(&m_sect);
IBindStatusCallback* pbsc=m_pclientbsc;
LeaveCriticalSection(&m_sect);
DEBUG_LEAVE(pbsc);
return pbsc;
}
/* HRESULT RevokeFromBC()
{
// Remove from BSC and reestablish original BSC
HRESULT hr=S_OK;
EnterCriticalSection(&m_sect);
if (m_pbc)
{
IBindCtx* pbc=m_pbc;
IBindStatusCallback* pclientbsc=m_pclientbsc;
m_pbc=NULL;
m_pclientbsc=NULL;
LeaveCriticalSection(&m_sect);
hr=RegisterBindStatusCallback(pbc, pclientbsc, NULL, NULL);
pbc->Release();
if (pclientbsc)
{
pclientbsc->Release();
}
}
else
{
LeaveCriticalSection(&m_sect);
}
return hr;
}
*/
STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IUnknown::QueryInterface",
"this=%#x, %#x, %#x",
this, &riid, ppv
));
HRESULT hr = S_OK;
*ppv = NULL;
if(IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IBindStatusCallback))
{
AddRef();
*ppv = (IBindStatusCallback *) this;
}
else if(IsEqualIID(riid, IID_IServiceProvider))
{
AddRef();
*ppv = (IServiceProvider *) this;
}
else
{
hr = E_NOINTERFACE;
}
DEBUG_LEAVE(hr);
return hr;
}
STDMETHODIMP_(ULONG) AddRef()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IUnknown::AddRef",
"this=%#x",
this
));
InterlockedIncrement((long *) &m_cRef);
DEBUG_LEAVE(m_cRef);
return m_cRef;
}
STDMETHODIMP_(ULONG) Release()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IUnknown::Release",
"this=%#x",
this
));
LONG count = m_cRef - 1;
if(0 == InterlockedDecrement((long *) &m_cRef))
{
delete this;
count = 0;
}
DEBUG_LEAVE(count);
return count;
}
// IBindStatusCallback/Holder
STDMETHODIMP OnStartBinding(DWORD grfBSCOption, IBinding* pbinding)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IBindStatusCallback::OnStartBinding",
"this=%#x, %#x, %#x",
this, grfBSCOption, pbinding
));
m_bObjectAvailableCalled=FALSE;
IBindStatusCallback* pclientbsc=GetClientBSC();
HRESULT hr = S_OK;
if (pclientbsc)
hr = pclientbsc->OnStartBinding(grfBSCOption, pbinding);
DEBUG_LEAVE(hr);
return hr;
}
STDMETHODIMP GetPriority(LONG* pnPriority)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IBindStatusCallback::GetPriority",
"this=%#x, %#x",
this, pnPriority
));
IBindStatusCallback* pclientbsc=GetClientBSC();
HRESULT hr = S_OK;
if (pclientbsc)
hr = pclientbsc->GetPriority(pnPriority);
DEBUG_LEAVE(hr);
return hr;
}
STDMETHODIMP OnLowResource(DWORD dwReserved)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IBindStatusCallback::OnLowResource",
"this=%#x, %#x",
this, dwReserved
));
IBindStatusCallback* pclientbsc=GetClientBSC();
HRESULT hr = S_OK;
if (pclientbsc)
hr = pclientbsc->OnLowResource(dwReserved);
DEBUG_LEAVE(hr);
return hr;
}
STDMETHODIMP OnStopBinding(HRESULT hrStatus, LPCWSTR pszError)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IBindStatusCallback::OnStopBinding",
"this=%#x, %#x, %.80wq",
this, hrStatus, pszError
));
HRESULT hr=S_OK;
// Save client BSC for last notification
IBindStatusCallback* pclientbsc=GetClientBSC();
if (!pclientbsc)
{
DEBUG_LEAVE(hr);
return hr;
}
pclientbsc->AddRef();
// We are done with this bind context! Unregister before caller unregisters it's IBSC
//hr=RevokeFromBC();
if (SUCCEEDED(hrStatus))
{
if (!m_bObjectAvailableCalled && (m_dwFlags & CD_FLAGS_NEED_CLASSFACTORY ) )
{
IUnknown* punk=NULL;
CLSID clsid;
hr = GetClsidFromExtOrMime( m_clsid, clsid, m_szExt, m_szType, NULL );
if ( SUCCEEDED(hr) )
hr = CoGetClassObject(clsid, m_dwClsContext, m_pvReserved, m_riid, (void**) &punk);
if (FAILED(hr))
{
hr = pclientbsc->OnStopBinding(hr, L"");
pclientbsc->Release();
DEBUG_LEAVE(hr);
return hr;
}
pclientbsc->OnObjectAvailable(m_riid, punk);
// release the IUnkown returned by CoGetClassObject
punk->Release();
m_bObjectAvailableCalled=TRUE;
}
}
hr=pclientbsc->OnStopBinding(hrStatus, pszError);
pclientbsc->Release();
DEBUG_LEAVE(hr);
return hr;
}
STDMETHODIMP GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IBindStatusCallback::GetBindInfo",
"this=%#x, %#x, %#x",
this, pgrfBINDF, pbindInfo
));
IBindStatusCallback* pclientbsc=GetClientBSC();
HRESULT hr = S_OK;
if (pclientbsc)
hr = pclientbsc->GetBindInfo(pgrfBINDF, pbindInfo);
DEBUG_LEAVE(hr);
return hr;
}
STDMETHODIMP OnDataAvailable(
DWORD grfBSCF,
DWORD dwSize,
FORMATETC* pfmtetc,
STGMEDIUM* pstgmed)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IBindStatusCallback::OnDataAvailable",
"this=%#x, %#x, %#x, %#x, %#x",
this, grfBSCF, dwSize, pfmtetc, pstgmed
));
IBindStatusCallback* pclientbsc=GetClientBSC();
HRESULT hr = S_OK;
if (pclientbsc)
hr = pclientbsc->OnDataAvailable(grfBSCF, dwSize, pfmtetc, pstgmed);
DEBUG_LEAVE(hr);
return hr;
}
STDMETHODIMP OnObjectAvailable(REFIID riid, IUnknown* punk)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IBindStatusCallback::OnObjectAvailable",
"this=%#x, %#x, %#x",
this, &riid, punk
));
HRESULT hr=S_OK;
// Save client BSC
IBindStatusCallback* pclientbsc=GetClientBSC();
if (pclientbsc) {
pclientbsc->AddRef();
hr=pclientbsc->OnObjectAvailable(riid, punk);
pclientbsc->Release();
m_bObjectAvailableCalled=TRUE;
}
DEBUG_LEAVE(hr);
return hr;
}
STDMETHODIMP OnProgress
(
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR pwzStatusText
)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IBindStatusCallback::OnProgress",
"this=%#x, %#x, %#x, %#x, %.80wq",
this, ulProgress, ulProgressMax, ulStatusCode, pwzStatusText
));
IBindStatusCallback* pclientbsc=GetClientBSC();
HRESULT hr = S_OK;
if (pclientbsc)
hr = pclientbsc->OnProgress(ulProgress, ulProgressMax, ulStatusCode, pwzStatusText);
DEBUG_LEAVE(hr);
return hr;
}
// IServiceProvider, chains all other interfaces
STDMETHODIMP QueryService(
REFGUID rsid,
REFIID iid,
void **ppvObj)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CBSCCreateObject::IserviceProvider::QueryService",
"this=%#x, %#x, %#x, %#x",
this, &rsid, &iid, ppvObj
));
HRESULT hr=S_OK;
IServiceProvider* pclientsp=NULL;
hr=m_pclientbsc->QueryInterface(IID_IServiceProvider, (void**) &pclientsp);
if (SUCCEEDED(hr))
{
hr=pclientsp->QueryService(rsid, iid, ppvObj);
pclientsp->Release();
if (SUCCEEDED(hr))
{
DEBUG_LEAVE(hr);
return hr;
}
}
IBindStatusCallback* pclientbsc=GetClientBSC();
if (pclientbsc)
hr = pclientbsc->QueryInterface(iid, ppvObj);
else
hr = QueryInterface(iid, ppvObj);
DEBUG_LEAVE(hr);
return hr;
}
};
#endif // WRAP_OLE32_COINSTALL
/*
* CoGetClassObjectFromURL
*
* This is the exposed entry point into the Code Downloader.
*
* It takes parameters closely matching the INSERT tag
* REFCLSID rCLASSID, // CLSID of object (may be NULL)
* LPCWSTR szCODE, // URL to code (may be NULL)
* DWORD dwFileVersionMS, // Version of primary object
* DWORD dwFileVersionLS, // Version of primary object
* LPCWSTR szTYPE, // MIME type (may be NULL)
* LPBINDCTX pBindCtx, // Bind ctx
* DWORD dwClsContext, // CLSCTX flags
* LPVOID pvReserved, // Must be NULL
* REFIID riid, // Usually IID_IClassFactory
* LPVOID * ppv // Ret - usually IClassFactory *
*/
STDAPI
CoGetClassObjectFromURL (
REFCLSID rCLASSID, // CLSID of object (may be NULL)
LPCWSTR szCODE, // URL to code (may be NULL)
DWORD dwFileVersionMS, // Version of primary object
DWORD dwFileVersionLS, // Version of primary object
LPCWSTR szTYPE, // MIME type (may be NULL)
LPBINDCTX pBindCtx, // Bind ctx
DWORD dwClsContext, // CLSCTX flags
LPVOID pvReserved, // Must be NULL
REFIID riid, // Usually IID_IClassFactory
LPVOID * ppv // Ret - usually IClassFactory *
)
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"CoGetClassObjectFromURL",
"%#x, %.80wq, %#x, %#x, %.80wq, %#x, %#x, %#x, %#x, %#x",
&rCLASSID, szCODE, dwFileVersionMS, dwFileVersionLS, szTYPE, pBindCtx,
dwClsContext, pvReserved, &riid, ppv
));
HRESULT hr = NO_ERROR;
CLSID myclsid;
int nForceDownload = 0;
CDLDebugLog * pdlog = NULL;
CodeDownloadData cdd;
LPWSTR pwszClsid = NULL;
WCHAR szCleanCODE[INTERNET_MAX_URL_LENGTH];
LPSTR szTmp = NULL;
LPWSTR wzPtr = NULL;
LPSTR szBuf = NULL;
cdd.szDistUnit = NULL;
cdd.szClassString = NULL;
cdd.szURL = NULL;
cdd.szExtension = NULL;
cdd.szMimeType = szTYPE;
cdd.szDll = NULL;
cdd.dwFileVersionMS = dwFileVersionMS;
cdd.dwFileVersionLS = dwFileVersionLS;
cdd.dwFlags = CD_FLAGS_NEED_CLASSFACTORY;
if (szCODE) {
StrCpyNW(szCleanCODE, szCODE, INTERNET_MAX_URL_LENGTH);
wzPtr = szCleanCODE;
while (*wzPtr && *wzPtr != L'#') {
wzPtr++;
}
*wzPtr = L'\0';
cdd.szURL = szCleanCODE;
if (FAILED(Unicode2Ansi(szCODE, &szBuf))) {
goto Exit;
}
szTmp = szBuf;
}
if (szTmp) {
while (*szTmp && *szTmp != L'#') {
szTmp++;
}
if (*szTmp == L'#') {
// Actual codebase ends here. Anchor with NULL char.
*szTmp = L'\0';
szTmp++;
// parsing code
// Only allow: CODEBASE=http://foo.com#VERSION=1,0,0,0
// or: CODEBASE=http://foo.com#EXACTVERSION=1,0,0,0
LPSTR szStart = szTmp;
while (*szTmp && *szTmp != '=') {
szTmp++;
}
if (*szTmp) {
// Found '=' delimiter. Anchor NULL
*szTmp = '\0';
szTmp++;
if (!StrCmpI(szStart, "version")) {
GetVersionFromString(szTmp, &dwFileVersionMS, &dwFileVersionLS, ',');
cdd.dwFileVersionMS = dwFileVersionMS;
cdd.dwFileVersionLS = dwFileVersionLS;
}
else if (!StrCmpI(szStart, ("exactversion"))) {
cdd.dwFlags |= CD_FLAGS_EXACT_VERSION;
GetVersionFromString(szTmp, &dwFileVersionMS, &dwFileVersionLS, ',');
cdd.dwFileVersionMS = dwFileVersionMS;
cdd.dwFileVersionLS = dwFileVersionLS;
}
}
}
}
hr = StringFromCLSID(rCLASSID, &pwszClsid);
if(FAILED(hr))
pwszClsid = NULL;
// Prepare the debuglog for this download
pdlog = CDLDebugLog::MakeDebugLog();
if(pdlog)
{
pdlog->AddRef();
// If there is some way to name the debug log, go ahead and initialize it
if(pdlog->Init(pwszClsid, cdd.szMimeType, cdd.szExtension, cdd.szURL) != FALSE)
{
CDLDebugLog::AddDebugLog(pdlog);
}
else
{
pdlog->Release();
pdlog = NULL;
}
}
UrlMkDebugOut((DEB_CODEDL, "IN CoGetClassObjectFromURL CLASSID: %lx, TYPE=%ws...szCODE:(%ws), VersionMS:%lx, VersionLS:%lx\n",
rCLASSID.Data1, cdd.szMimeType, cdd.szURL, cdd.dwFileVersionMS, cdd.dwFileVersionLS));
forcedownload:
// Note about pvReserved: this is used by DCOM to get in the remote server
// name and other info. If not NULL, then the caller wants us to use
// dcom. if (pvReserved) just call CoGetClassObject.
// call AsyncGetClassBits to do the real work
if (pvReserved == NULL && (nForceDownload <= 1) )
hr = AsyncGetClassBitsEx(rCLASSID, &cdd,
pBindCtx, dwClsContext, pvReserved, riid);
if (SUCCEEDED(hr) && (hr != MK_S_ASYNCHRONOUS)) {
hr = GetClsidFromExtOrMime( rCLASSID, myclsid, cdd.szExtension, cdd.szMimeType, NULL);
Assert(hr == S_OK);
if (hr != S_OK)
{
hr = E_UNEXPECTED;
if(pdlog)
pdlog->DebugOut(DEB_CODEDL, TRUE, ID_CDLDBG_FAILED_CONVERT_CLSID, cdd.szExtension, cdd.szMimeType);
goto Exit;
}
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"EXTERNAL::CoGetClassObject",
"%#x, %#x, %#x, %#x, %#x",
&myclsid, dwClsContext, pvReserved, &riid, ppv
));
hr = CoGetClassObject(myclsid, dwClsContext, pvReserved, riid, ppv);
DEBUG_LEAVE(hr);
// BUGBUG: move this policy into the client with a CodeInstallProblem?
// this hack is easier than reprocessing INF from previous download
// if we're not using dcom, and the api thinks we're good to go (didn't need to
// install), yet for these reasons we couldn't get the class object, force a download
// If there's some other error, or the get class objct was successful, quit.
if ( !pvReserved && ((hr == HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND)) ||
(hr == REGDB_E_CLASSNOTREG) ||
(hr == HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND))) ) {
if (hr == REGDB_E_CLASSNOTREG) {
// if its a hack GUID that is not a COM object but just to
// get the OBJECT tag to automatically slam a reg key or
// install some software (like java vm) then don't
// force an install
if (!AdviseForceDownload(&myclsid, dwClsContext))
goto Exit;
}
UrlMkDebugOut((DEB_CODEDL, "WRN CoGetClassObjectFromURL hr:%lx%s, CLASSID: %lx..., szCODE:(%ws), VersionMS:%lx, VersionLS:%lx\n",
hr,": dependent dll probably missing, forcing download!",myclsid.Data1, cdd.szURL, cdd.dwFileVersionMS, cdd.dwFileVersionLS));
cdd.dwFlags |= CD_FLAGS_FORCE_DOWNLOAD;
nForceDownload++;
goto forcedownload;
}
// falling through to exit
}
Exit:
// If we had some problem, dump the debuglog
if(FAILED(hr) && pdlog)
{
pdlog->DumpDebugLog(NULL, 0, NULL, hr);
}
if(pwszClsid)
delete pwszClsid;
UrlMkDebugOut((DEB_CODEDL, "OUT CoGetClassObjectFromURL hr:%lx%s, CLASSID: %lx, TYPE=%ws..., szCODE:(%ws), VersionMS:%lx, VersionLS:%lx\n",
hr,(hr == MK_S_ASYNCHRONOUS)?"(PENDING)":(hr == S_OK)?"(SUCCESS)":"",rCLASSID.Data1, cdd.szMimeType, cdd.szURL, cdd.dwFileVersionMS, cdd.dwFileVersionLS));
if(pdlog)
{
CDLDebugLog::RemoveDebugLog(pdlog);
pdlog->Release();
pdlog = NULL;
}
if (szBuf) {
delete [] szBuf;
}
DEBUG_LEAVE_API(hr);
return hr;
}
/*
* SetCodeDownloadTLSVars
*
* sets up a bunch of tls variables
* eg, setup cookie, trust cookie list, code download list and
* the CDLPacket list
* since TLS vars are HeapAllocated rather than using the new operator
* the constructors for classes won't get run automatically
* which is why we have all pointers and the actual classes get allocated
* here
*/
HRESULT SetCodeDownloadTLSVars()
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"SetCodeDownloadTLSVars",
NULL
));
HRESULT hr = NO_ERROR;
CUrlMkTls tls(hr); // hr passed by reference!
if (FAILED(hr)) // if tls ctor failed above
goto Exit;
if (!tls->pSetupCookie) {
tls->pSetupCookie = new CCookie<CCodeDownload *>(CODE_DOWNLOAD_SETUP);
if (!tls->pSetupCookie) {
hr = E_OUTOFMEMORY;
goto Exit;
}
}
if (!tls->pTrustCookie) {
tls->pTrustCookie = new CCookie<CDownload *>(CODE_DOWNLOAD_TRUST_PIECE);
if (!tls->pTrustCookie) {
hr = E_OUTOFMEMORY;
goto Exit;
}
}
if (!tls->pCodeDownloadList) {
tls->pCodeDownloadList = new CList<CCodeDownload *, CCodeDownload *>;
if (!tls->pCodeDownloadList) {
hr = E_OUTOFMEMORY;
goto Exit;
}
}
if (!tls->pRejectedFeaturesList) {
tls->pRejectedFeaturesList= new CList<LPCWSTR , LPCWSTR >;
if (!tls->pRejectedFeaturesList) {
hr = E_OUTOFMEMORY;
goto Exit;
}
}
if (!tls->pCDLPacketMgr) {
tls->pCDLPacketMgr = new CCDLPacketMgr();
if (!tls->pCDLPacketMgr) {
hr = E_OUTOFMEMORY;
goto Exit;
}
}
Exit:
DEBUG_LEAVE(hr);
return hr;
}
/*
* CoInstall
*
*
* CoInstall() is a public API, also used by CoGetClassObjectFromUrl
*
* It's primary implementation is in OLE32.
*/
STDAPI CoInstall(
IBindCtx *pbc,
DWORD dwFlags,
uCLSSPEC *pClassSpec,
QUERYCONTEXT *pQuery,
LPWSTR pszCodeBase)
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"CoInstall",
"%#x, %#x, %#x, %#x, %.80wq",
pbc, dwFlags, pClassSpec, pQuery, pszCodeBase
));
HRESULT hr;
// BUGBUG: The real CoInstall needs this always set for now. MSICD/PrivateCoInstall shouldn't,
// so we'll only set it in the case where we're calling NT5 OLE.
// Setting this flag unconditionally may have bad side effects on offline mode.
dwFlags = dwFlags | CD_FLAGS_FORCE_INTERNET_DOWNLOAD;
#ifndef WRAP_OLE32_COINSTALL
dwFlags = dwFlags | CD_FLAGS_NEED_CLASSFACTORY;
#else
dwFlags = dwFlags & (~CD_FLAGS_NEED_CLASSFACTORY);
#endif
hr = SetCoInstall();
if ( SUCCEEDED(hr) )
{
if ( g_bUseOLECoInstall )
hr = (*g_pfnCoInstall)(pbc, dwFlags, pClassSpec, pQuery, pszCodeBase);
else
hr = PrivateCoInstall(pbc, dwFlags, pClassSpec, pQuery, pszCodeBase);
}
DEBUG_LEAVE_API(hr);
return hr;
};
/*
* AsyncInstallDistUnit
*
*
* AsyncInstallDistributionUnit() the entry into the Code downloader creates this obj
* for the given CODE, DistUnit, FileVersion, BSC (from BindCtx)
*
* The CodeDownload obj once created is asked to perform its function
* thru CCodeDownload::DoCodeDownload().
*/
STDAPI
AsyncInstallDistributionUnitEx(
CodeDownloadData * pcdd, // Contains requested object's descriptors
IBindCtx *pbc, // bind ctx
REFIID riid,
IUnknown **ppUnk,
LPVOID pvReserved) // Must be NULL
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"AsyncInstallDistributionUnitEx",
"%#x, %#x, %#x, %#x, %#x",
pcdd, pbc, &riid, ppUnk, pvReserved
));
LPCWSTR szClientID = NULL;
pcdd->dwFlags &= (CD_FLAGS_EXTERNAL_MASK | CD_FLAGS_EXACT_VERSION);
HRESULT hr = AsyncGetClassBits2Ex(szClientID, pcdd, pbc, 0, NULL, riid, ppUnk);
DEBUG_LEAVE_API(hr);
return hr;
}
// backwards compatability (no dll name parameter)
STDAPI
AsyncInstallDistributionUnit(
LPCWSTR szDistUnit,
LPCWSTR szTYPE,
LPCWSTR szExt,
DWORD dwFileVersionMS, // CODEBASE=http://foo#Version=a,b,c,d
DWORD dwFileVersionLS, // MAKEDWORD(c,b) of above
LPCWSTR szURL, // CODEBASE
IBindCtx *pbc, // bind ctx
LPVOID pvReserved, // Must be NULL
DWORD flags)
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"AsyncInstallDistributionUnit",
"%.80wq, %.80wq, %.80wq, %#x, %#x, %.80wq, %#x, %#x, %#x",
szDistUnit, szTYPE, szExt, dwFileVersionMS, dwFileVersionLS, szURL, pbc, pvReserved, flags
));
CodeDownloadData cdd;
cdd.szDistUnit = szDistUnit;
cdd.szClassString = szDistUnit; // best choice we've got
cdd.szURL = szURL;
cdd.szMimeType = szTYPE;
cdd.szExtension = szExt;
cdd.szDll = NULL;
cdd.dwFileVersionMS = dwFileVersionMS;
cdd.dwFileVersionLS = dwFileVersionLS;
cdd.dwFlags = flags;
HRESULT hr = AsyncInstallDistributionUnitEx(&cdd, pbc, IID_IUnknown, NULL, pvReserved);
DEBUG_LEAVE_API(hr);
return hr;
}
/*
* AsyncGetClassBits
*
*
* AsyncGetClassBits() the entry into the Code downloader creates this obj
* for the given CODE, CLSID, FileVersion, BSC (from BindCtx)
* we do not check to see if a code download is already in progress
* in the system at a given moment. Nor we do we keep track of individual
* downloads and possible clashes between various silmultaneous code
* downloads system wide. We leave it to URL moniker (above us) to ensure
* that duplicate calls are not made into AsynGetClassBits. The second
* problem of different code downloads trying to bring down a common
* dependent DLL is POSTPONED to version 2 implementation.
*
* The CodeDownload obj once created is asked to perform its function
* thru CCodeDownload::DoCodeDownload().
*/
STDAPI
AsyncGetClassBits(
REFCLSID rclsid, // CLSID
LPCWSTR szTYPE,
LPCWSTR szExt,
DWORD dwFileVersionMS, // CODE=http://foo#Version=a,b,c,d
DWORD dwFileVersionLS, // MAKEDWORD(c,b) of above
LPCWSTR szURL, // CODE= in INSERT tag
IBindCtx *pbc, // bind ctx
DWORD dwClsContext, // CLSCTX flags
LPVOID pvReserved, // Must be NULL
REFIID riid, // Usually IID_IClassFactory
DWORD flags)
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"AsyncGetClassBits",
"%#x, %.80wq, %.80wq, %#x, %#x, %.80wq, %#x, %#x, %#x, %#x, %#x",
&rclsid, szTYPE, szExt, dwFileVersionMS, dwFileVersionLS, szURL, pbc, dwClsContext, pvReserved, &riid, flags
));
CodeDownloadData cdd;
cdd.szDistUnit = NULL;
cdd.szClassString = NULL;
cdd.szURL = szURL;
cdd.szMimeType = szTYPE;
cdd.szExtension = szExt;
cdd.szDll = NULL;
cdd.dwFileVersionMS = dwFileVersionMS;
cdd.dwFileVersionLS = dwFileVersionLS;
cdd.dwFlags = flags;
HRESULT hr = AsyncGetClassBitsEx(rclsid, &cdd, pbc, dwClsContext, pvReserved, riid);
DEBUG_LEAVE_API(hr);
return hr;
}
STDAPI
AsyncGetClassBitsEx(
REFCLSID rclsid, // CLSID
CodeDownloadData *pcdd,
IBindCtx *pbc, // bind ctx
DWORD dwClsContext, // CLSCTX flags
LPVOID pvReserved, // Must be NULL
REFIID riid) // Usually IID_IClassFactory
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"AsyncGetClassBitsEx",
"%#x, %#x, %#x, %#x, %#x, %#x",
&rclsid, pcdd, pbc, dwClsContext, pvReserved, &riid
));
LPOLESTR pwcsClsid = NULL;
LPCWSTR szClientID = NULL;
HRESULT hr=S_OK;
pcdd->dwFlags &= (CD_FLAGS_EXTERNAL_MASK | CD_FLAGS_EXACT_VERSION);
CDLDebugLog * pdlog = NULL;
// return if we can't get a valid string representation of the CLSID
if (!IsEqualGUID(rclsid , CLSID_NULL) &&
(FAILED((hr=StringFromCLSID(rclsid, &pwcsClsid)))) )
{
pdlog = CDLDebugLog::GetDebugLog(NULL, pcdd->szMimeType,
pcdd->szExtension, pcdd->szURL);
if(pdlog)
{
pdlog->DebugOut(DEB_CODEDL, TRUE, ID_CDLDBG_FAILED_STRING_FROM_CLSID);
}
goto Exit;
}
pcdd->szDistUnit = pwcsClsid;
pcdd->szClassString = pwcsClsid; // dist unit and clsid are the same from this entry point
#ifndef WRAP_OLE32_COINSTALL
// Work around a problem with OLE32's CoInstall (until wrappers are enabled or OLE calls PrivateCoInstall):
// since CoInstall doesn't known the IID,
// it calls AsyncGetClassBits with IID_IUnknown (instead of IID_IClassFactory)
if ((pcdd->dwFlags & CD_FLAGS_FORCE_INTERNET_DOWNLOAD) && IsEqualGUID(riid, IID_IUnknown))
{
hr = AsyncGetClassBits2Ex(szClientID, pcdd,
pbc,dwClsContext,pvReserved,IID_IClassFactory,NULL);
}
else
{
#endif
hr = AsyncGetClassBits2Ex(szClientID, pcdd,
pbc,dwClsContext,pvReserved,riid,NULL);
#ifndef WRAP_OLE32_COINSTALL
}
#endif
Exit:
if (pwcsClsid != NULL)
delete pwcsClsid;
DEBUG_LEAVE_API(hr);
return hr;
}
HRESULT GetIEFeatureVersion(
LPCWSTR szDistUnit, // CLSID, can be an arbit unique str
LPCWSTR szTYPE,
CLSID clsid,
QUERYCONTEXT *pqc)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"GetIEFeatureVersion",
"%.80wq, %.80wq, %#x, %#x",
szDistUnit, szTYPE, &clsid, pqc
));
HRESULT hr = S_OK;
memset(pqc, 0, sizeof(QUERYCONTEXT));
if (!IsEqualGUID(clsid, CLSID_NULL)) {
hr = GetIEFeatureFromClass(NULL, clsid, pqc);
} else if (szTYPE) {
hr = GetIEFeatureFromMime(NULL, szTYPE, pqc);
} else {
// not an IE feature
hr = S_FALSE;
goto Exit;
}
Exit:
DEBUG_LEAVE(hr);
return hr;
}
HRESULT InstallIEFeature(
LPCWSTR szDistUnit, // CLSID, can be an arbit unique str
LPCWSTR szTYPE,
CLSID clsid,
IBindCtx *pbc,
DWORD dwFileVersionMS,
DWORD dwFileVersionLS,
DWORD dwFlags,
BOOL bIEVersion
)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"InstallIEFeature",
"%.80wq, %.80wq, %#x, %#x, %#x, %#x, %#x, %B",
szDistUnit, szTYPE, &clsid, pbc, dwFileVersionMS, dwFileVersionLS, dwFlags, bIEVersion
));
HRESULT hr = S_OK;
uCLSSPEC classpec;
IWindowForBindingUI *pWindowForBindingUI = NULL;
IBindStatusCallback *pclientbsc = NULL;
HWND hWnd = NULL;
REFGUID rguidReason = IID_ICodeInstall;
QUERYCONTEXT qc;
DWORD dwJITFlags = 0;
memset(&qc, 0, sizeof(qc));
qc.dwVersionHi = dwFileVersionMS;
qc.dwVersionLo = dwFileVersionLS;
if (!IsEqualGUID(clsid, CLSID_NULL)) {
classpec.tyspec=TYSPEC_CLSID;
classpec.tagged_union.clsid=clsid;
} else if (szTYPE) {
classpec.tyspec=TYSPEC_MIMETYPE;
classpec.tagged_union.pMimeType=(LPWSTR)szTYPE;
} else {
// not an IE feature
hr = S_FALSE;
goto Exit;
}
hr = FaultInIEFeature(hWnd, &classpec, &qc, (bIEVersion?0:FIEF_FLAG_CHECK_CIFVERSION)|FIEF_FLAG_PEEK);
if ( hr == HRESULT_FROM_WIN32(ERROR_UNKNOWN_REVISION) ||
hr == E_ACCESSDENIED) {
// the version in the CIF will not satisfy the requested version
// or the admin has turned off JIT or the user has turned off
// JIT in Inetcpl: if they wanted to turn off code download
// they should do it per-zone activex signed/unsigned policy.
// fall thru to code download from the CODEBASE
hr = S_FALSE;
}
if (hr == S_OK) {
// We always come in here from code downloader at a point
// where looking at the com branch and DU key, something is
// busted and so needs code download. We can't at this point
// succeed. Likely the AS keys are just orphaned and it looks
// installed, but is not.
// However, to account for cases where JIT could be right
// we will only force JIT download when instrcuted to
if (dwFlags & CD_FLAGS_FORCE_DOWNLOAD) {
hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_UNINSTALLED);
dwJITFlags |= FIEF_FLAG_SKIP_INSTALLED_VERSION_CHECK;
}
}
if (hr != HRESULT_FROM_WIN32(ERROR_PRODUCT_UNINSTALLED)) {
goto Exit;
}
// must really install now, get an hwnd
hr = pbc->GetObjectParam(REG_BSCB_HOLDER, (IUnknown **)&pclientbsc);
if (FAILED(hr))
goto Exit;
// don't JIT if web crawling
{
DWORD grfBINDF = 0;
BINDINFO bindInfo;
memset(&bindInfo, 0, sizeof(BINDINFO));
bindInfo.cbSize = sizeof(BINDINFO);
pclientbsc->GetBindInfo(&grfBINDF, &bindInfo);
ReleaseBindInfo(&bindInfo);
if (grfBINDF & BINDF_SILENTOPERATION)
{
hr = MK_E_MUSTBOTHERUSER;
goto Exit;
}
}
// Get IWindowForBindingUI ptr
hr = pclientbsc->QueryInterface(IID_IWindowForBindingUI,
(LPVOID *)&pWindowForBindingUI);
if (FAILED(hr)) {
IServiceProvider *pServProv;
hr = pclientbsc->QueryInterface(IID_IServiceProvider,
(LPVOID *)&pServProv);
if (hr == NOERROR) {
pServProv->QueryService(IID_IWindowForBindingUI,IID_IWindowForBindingUI,
(LPVOID *)&pWindowForBindingUI);
pServProv->Release();
}
}
// get hWnd
if (pWindowForBindingUI) {
pWindowForBindingUI->GetWindow(rguidReason, &hWnd);
pWindowForBindingUI->Release();
memset(&qc, 0, sizeof(qc)); // reset, peek state modifies this with
// currently installed version info!
qc.dwVersionHi = dwFileVersionMS;
qc.dwVersionLo = dwFileVersionLS;
hr = FaultInIEFeature(hWnd, &classpec, &qc, dwJITFlags);
}
else {
hr = MK_E_MUSTBOTHERUSER;
// fallthru to Exit
// goto Exit;
}
Exit:
if (pclientbsc)
pclientbsc->Release();
DEBUG_LEAVE(hr);
return hr;
}
// if both signed and unsigned activex is disallowed then return failure
// so we can avoid downloading the CAB altogether
HRESULT
CheckActiveXDownloadEnabled(
IInternetHostSecurityManager *pHostSecurityManager,
LPCWSTR szCodebase,
IBindStatusCallback* pBSC)
{
DEBUG_ENTER((DBG_DOWNLOAD,
Hresult,
"CheckActiveXDownloadEnabled",
"%#x, %.80wq",
pHostSecurityManager, szCodebase
));
HRESULT hr = S_OK;
DWORD dwPolicy;
DWORD grfBINDF = 0;
BINDINFO bindInfo;
memset(&bindInfo, 0, sizeof(BINDINFO));
bindInfo.cbSize = sizeof(BINDINFO);
BOOL fEnforceRestricted = FALSE;
hr = pBSC->GetBindInfo(&grfBINDF, &bindInfo);
if (SUCCEEDED(hr))
{
ReleaseBindInfo(&bindInfo);
if (grfBINDF & BINDF_ENFORCERESTRICTED)
fEnforceRestricted = TRUE;
}
hr = GetActivePolicy(pHostSecurityManager, szCodebase,
URLACTION_DOWNLOAD_SIGNED_ACTIVEX, dwPolicy, fEnforceRestricted);
if (FAILED(hr)) {
hr = GetActivePolicy(pHostSecurityManager, szCodebase,
URLACTION_DOWNLOAD_UNSIGNED_ACTIVEX, dwPolicy, fEnforceRestricted);
}
DEBUG_LEAVE(hr);
return hr;
}
// backwards compatability
STDAPI
AsyncGetClassBits2(
LPCWSTR szClientID, // client ID, root object if NULL
LPCWSTR szDistUnit, // CLSID, can be an arbit unique str
LPCWSTR szTYPE,
LPCWSTR szExt,
DWORD dwFileVersionMS, // CODE=http://foo#Version=a,b,c,d
DWORD dwFileVersionLS, // MAKEDWORD(c,b) of above
LPCWSTR szURL, // CODE= in INSERT tag
IBindCtx *pbc, // bind ctx
DWORD dwClsContext, // CLSCTX flags
LPVOID pvReserved, // Must be NULL
REFIID riid, // Usually IID_IClassFactory
DWORD flags)
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"AsyncGetClassBits2",
"%.80wq, %.80wq, %.80wq, %.80wq, %#x, %#x, %.80wq, %#x, %#x, %#x, %#x, %#x",
szClientID, szDistUnit, szTYPE, szExt, dwFileVersionMS, dwFileVersionLS, szURL, pbc, dwClsContext, pvReserved, &riid, flags
));
CodeDownloadData cdd;
cdd.szDistUnit = szDistUnit;
cdd.szClassString = szDistUnit; // best choice we've got
cdd.szURL = szURL;
cdd.szMimeType = szTYPE;
cdd.szExtension = szExt;
cdd.szDll = NULL;
cdd.dwFileVersionMS = dwFileVersionMS;
cdd.dwFileVersionLS = dwFileVersionLS;
cdd.dwFlags = flags;
HRESULT hr = AsyncGetClassBits2Ex(szClientID,&cdd,pbc,dwClsContext,
pvReserved,riid,NULL);
DEBUG_LEAVE_API(hr);
return hr;
}
STDAPI
AsyncGetClassBits2Ex(
LPCWSTR szClientID, // client ID, root object if NULL
CodeDownloadData * pcdd,
IBindCtx *pbc, // bind ctx
DWORD dwClsContext, // CLSCTX flags
LPVOID pvReserved, // Must be NULL
REFIID riid, // Usually IID_IClassFactory
IUnknown **ppUnk) // pass back pUnk for synchronous case
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"AsyncGetClassBits2Ex",
"%.80wq, %#x, %#x, %#x, %#x, %#x, %#x",
szClientID, pcdd, pbc, dwClsContext, pvReserved, &riid, ppUnk
));
LPCWSTR szDistUnit = pcdd->szDistUnit; // Name of dist unit, may be a clsid
LPCWSTR szClassString = pcdd->szClassString; // Clsid to call com with/get object on
LPCWSTR szTYPE = pcdd->szMimeType;
LPCWSTR szExt = pcdd->szExtension;
DWORD dwFileVersionMS = pcdd->dwFileVersionMS; // CODE=http://foo#Version=a,b,c,d
DWORD dwFileVersionLS = pcdd->dwFileVersionLS; // MAKEDWORD(c,b) of above
LPCWSTR szURL = pcdd->szURL; // CODE= in INSERT tag
LPCWSTR szDll = pcdd->szDll; // Dll name for zero impact (can be NULL)
DWORD flags = pcdd->dwFlags;
CCodeDownload* pcdl = NULL;
HRESULT hr = NO_ERROR;
IBindStatusCallback *pclientbsc = NULL;
LISTPOSITION pos;
CClBinding *pClientbinding;
char szExistingBuf[MAX_PATH];
DWORD cExistingSize = MAX_PATH;
char *pBaseExistingName = NULL;
HKEY hKeyIESettings = 0;
LONG lResult;
CLSID myclsid;
CLSID inclsid = CLSID_NULL;
LPSTR pPluginFileName = NULL;
BOOL bHintActiveX = (flags & CD_FLAGS_HINT_ACTIVEX)?TRUE:FALSE;
BOOL bHintJava = (flags & CD_FLAGS_HINT_JAVA)?TRUE:FALSE;
BOOL bNullClsid;
BOOL bIEVersion = FALSE;
CLocalComponentInfo* plci = NULL;
IInternetHostSecurityManager *pHostSecurityManager = NULL;
CDLDebugLog * pdlog = NULL;
CUrlMkTls tls(hr); // hr passed by reference!
if (FAILED(hr)) // if tls ctor failed above
goto AGCB_Exit;
pdlog = CDLDebugLog::GetDebugLog(szDistUnit, szTYPE, szExt, szURL);
if(pdlog)
pdlog->AddRef();
plci = new CLocalComponentInfo();
if(!plci) {
hr = E_OUTOFMEMORY;
goto AGCB_Exit;
}
if(szClassString)
CLSIDFromString((LPOLESTR)szClassString, &inclsid);// if fails szClassString is not clsid
// Fill myclsid with inclsid (if not null-clsid), the clsid corresponding
// to the mime type in szTYPE (if not NULL), the clsid corresponding
// to the file extension in szExt (if not NULL), or the clsid for the
// plugin corresponging to the mime type or extension (if not NULL),
// in that order of preference
hr = GetClsidFromExtOrMime( inclsid, myclsid, szExt, szTYPE,
&pPluginFileName);
// hr = S_OK: mapped to a clsid
// hr = S_FALSE: don't know what it is
// hr error, fail
if (FAILED(hr))
{
if(pdlog)
{
pdlog->DebugOut(DEB_CODEDL, TRUE, ID_CDLDBG_FAILED_CONVERT_CLSID, szExt, szTYPE);
}
goto AGCB_Exit;
}
hr = S_OK; //reset
if (!(dwFileVersionMS | dwFileVersionLS)) {
// maybe this is an IE feature like Java
// that may require that a matching version
// that is later than one currently installed
// is needed
if (!g_bNT5OrGreater) {
QUERYCONTEXT qc;
hr = GetIEFeatureVersion(szDistUnit, szTYPE, inclsid, &qc);
if (FAILED(hr))
goto AGCB_Exit;
dwFileVersionMS = qc.dwVersionHi;
dwFileVersionLS = qc.dwVersionLo;
if (dwFileVersionMS | dwFileVersionLS) {
bIEVersion = TRUE;
}
}
}
bNullClsid = IsEqualGUID(myclsid, CLSID_NULL);
if (szDistUnit || !bNullClsid ) {
// manage to map to a clsid or has a distunit name
CLSID pluginclsid = CLSID_ActiveXPlugin;
if (!IsEqualGUID(myclsid , pluginclsid)) {
// mark that we now have a clsid throw away TYPE, Ext
szTYPE = NULL;
szExt = NULL;
}
// check to see if locally installed.
HRESULT hrExact;
HRESULT hrAny;
if (FAILED((hrAny = IsControlLocallyInstalled(pPluginFileName,
(pPluginFileName)?(LPCLSID)&inclsid:&myclsid, szDistUnit,
dwFileVersionMS, dwFileVersionLS, plci, NULL, FALSE)))) {
goto AGCB_Exit;
}
if (pcdd->dwFlags & CD_FLAGS_EXACT_VERSION) {
if (FAILED((hrExact = IsControlLocallyInstalled(pPluginFileName,
(pPluginFileName)?(LPCLSID)&inclsid:&myclsid, szDistUnit,
dwFileVersionMS, dwFileVersionLS, plci, NULL, TRUE)))) {
goto AGCB_Exit;
}
if (hrAny != S_OK) {
// Don't have at least the requested version. Do the download.
hr = hrAny;
}
else if (hrAny == S_OK && hrExact == S_FALSE) {
// Newer control installed, must downgrade
// Check if this is a system control, and disallow if it is
BOOL bIsDPFComponent = FALSE;
HKEY hKeyIESettings = 0;
CHAR szOCXCacheDirSFN[MAX_PATH];
CHAR szFNameSFN[MAX_PATH];
DWORD dwType;
DWORD Size;
Size = MAX_PATH;
dwType = REG_SZ;
if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_IE_SETTINGS, 0,
KEY_READ, &hKeyIESettings))) {
if (SUCCEEDED(RegQueryValueEx(hKeyIESettings, g_szActiveXCache,
NULL, &dwType, (unsigned char *)g_szOCXCacheDir, &Size))) {
if (plci->szExistingFileName[0]) {
GetShortPathName(plci->szExistingFileName, szFNameSFN, MAX_PATH);
GetShortPathName(g_szOCXCacheDir, szOCXCacheDirSFN, MAX_PATH);
if (StrStrI(szFNameSFN, szOCXCacheDirSFN)) {
bIsDPFComponent = TRUE;
}
}
RegCloseKey(hKeyIESettings);
}
}
if (!bIsDPFComponent) {
// Trying to do a downgrade of a non-DPF component.
// Cut this off, and just act as if the existing (newer)
// version is already good enough. No download/install.
hr = S_OK;
}
else {
// Do the download
hr = hrExact;
}
}
else {
// hrAny == hrExact == S_OK, so we're done
Assert(hrAny == S_OK && hrExact == S_OK);
hr = hrAny;
}
}
else {
// Continue as before
hr = hrAny;
}
if ( hr == S_OK) { // local version OK
if (!(flags & CD_FLAGS_FORCE_DOWNLOAD)) {
// check here is a new version has been
// advertised for this DU. If so, force a -1 or Get Latest
if (!((plci->dwAvailMS > plci->dwLocFVMS) ||
((plci->dwAvailMS == plci->dwLocFVMS) &&
(plci->dwAvailLS > plci->dwLocFVLS))) ) {
// Code Download thinks the
// current version is good enough
goto AGCB_Exit;
}
dwFileVersionMS = 0xffffffff;
dwFileVersionLS = 0xffffffff;
}
// force download flag! Fall thru and Do it!
}
} else {
// couldn't map to a clsid
// just have ext or mime type
}
// here if we are going to do a code download.
if ((flags & CD_FLAGS_NEED_CLASSFACTORY) &&
(dwFileVersionMS == 0) && (dwFileVersionLS == 0)) {
HRESULT hrResult = S_OK;
IUnknown *punk = NULL;
// We don't care about the version. Check if someone registered
// themselves with COM via CoRegisterClassObject, and if it
// succeeds, we don't need to download.
hrResult = CoGetClassObject(((pPluginFileName) ? (inclsid) : (myclsid)),
dwClsContext, pvReserved, riid, (void**)&punk);
if (SUCCEEDED(hrResult)) {
punk->Release();
hr = S_OK;
goto AGCB_Exit;
}
}
if (flags & CD_FLAGS_PEEK_STATE) {
hr = S_FALSE;
goto AGCB_Exit;
}
// check language being right version
if (plci->bForceLangGetLatest) {
dwFileVersionMS = 0xffffffff;
dwFileVersionLS = 0xffffffff;
} else if ((dwFileVersionMS != 0xffffffff) && (dwFileVersionLS != 0xffffffff)) {
// try the JIT API first before code download
hr = InstallIEFeature(szDistUnit, szTYPE, inclsid, pbc, dwFileVersionMS, dwFileVersionLS, flags, bIEVersion);
if (hr != S_FALSE)
goto AGCB_Exit;
}
hr = SetCoInstall();
if ( FAILED(hr) )
goto AGCB_Exit;
if ( g_bUseOLECoInstall && !(flags & CD_FLAGS_FORCE_INTERNET_DOWNLOAD) )
{
CLSID inclsid;
CLSIDFromString((LPOLESTR)szDistUnit, &inclsid);
hr = WrapCoInstall( inclsid, szURL, dwFileVersionMS, dwFileVersionLS, szTYPE, pbc,
dwClsContext,pvReserved, riid, flags);
if ( SUCCEEDED(hr) )
goto AGCB_Exit;
}
hr = SetCodeDownloadTLSVars();
if (FAILED(hr))
goto AGCB_Exit;
hr = SetGlobals();
if (FAILED(hr))
goto AGCB_Exit;
if (g_bLockedDown) {
hr = E_ACCESSDENIED;
goto AGCB_Exit;
}
hr = pbc->GetObjectParam(REG_BSCB_HOLDER, (IUnknown **)&pclientbsc);
if (FAILED(hr))
goto AGCB_Exit;
pbc->AddRef(); // pbc gets saved in the CClBinding and gets released in
// ~CClBinding()
// after this point if a CClBinding does not get
// created, we will leak the client BC and BSC
// check appropriately and release client
// check to see if a code download is in progress for this CLSID
// Note, this only checks for top level objects now.
pHostSecurityManager = GetHostSecurityManager(pclientbsc);
hr = CCodeDownload::HandleDuplicateCodeDownloads(szURL, szTYPE, szExt,
myclsid, szDistUnit, dwClsContext, pvReserved, riid, pbc, pclientbsc, flags, pHostSecurityManager);
// if it was a dulplicate request and got piggybacked to
// a CodeDownload in progress we will get back MK_S_ASYNCHRONOUS
// return of S_OK means that no DUP was found and we are to issue
// fresh code download
if (FAILED(hr)) {
// release client here
pclientbsc->Release();
pbc->Release();
goto AGCB_Exit;
}
if (hr == MK_S_ASYNCHRONOUS)
goto AGCB_Exit;
pcdl = new CCodeDownload(szDistUnit, szURL, szTYPE, szExt, dwFileVersionMS, dwFileVersionLS, &hr);
if (FAILED(hr)) {
// constructor failed!
pcdl->Release();
}
if (!pcdl) {
hr = E_OUTOFMEMORY;
}
if (FAILED(hr)) {
// release client here
pclientbsc->Release();
pbc->Release();
goto AGCB_Exit;
}
// Pass off the debug log to the ccodedownload
if(pdlog)
{
pcdl->SetDebugLog(pdlog);
}
pcdl->SetExactVersion((pcdd->dwFlags & CD_FLAGS_EXACT_VERSION) != 0);
CClBinding *pClientBinding;
hr = pcdl->CreateClientBinding( &pClientBinding, pbc, pclientbsc,
inclsid, dwClsContext, pvReserved, riid,
TRUE /* fAddHead */, pHostSecurityManager);
if (FAILED(hr)) {
// release client here
pclientbsc->Release();
pbc->Release();
pcdl->Release();
goto AGCB_Exit;
}
pClientBinding->SetClassString(szClassString);
// check if our current security settings allow us to do a code download
// this is a quick out to avoid doing a download when all activex is
// disabled and we think this is ActiveX.
if (bHintActiveX || (!bNullClsid && !bHintJava)) {
// get the zone mgr and check for policy on signed and unsigned
// activex controls. If both are disabled then stop here
// otherwise proceed to download main CAB and WVT will determine the
// policy based on the appropriate urlaction (singed/unsigned)
hr = CheckActiveXDownloadEnabled(pHostSecurityManager, szURL, pclientbsc);
if (FAILED(hr)) {
pcdl->Release();
goto AGCB_Exit;
}
}
// chain this CodeDownload to the list of downloads in this thread
pos = tls->pCodeDownloadList->AddHead(pcdl);
pcdl->SetListCookie(pos);
hr = pcdl->DoCodeDownload(plci, flags);
plci = NULL;
pcdl->Release(); // if async binding OnStartBinding would have addref'ed
AGCB_Exit:
if (pPluginFileName)
delete pPluginFileName;
if(pdlog)
pdlog->Release();
SAFEDELETE(plci);
SAFERELEASE(pHostSecurityManager);
DEBUG_LEAVE_API(hr);
return hr;
}
STDAPI
WrapCoInstall (
REFCLSID rCLASSID, // CLSID of object (may be NULL)
LPCWSTR szCODE, // URL to code (may be NULL)
DWORD dwFileVersionMS, // Version of primary object
DWORD dwFileVersionLS, // Version of primary object
LPCWSTR szTYPE, // MIME type (may be NULL)
LPBINDCTX pBindCtx, // Bind ctx
DWORD dwClsContext, // CLSCTX flags
LPVOID pvReserved, // Must be NULL
REFIID riid, // Usually IID_IClassFactory
DWORD flags
)
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"WrapCoInstall",
"%#x, %.80wq, %#x, %#x, %.80wq, %#x, %#x, %#x, %#x, %#x",
&rCLASSID, szCODE, dwFileVersionMS, dwFileVersionLS, szTYPE, pBindCtx, dwClsContext, pvReserved, &riid, flags
));
HRESULT hr = NO_ERROR;
// DWORD flags = 0; // CD_FLAGS_NEED_CLASSFACTORY;
WCHAR *szExt = NULL;
#ifdef WRAP_OLE32_COINSTALL
CBSCCreateObject* pobjectbsc=NULL;
#endif
UrlMkDebugOut((DEB_CODEDL, "IN WrapCoInstall CLASSID: %lx, TYPE=%ws...szCODE:(%ws), VersionMS:%lx, VersionLS:%lx\n", rCLASSID.Data1, szTYPE, szCODE, dwFileVersionMS, dwFileVersionLS));
// Note about pvReserved: this is used by DCOM to get in the remote server
// name and other info. If not NULL, then the caller wants us to use
// dcom. if (pvReserved) just call CoGetClassObject.
// call AsyncGetClassBits to do the real work
if (pvReserved == NULL)
{
// Set up CoInstall parameters
uCLSSPEC classpec;
QUERYCONTEXT query;
if (!IsEqualGUID(rCLASSID, CLSID_NULL))
{
classpec.tyspec=TYSPEC_CLSID;
classpec.tagged_union.clsid=rCLASSID; // use original class ID so that MIME can still be processed
}
else if (szTYPE && *szTYPE)
{
classpec.tyspec=TYSPEC_MIMETYPE;
classpec.tagged_union.pMimeType=(LPWSTR) szTYPE; // BUGBUG uCLSSPEC::pMimeType should be declared const!
}
else
{
hr=E_INVALIDARG;
goto Exit;
}
query.dwContext = dwClsContext;
GetDefaultPlatform(&query.Platform);
query.Locale = GetThreadLocale();
query.dwVersionHi = dwFileVersionMS;
query.dwVersionLo = dwFileVersionLS;
#ifdef WRAP_OLE32_COINSTALL
// Override the client's BSC with a BSC that will create the object when it receives a successful OnStopBinding
// CBSCCreateObject registers itself in the bind context and saves a pointer to the client's BSC
// it unregisters itself upon OnStopBinding
pobjectbsc=new CBSCCreateObject(rCLASSID, szTYPE, szExt, dwClsContext, pvReserved, riid, pBindCtx, flags, hr); // hr by reference!
if (!pobjectbsc)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
if (FAILED(hr))
{
goto Exit;
}
#endif
hr=CoInstall(pBindCtx, flags, &classpec, &query, (LPWSTR) szCODE); //BUGBUG CoInstall szCodeBase should be const!
if (hr!=MK_S_ASYNCHRONOUS)
{
// clean up the bind context for synchronous and failure case
// in asynchronous case pobjectbsc revokes itself in OnStopBinding!
//pobjectbsc->RevokeFromBC();
}
//hr = AsyncGetClassBits( rCLASSID,
// szTYPE, szExt,
// dwFileVersionMS, dwFileVersionLS, szCODE,
// pBindCtx, dwClsContext, pvReserved, riid, flags);
#ifdef WRAP_OLE32_COINSTALL
pobjectbsc->Release();
pobjectbsc=NULL;
#endif
}
Exit:
#ifdef WRAP_OLE32_COINSTALL
if (pobjectbsc)
{
pobjectbsc->Release();
pobjectbsc=NULL;
}
#endif
UrlMkDebugOut((DEB_CODEDL, "OUT WrapCoInstall hr:%lx%s, CLASSID: %lx, TYPE=%ws..., szCODE:(%ws), VersionMS:%lx, VersionLS:%lx\n",
hr,(hr == MK_S_ASYNCHRONOUS)?"(PENDING)":(hr == S_OK)?"(SUCCESS)":"",
rCLASSID.Data1, szTYPE, szCODE, dwFileVersionMS, dwFileVersionLS));
DEBUG_LEAVE_API(hr);
return hr;
}
STDAPI PrivateCoInstall(
IBindCtx *pbc,
DWORD dwFlags,
uCLSSPEC *pClassSpec,
QUERYCONTEXT *pQuery,
LPWSTR pszCodeBase)
{
DEBUG_ENTER_API((DBG_DOWNLOAD,
Hresult,
"PrivateCoInstall",
"%#x, %#x, %#x, %#x, %.80wq",
pbc, dwFlags, pClassSpec, pQuery, pszCodeBase
));
HRESULT hr = NO_ERROR;
CLSID inclsid = CLSID_NULL;
LPCWSTR pszDistUnit=NULL;
LPCWSTR pszFileExt=NULL;
LPCWSTR pszMimeType=NULL;
QUERYCONTEXT query;
// "Parse" the parameters from the CLSSPEC and QUERYCONTEXT
// Get the class spec.
if(pClassSpec != NULL)
{
switch(pClassSpec->tyspec)
{
case TYSPEC_CLSID:
inclsid = pClassSpec->tagged_union.clsid;
break;
case TYSPEC_MIMETYPE:
pszMimeType = (LPCWSTR) pClassSpec->tagged_union.pMimeType;
break;
case TYSPEC_FILEEXT:
pszFileExt = (LPCWSTR) pClassSpec->tagged_union.pFileExt;
break;
case TYSPEC_FILENAME:
pszDistUnit= (LPCWSTR) pClassSpec->tagged_union.pFileName; // clean-up: this is the only case where we assign existing mem to pszDistUnit!
// BUGBUG: need to do this in OLE's CoInstall as well!
CLSIDFromString((LPOLESTR)pszDistUnit, &inclsid);// if fails szDistUnit is not clsid
break;
default:
break;
}
}
//Get the query context.
if(pQuery != NULL)
{
query = *pQuery;
}
else
{
query.dwContext = CLSCTX_ALL;
GetDefaultPlatform(&query.Platform);
query.Locale = GetThreadLocale();
query.dwVersionHi = (DWORD) -1;
query.dwVersionLo = (DWORD) -1;
}
hr = AsyncInstallDistributionUnit( pszDistUnit,
pszMimeType,
pszFileExt,
query.dwVersionHi, query.dwVersionLo,
pszCodeBase,
pbc,
NULL,
dwFlags | CD_FLAGS_FORCE_INTERNET_DOWNLOAD // ensure no mutual recursion
);
DEBUG_LEAVE_API(hr);
return hr;
}