/*++ Copyright (c) 1995 Microsoft Corporation Module Name: cachecfg.cxx Abstract: This module contains the functions to get and set disk cache configuration parameters. Contents: GetCacheConfigInfoA SetCacheConfigInfoA Author: Sophia Chung (sophiac) 1-May-1995 Environment: User Mode - Win32 Revision History: Mucho rewritten by Akabir 1Q 98 To understand how the new registration code works, it might be better for you to start with ConfigureCache, GetCacheConfigInfo, etc. for a high level acquaintance; _then_ start poring over the actual registry sets code. --*/ #include #include #include #include #define CACHE_TAG "Cache" // Cache path keys. CHAR* g_szSubKey[] = {CONTENT_PATH_KEY, COOKIE_PATH_KEY, HISTORY_PATH_KEY}; CHAR* g_szOldSubKey[] = {CACHE_TAG, COOKIE_PATH_KEY, HISTORY_PATH_KEY}; INT g_iContainerCSIDL[] = { CSIDL_INTERNET_CACHE, CSIDL_COOKIES, CSIDL_HISTORY }; // Top level cache paths resource IDs #ifndef UNIX DWORD g_dwCachePathResourceID[] = {IDS_CACHE_DEFAULT_SUBDIR, IDS_COOKIES_DEFAULT_SUBDIR, IDS_HISTORY_DEFAULT_SUBDIR}; #else DWORD g_dwCachePathResourceID[] = {IDS_CACHE_DEFAULT_SUBDIR_UNIX, IDS_COOKIES_DEFAULT_SUBDIR, IDS_HISTORY_DEFAULT_SUBDIR}; #endif /* UNIX */ // Cache prefixes. CHAR* g_szCachePrefix[] = {CONTENT_PREFIX, COOKIE_PREFIX, HISTORY_PREFIX}; CHAR* g_szVersionName[] = { CONTENT_VERSION_SUBDIR, "", "History.IE5" }; #define OLD_CACHE_PATH "Path1" #define OLD_CACHE_SUBKEY DIR_SEPARATOR_STRING##"Cache1" typedef BOOL (WINAPI *PFNGETDISKFREESPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); void FlushShellFolderCache() { SHFlushSFCacheWrap( ); return; } typedef HRESULT (*PFNSHGETFOLDERPATH)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPSTR pszPath); VOID CheckCacheLocationConsistency(); //#define DEBUG_CACHE_UPGRADE #ifdef DEBUG_CACHE_UPGRADE VOID LOG_UPGRADE_DATA(PTSTR pszData) { CHAR szFileName[MAX_PATH]; DWORD dwSize = MAX_PATH; CHAR szComputerName[MAX_PATH]; HANDLE hResultsFile = NULL; strcpy(szFileName, "\\\\BANYAN\\IPTD\\AKABIR\\cacheupgrade\\"); if (!GetComputerNameA(szComputerName, &dwSize)) { goto exit; } lstrcatA(szFileName, szComputerName); hResultsFile = CreateFileA( szFileName, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL); if (hResultsFile != INVALID_HANDLE_VALUE) { if (SetFilePointer(hResultsFile, 0, NULL, FILE_END)==0xFFFFFFFF) { goto exit; } DWORD dwFoo; if (0==WriteFile(hResultsFile, (PVOID)pszData, lstrlenA(pszData), &dwFoo, NULL)) { DWORD dwE = GetLastError(); } } exit: if (hResultsFile) { CloseHandle(hResultsFile); } } #else #define LOG_UPGRADE_DATA(x) #endif #undef SHGetFolderPath HRESULT SHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPSTR pszPath) { HRESULT hr = E_POINTER; static PFNSHGETFOLDERPATH pfn = NULL; if (NULL == pfn) { if (NULL == g_HMODShell32) g_HMODShell32 = LoadLibrary("shell32.dll"); if (NULL != g_HMODShell32) pfn = (PFNSHGETFOLDERPATH)GetProcAddress(g_HMODShell32, "SHGetFolderPathA"); if (NULL == pfn) { if (NULL == g_HMODSHFolder) g_HMODSHFolder = LoadLibrary("shfolder.dll"); if (NULL != g_HMODSHFolder) pfn = (PFNSHGETFOLDERPATH)GetProcAddress(g_HMODSHFolder, "SHGetFolderPathA"); } if (NULL != pfn) hr = pfn(hwnd, csidl, hToken, dwFlags, pszPath); } else hr = pfn(hwnd, csidl, hToken, dwFlags, pszPath); return hr; } #define CACHE_SIZE_CAP 32000000 DWORD GetDefaultCacheQuota( LPSTR pszCachePath, DWORD dwFraction ) { DWORDLONG cKBLimit = 0, cbTotal; if (GetDiskInfo(pszCachePath, NULL, NULL, &cbTotal)) { cKBLimit = (cbTotal / (DWORDLONG)(1024*dwFraction)); } if (cKBLimit<1024) { cKBLimit = DEF_CACHE_LIMIT; } else if (cKBLimit > CACHE_SIZE_CAP) { cKBLimit = CACHE_SIZE_CAP; } return (DWORD)cKBLimit; } VOID CleanPath(PTSTR pszPath); /*----------------------------------------------------------------------------- NormalisePath (code swiped from shell32\folder.c: UnexpandEnvironmentstring) Collapses paths of the form C:\foobar\dir1\...\dirn to %FOOBAR%\dir1\...\dirn where %FOOBAR% = "C:\foobar". storing result in pszOut. If collapse is not possible, returns FALSE and path is unchanged. If the given environment variable exists as the first part of the path, then the environment variable is inserted into the output buffer. Returns TRUE if pszResult is filled in. Example: Input -- C:\WINNT\SYSTEM32\FOO.TXT -and- lpEnvVar = %SystemRoot% Output -- %SystemRoot%\SYSTEM32\FOO.TXT ---------------------------------------------------------------------------*/ BOOL NormalisePath(LPCTSTR pszPath, LPCTSTR pszEnvVar, LPTSTR pszResult, UINT cbResult) { TCHAR szEnvVar[MAX_PATH]; // DWORD dwEnvVar = ExpandEnvironmentStrings(pszEnvVar, szEnvVar, sizeof(szEnvVar)) - 1; // don't count the NULL // akabir: a curious bug? causes ExpandEnvironmentStrings to return twice the number of characters. ExpandEnvironmentStrings(pszEnvVar, szEnvVar, sizeof(szEnvVar)-1); // don't count the NULL DWORD dwEnvVar = lstrlen(szEnvVar); if (CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, szEnvVar, dwEnvVar, pszPath, dwEnvVar) == 2) { if (lstrlen(pszPath) + dwEnvVar < cbResult) { strncpy(pszResult, pszEnvVar, MAX_PATH); strncat(pszResult, pszPath + dwEnvVar, MAX_PATH); return TRUE; } } return FALSE; } VOID CompressPath(LPTSTR pszSrc, LPTSTR pszDest) { if (!NormalisePath(pszSrc, TEXT("%USERPROFILE%"), pszDest, MAX_PATH)) { if (!NormalisePath(pszSrc, TEXT("%SystemRoot%"), pszDest, MAX_PATH)) { strncpy(pszDest, pszSrc, MAX_PATH); } } } // ------------------------------------------------------------------------------------------------ // IE 3, 4 and 5 have different registry settings. These classes help ensure they all stay in sync. // -- IE5's registry set ------------------------------------------------------------------------------------------------ // *** If using multiple registry sets, use InitialiseKeys for IE5 first.*** // This is to ensure that profiles-capabilities are noted. class IE5_REGISTRYSET { protected: REGISTRY_OBJ m_roHKLMCache, m_roHKCUCache, m_roShellFolder, m_roUserShellFolder, m_roWorking; BOOL m_fProfiles; BOOL m_fWorkingPerUser; TCHAR m_szSharedPath[MAX_PATH]; TCHAR m_szProfilePath[MAX_PATH]; DWORD cbP, cbS, m_dwWorking; BOOL m_fInitialised; DWORD InitCommonKeys(BOOL fProfilesCapable, LPSTR pszReg) { DWORD dwError, dwFlag = CREATE_KEY_IF_NOT_EXISTS; m_fProfiles = fProfilesCapable; // Shared item info are located in HKLM/[...]/Internet Settings/5.0/Cache/* m_roHKLMCache.WorkWith(HKEY_LOCAL_MACHINE, pszReg, dwFlag); if (dwError = m_roHKLMCache.GetStatus()!=ERROR_SUCCESS) { m_roHKLMCache.WorkWith(HKEY_LOCAL_MACHINE, pszReg, dwFlag, BASIC_ACCESS); if (dwError = m_roHKLMCache.GetStatus()!=ERROR_SUCCESS) goto exit; } m_roShellFolder.WorkWith(HKEY_CURRENT_USER, SHELL_FOLDER_KEY, dwFlag); if (dwError = m_roShellFolder.GetStatus()!=ERROR_SUCCESS) goto exit; if (fProfilesCapable) { m_roHKCUCache.WorkWith(HKEY_CURRENT_USER, pszReg, dwFlag); if (dwError = m_roHKCUCache.GetStatus()!=ERROR_SUCCESS) goto exit; } m_roUserShellFolder.WorkWith(HKEY_CURRENT_USER, USER_SHELL_FOLDER_KEY, dwFlag); dwError = m_roUserShellFolder.GetStatus(); if (dwError==ERROR_SUCCESS) { m_fInitialised = TRUE; } // Per-user items are located in HKCU/[...]/Explorer/Shell Folders and /Internet Settings/[5.0/]Cache/* exit: return dwError; } virtual BOOL DetermineKeyPlacing(DWORD dwWhich) { // Determine if this is a per-user item // HKCU overrides HKLM // If any of the following fail, for content, we'll default to shared. if (!m_fProfiles) { return FALSE; } DWORD dwTemp; REGISTRY_OBJ roCUContainer(&m_roHKCUCache, g_szSubKey[dwWhich], CREATE_KEY_IF_NOT_EXISTS); if ((roCUContainer.GetStatus()==ERROR_SUCCESS) && (roCUContainer.GetValue(PER_USER_KEY, &dwTemp)==ERROR_SUCCESS)) { return dwTemp; } REGISTRY_OBJ roLMContainer(&m_roHKLMCache, g_szSubKey[dwWhich], CREATE_KEY_IF_NOT_EXISTS); BOOL fPerUser = FALSE; if ((roLMContainer.GetStatus()==ERROR_SUCCESS) && (roLMContainer.GetValue(PER_USER_KEY, &dwTemp)==ERROR_SUCCESS)) { return dwTemp; } // On NT, the default will be a per-user container. #ifndef UNIX dwTemp = (GlobalPlatformType == PLATFORM_TYPE_WINNT) ? TRUE : (dwWhich!=CONTENT); #else dwTemp = (GlobalPlatformType == PLATFORM_TYPE_UNIX) ? TRUE : (dwWhich!=CONTENT); #endif /* UNIX */ roLMContainer.SetValue(PER_USER_KEY, &dwTemp); roCUContainer.SetValue(PER_USER_KEY, &dwTemp); return (BOOL)dwTemp; } // -- ValidatePath ------ // We always assume we've been given a valid path, but we have to test that it's there // and available. BOOL ValidatePath(PSTR pszPath) { DWORD dwAttribute = GetFileAttributes(pszPath); if (dwAttribute==0xFFFFFFFF) { // We assume that the directory just isn't there. So we create it. hConstructSubDirs(pszPath); dwAttribute = GetFileAttributes(pszPath); } if ((dwAttribute==0xFFFFFFFF) || (dwAttribute & FILE_ATTRIBUTE_READONLY) || (!(dwAttribute & FILE_ATTRIBUTE_DIRECTORY))) { // BUG BUG BUG We probably want to make sure that the old path gets deleted on other machines.... // We'll use the system path // BUG BUG BUG BUG BUG We are *NOT* recording this default location in the registry. Thus, on another // machine, the user might still be able to use the set cache location. memcpy(pszPath, m_szSharedPath, cbS); LoadString(GlobalDllHandle, g_dwCachePathResourceID[m_dwWorking], pszPath+cbS, MAX_PATH - cbS); SetPerUserStatus(FALSE); } return ERROR_SUCCESS; } public: IE5_REGISTRYSET() { m_fInitialised = FALSE; } virtual DWORD InitialiseKeys(BOOL& fProfilesCapable) { if (m_fInitialised) { fProfilesCapable = m_fProfiles; return ERROR_SUCCESS; } DWORD dwError = ERROR_SUCCESS; fProfilesCapable = TRUE; #ifndef UNIX cbS = GetWindowsDirectory(m_szSharedPath, sizeof(m_szSharedPath)); #else /* On Unix, GetWindowsDirectory points to /common * And, we don't want to put the cache here. */ lstrcpy(m_szSharedPath, UNIX_SHARED_CACHE_PATH); cbS = lstrlen(m_szSharedPath); #endif /* UNIX */ if (!cbS || (cbS>sizeof(m_szSharedPath))) return ERROR_PATH_NOT_FOUND; AppendSlashIfNecessary(m_szSharedPath, &cbS); cbP = 0; // We think that profiles are enabled, so we want to get some info before // proceeding. If any of this fails, though, we'll default to no profiles. switch (GlobalPlatformType) { #ifndef UNIX case PLATFORM_TYPE_WIN95: { REGISTRY_OBJ roProfilesEnabled(HKEY_LOCAL_MACHINE, PROFILES_ENABLED_VALUE); DWORD dwProfilesEnabled = 0; if ( (roProfilesEnabled.GetStatus() == ERROR_SUCCESS) && ((roProfilesEnabled.GetValue(PROFILES_ENABLED, &dwProfilesEnabled))==ERROR_SUCCESS) && dwProfilesEnabled) { // Windows 95 sets the profiles path in the registry. CHAR szProfilesRegValue[MAX_PATH]; memcpy(szProfilesRegValue, PROFILES_PATH_VALUE, sizeof(PROFILES_PATH_VALUE)-1); cbP = sizeof(PROFILES_PATH_VALUE)-1; AppendSlashIfNecessary(szProfilesRegValue, &cbP); cbP = MAX_PATH-sizeof(PROFILES_PATH_VALUE); if (GetUserName(szProfilesRegValue + sizeof(PROFILES_PATH_VALUE), &cbP)) { cbP = MAX_PATH; REGISTRY_OBJ roProfilesDirKey(HKEY_LOCAL_MACHINE, szProfilesRegValue); if (!(((dwError = roProfilesDirKey.GetStatus()) != ERROR_SUCCESS) || ((dwError = roProfilesDirKey.GetValue(PROFILES_PATH, (LPBYTE) m_szProfilePath, &cbP)) != ERROR_SUCCESS))) { m_szProfilePath[cbP-1] = DIR_SEPARATOR_CHAR; m_szProfilePath[cbP] = '\0'; break; } } } // Either // (a) Couldn't get the profiles path from the registry. // (b) Couldn't get the user name! // Make the directory the windows directory fProfilesCapable = FALSE; break; } case PLATFORM_TYPE_WINNT: // Windows NT sets the USERPROFILE environment // string which contains the user's profile path if (cbP = GetEnvironmentVariable("USERPROFILE", m_szProfilePath, MAX_PATH)) { m_szProfilePath[cbP++] = DIR_SEPARATOR_CHAR; m_szProfilePath[cbP] = '\0'; } else { INET_ASSERT(FALSE); // Getting the user profiles dir from the environment // failed. Set the profiles directory to default. memcpy(m_szProfilePath, m_szSharedPath, cbS); memcpy(m_szProfilePath + cbS, DEFAULT_PROFILES_DIRECTORY, sizeof(DEFAULT_PROFILES_DIRECTORY)); cbP = cbS + sizeof(DEFAULT_PROFILES_DIRECTORY) - 1; DWORD cbUser = MAX_PATH - cbP;; GetUserName(m_szProfilePath + cbP, &cbUser); cbP += cbUser; } break; #else /* UNIX */ case PLATFORM_TYPE_UNIX: lstrcpy(m_szProfilePath,TEXT("%USERPROFILE%")); lstrcat(m_szProfilePath,DIR_SEPARATOR_STRING); cbP = lstrlen(m_szProfilePath); break; #endif /* UNIX */ default: // This should never happen. INET_ASSERT(FALSE); } if (dwError==ERROR_SUCCESS) { dwError = InitCommonKeys(fProfilesCapable, CACHE5_KEY); } return dwError; } DWORD SetWorkingContainer(DWORD dwWhich) { m_dwWorking = dwWhich; m_fWorkingPerUser = DetermineKeyPlacing(dwWhich); return m_roWorking.WorkWith((m_fWorkingPerUser ? &m_roHKCUCache : &m_roHKLMCache), g_szSubKey[dwWhich], CREATE_KEY_IF_NOT_EXISTS); } VOID AttemptToUseSharedCache(PTSTR pszPath, DWORD ckbLimit); // Path -------------------------------------------------------------------------- virtual DWORD GetPath(PTSTR pszPath) { if ((S_OK==SHGetFolderPath(NULL, g_iContainerCSIDL[m_dwWorking] | CSIDL_FLAG_CREATE, NULL, 0, pszPath)) && (*pszPath!='\0')) { DWORD dwErr = ValidatePath(pszPath); if (dwErr==ERROR_SUCCESS) { DWORD ccPath = lstrlen(pszPath); // We check the lengths of the strings only when we're moving the containers. No need to do the check every // time (assume a valid path) if (m_dwWorking!=COOKIE) { EnableCacheVu(pszPath, m_dwWorking); } AppendSlashIfNecessary(pszPath, &ccPath); #ifdef UNIX /* On Unix, it is possible that IE4 and IE5 co-exist on a user's * installation. So, we need to keep the IE4 cookies which are * different from the IE5 cookies. For IE5, we have the following * configuration for the caches - * * cookies - %HOME%/.microsoft/ie5/Cookies * content - %HOME%/.microsoft/ie5/TempInternetFiles/Content.IE5 * history - %HOME%/.microsoft/ie5/History/History.IE5 */ CHAR szIE5Dir[] = "ie5/"; int index = ccPath-2; // skip the last slash int lenIE5Dir = lstrlen(szIE5Dir); while(index >= 0 && pszPath[index] != FILENAME_SEPARATOR) index--; index++; memmove(&pszPath[index+lenIE5Dir],&pszPath[index],ccPath-index+2); memcpy(&pszPath[index],szIE5Dir,lenIE5Dir); ccPath += lenIE5Dir; #endif /* UNIX */ memcpy(pszPath+ccPath, g_szVersionName[m_dwWorking], lstrlen(g_szVersionName[m_dwWorking])+1); if (GlobalIdentity && (m_dwWorking!=CONTENT)) { if (!GenerateStringWithOrdinal(NULL, GlobalIdentity, pszPath, MAX_PATH)) { dwErr = ERROR_INTERNET_INTERNAL_ERROR; } } } return dwErr; } return ERROR_INTERNET_INTERNAL_ERROR; } virtual DWORD SetPath(PTSTR pszPath) { INET_ASSERT(m_roWorking.GetStatus()==ERROR_SUCCESS); DWORD dwError; /* Try to preserve the environment variables on Unix */ UNIX_NORMALIZE_PATH_ALWAYS(pszPath, TEXT("%USERPROFILE%")); if (m_fProfiles) { CHAR szScratch[MAX_PATH]; CompressPath(pszPath, szScratch); if ((dwError = m_roUserShellFolder.SetValue(g_szOldSubKey[m_dwWorking], szScratch, REG_EXPAND_SZ))==ERROR_SUCCESS) { #ifndef UNIX DWORD dwType = REG_SZ; dwError = m_roShellFolder.SetValue(g_szOldSubKey[m_dwWorking],pszPath, dwType); #else dwError = m_roShellFolder.SetValue(g_szOldSubKey[m_dwWorking],szScratch, REG_EXPAND_SZ); #endif /* UNIX */ } // Possible BUG: If we move from the profiled path to the shared path, we still record it as a peruseritem. SetPerUserStatus(TRUE); } // Non-profiles-enabled machine // On Win9x machines, with profiles disabled, we need to write the path to the // HKEY_USERS/.default/blah blah/Explorer/User Shell Folders to ensure that SHGetFolderPath returns // the proper value for other users. else { REGISTRY_OBJ roProfilesLessPath(HKEY_USERS, PROFILELESS_USF_KEY); dwError = roProfilesLessPath.GetStatus(); if (dwError==ERROR_SUCCESS) { if ((dwError = roProfilesLessPath.SetValue(g_szOldSubKey[m_dwWorking],pszPath, REG_EXPAND_SZ))==ERROR_SUCCESS) { #ifndef UNIX DWORD dwType = REG_SZ; #else DWORD dwType = REG_EXPAND_SZ; #endif /* UNIX */ dwError = m_roUserShellFolder.SetValue(g_szOldSubKey[m_dwWorking],pszPath, dwType); } } // For IE4 compatibility, we might have to adjust the old cache location here, as well. } return dwError; } // Prefix ------------------------------------------------------------------------ virtual DWORD GetPrefix(LPSTR szPrefix) { DWORD dwError, cbKeyLen = MAX_PATH; if ((dwError = m_roWorking.GetValue(CACHE_PREFIX_VALUE, (LPBYTE) szPrefix, &cbKeyLen))==ERROR_SUCCESS) { if (cbKeyLen > 0) { // Strip trailing whitespace. cbKeyLen--; StripTrailingWhiteSpace(szPrefix, &cbKeyLen); } } else { // If no prefix found in registry create via // defaults and write back to registry. strncpy(szPrefix, g_szCachePrefix[m_dwWorking], MAX_PATH); SetPrefix(szPrefix); dwError = ERROR_SUCCESS; } return dwError; } virtual DWORD SetPrefix(PTSTR pszPrefix) { INET_ASSERT(m_roWorking.GetStatus()==ERROR_SUCCESS); return m_roWorking.SetValue(CACHE_PREFIX_VALUE, (pszPrefix) ? pszPrefix : g_szCachePrefix[m_dwWorking], REG_SZ); } // Limit ------------------------------------------------------------------------- virtual DWORD GetLimit(PTSTR pszCachePath, DWORD& cbLimit) { if ((m_roWorking.GetValue(CACHE_LIMIT_VALUE, &cbLimit)!=ERROR_SUCCESS) || (cbLimit < 512)) { cbLimit = 0; return SetLimit(pszCachePath, cbLimit); } return ERROR_SUCCESS; } virtual DWORD SetLimit(PTSTR pszCachePath, DWORD& cbLimit); // Use IsFirstTime* to figure out if this is the first time for this install of wininet and for marking it so ------------ private: BOOL IsFirstTimeFor(HKEY hKey) { DWORD cb = MAX_PATH; CHAR szSigKey[MAX_PATH]; REGISTRY_OBJ roSig(hKey, CACHE5_KEY); return roSig.GetValue(CACHE_SIGNATURE_VALUE, (LPBYTE) szSigKey, &cb)==ERROR_SUCCESS ? strcmp(szSigKey, CACHE_SIGNATURE) : TRUE; } public: BOOL IsFirstTimeForUser() { return IsFirstTimeFor((m_fProfiles ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE)); } BOOL IsFirstTimeForMachine() { return IsFirstTimeFor(HKEY_LOCAL_MACHINE); } VOID SetIfFirstTime() { DWORD cb = MAX_PATH; CHAR szSigKey[MAX_PATH]; // On a profiles-not-enabled machine, store the signature in HKLM so we don't have to research for values // On a profiles-enabled machine, store there to notify IE of previous installation of IE5. REGISTRY_OBJ roSig(HKEY_LOCAL_MACHINE, CACHE5_KEY); roSig.SetValue(CACHE_SIGNATURE_VALUE, CACHE_SIGNATURE, REG_SZ); // On profiles-enabled machines, we store a signature in HKCU so that we don't have to do // much hunting for registry values if (m_fProfiles) { REGISTRY_OBJ roSig(HKEY_CURRENT_USER, CACHE5_KEY); roSig.SetValue(CACHE_SIGNATURE_VALUE, CACHE_SIGNATURE, REG_SZ); } } // PerUserItem --------------------------------------------------------------------- virtual VOID SetPerUserStatus(BOOL fState) { DWORD flState = fState; if (m_fProfiles && fState!=m_fWorkingPerUser) { REGISTRY_OBJ roTemp(&m_roHKCUCache, g_szSubKey[m_dwWorking], CREATE_KEY_IF_NOT_EXISTS); if (roTemp.GetStatus()==ERROR_SUCCESS) { roTemp.SetValue(PER_USER_KEY, &flState); m_roWorking.WorkWith((fState ? &m_roHKCUCache : &m_roHKLMCache), g_szSubKey[m_dwWorking], CREATE_KEY_IF_NOT_EXISTS); m_fWorkingPerUser = fState; } } } virtual DWORD GetPerUserStatus() { return m_fWorkingPerUser; } DWORD UpdateContentPath(PSTR pszNewPath) { TCHAR szOldPath[MAX_PATH]; DWORD dwError; dwError = ERROR_SUCCESS; if ((dwError=SetWorkingContainer(CONTENT))==ERROR_SUCCESS) { INTERNET_CACHE_CONFIG_INFOA icci; icci.dwContainer = CONTENT; GetUrlCacheConfigInfoA(&icci, NULL, CACHE_CONFIG_DISK_CACHE_PATHS_FC); strncpy(szOldPath, icci.CachePath, ARRAY_ELEMENTS(szOldPath)); if (((dwError=MoveCachedFiles(szOldPath, pszNewPath))==ERROR_SUCCESS) && ((dwError=SetPath(pszNewPath))==ERROR_SUCCESS)) { EnableCacheVu(pszNewPath); // Right now, we're adding entries so that once we restart, we'll delete any // stray files. // BUT, there's a case that Move will be interrupted; in that case, we ought // to finish the move on start up -- pop up a dialog notifying user of such // and then delete. // Also, if the Move's interrupted, then this info never will get written. OTOH, // we can argue that the user can just move from the old location to the new. CHAR szRunOnce [2 * MAX_PATH]; CHAR szSystemPath [MAX_PATH]; // Add a RunOnce entry to be run on reboot. REGISTRY_OBJ roRunOnce((m_fWorkingPerUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE), RUN_ONCE_KEY, CREATE_KEY_IF_NOT_EXISTS); if ((dwError=roRunOnce.GetStatus())!=ERROR_SUCCESS) return dwError; // create RunOnce string in the form: // "rundll32.exe \wininet.dll,RunOnceUrlCache C:\Windows\NewCacheLocation" if (!GetSystemDirectory(szSystemPath, MAX_PATH)) return ERROR_INTERNAL_ERROR; DisableCacheVu(szOldPath); // Get rid of content.ie5. PathRemoveBackslash(szOldPath); PathRemoveFileSpec(szOldPath); DisableCacheVu(szOldPath); GetShortPathName(szOldPath, szOldPath, ARRAY_ELEMENTS(szOldPath)); wnsprintf(szRunOnce, sizeof(szRunOnce), "rundll32.exe %s\\wininet.dll,RunOnceUrlCache %s", szSystemPath, szOldPath); // Set the RunOnce command in registry for wininet. roRunOnce.SetValue(TEXT("MovingCacheA Wininet Settings"), (LPSTR)szRunOnce, REG_SZ); } } return dwError; } }; #define m_roPaths m_roShellFolder #define m_roSpecialPaths m_roHKCUCache class IE3_REGISTRYSET : public IE5_REGISTRYSET { // Registry keys shipped with IE 3: // // Software\Microsoft\Windows\CurrentVersion\Internet Settings\Cache\Paths // // Software\Microsoft\Windows\CurrentVersion\Internet Settings\Cache\Paths\path1 // \path2 // \path3 // \path4 // // Software\Microsoft\Windows\CurrentVersion\Internet Settings\Cache\Special Paths // // Software\Microsoft\Windows\CurrentVersion\Internet Settings\Cache\Special Paths\Cookies // \History // // Software\Microsoft\Windows\CurrentVersion\Internet Settings\Cache\Url History private: REGISTRY_OBJ m_roPath[DEF_NUM_PATHS]; public: // Initialise the IE3 keys that we might work with. DWORD InitialiseKeys() { DWORD dwError, i; TCHAR szScratch[MAX_PATH]; TCHAR pszBase[MAX_PATH]; DWORD dwBaseLen; if (m_fInitialised) { return ERROR_SUCCESS; } m_roHKLMCache.WorkWith(HKEY_LOCAL_MACHINE, OLD_CACHE_KEY, CREATE_KEY_IF_NOT_EXISTS); if ((dwError=m_roHKLMCache.GetStatus())!=ERROR_SUCCESS) goto exit; m_roPaths.WorkWith(&m_roHKLMCache, CACHE_PATHS_KEY, CREATE_KEY_IF_NOT_EXISTS); if ((dwError=m_roPaths.GetStatus())!=ERROR_SUCCESS) goto exit; memcpy(pszBase, OLD_CACHE_PATH, sizeof(OLD_CACHE_PATH)); dwBaseLen = sizeof(OLD_CACHE_PATH) - 1; for (i = 0; i < DEF_NUM_PATHS; i++) { pszBase[dwBaseLen-1] = (TCHAR)('1' + i); m_roPath[i].WorkWith(&m_roPaths, pszBase, CREATE_KEY_IF_NOT_EXISTS); if ((dwError=m_roPath[i].GetStatus())!=ERROR_SUCCESS) goto exit; } m_roSpecialPaths.WorkWith(&m_roHKLMCache, CACHE_SPECIAL_PATHS_KEY); m_fInitialised = TRUE; exit: return dwError; } BOOL GetContentDetails(LPSTR szPath, DWORD& cbLimit) { DWORD cbKey = MAX_PATH; if (m_roPaths.GetValue(CACHE_DIRECTORY_VALUE, (LPBYTE)szPath, &cbKey)!=ERROR_SUCCESS) return FALSE; cbLimit = 0; for (int i=0; iGetStatus(); if (dwError!=ERROR_SUCCESS) { delete _coContainer[i]; break; } ConList.Add(_coContainer[i]); _coContainer[i]->SetPerUserItem(fPerUser); } else { dwError = ERROR_NOT_ENOUGH_MEMORY; break; } // Maintain values for backwards compatibility if (i==CONTENT) { // If repairing IE3's settings fails, well, who cares? IE5 is still going. IE3_REGISTRYSET ie3rs; ie3rs.FixLegacySettings(szCachePath, cbCacheLimit); } } exit: return dwError; } /*----------------------------------------------------------------------------- DWORD CConMgr::GetSysRootCacheConfigInfo ----------------------------------------------------------------------------*/ DWORD CConMgr::GetSysRootCacheConfigInfo() { CHAR szParentPath[MAX_PATH]; DWORD cb = ARRAY_ELEMENTS(szParentPath); #ifndef UNIX { // In case of failure to create containers in the regular location, // we should put the cache in a user-specifiable location HKLM\* REG_EXPAND_SZ // Otherwise in the temp directory // Otherwise in the Windows directory REGISTRY_OBJ roCache(HKEY_LOCAL_MACHINE, CACHE5_KEY); BOOL fResult = FALSE; if (roCache.GetStatus()==ERROR_SUCCESS) { TCHAR szTemp[MAX_PATH]; if (roCache.GetValue(TEXT("SystemCache"), (LPBYTE)szTemp, &cb)==ERROR_SUCCESS) { cb = ExpandEnvironmentStrings(szTemp,szParentPath,ARRAY_ELEMENTS(szParentPath)) - 1; if (cb && (cb < ARRAY_ELEMENTS(szParentPath))) { fResult = TRUE; } } } if (!fResult) { cb = ExpandEnvironmentStrings(TEXT("%TEMP%"),szParentPath,ARRAY_ELEMENTS(szParentPath)) - 1; if (!cb || (cb > ARRAY_ELEMENTS(szParentPath))) { cb = GetWindowsDirectory(szParentPath, ARRAY_ELEMENTS(szParentPath)); } } } #else /* On Unix, GetWindowsDirectory will point to /common * and the cache should not be created here in any case */ lstrcpy(szParentPath,UNIX_SHARED_CACHE_PATH); cb = lstrlen(szParentPath); #endif /* UNIX */ if (!cb || (cb>sizeof(szParentPath))) { return ERROR_PATH_NOT_FOUND; } AppendSlashIfNecessary(szParentPath, &cb); for (DWORD idx = CONTENT; idx < NCONTAINERS; idx++) { CHAR szCachePath[MAX_PATH]; CHAR szCachePrefix[MAX_PATH]; LONGLONG cbCacheLimit; // Get cache paths out of dll resource and form absolute // paths to top level cache directories. memcpy(szCachePath, szParentPath, cb); if (!LoadString(GlobalDllHandle, g_dwCachePathResourceID[idx], szCachePath + cb, MAX_PATH - cb)) { return GetLastError(); } DWORD ccPath = lstrlen(szCachePath); AppendSlashIfNecessary(szCachePath, &ccPath); memcpy(szCachePath+ccPath, g_szVersionName[idx], lstrlen(g_szVersionName[idx])+1); // Cache prefix. memcpy(szCachePrefix, g_szCachePrefix[idx], strlen(g_szCachePrefix[idx]) + 1); // Cache limit - for the content cache we calculate the cache limit // as being max(DEF_CACHE_LIMIT, 1/32 of the disk size) All others caches // are set to DEF_CACHE_LIMIT. if (idx == CONTENT) { REGISTRY_OBJ roCache(HKEY_LOCAL_MACHINE, CACHE5_KEY); BOOL fResult = (roCache.GetStatus()==ERROR_SUCCESS); if (fResult) { REGISTRY_OBJ roLimit(&roCache, CONTENT_PATH_KEY); fResult = FALSE; if (roLimit.GetStatus()==ERROR_SUCCESS) { DWORD cKBLimit; if (roLimit.GetValue(CACHE_LIMIT_VALUE, &cKBLimit)==ERROR_SUCCESS) { cbCacheLimit = cKBLimit * (LONGLONG)1024; fResult = TRUE; } } } if (!fResult) { cbCacheLimit = (DWORDLONG)GetDefaultCacheQuota(szCachePath, NEW_CONTENT_QUOTA_DEFAULT_DISK_FRACTION) * (DWORDLONG)1024; } } else { // Non-CONTENT cache; use default. cbCacheLimit = DEF_CACHE_LIMIT * (LONGLONG)1024; } _coContainer[idx] = new URL_CONTAINER(g_szSubKey[idx], szCachePath, szCachePrefix, cbCacheLimit, 0); if (_coContainer[idx]) { DWORD dwError = _coContainer[idx]->GetStatus(); if (dwError!=ERROR_SUCCESS) { delete _coContainer[idx]; return dwError; } ConList.Add(_coContainer[idx]); _coContainer[idx]->SetPerUserItem(FALSE); } else { return ERROR_NOT_ENOUGH_MEMORY; } } _fUsingBackupContainers = TRUE; return ERROR_SUCCESS; } /*----------------------------------------------------------------------------- BOOL CConMgr::GetUrlCacheConfigInfo ----------------------------------------------------------------------------*/ BOOL CConMgr::GetUrlCacheConfigInfo(LPCACHE_CONFIG_INFO lpCacheConfigInfo, LPDWORD lpdwCacheConfigInfoBufferSize, DWORD dwFieldControl) { LOCK_CACHE(); BOOL fIE5Struct = (lpCacheConfigInfo->dwStructSize == sizeof(INTERNET_CACHE_CONFIG_INFO)); if(IsFieldSet( dwFieldControl, CACHE_CONFIG_SYNC_MODE_FC)) { lpCacheConfigInfo->dwSyncMode = GlobalUrlCacheSyncMode; } if (IsFieldSet(dwFieldControl, CACHE_CONFIG_QUOTA_FC)) { lpCacheConfigInfo->dwQuota = (DWORD) (_coContainer[lpCacheConfigInfo->dwContainer]->GetCacheLimit()/1024L); } if (fIE5Struct && IsFieldSet(dwFieldControl, CACHE_CONFIG_CONTENT_USAGE_FC)) { lpCacheConfigInfo->dwNormalUsage = (DWORD) (_coContainer[lpCacheConfigInfo->dwContainer]->GetCacheSize()/1024L); } if (fIE5Struct && IsFieldSet(dwFieldControl, CACHE_CONFIG_STICKY_CONTENT_USAGE_FC) && (lpCacheConfigInfo->dwContainer==CONTENT)) { lpCacheConfigInfo->dwExemptUsage = (DWORD) (_coContainer[CONTENT]->GetExemptUsage()/1024L); } lpCacheConfigInfo->fPerUser = IsFieldSet( dwFieldControl, CACHE_CONFIG_USER_MODE_FC) ? _coContainer[lpCacheConfigInfo->dwContainer]->IsPerUserItem() : _coContent->IsPerUserItem(); if (IsFieldSet(dwFieldControl, CACHE_CONFIG_CONTENT_PATHS_FC)) { lpCacheConfigInfo->dwContainer = CONTENT; } else if (IsFieldSet(dwFieldControl, CACHE_CONFIG_HISTORY_PATHS_FC)) { lpCacheConfigInfo->dwContainer = HISTORY; } else if (IsFieldSet(dwFieldControl, CACHE_CONFIG_COOKIES_PATHS_FC)) { lpCacheConfigInfo->dwContainer = COOKIE; } // These are the actual field codes that should be sent for cache paths. // Note that the path returned *does not* contain subdirs (cache1..N). if (lpCacheConfigInfo->dwContainer <= HISTORY) { memcpy(lpCacheConfigInfo->CachePath, _coContainer[lpCacheConfigInfo->dwContainer]->GetCachePath(), _coContainer[lpCacheConfigInfo->dwContainer]->GetCachePathLen() + 1); lpCacheConfigInfo->dwQuota = (DWORD) (_coContainer[lpCacheConfigInfo->dwContainer]->GetCacheLimit() / 1024); lpCacheConfigInfo->dwNumCachePaths = (DWORD) 1; } UNLOCK_CACHE(); return TRUE; } /*----------------------------------------------------------------------------- BOOL CConMgr::SetUrlCacheConfigInfo ----------------------------------------------------------------------------*/ BOOL CConMgr::SetUrlCacheConfigInfo(LPCACHE_CONFIG_INFO pConfig, DWORD dwFieldControl) { DWORD i, dwError = ERROR_SUCCESS; UNIX_RETURN_ERR_IF_READONLY_CACHE(dwError); LOCK_CACHE(); // Check FieldControl bits and set the values for set fields if( IsFieldSet( dwFieldControl, CACHE_CONFIG_SYNC_MODE_FC )) { INET_ASSERT(pConfig->dwSyncMode <= WININET_SYNC_MODE_AUTOMATIC); InternetWriteRegistryDword(vszSyncMode, pConfig->dwSyncMode); GlobalUrlCacheSyncMode = pConfig->dwSyncMode; // set a new version and simultaneously // increment copy for this process, so we don't // read registry for this process IncrementHeaderData(CACHE_HEADER_DATA_CURRENT_SETTINGS_VERSION, &GlobalSettingsVersion); } if ( IsFieldSet( dwFieldControl, CACHE_CONFIG_DISK_CACHE_PATHS_FC )) { dwError = ERROR_INVALID_PARAMETER; goto exit; } if ( IsFieldSet( dwFieldControl, CACHE_CONFIG_QUOTA_FC ) && pConfig->dwContainer==CONTENT) { DWORD cbSize = pConfig->dwQuota; INET_ASSERT(cbSize); if (!_fUsingBackupContainers) { IE5_REGISTRYSET ie5; IE3_REGISTRYSET ie3; if ((dwError=ie5.InitialiseKeys(_fProfilesCapable))!=ERROR_SUCCESS) { goto exit; } ie5.SetWorkingContainer(CONTENT); TCHAR szTemp[MAX_PATH]; ie5.GetPath(szTemp); ie5.SetLimit(szTemp, cbSize); if (ie3.InitialiseKeys()==ERROR_SUCCESS) { ie3.FixLegacySettings(szTemp, cbSize); } } else { REGISTRY_OBJ roCache(HKEY_LOCAL_MACHINE, CACHE5_KEY, CREATE_KEY_IF_NOT_EXISTS); BOOL fResult = (roCache.GetStatus()==ERROR_SUCCESS); if (fResult) { REGISTRY_OBJ roLimit(&roCache, CONTENT_PATH_KEY, CREATE_KEY_IF_NOT_EXISTS); if (roLimit.GetStatus()==ERROR_SUCCESS) { roLimit.SetValue(CACHE_LIMIT_VALUE, &cbSize); } } } if ((((LONGLONG)cbSize * 1024) < _coContent->GetCacheSize())) _coContent->CleanupUrls (DEFAULT_CLEANUP_FACTOR, 0); _coContent->SetCacheLimit(cbSize* (LONGLONG)1024); } exit: UNLOCK_CACHE(); BOOL fRet = (dwError==ERROR_SUCCESS); if (!fRet) { SetLastError(dwError); DEBUG_ERROR(INET, dwError); } return fRet; } /*----------------------------------------------------------------------------- DWORD CConMgr::SetContentPath UpdateUrlCacheContentPath leads to this function. This initiates the cache move. Should be called just before shutdown. ----------------------------------------------------------------------------*/ BOOL CConMgr::SetContentPath(PTSTR pszNewPath) { IE5_REGISTRYSET ie5rs; DWORD dwError; BOOL fLock; if (_coContent->LockContainer(&fLock) && (dwError=ie5rs.InitialiseKeys(_fProfilesCapable))==ERROR_SUCCESS) { dwError = ie5rs.UpdateContentPath(pszNewPath); } if (fLock) { _coContent->UnlockContainer(); } if (dwError==ERROR_SUCCESS) { return TRUE; } SetLastError(dwError); return FALSE; } /*----------------------------------------------------------------------------- DWORD CConMgr::GetExtensibleCacheConfigInfo ----------------------------------------------------------------------------*/ DWORD CConMgr::GetExtensibleCacheConfigInfo(BOOL fAlways) { CHAR szCachePath[MAX_PATH]; CHAR szCachePrefix[MAX_PATH]; CHAR szPrefixMap[MAX_PATH]; CHAR szVolumeLabel[MAX_PATH]; CHAR szVolumeTitle[MAX_PATH]; LONGLONG cbCacheLimit; HKEY hKey = (HKEY) INVALID_HANDLE_VALUE; DWORD cbKeyLen, cbKBLimit, dwError = ERROR_SUCCESS; CHAR szVendorKey[MAX_PATH]; URL_CONTAINER* pNewContainer; URL_CONTAINER* co; MUTEX_HOLDER mh; DWORD idx; DWORD idxPrefix; DWORD dwNow; DWORD dwOptions; BOOL fModified; BOOL fCDContainer; REGISTRY_OBJ* pro = NULL; LOCK_CACHE(); fModified = WasModified(TRUE); hKey = _fProfilesCapable ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; // WasModified MUST come first, so that we update our cached count! if (!fModified && !fAlways) { // Unmap every container that hasn't be referenced in UNMAP_TIME dwNow = GetTickCountWrap(); if ((dwNow - _dwLastUnmap) > UNMAP_TIME) { for (idx = ConList.Size()-1; idx >= NCONTAINERS; idx--) { co = ConList.Get(idx); if (co) { if (co->GetDeletePending() || ((dwNow - co->GetLastReference()) > UNMAP_TIME)) { co->TryToUnmap(1); // RefCount should be 1 == Us } co->Release(FALSE); } } _dwLastUnmap = dwNow; } goto exit; } // Create registry object and entry. pro = CreateExtensiRegObj(hKey); if (!pro) { dwError = ERROR_ACCESS_DENIED; goto exit; } { REGISTRY_OBJ& roExtensibleCache = *pro; for (idx = NCONTAINERS; idx < ConList.Size(); idx++) { URL_CONTAINER *co = ConList.Get(idx); if (co) { co->Mark(FALSE); co->Release(FALSE); } } mh.Grab(_hMutexExtensible, FAILSAFE_TIMEOUT); idx = NCONTAINERS; // Get the container paths, prefixes (if any) and default limit values. while (roExtensibleCache.FindNextKey(szVendorKey, MAX_PATH) == ERROR_SUCCESS) { REGISTRY_OBJ roVendor(&roExtensibleCache, szVendorKey); if (roVendor.GetStatus()==ERROR_SUCCESS) { // Path. cbKeyLen = MAX_PATH; DWORD dwRepair = TRUE; CHAR szScratch[MAX_PATH]; roVendor.GetValue(CACHE_PATCH_VALUE, &dwRepair); if (roVendor.GetValue(CACHE_PATH_VALUE, (LPBYTE) szScratch, &cbKeyLen) != ERROR_SUCCESS) continue; if (!*szScratch) { roExtensibleCache.DeleteKey(szVendorKey); continue; } if (dwRepair) { CompressPath(szScratch, szCachePath); cbKeyLen = lstrlen(szCachePath)+1; roVendor.SetValue(CACHE_PATH_VALUE, szCachePath, REG_EXPAND_SZ); dwRepair = FALSE; roVendor.SetValue(CACHE_PATCH_VALUE, &dwRepair); } ExpandEnvironmentStrings(szScratch, szCachePath, sizeof(szCachePath)-1); // don't count the NULL // Prefix. cbKeyLen = MAX_PATH; if (roVendor.GetValue(CACHE_PREFIX_VALUE, (LPBYTE) szCachePrefix, &cbKeyLen) != ERROR_SUCCESS) continue; // Limit. if (roVendor.GetValue(CACHE_LIMIT_VALUE, &cbKBLimit) != ERROR_SUCCESS) continue; // Options. if (roVendor.GetValue(CACHE_OPTIONS_VALUE, &dwOptions) != ERROR_SUCCESS) continue; if (dwOptions & INTERNET_CACHE_CONTAINER_MAP_ENABLED) { fCDContainer = TRUE; // PrefixMap cbKeyLen = MAX_PATH; if ((roVendor.GetValue(CACHE_PREFIX_MAP_VALUE, (LPBYTE) szPrefixMap, &cbKeyLen) != ERROR_SUCCESS) || (*szPrefixMap == '\0')) continue; // Volume label. cbKeyLen = MAX_PATH; if ((roVendor.GetValue(CACHE_VOLUME_LABLE_VALUE, (LPBYTE) szVolumeLabel, &cbKeyLen) != ERROR_SUCCESS) || (*szVolumeLabel == '\0')) continue; // Volume title. cbKeyLen = MAX_PATH; if ((roVendor.GetValue(CACHE_VOLUME_TITLE_VALUE, (LPBYTE) szVolumeTitle, &cbKeyLen) != ERROR_SUCCESS) || (*szVolumeTitle == '\0')) continue; } else { fCDContainer = FALSE; *szPrefixMap = '\0'; dwOptions &= ~INTERNET_CACHE_CONTAINER_PREFIXMAP; } cbCacheLimit = ((LONGLONG) cbKBLimit) * 1024; idxPrefix = FindExtensibleContainer(szVendorKey); if (idxPrefix != NOT_AN_INDEX) { co = ConList.Get(idxPrefix); if (co) { // what if the container has been added // with the same name but a different path, prefix, or options! if (stricmp(co->GetCachePath(), szCachePath) || stricmp(co->GetCachePrefix(), szCachePrefix) || co->GetOptions() != dwOptions) { idxPrefix = NOT_AN_INDEX; } else if (fCDContainer && stricmp(co->GetPrefixMap(), szPrefixMap)) { idxPrefix = NOT_AN_INDEX; } else { co->Mark(TRUE); } co->Release(FALSE); } } if (idxPrefix == NOT_AN_INDEX) { // Construct either a normal container, or a CD container. if (!fCDContainer) { pNewContainer = new URL_CONTAINER(szVendorKey, szCachePath, szCachePrefix, cbCacheLimit, dwOptions); } else { pNewContainer = new CInstCon(szVendorKey, szVolumeLabel, szVolumeTitle, szCachePath, szCachePrefix, szPrefixMap, cbCacheLimit, dwOptions); } if (pNewContainer) { dwError = pNewContainer->GetStatus(); if (dwError!=ERROR_SUCCESS) { delete pNewContainer; pNewContainer = NULL; } else { pNewContainer->Mark(TRUE); ConList.Add(pNewContainer); } } else { dwError = ERROR_NOT_ENOUGH_MEMORY; } } idx++; } } mh.Release(); // Mark every container that's no longer in the registry for pending delete // Unmap every container that hasn't be referenced in UNMAP_TIME dwNow = GetTickCountWrap(); idx = ConList.Size() - 1; while (idx >= NCONTAINERS) { co = ConList.Get(idx); if (co) { if (!co->GetMarked() && !co->GetDeleted()) { co->SetDeletePending(TRUE); } if (co->GetDeletePending() || ((dwNow - co->GetLastReference()) > UNMAP_TIME)) { co->TryToUnmap(1); // RefCount should be 1 == Us, unless enumerator // is still open } co->Release(FALSE); } idx--; } _dwLastUnmap = dwNow; } delete pro; exit: UNLOCK_CACHE(); return dwError; } // // Mixed environment of IE4 and IE5 sharing a server causes HKCU keys to get resaved as REG_SZ incorrectly // so we repair it here VOID CheckCacheLocationConsistency() { // Read user shell folders (necessary only in HKCU) and write back as REG_EXPAND_SZ if necessary REGISTRY_OBJ roUserShellFolders(HKEY_CURRENT_USER, USER_SHELL_FOLDER_KEY); if (roUserShellFolders.GetStatus()!=ERROR_SUCCESS) { return; } for (int i=0; iszTemp) { psz--; } if ((psz>szTemp) && !StrCmpNI(psz+1, TEXT("content."), ARRAY_ELEMENTS("content.")-1)) { *(psz+1) = TEXT('\0'); } StrCatBuff(szTemp, TEXT("cache1"), MAX_PATH); cb = lstrlen(szTemp) - 1; for (int i=0; i<4; i++) { szTemp[cb] = TEXT('1')+i; DeleteCachedFilesInDir(szTemp); RemoveDirectory(szTemp); } } } return ERROR_SUCCESS; } // -- Externally available apis URLCACHEAPI_(BOOL) SetUrlCacheConfigInfoA( LPCACHE_CONFIG_INFO pConfig, DWORD dwFieldControl ) /*++ Routine Description: This function sets the cache configuration parameters. Arguments: lpCacheConfigInfo - place holding cache configuration information to be set dwFieldControl - items to get Return Value: Error Code --*/ { // Initialize globals if (!InitGlobals()) { SetLastError (ERROR_INTERNET_INTERNAL_ERROR); return FALSE; } return GlobalUrlContainers->SetUrlCacheConfigInfo(pConfig,dwFieldControl); } URLCACHEAPI_(BOOL) GetUrlCacheConfigInfoA( LPCACHE_CONFIG_INFO lpCacheConfigInfo, IN OUT LPDWORD lpdwCacheConfigInfoBufferSize, DWORD dwFieldControl ) /*++ Routine Description: This function retrieves cache configuration values from globals Arguments: pConfig - pointer to a location where configuration information is stored on a successful return lpdwCacheConfigInfoBufferSize : pointer to a location where length of the above buffer is passed in. On return, this contains the length of the above buffer that is fulled in. dwFieldControl - items to get Return Value: Error Code --*/ { ENTER_CACHE_API ((DBG_API, Bool, "GetUrlCacheConfigInfoA", "%#x, %#x, %#x", lpCacheConfigInfo, lpdwCacheConfigInfoBufferSize, dwFieldControl )); BOOL fError; // Initialize globals if (!InitGlobals()) { SetLastError (ERROR_INTERNET_INTERNAL_ERROR); DEBUG_ERROR(API, ERROR_INTERNET_INTERNAL_ERROR); fError = FALSE; } else { fError = GlobalUrlContainers->GetUrlCacheConfigInfo(lpCacheConfigInfo, lpdwCacheConfigInfoBufferSize, dwFieldControl); } DEBUG_LEAVE_API (fError); return fError; } // declared in wininet\inc\urlcache.h BOOL GetIE5ContentPath( LPSTR szPath) { BOOL retVal = FALSE; IE5_REGISTRYSET ie5rs; BOOL fProfilesCapable; if( ie5rs.InitialiseKeys(fProfilesCapable) != ERROR_SUCCESS) goto doneGetContentPath; if( ie5rs.SetWorkingContainer(CONTENT) != ERROR_SUCCESS) goto doneGetContentPath; if( ie5rs.GetPath( szPath) != ERROR_SUCCESS) goto doneGetContentPath; retVal = TRUE; doneGetContentPath: return retVal; } // SHDOCVW needs to know whether profiles are enabled, to determine whether // or not it needs to filter out user names. This function will help keep things simple. // And minimise perf impact. STDAPI_(BOOL) IsProfilesEnabled() { IE5_REGISTRYSET ie5rs; BOOL fProfilesEnabled; if (ie5rs.InitialiseKeys(fProfilesEnabled) != ERROR_SUCCESS) { fProfilesEnabled = FALSE; } return fProfilesEnabled; } BOOL CConMgr::DiscoverIE4Settings(IE5_REGISTRYSET* pie5rs) { IE4_REGISTRYSET ie4rs; CHAR szTemp[MAX_PATH+1], szPrefix[MAX_PATH+1]; DWORD cbLimit, dwTemp; BOOL fPerUser, fCaughtIE4; // Try to find IE4 settings. If any paths are found, we will not look for IE3 settings fCaughtIE4 = FALSE; if (ie4rs.WasIE4Present(_fProfilesCapable)) { if (ie4rs.InitialiseKeys(_fProfilesCapable)!=ERROR_SUCCESS) { LOG_UPGRADE_DATA("IE4 initialisation failed...\n"); return FALSE; } for (dwTemp=0;dwTemp < NCONTAINERS; dwTemp++) { ie4rs.SetWorkingContainer(dwTemp); if (ie4rs.GetPath(szTemp)!=ERROR_SUCCESS) { continue; } LOG_UPGRADE_DATA("DIE4Settings: "); LOG_UPGRADE_DATA(szTemp); LOG_UPGRADE_DATA("\n"); DisableCacheVu(szTemp); pie5rs->SetWorkingContainer(dwTemp); // Because SHGetFolderPath uses shell folders to determine where the items are, we have to accomodate this // on no-profiles machines. if (!_fProfilesCapable) { pie5rs->SetPath(szTemp); } else if (dwTemp==CONTENT) { #ifndef UNIX if (ie4rs.GetPerUserStatus() || GlobalPlatformVersion5) #else if (ie4rs.GetPerUserStatus() || GlobalPlatformType == PLATFORM_TYPE_UNIX) #endif /* UNIX */ { // If it's NT5, we want to go to a per-user, non-roaming location // which is the NT5 default anyway LOG_UPGRADE_DATA("DIE4Settings: If NT5, ignore shared cache. Else this isn't shared anyway."); pie5rs->SetPerUserStatus(TRUE); } else { // Because IE4 locates a shared cache differently from IE5, we need to // save the path and status. LOG_UPGRADE_DATA("DIE4Settings: Will try to use shared cache"); pie5rs->AttemptToUseSharedCache(szTemp, 0); } } fCaughtIE4 = TRUE; // We don't need to check return values since we come up with // reasonable values on our own. ie4rs.GetLimit(szTemp, cbLimit); pie5rs->SetLimit(szTemp, cbLimit); ie4rs.GetPrefix(szPrefix); pie5rs->SetPrefix(szPrefix); } } if (!fCaughtIE4) { LOG_UPGRADE_DATA("No IE4 settings...\n"); } else { FlushShellFolderCache(); } return fCaughtIE4; } VOID CConMgr::DiscoverIE3Settings(IE5_REGISTRYSET* pie5rs) { IE3_REGISTRYSET ie3rs; if (ie3rs.InitialiseKeys()!=ERROR_SUCCESS) { return; } TCHAR szTemp[MAX_PATH]; DWORD cbLimit; // This fragment will look for a cache location, and test for its share-ability. If it is, // we'll use the location; otherwise, we'll use our own shared location. if (ie3rs.GetContentDetails(szTemp, cbLimit)) { DeleteCachedFilesInDir(szTemp); // No IE4. Steal IE3's settings? We only care about content cache. // Is that a good idea? There's no UI for modifying the cookies/history path; // if someone plumbs into the registry, do we want to support that? *sigh* // BUG? We're moving the cache one level deeper. We probably want to be a // bit more intelligent about this. CleanPath(szTemp); pie5rs->AttemptToUseSharedCache(szTemp, cbLimit); } // This fragment deletes the shared history. It should happen ONLY ONCE. DWORD cbKeyLen = ARRAY_ELEMENTS(szTemp); REGISTRY_OBJ roHist(HKEY_LOCAL_MACHINE, IE3_HISTORY_PATH_KEY); if ((roHist.GetStatus()==ERROR_SUCCESS) && (roHist.GetValue(NULL,(LPBYTE)szTemp, &cbKeyLen)==ERROR_SUCCESS)) { REGISTRY_OBJ roUrlHist(HKEY_LOCAL_MACHINE, szTemp); cbKeyLen = ARRAY_ELEMENTS(szTemp); if ((roUrlHist.GetStatus()==ERROR_SUCCESS) && (roUrlHist.GetValue(CACHE_DIRECTORY_VALUE, (LPBYTE)szTemp, &cbKeyLen)==ERROR_SUCCESS)) { DeleteCachedFilesInDir(szTemp); } } } // Logic for determining the location of the cache ------------------------------------------------------------- // PROFILES ENABLED -------------------------- // [on logon] // If profiles are enabled, look in HKCU/Software/Microsoft/Windows/Internet Settings/5.0/Cache // for a signature. // If a signature is present, [carry on] // Look in HKLM/Software/Microsoft/Windows/Internet Settings/5.0/Cache // for a signature. // If a signature is not present, jump to [over IE4 install] // For history and cookies, the containers will be located in the profiles directory // For content, // if it's marked per user, // and there isn't a shell folder/user shell folder value, construct and put it in // otherwise feed the HKLM shared location into (user) shell folder. // Insert signature and [carry on]. // [over IE4 install] // If a signature is not present in HKCU/Software/Microsoft/Windows/Internet Settings/Cache, // jump to [over IE3 install] // Determine if the content cache is per-user or not. // [over IE3 install] // For history and cookies, the containers will be located in the profiles directory // Examine HKLM/Software/Microsoft/Windows/Internet Settings/Cache/Paths // If not present, go to [clean install] // If the cache path is located in a user's profiles directory, ignore and [clean install] // Otherwise, adopt the values and [carry on] // [clean install] // Set up history/cookies to be per user. // Set up the content cache to be shared. // Write in default values. // PROFILES NOT ENABLED -------------------------- // * If profiles are _not_ enabled, we'll look in HKLM/Software/Microsoft/Windows/Internet Settings/5.0/Cache // for a signature. // If a signature is present, go ahead and gather information for the paths // [carry on] // Get info from the registry, and create the container // -- DiscoverAnyIE5Settings // We're going to call this function if we haven't any IE4 settings to upgrade, // but _before_ we check for IE3, DWORD GetIEVersion(); BOOL CConMgr::DiscoverAnyIE5Settings(IE5_REGISTRYSET* pie5rs) { // Let's consider the following scenario: // User A logs on to the machine with IE4 installed; installs IE5, and then shuts down // the machine. User B comes along, but IE5 hasn't been installed yet. If User B has // admin privileges, install will continue, BUT still not have any IE4/5 settings. // Which resulted in skipping DiscoverIE4Settings. However, we don't want to look // at IE3's settings. // If we're installing over IE4/2/5, but we don't have any settings for this user, // we must avoid an IE3 upgrade. Instead, short circuit to use last-minute info-gathering // For IE3, use a shared cache DWORD dwVer = GetIEVersion(); // We're going to use a shared cache for IE3 and Win9x users. // Upgrading over IE4 and 5 -- for users who have logged in before, // their signatures shoudl be in place already. In those cases, we shouldn't // be in this function anyway. For other users, we'll use the shared cache. // This is the first time for the machine. if ((dwVer==3) && pie5rs->IsFirstTimeForMachine()) { return FALSE; } pie5rs->AttemptToUseSharedCache(NULL, 0); return TRUE; } VOID CConMgr::DiscoverRegistrySettings(IE5_REGISTRYSET* pie5rs) { LOG_UPGRADE_DATA("Attempting to discover IE4 settings...\n"); if (DiscoverIE4Settings(pie5rs)) { goto exit; } #ifndef UNIX if (GlobalPlatformType == PLATFORM_TYPE_WINNT) #else if (GlobalPlatformType == PLATFORM_TYPE_UNIX) #endif /* UNIX */ { LOG_UPGRADE_DATA("This is NT. Fuhgedabout IE3 et al settings...\n"); // This will override NT's default behaviour to use per-user containers. pie5rs->SetWorkingContainer(CONTENT); if (!pie5rs->GetPerUserStatus()) { pie5rs->AttemptToUseSharedCache(NULL, 0); } // Suppose this is an install over NT. Each user should get a per-user, non-roaming // path by default. The values we'd pick up from SHFolderGetPath will be okay; but, we need to make // sure that we treat this as a per-user container. // Suppose we upgraded from some previous version of Win9x to NT5. There are eight scenarios: // nothing : in which case, we do as above // IE3 : same; need to delete old cache // IE4 -- single-user machine:same as fresh install; need to delete old cache. // -- shared cache : same as fresh install // -- per user cache : preserve path // -- moved cache : preserve path // IE5 -- single-user machine:same as fresh install; need to delete old cache. // -- shared cache : preserve shared path // -- per user cache : preserve path // -- moved cache : preserve path // If an admin wants to use a shared cache under NT5, s/he will have to set // HKCU/Shell Folders and User Shell Folders to point to the common path // AND {HKCU|HKLM}/blah/PerUserItem to 0 (or delete the HKCU value). if ((GetIEVersion()==3) && pie5rs->IsFirstTimeForMachine()) { IE3_REGISTRYSET ie3rs; TCHAR szTemp[MAX_PATH]; DWORD cbLimit; if ((ie3rs.InitialiseKeys()==ERROR_SUCCESS) && (ie3rs.GetContentDetails(szTemp, cbLimit))) { DeleteCachedFilesInDir(szTemp); } } goto exit; } LOG_UPGRADE_DATA("Attempting to discover any IE5 settings...\n"); if (DiscoverAnyIE5Settings(pie5rs)) { goto exit; } LOG_UPGRADE_DATA("Attempting to discover any IE3 settings...\n"); DiscoverIE3Settings(pie5rs); exit: LOG_UPGRADE_DATA("Flushing shell folders cache...\n"); pie5rs->SetIfFirstTime(); FlushShellFolderCache(); } // -- AttemptToUseSharedCache // Given a path (and limit) attempt to use the path for a shared location. // If the path is null, then try to use any value if present, else invent one. VOID IE5_REGISTRYSET::AttemptToUseSharedCache(PTSTR pszPath, DWORD ckbLimit) { TCHAR szSharedPath[MAX_PATH]; DWORD cc = ARRAY_ELEMENTS(szSharedPath); REGISTRY_OBJ roContent(&m_roHKLMCache, g_szSubKey[CONTENT], CREATE_KEY_IF_NOT_EXISTS); if ((roContent.GetStatus()==ERROR_SUCCESS) && (roContent.GetValue(CACHE_PATH_VALUE, (LPBYTE)szSharedPath, &cc)==ERROR_SUCCESS)) { LOG_UPGRADE_DATA("Found a shared cache location...\n"); goto write_value; } if (pszPath!=NULL) { LOG_UPGRADE_DATA(pszPath); LOG_UPGRADE_DATA("\n is "); GetUserName(szSharedPath, &cc); // We're going to ignore just the user name, during this comparison. And // if it's in the profiles directory, fuhgedaboutit. if (m_fProfiles && !StrCmpNI(m_szProfilePath, pszPath, cbP-cc)) { pszPath = NULL; LOG_UPGRADE_DATA("not okay \n"); goto carryon; } LOG_UPGRADE_DATA("okay \n"); strcpy(szSharedPath, pszPath); } carryon: if (pszPath==NULL) { memcpy(szSharedPath, m_szSharedPath, (cbS+1)*sizeof(TCHAR)); CleanPath(szSharedPath); LOG_UPGRADE_DATA("Using a constructed shared path\n"); } // We've finally decided on the path. Now let's write the value into the registry. roContent.SetValue(CACHE_PATH_VALUE, szSharedPath, REG_SZ); SetWorkingContainer(CONTENT); SetPerUserStatus(FALSE); SetLimit(szSharedPath, ckbLimit); write_value: LOG_UPGRADE_DATA("The shared cache will be located at "); LOG_UPGRADE_DATA(szSharedPath); LOG_UPGRADE_DATA("\n"); // This will take care of HKCU CHAR szScratch[MAX_PATH]; #ifndef UNIX if (!NormalisePath(szSharedPath, TEXT("%SystemRoot%"), szScratch, sizeof(szScratch))) #else if (!NormalisePath(szSharedPath, TEXT("%USERPROFILE%"), szScratch, sizeof(szScratch))) #endif /* UNIX */ { strncpy(szScratch, szSharedPath, MAX_PATH); } if (m_roUserShellFolder.SetValue(g_szOldSubKey[CONTENT], szScratch, REG_EXPAND_SZ)==ERROR_SUCCESS) { #ifndef UNIX DWORD dwType = REG_SZ; m_roShellFolder.SetValue(g_szOldSubKey[CONTENT], szSharedPath, dwType); #else m_roShellFolder.SetValue(g_szOldSubKey[CONTENT], szScratch, REG_EXPAND_SZ); #endif /* UNIX */ } } // -- CleanPath // Given a path, strip away any trailing content.ie5's, and if necessary, add a trailing brand mark, // i.e. "Temporary Internet Files" or localised version. VOID CleanPath(PTSTR pszPath) { DWORD ccPath = strlen(pszPath); PTSTR pszLastSep = pszPath + ccPath; // Now we're at the null terminator, but if the last character is also a separator, we want // to skip that too. if (*(pszLastSep-1)==DIR_SEPARATOR_CHAR) { pszLastSep--; } BOOL fSlash; // Strip away any "content.ie5"'s from the path for (;(fSlash = ScanToLastSeparator(pszPath, &pszLastSep));) { if (StrCmpNI((pszLastSep+1), TEXT("content.ie"), ARRAY_ELEMENTS(TEXT("content.ie"))-1)) { break; } *pszLastSep = '\0'; } // Load temp int files TCHAR szBrand[MAX_PATH]; DWORD ccBrand = 0; ccBrand = LoadString(GlobalDllHandle, g_dwCachePathResourceID[CONTENT], szBrand, ARRAY_ELEMENTS(szBrand)); // The following fragment should never happen, but just in case... if (!ccBrand) { ccBrand = sizeof(TEXT("Temporary Internet Files")); memcpy(szBrand, TEXT("Temporary Internet Files"), ccBrand); ccBrand /= sizeof(TCHAR); ccBrand--; } // If "Temporary Internet Files" doesn't trail the path, add it. if (!fSlash) { *pszLastSep++ = DIR_SEPARATOR_CHAR; *pszLastSep = '\0'; } else { pszLastSep++; } if (StrCmpNI((pszLastSep), szBrand, ccBrand)) { while (*pszLastSep && *pszLastSep!=DIR_SEPARATOR_CHAR) { pszLastSep++; } if (!*pszLastSep && (*(pszLastSep-1)!=DIR_SEPARATOR_CHAR)) { *pszLastSep = DIR_SEPARATOR_CHAR; pszLastSep++; } else if (*pszLastSep) { pszLastSep++; } memcpy(pszLastSep, szBrand, ccBrand*sizeof(TCHAR)); } *(pszLastSep+ccBrand)='\0'; } DWORD GetIEVersion() { DWORD dwVer = 0; REGISTRY_OBJ roVersion(HKEY_LOCAL_MACHINE, OLD_VERSION_KEY); TCHAR szKey[MAX_PATH]; DWORD cb = ARRAY_ELEMENTS(szKey); if ((roVersion.GetStatus()!=ERROR_SUCCESS) || (roVersion.GetValue(OLD_VERSION_VALUE, (LPBYTE)szKey, &cb)!=ERROR_SUCCESS)) { // This should never happen during a proper setup. // In case it does, however, we'll just use construct IE5's default settings. return 0; } PTSTR psz = szKey; PTSTR pszFirst = szKey; // Get the major version number while (*psz!='.') { psz++; } *psz = '\0'; dwVer = (DWORD)StrToInt(pszFirst); if (dwVer==4) { psz++; // Skip the second number while (*psz!='.') { psz++; } pszFirst = psz; psz++; while (*psz!='.') { psz++; } *psz = '\0'; dwVer = ((DWORD)StrToInt(pszFirst))==0 ? 3 : 4; } return dwVer; }