Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

534 lines
18 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: config.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include <shlwapi.h>
  13. #include "config.h"
  14. #include "util.h"
  15. #include "strings.h"
  16. //
  17. // Determine if a character is a DBCS lead byte.
  18. // If build is UNICODE, always returns false.
  19. //
  20. inline bool DBCSLEADBYTE(TCHAR ch)
  21. {
  22. if (sizeof(ch) == sizeof(char))
  23. return boolify(IsDBCSLeadByte((BYTE)ch));
  24. return false;
  25. }
  26. LPCTSTR CConfig::s_rgpszSubkeys[] = { REGSTR_KEY_OFFLINEFILES,
  27. REGSTR_KEY_OFFLINEFILESPOLICY };
  28. LPCTSTR CConfig::s_rgpszValues[] = { REGSTR_VAL_DEFCACHESIZE,
  29. REGSTR_VAL_CSCENABLED,
  30. REGSTR_VAL_GOOFFLINEACTION,
  31. REGSTR_VAL_NOCONFIGCACHE,
  32. REGSTR_VAL_NOCACHEVIEWER,
  33. REGSTR_VAL_NOMAKEAVAILABLEOFFLINE,
  34. REGSTR_VAL_SYNCATLOGOFF,
  35. REGSTR_VAL_SYNCATLOGON,
  36. REGSTR_VAL_SYNCATSUSPEND,
  37. REGSTR_VAL_NOREMINDERS,
  38. REGSTR_VAL_REMINDERFREQMINUTES,
  39. REGSTR_VAL_INITIALBALLOONTIMEOUTSECONDS,
  40. REGSTR_VAL_REMINDERBALLOONTIMEOUTSECONDS,
  41. REGSTR_VAL_EVENTLOGGINGLEVEL,
  42. REGSTR_VAL_PURGEATLOGOFF,
  43. REGSTR_VAL_PURGEONLYAUTOCACHEATLOGOFF,
  44. REGSTR_VAL_FIRSTPINWIZARDSHOWN,
  45. REGSTR_VAL_SLOWLINKSPEED,
  46. REGSTR_VAL_ALWAYSPINSUBFOLDERS,
  47. REGSTR_VAL_ENCRYPTCACHE,
  48. REGSTR_VAL_NOFRADMINPIN
  49. };
  50. //
  51. // Returns the single instance of the CConfig class.
  52. // Note that by making the singleton instance a function static
  53. // object it is not created until the first call to GetSingleton.
  54. //
  55. CConfig& CConfig::GetSingleton(
  56. void
  57. )
  58. {
  59. static CConfig TheConfig;
  60. return TheConfig;
  61. }
  62. //
  63. // This is the workhorse of the CSCUI policy code for scalar values.
  64. // The caller passes in a value (iVAL_XXXXXX) identifier from the eValues
  65. // enumeration to identify the policy/preference value of interest.
  66. // Known keys in the registry are scanned until a value is found.
  67. // The scanning order enforces the precedence of policy vs. default vs.
  68. // preference and machine vs. user.
  69. //
  70. DWORD CConfig::GetValue(
  71. eValues iValue,
  72. bool *pbSetByPolicy
  73. ) const
  74. {
  75. //
  76. // This table identifies each DWORD policy/preference item used by CSCUI.
  77. // The entries MUST be ordered the same as the eValues enumeration.
  78. // Each entry describes the possible sources for data and a default value
  79. // to be used if no registry entries are present or if there's a problem reading
  80. // the registry.
  81. //
  82. static const struct Item
  83. {
  84. DWORD fSrc; // Mask indicating the reg locations to read.
  85. DWORD dwDefault; // Hard-coded default.
  86. } rgItems[] = {
  87. // Value ID eSRC_PREF_CU | eSRC_PREF_LM | eSRC_POL_CU | eSRC_POL_LM Default value
  88. // -------------------------------------- ------------ ------------ ----------- ----------- -------------------
  89. /* iVAL_DEFCACHESIZE */ { eSRC_POL_LM, 1000 },
  90. /* iVAL_CSCENABLED */ { eSRC_POL_LM, 1 },
  91. /* iVAL_GOOFFLINEACTION */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, eGoOfflineSilent },
  92. /* iVAL_NOCONFIGCACHE */ { eSRC_POL_CU | eSRC_POL_LM, 0 },
  93. /* iVAL_NOCACHEVIEWER */ { eSRC_POL_CU | eSRC_POL_LM, 0 },
  94. /* iVAL_NOMAKEAVAILABLEOFFLINE */ { eSRC_POL_CU | eSRC_POL_LM, 0 },
  95. /* iVAL_SYNCATLOGOFF */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, eSyncFull },
  96. /* iVAL_SYNCATLOGON */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, eSyncNone },
  97. /* iVAL_SYNCATSUSPEND */ { eSRC_POL_CU | eSRC_POL_LM, eSyncNone },
  98. /* iVAL_NOREMINDERS */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, 0 },
  99. /* iVAL_REMINDERFREQMINUTES */ { eSRC_PREF_CU | eSRC_POL_CU | eSRC_POL_LM, 60 },
  100. /* iVAL_INITIALBALLOONTIMEOUTSECONDS */ { eSRC_POL_CU | eSRC_POL_LM, 30 },
  101. /* iVAL_REMINDERBALLOONTIMEOUTSECONDS */ { eSRC_POL_CU | eSRC_POL_LM, 15 },
  102. /* iVAL_EVENTLOGGINGLEVEL */ { eSRC_PREF_CU | eSRC_PREF_LM | eSRC_POL_CU | eSRC_POL_LM, 0 },
  103. /* iVAL_PURGEATLOGOFF */ { eSRC_POL_LM, 0 },
  104. /* iVAL_PURGEONLYAUTOCACHEATLOGOFF */ { eSRC_POL_LM, 0 },
  105. /* iVAL_FIRSTPINWIZARDSHOWN */ { eSRC_PREF_CU , 0 },
  106. /* iVAL_SLOWLINKSPEED */ { eSRC_PREF_CU | eSRC_PREF_LM | eSRC_POL_CU | eSRC_POL_LM, 640 },
  107. /* iVAL_ALWAYSPINSUBFOLDERS */ { eSRC_POL_LM, 0 },
  108. /* iVAL_ENCRYPTCACHE */ { eSRC_PREF_LM | eSRC_POL_LM, 0 },
  109. /* iVAL_NOFRADMINPIN */ { eSRC_POL_CU | eSRC_POL_LM, 0 }
  110. };
  111. //
  112. // This table maps registry keys and subkey names to our
  113. // source mask values. The array is ordered with the highest
  114. // precedence sources first. A policy level mask is also
  115. // associated with each entry so that we honor the "big switch"
  116. // for enabling/disabling CSCUI policies.
  117. //
  118. static const struct Source
  119. {
  120. eSources fSrc; // Source for reg data.
  121. HKEY hkeyRoot; // Root key in registry (hkcu, hklm).
  122. eSubkeys iSubkey; // Index into s_rgpszSubkeys[]
  123. } rgSrcs[] = { { eSRC_POL_LM, HKEY_LOCAL_MACHINE, iSUBKEY_POL },
  124. { eSRC_POL_CU, HKEY_CURRENT_USER, iSUBKEY_POL },
  125. { eSRC_PREF_CU, HKEY_CURRENT_USER, iSUBKEY_PREF },
  126. { eSRC_PREF_LM, HKEY_LOCAL_MACHINE, iSUBKEY_PREF }
  127. };
  128. const Item& item = rgItems[iValue];
  129. DWORD dwResult = item.dwDefault; // Set default return value.
  130. bool bSetByPolicy = false;
  131. //
  132. // Iterate over all of the sources until we find one that is specified
  133. // for this item. For each iteration, if we're able to read the value,
  134. // that's the one we return. If not we drop down to the next source
  135. // in the precedence order (rgSrcs[]) and try to read it's value. If
  136. // we've tried all of the sources without a successful read we return the
  137. // hard-coded default.
  138. //
  139. for (int i = 0; i < ARRAYSIZE(rgSrcs); i++)
  140. {
  141. const Source& src = rgSrcs[i];
  142. //
  143. // Is this source valid for this item?
  144. //
  145. if (0 != (src.fSrc & item.fSrc))
  146. {
  147. //
  148. // This source is valid for this item. Read it.
  149. //
  150. DWORD cbResult = sizeof(dwResult);
  151. DWORD dwType;
  152. if (ERROR_SUCCESS == SHGetValue(src.hkeyRoot,
  153. s_rgpszSubkeys[src.iSubkey],
  154. s_rgpszValues[iValue],
  155. &dwType,
  156. &dwResult,
  157. &cbResult))
  158. {
  159. //
  160. // We read a value from the registry so we're done.
  161. //
  162. bSetByPolicy = (0 != (eSRC_POL & src.fSrc));
  163. break;
  164. }
  165. }
  166. }
  167. if (NULL != pbSetByPolicy)
  168. *pbSetByPolicy = bSetByPolicy;
  169. return dwResult;
  170. }
  171. //
  172. // Save a custom GoOfflineAction list to the registry.
  173. // See comments for LoadCustomGoOfflineActions for formatting details.
  174. //
  175. HRESULT
  176. CConfig::SaveCustomGoOfflineActions(
  177. RegKey& key,
  178. HDPA hdpaGOA
  179. )
  180. {
  181. if (NULL == hdpaGOA)
  182. {
  183. return E_INVALIDARG;
  184. }
  185. HRESULT hr = NOERROR;
  186. int cValuesNotDeleted = 0;
  187. key.DeleteAllValues(&cValuesNotDeleted);
  188. if (0 != cValuesNotDeleted)
  189. {
  190. Trace((TEXT("%d GoOfflineAction values not deleted from registry"),
  191. cValuesNotDeleted));
  192. }
  193. TCHAR szServer[MAX_PATH];
  194. TCHAR szAction[20];
  195. const int cGOA = DPA_GetPtrCount(hdpaGOA);
  196. for (int i = 0; i < cGOA; i++)
  197. {
  198. //
  199. // Write each sharename-action pair to the registry.
  200. // The action value must be converted to ASCII to be
  201. // compatible with the values generated by poledit.
  202. //
  203. CustomGOA *pGOA = (CustomGOA *)DPA_GetPtr(hdpaGOA, i);
  204. wnsprintf(szAction, ARRAYSIZE(szAction), TEXT("%d"), DWORD(pGOA->GetAction()));
  205. pGOA->GetServerName(szServer, ARRAYSIZE(szServer));
  206. hr = key.SetValue(szServer, szAction);
  207. if (FAILED(hr))
  208. {
  209. Trace((TEXT("Error 0x%08X saving GoOfflineAction for \"%s\" to registry."),
  210. hr, szServer));
  211. break;
  212. }
  213. }
  214. return hr;
  215. }
  216. bool
  217. CConfig::CustomGOAExists(
  218. HDPA hdpaGOA,
  219. const CustomGOA& goa
  220. )
  221. {
  222. if (NULL != hdpaGOA)
  223. {
  224. const int cEntries = DPA_GetPtrCount(hdpaGOA);
  225. for (int i = 0; i < cEntries; i++)
  226. {
  227. CustomGOA *pGOA = (CustomGOA *)DPA_GetPtr(hdpaGOA, i);
  228. if (NULL != pGOA)
  229. {
  230. if (0 == goa.CompareByServer(*pGOA))
  231. return true;
  232. }
  233. }
  234. }
  235. return false;
  236. }
  237. //
  238. // Builds an array of Go-offline actions.
  239. // Each entry is a server-action pair.
  240. //
  241. void
  242. CConfig::GetCustomGoOfflineActions(
  243. HDPA hdpa,
  244. bool *pbSetByPolicy // optional. Can be NULL.
  245. )
  246. {
  247. TraceAssert(NULL != hdpa);
  248. static const struct Source
  249. {
  250. eSources fSrc; // Source for reg data.
  251. HKEY hkeyRoot; // Root key in registry (hkcu, hklm).
  252. eSubkeys iSubkey; // Index into s_rgpszSubkeys[]
  253. } rgSrcs[] = { { eSRC_POL_LM, HKEY_LOCAL_MACHINE, iSUBKEY_POL },
  254. { eSRC_POL_CU, HKEY_CURRENT_USER, iSUBKEY_POL },
  255. { eSRC_PREF_CU, HKEY_CURRENT_USER, iSUBKEY_PREF }
  256. };
  257. ClearCustomGoOfflineActions(hdpa);
  258. TCHAR szName[MAX_PATH];
  259. HRESULT hr;
  260. bool bSetByPolicyAny = false;
  261. bool bSetByPolicy = false;
  262. //
  263. // Iterate over all of the possible sources.
  264. //
  265. for (int i = 0; i < ARRAYSIZE(rgSrcs); i++)
  266. {
  267. const Source& src = rgSrcs[i];
  268. RegKey key(src.hkeyRoot, s_rgpszSubkeys[src.iSubkey]);
  269. if (SUCCEEDED(key.Open(KEY_READ)))
  270. {
  271. RegKey keyGOA(key, REGSTR_SUBKEY_CUSTOMGOOFFLINEACTIONS);
  272. if (SUCCEEDED(keyGOA.Open(KEY_READ)))
  273. {
  274. TCHAR szValue[20];
  275. DWORD dwType;
  276. DWORD cbValue = sizeof(szValue);
  277. RegKey::ValueIterator iter = keyGOA.CreateValueIterator();
  278. while(S_OK == (hr = iter.Next(szName, ARRAYSIZE(szName), &dwType, (LPBYTE)szValue, &cbValue)))
  279. {
  280. if (REG_SZ == dwType)
  281. {
  282. //
  283. // Convert from "0","1","2" to 0,1,2
  284. //
  285. DWORD dwValue = szValue[0] - TEXT('0');
  286. if (IsValidGoOfflineAction(dwValue))
  287. {
  288. //
  289. // Only add if value is of proper type and value.
  290. // Protects against someone manually adding garbage
  291. // to the registry.
  292. //
  293. // Server names can also be entered into the registry
  294. // using poledit (and winnt.adm). This entry mechanism
  295. // can't validate format so we need to ensure the entry
  296. // doesn't have leading '\' or space characters.
  297. //
  298. LPCTSTR pszServer = szName;
  299. while(*pszServer && (TEXT('\\') == *pszServer || TEXT(' ') == *pszServer))
  300. pszServer++;
  301. bSetByPolicy = (0 != (src.fSrc & eSRC_POL));
  302. bSetByPolicyAny = bSetByPolicyAny || bSetByPolicy;
  303. CustomGOA *pGOA = new CustomGOA(pszServer,
  304. (CConfig::OfflineAction)dwValue,
  305. bSetByPolicy);
  306. if (NULL != pGOA)
  307. {
  308. if (CustomGOAExists(hdpa, *pGOA) || -1 == DPA_AppendPtr(hdpa, pGOA))
  309. {
  310. delete pGOA;
  311. }
  312. }
  313. }
  314. else
  315. {
  316. Trace((TEXT("GoOfflineAction value %d invalid for \"%s\""),
  317. dwValue, szName));
  318. }
  319. }
  320. else
  321. {
  322. Trace((TEXT("GoOfflineAction for \"%s\" has invalid reg type %d"),
  323. szName, dwType));
  324. }
  325. }
  326. }
  327. }
  328. }
  329. if (NULL != pbSetByPolicy)
  330. *pbSetByPolicy = bSetByPolicyAny;
  331. }
  332. //
  333. // Delete all CustomGOA blocks attached to a DPA.
  334. // When complete, the DPA is empty.
  335. //
  336. void
  337. CConfig::ClearCustomGoOfflineActions( // [static]
  338. HDPA hdpaGOA
  339. )
  340. {
  341. if (NULL != hdpaGOA)
  342. {
  343. const int cEntries = DPA_GetPtrCount(hdpaGOA);
  344. for (int i = cEntries - 1; 0 <= i; i--)
  345. {
  346. CustomGOA *pGOA = (CustomGOA *)DPA_GetPtr(hdpaGOA, i);
  347. delete pGOA;
  348. DPA_DeletePtr(hdpaGOA, i);
  349. }
  350. }
  351. }
  352. //
  353. // Retrieve the go-offline action for a specific server. If the server
  354. // has a "customized" action defined by either system policy or user
  355. // setting, that action is used. Otherwise, the "default" action is
  356. // used.
  357. //
  358. int
  359. CConfig::GoOfflineAction(
  360. LPCTSTR pszServer
  361. ) const
  362. {
  363. int iAction = GoOfflineAction(); // Get default action.
  364. if (NULL == pszServer)
  365. return iAction;
  366. TraceAssert(NULL != pszServer);
  367. //
  368. // Skip passed any leading backslashes for comparison.
  369. // The values we store in the registry don't have a leading "\\".
  370. //
  371. while(*pszServer && TEXT('\\') == *pszServer)
  372. pszServer++;
  373. HRESULT hr;
  374. CConfig::OfflineActionInfo info;
  375. CConfig::OfflineActionIter iter = CreateOfflineActionIter();
  376. while(S_OK == (hr = iter.Next(&info)))
  377. {
  378. if (0 == lstrcmpi(pszServer, info.szServer))
  379. {
  380. iAction = info.iAction; // Return custom action.
  381. break;
  382. }
  383. }
  384. //
  385. // Guard against bogus reg data.
  386. //
  387. if (eNumOfflineActions <= iAction || 0 > iAction)
  388. iAction = eGoOfflineSilent;
  389. return iAction;
  390. }
  391. //-----------------------------------------------------------------------------
  392. // CConfig::CustomGOA
  393. // "GOA" is "Go Offline Action"
  394. //-----------------------------------------------------------------------------
  395. bool
  396. CConfig::CustomGOA::operator < (
  397. const CustomGOA& rhs
  398. ) const
  399. {
  400. int diff = CompareByServer(rhs);
  401. if (0 == diff)
  402. diff = m_action - rhs.m_action;
  403. return diff < 0;
  404. }
  405. //
  406. // Compare two CustomGoOfflineAction objects by their
  407. // server names. Comparison is case-insensitive.
  408. // Returns: <0 = *this < rhs
  409. // 0 = *this == rhs
  410. // >0 = *this > rhs
  411. //
  412. int
  413. CConfig::CustomGOA::CompareByServer(
  414. const CustomGOA& rhs
  415. ) const
  416. {
  417. return lstrcmpi(GetServerName(), rhs.GetServerName());
  418. }
  419. //-----------------------------------------------------------------------------
  420. // CConfig::OfflineActionIter
  421. //-----------------------------------------------------------------------------
  422. CConfig::OfflineActionIter::OfflineActionIter(
  423. const CConfig *pConfig
  424. ) : m_pConfig(const_cast<CConfig *>(pConfig)),
  425. m_iAction(-1),
  426. m_hdpaGOA(DPA_Create(4))
  427. {
  428. }
  429. CConfig::OfflineActionIter::~OfflineActionIter(
  430. void
  431. )
  432. {
  433. if (NULL != m_hdpaGOA)
  434. {
  435. CConfig::ClearCustomGoOfflineActions(m_hdpaGOA);
  436. DPA_Destroy(m_hdpaGOA);
  437. }
  438. }
  439. HRESULT
  440. CConfig::OfflineActionIter::Next(
  441. OfflineActionInfo *pInfo
  442. )
  443. {
  444. if (NULL == m_hdpaGOA)
  445. {
  446. return E_OUTOFMEMORY;
  447. }
  448. HRESULT hr = S_FALSE;
  449. if (-1 == m_iAction)
  450. {
  451. m_pConfig->GetCustomGoOfflineActions(m_hdpaGOA);
  452. m_iAction = 0;
  453. }
  454. if (m_iAction < DSA_GetItemCount(m_hdpaGOA))
  455. {
  456. CustomGOA *pGOA = (CustomGOA *)DPA_GetPtr(m_hdpaGOA, m_iAction);
  457. if (NULL != pGOA)
  458. {
  459. lstrcpyn(pInfo->szServer, pGOA->GetServerName(), ARRAYSIZE(pInfo->szServer));
  460. pInfo->iAction = (DWORD)pGOA->GetAction();
  461. m_iAction++;
  462. hr = S_OK;
  463. }
  464. }
  465. return hr;
  466. }