//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: config.cpp // //-------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include #include "config.h" #include "util.h" #include "strings.h" // // Determine if a character is a DBCS lead byte. // If build is UNICODE, always returns false. // inline bool DBCSLEADBYTE(TCHAR ch) { if (sizeof(ch) == sizeof(char)) return boolify(IsDBCSLeadByte((BYTE)ch)); return false; } LPCTSTR CConfig::s_rgpszSubkeys[] = { REGSTR_KEY_OFFLINEFILES, REGSTR_KEY_OFFLINEFILESPOLICY }; LPCTSTR CConfig::s_rgpszValues[] = { REGSTR_VAL_DEFCACHESIZE, REGSTR_VAL_CSCENABLED, REGSTR_VAL_GOOFFLINEACTION, REGSTR_VAL_NOCONFIGCACHE, REGSTR_VAL_NOCACHEVIEWER, REGSTR_VAL_NOMAKEAVAILABLEOFFLINE, REGSTR_VAL_SYNCATLOGOFF, REGSTR_VAL_SYNCATLOGON, REGSTR_VAL_SYNCATSUSPEND, REGSTR_VAL_NOREMINDERS, REGSTR_VAL_REMINDERFREQMINUTES, REGSTR_VAL_INITIALBALLOONTIMEOUTSECONDS, REGSTR_VAL_REMINDERBALLOONTIMEOUTSECONDS, REGSTR_VAL_EVENTLOGGINGLEVEL, REGSTR_VAL_PURGEATLOGOFF, REGSTR_VAL_PURGEONLYAUTOCACHEATLOGOFF, REGSTR_VAL_FIRSTPINWIZARDSHOWN, REGSTR_VAL_SLOWLINKSPEED, REGSTR_VAL_ALWAYSPINSUBFOLDERS, REGSTR_VAL_ENCRYPTCACHE, REGSTR_VAL_NOFRADMINPIN }; // // Returns the single instance of the CConfig class. // Note that by making the singleton instance a function static // object it is not created until the first call to GetSingleton. // CConfig& CConfig::GetSingleton( void ) { static CConfig TheConfig; return TheConfig; } // // This is the workhorse of the CSCUI policy code for scalar values. // The caller passes in a value (iVAL_XXXXXX) identifier from the eValues // enumeration to identify the policy/preference value of interest. // Known keys in the registry are scanned until a value is found. // The scanning order enforces the precedence of policy vs. default vs. // preference and machine vs. user. // DWORD CConfig::GetValue( eValues iValue, bool *pbSetByPolicy ) const { // // This table identifies each DWORD policy/preference item used by CSCUI. // The entries MUST be ordered the same as the eValues enumeration. // Each entry describes the possible sources for data and a default value // to be used if no registry entries are present or if there's a problem reading // the registry. // static const struct Item { DWORD fSrc; // Mask indicating the reg locations to read. DWORD dwDefault; // Hard-coded default. } rgItems[] = { // Value ID eSRC_PREF_CU | eSRC_PREF_LM | eSRC_POL_CU | eSRC_POL_LM Default value // -------------------------------------- ------------ ------------ ----------- ----------- ------------------- /* iVAL_DEFCACHESIZE */ { eSRC_POL_LM, 1000 }, /* iVAL_CSCENABLED */ { eSRC_POL_LM, 1 }, /* iVAL_GOOFFLINEACTION */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, eGoOfflineSilent }, /* iVAL_NOCONFIGCACHE */ { eSRC_POL_CU | eSRC_POL_LM, 0 }, /* iVAL_NOCACHEVIEWER */ { eSRC_POL_CU | eSRC_POL_LM, 0 }, /* iVAL_NOMAKEAVAILABLEOFFLINE */ { eSRC_POL_CU | eSRC_POL_LM, 0 }, /* iVAL_SYNCATLOGOFF */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, eSyncFull }, /* iVAL_SYNCATLOGON */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, eSyncNone }, /* iVAL_SYNCATSUSPEND */ { eSRC_POL_CU | eSRC_POL_LM, eSyncNone }, /* iVAL_NOREMINDERS */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, 0 }, /* iVAL_REMINDERFREQMINUTES */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, 60 }, /* iVAL_INITIALBALLOONTIMEOUTSECONDS */ { eSRC_POL_CU | eSRC_POL_LM, 30 }, /* iVAL_REMINDERBALLOONTIMEOUTSECONDS */ { eSRC_POL_CU | eSRC_POL_LM, 15 }, /* iVAL_EVENTLOGGINGLEVEL */ { eSRC_PREF_CU | eSRC_PREF_LM | eSRC_POL_CU | eSRC_POL_LM, 0 }, /* iVAL_PURGEATLOGOFF */ { eSRC_POL_LM, 0 }, /* iVAL_PURGEONLYAUTOCACHEATLOGOFF */ { eSRC_POL_LM, 0 }, /* iVAL_FIRSTPINWIZARDSHOWN */ { eSRC_PREF_CU , 0 }, /* iVAL_SLOWLINKSPEED */ { eSRC_PREF_CU | eSRC_PREF_LM | eSRC_POL_CU | eSRC_POL_LM, 640 }, /* iVAL_ALWAYSPINSUBFOLDERS */ { eSRC_POL_LM, 0 }, /* iVAL_ENCRYPTCACHE */ { eSRC_PREF_LM | eSRC_POL_LM, 0 }, /* iVAL_NOFRADMINPIN */ { eSRC_POL_CU | eSRC_POL_LM, 0 } }; // // This table maps registry keys and subkey names to our // source mask values. The array is ordered with the highest // precedence sources first. A policy level mask is also // associated with each entry so that we honor the "big switch" // for enabling/disabling CSCUI policies. // static const struct Source { eSources fSrc; // Source for reg data. HKEY hkeyRoot; // Root key in registry (hkcu, hklm). eSubkeys iSubkey; // Index into s_rgpszSubkeys[] } rgSrcs[] = { { eSRC_POL_LM, HKEY_LOCAL_MACHINE, iSUBKEY_POL }, { eSRC_POL_CU, HKEY_CURRENT_USER, iSUBKEY_POL }, { eSRC_PREF_CU, HKEY_CURRENT_USER, iSUBKEY_PREF }, { eSRC_PREF_LM, HKEY_LOCAL_MACHINE, iSUBKEY_PREF } }; const Item& item = rgItems[iValue]; DWORD dwResult = item.dwDefault; // Set default return value. bool bSetByPolicy = false; // // Iterate over all of the sources until we find one that is specified // for this item. For each iteration, if we're able to read the value, // that's the one we return. If not we drop down to the next source // in the precedence order (rgSrcs[]) and try to read it's value. If // we've tried all of the sources without a successful read we return the // hard-coded default. // for (int i = 0; i < ARRAYSIZE(rgSrcs); i++) { const Source& src = rgSrcs[i]; // // Is this source valid for this item? // if (0 != (src.fSrc & item.fSrc)) { // // This source is valid for this item. Read it. // DWORD cbResult = sizeof(dwResult); DWORD dwType; if (ERROR_SUCCESS == SHGetValue(src.hkeyRoot, s_rgpszSubkeys[src.iSubkey], s_rgpszValues[iValue], &dwType, &dwResult, &cbResult)) { // // We read a value from the registry so we're done. // bSetByPolicy = (0 != (eSRC_POL & src.fSrc)); break; } } } if (NULL != pbSetByPolicy) *pbSetByPolicy = bSetByPolicy; return dwResult; } // // Save a custom GoOfflineAction list to the registry. // See comments for LoadCustomGoOfflineActions for formatting details. // HRESULT CConfig::SaveCustomGoOfflineActions( RegKey& key, HDPA hdpaGOA ) { if (NULL == hdpaGOA) { return E_INVALIDARG; } HRESULT hr = NOERROR; int cValuesNotDeleted = 0; key.DeleteAllValues(&cValuesNotDeleted); if (0 != cValuesNotDeleted) { Trace((TEXT("%d GoOfflineAction values not deleted from registry"), cValuesNotDeleted)); } TCHAR szServer[MAX_PATH]; TCHAR szAction[20]; const int cGOA = DPA_GetPtrCount(hdpaGOA); for (int i = 0; i < cGOA; i++) { // // Write each sharename-action pair to the registry. // The action value must be converted to ASCII to be // compatible with the values generated by poledit. // CustomGOA *pGOA = (CustomGOA *)DPA_GetPtr(hdpaGOA, i); wnsprintf(szAction, ARRAYSIZE(szAction), TEXT("%d"), DWORD(pGOA->GetAction())); pGOA->GetServerName(szServer, ARRAYSIZE(szServer)); hr = key.SetValue(szServer, szAction); if (FAILED(hr)) { Trace((TEXT("Error 0x%08X saving GoOfflineAction for \"%s\" to registry."), hr, szServer)); break; } } return hr; } bool CConfig::CustomGOAExists( HDPA hdpaGOA, const CustomGOA& goa ) { if (NULL != hdpaGOA) { const int cEntries = DPA_GetPtrCount(hdpaGOA); for (int i = 0; i < cEntries; i++) { CustomGOA *pGOA = (CustomGOA *)DPA_GetPtr(hdpaGOA, i); if (NULL != pGOA) { if (0 == goa.CompareByServer(*pGOA)) return true; } } } return false; } // // Builds an array of Go-offline actions. // Each entry is a server-action pair. // void CConfig::GetCustomGoOfflineActions( HDPA hdpa, bool *pbSetByPolicy // optional. Can be NULL. ) { TraceAssert(NULL != hdpa); static const struct Source { eSources fSrc; // Source for reg data. HKEY hkeyRoot; // Root key in registry (hkcu, hklm). eSubkeys iSubkey; // Index into s_rgpszSubkeys[] } rgSrcs[] = { { eSRC_POL_LM, HKEY_LOCAL_MACHINE, iSUBKEY_POL }, { eSRC_POL_CU, HKEY_CURRENT_USER, iSUBKEY_POL }, { eSRC_PREF_CU, HKEY_CURRENT_USER, iSUBKEY_PREF } }; ClearCustomGoOfflineActions(hdpa); TCHAR szName[MAX_PATH]; HRESULT hr; bool bSetByPolicyAny = false; bool bSetByPolicy = false; // // Iterate over all of the possible sources. // for (int i = 0; i < ARRAYSIZE(rgSrcs); i++) { const Source& src = rgSrcs[i]; RegKey key(src.hkeyRoot, s_rgpszSubkeys[src.iSubkey]); if (SUCCEEDED(key.Open(KEY_READ))) { RegKey keyGOA(key, REGSTR_SUBKEY_CUSTOMGOOFFLINEACTIONS); if (SUCCEEDED(keyGOA.Open(KEY_READ))) { TCHAR szValue[20]; DWORD dwType; DWORD cbValue = sizeof(szValue); RegKey::ValueIterator iter = keyGOA.CreateValueIterator(); while(S_OK == (hr = iter.Next(szName, ARRAYSIZE(szName), &dwType, (LPBYTE)szValue, &cbValue))) { if (REG_SZ == dwType) { // // Convert from "0","1","2" to 0,1,2 // DWORD dwValue = szValue[0] - TEXT('0'); if (IsValidGoOfflineAction(dwValue)) { // // Only add if value is of proper type and value. // Protects against someone manually adding garbage // to the registry. // // Server names can also be entered into the registry // using poledit (and winnt.adm). This entry mechanism // can't validate format so we need to ensure the entry // doesn't have leading '\' or space characters. // LPCTSTR pszServer = szName; while(*pszServer && (TEXT('\\') == *pszServer || TEXT(' ') == *pszServer)) pszServer++; bSetByPolicy = (0 != (src.fSrc & eSRC_POL)); bSetByPolicyAny = bSetByPolicyAny || bSetByPolicy; CustomGOA *pGOA = new CustomGOA(pszServer, (CConfig::OfflineAction)dwValue, bSetByPolicy); if (NULL != pGOA) { if (CustomGOAExists(hdpa, *pGOA) || -1 == DPA_AppendPtr(hdpa, pGOA)) { delete pGOA; } } } else { Trace((TEXT("GoOfflineAction value %d invalid for \"%s\""), dwValue, szName)); } } else { Trace((TEXT("GoOfflineAction for \"%s\" has invalid reg type %d"), szName, dwType)); } } } } } if (NULL != pbSetByPolicy) *pbSetByPolicy = bSetByPolicyAny; } // // Delete all CustomGOA blocks attached to a DPA. // When complete, the DPA is empty. // void CConfig::ClearCustomGoOfflineActions( // [static] HDPA hdpaGOA ) { if (NULL != hdpaGOA) { const int cEntries = DPA_GetPtrCount(hdpaGOA); for (int i = cEntries - 1; 0 <= i; i--) { CustomGOA *pGOA = (CustomGOA *)DPA_GetPtr(hdpaGOA, i); delete pGOA; DPA_DeletePtr(hdpaGOA, i); } } } // // Retrieve the go-offline action for a specific server. If the server // has a "customized" action defined by either system policy or user // setting, that action is used. Otherwise, the "default" action is // used. // int CConfig::GoOfflineAction( LPCTSTR pszServer ) const { int iAction = GoOfflineAction(); // Get default action. if (NULL == pszServer) return iAction; TraceAssert(NULL != pszServer); // // Skip passed any leading backslashes for comparison. // The values we store in the registry don't have a leading "\\". // while(*pszServer && TEXT('\\') == *pszServer) pszServer++; HRESULT hr; CConfig::OfflineActionInfo info; CConfig::OfflineActionIter iter = CreateOfflineActionIter(); while(S_OK == (hr = iter.Next(&info))) { if (0 == lstrcmpi(pszServer, info.szServer)) { iAction = info.iAction; // Return custom action. break; } } // // Guard against bogus reg data. // if (eNumOfflineActions <= iAction || 0 > iAction) iAction = eGoOfflineSilent; return iAction; } //----------------------------------------------------------------------------- // CConfig::CustomGOA // "GOA" is "Go Offline Action" //----------------------------------------------------------------------------- bool CConfig::CustomGOA::operator < ( const CustomGOA& rhs ) const { int diff = CompareByServer(rhs); if (0 == diff) diff = m_action - rhs.m_action; return diff < 0; } // // Compare two CustomGoOfflineAction objects by their // server names. Comparison is case-insensitive. // Returns: <0 = *this < rhs // 0 = *this == rhs // >0 = *this > rhs // int CConfig::CustomGOA::CompareByServer( const CustomGOA& rhs ) const { return lstrcmpi(GetServerName(), rhs.GetServerName()); } //----------------------------------------------------------------------------- // CConfig::OfflineActionIter //----------------------------------------------------------------------------- CConfig::OfflineActionIter::OfflineActionIter( const CConfig *pConfig ) : m_pConfig(const_cast(pConfig)), m_iAction(-1), m_hdpaGOA(DPA_Create(4)) { } CConfig::OfflineActionIter::~OfflineActionIter( void ) { if (NULL != m_hdpaGOA) { CConfig::ClearCustomGoOfflineActions(m_hdpaGOA); DPA_Destroy(m_hdpaGOA); } } HRESULT CConfig::OfflineActionIter::Next( OfflineActionInfo *pInfo ) { if (NULL == m_hdpaGOA) { return E_OUTOFMEMORY; } HRESULT hr = S_FALSE; if (-1 == m_iAction) { m_pConfig->GetCustomGoOfflineActions(m_hdpaGOA); m_iAction = 0; } if (m_iAction < DSA_GetItemCount(m_hdpaGOA)) { CustomGOA *pGOA = (CustomGOA *)DPA_GetPtr(m_hdpaGOA, m_iAction); if (NULL != pGOA) { lstrcpyn(pInfo->szServer, pGOA->GetServerName(), ARRAYSIZE(pInfo->szServer)); pInfo->iAction = (DWORD)pGOA->GetAction(); m_iAction++; hr = S_OK; } } return hr; }