#include #pragma hdrstop #include "verp.h" #include #include 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 = * * 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 = * 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); }