Leaked source code of windows server 2003
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.

2059 lines
57 KiB

  1. /*******************************************************
  2. MultiUsr.cpp
  3. Code for handling multiple user functionality in IE
  4. and friends
  5. Initially by Christopher Evans (cevans) 4/28/98
  6. ********************************************************/
  7. #define DONT_WANT_SHELLDEBUG
  8. #include "private.h"
  9. #include "resource.h"
  10. #include "multiusr.h"
  11. #include <assert.h>
  12. #include "multiutl.h"
  13. #include "strconst.h"
  14. #include "Shlwapi.h"
  15. #include "multiui.h"
  16. #include <shlobj.h>
  17. #include "mluisup.h"
  18. #include <lmwksta.h>
  19. TCHAR g_szRegRoot[MAX_PATH] = "";
  20. extern HINSTANCE g_hInst;
  21. static void _CreateIdentitiesFolder();
  22. // add a backslash to a qualified path
  23. //
  24. // in:
  25. // lpszPath path (A:, C:\foo, etc)
  26. //
  27. // out:
  28. // lpszPath A:\, C:\foo\ ;
  29. //
  30. // returns:
  31. // pointer to the NULL that terminates the path
  32. // this is here to avoid a dependancy on shlwapi.dll
  33. #define CH_WHACK TEXT('\\')
  34. STDAPI_(LPTSTR)
  35. _PathAddBackslash(
  36. LPTSTR lpszPath)
  37. {
  38. LPTSTR lpszEnd;
  39. // perf: avoid lstrlen call for guys who pass in ptr to end
  40. // of buffer (or rather, EOB - 1).
  41. // note that such callers need to check for overflow themselves.
  42. int ichPath = (*lpszPath && !*(lpszPath + 1)) ? 1 : lstrlen(lpszPath);
  43. // try to keep us from tromping over MAX_PATH in size.
  44. // if we find these cases, return NULL. Note: We need to
  45. // check those places that call us to handle their GP fault
  46. // if they try to use the NULL!
  47. if (ichPath >= (MAX_PATH - 1))
  48. {
  49. Assert(FALSE); // Let the caller know!
  50. return(NULL);
  51. }
  52. lpszEnd = lpszPath + ichPath;
  53. // this is really an error, caller shouldn't pass
  54. // an empty string
  55. if (!*lpszPath)
  56. return lpszEnd;
  57. /* Get the end of the source directory
  58. */
  59. switch(*CharPrev(lpszPath, lpszEnd)) {
  60. case CH_WHACK:
  61. break;
  62. default:
  63. *lpszEnd++ = CH_WHACK;
  64. *lpszEnd = TEXT('\0');
  65. }
  66. return lpszEnd;
  67. }
  68. STDAPI_(DWORD)
  69. _SHGetValueA(
  70. IN HKEY hkey,
  71. IN LPCSTR pszSubKey, OPTIONAL
  72. IN LPCSTR pszValue, OPTIONAL
  73. OUT LPDWORD pdwType, OPTIONAL
  74. OUT LPVOID pvData, OPTIONAL
  75. OUT LPDWORD pcbData) OPTIONAL
  76. {
  77. DWORD dwRet;
  78. HKEY hkeyNew;
  79. dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, KEY_QUERY_VALUE, &hkeyNew);
  80. if (NO_ERROR == dwRet)
  81. {
  82. dwRet = RegQueryValueEx(hkeyNew, pszValue, NULL, pdwType, (LPBYTE)pvData, pcbData);
  83. RegCloseKey(hkeyNew);
  84. }
  85. else if (pcbData)
  86. *pcbData = 0;
  87. return dwRet;
  88. }
  89. /*----------------------------------------------------------
  90. Purpose: Recursively delete the key, including all child values
  91. and keys. Mimics what RegDeleteKey does in Win95.
  92. Returns:
  93. Cond: --
  94. */
  95. DWORD
  96. _DeleteKeyRecursively(
  97. IN HKEY hkey,
  98. IN LPCSTR pszSubKey)
  99. {
  100. DWORD dwRet;
  101. HKEY hkSubKey;
  102. // Open the subkey so we can enumerate any children
  103. dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, MAXIMUM_ALLOWED, &hkSubKey);
  104. if (ERROR_SUCCESS == dwRet)
  105. {
  106. DWORD dwIndex;
  107. CHAR szSubKeyName[MAX_PATH + 1];
  108. DWORD cchSubKeyName = ARRAYSIZE(szSubKeyName);
  109. CHAR szClass[MAX_PATH];
  110. DWORD cbClass = ARRAYSIZE(szClass);
  111. // I can't just call RegEnumKey with an ever-increasing index, because
  112. // I'm deleting the subkeys as I go, which alters the indices of the
  113. // remaining subkeys in an implementation-dependent way. In order to
  114. // be safe, I have to count backwards while deleting the subkeys.
  115. // Find out how many subkeys there are
  116. dwRet = RegQueryInfoKeyA(hkSubKey,
  117. szClass,
  118. &cbClass,
  119. NULL,
  120. &dwIndex, // The # of subkeys -- all we need
  121. NULL,
  122. NULL,
  123. NULL,
  124. NULL,
  125. NULL,
  126. NULL,
  127. NULL);
  128. if (NO_ERROR == dwRet)
  129. {
  130. // dwIndex is now the count of subkeys, but it needs to be
  131. // zero-based for RegEnumKey, so I'll pre-decrement, rather
  132. // than post-decrement.
  133. while (ERROR_SUCCESS == RegEnumKeyA(hkSubKey, --dwIndex, szSubKeyName, cchSubKeyName))
  134. {
  135. _DeleteKeyRecursively(hkSubKey, szSubKeyName);
  136. }
  137. }
  138. RegCloseKey(hkSubKey);
  139. dwRet = RegDeleteKeyA(hkey, pszSubKey);
  140. }
  141. return dwRet;
  142. }
  143. // ****************************************************************************************************
  144. // C S T R I N G L I S T C L A S S
  145. //
  146. // A really basic string list class. Actually, its a string array class, but you don't need to know
  147. // that. It could do so much more, but for now, it only maintains an array of C strings.
  148. //
  149. CStringList::CStringList()
  150. {
  151. m_count = 0;
  152. m_ptrCount = 0;
  153. m_strings = NULL;
  154. }
  155. /*
  156. CStringList::~CStringList
  157. Clean up any memory that was allocated in the CStringList object
  158. */
  159. CStringList::~CStringList()
  160. {
  161. if (m_strings)
  162. {
  163. for (int i = 0; i < m_count; i++)
  164. {
  165. if (m_strings[i])
  166. {
  167. MemFree(m_strings[i]);
  168. m_strings[i] = NULL;
  169. }
  170. }
  171. MemFree(m_strings);
  172. m_strings = NULL;
  173. m_count = 0;
  174. }
  175. }
  176. /*
  177. CStringList::AddString
  178. Add a string to the end of the string list.
  179. */
  180. void CStringList::AddString(TCHAR* lpszInString)
  181. {
  182. // make more room for pointers, if necessary
  183. if (m_ptrCount == m_count)
  184. {
  185. m_ptrCount += 5;
  186. if (!MemRealloc((void **)&m_strings, sizeof(TCHAR *) * m_ptrCount))
  187. {
  188. m_ptrCount -= 5;
  189. Assert(false);
  190. return;
  191. }
  192. // initialize the new strings to nil
  193. for (int i = m_count; i < m_ptrCount; i++)
  194. m_strings[i] = NULL;
  195. }
  196. //now put the string in the next location
  197. int iNewIndex = m_count++;
  198. if(MemAlloc((void **)&m_strings[iNewIndex], sizeof(TCHAR) * lstrlen(lpszInString)+1))
  199. {
  200. lstrcpy(m_strings[iNewIndex], lpszInString);
  201. }
  202. else
  203. {
  204. // couldn't allocate space for the string. Don't count that spot as filled
  205. m_count--;
  206. }
  207. }
  208. /*
  209. CStringList::RemoveString
  210. Remove a string at zero based index iIndex
  211. */
  212. void CStringList::RemoveString(int iIndex)
  213. {
  214. int iCopySize;
  215. iCopySize = ((m_count - iIndex) - 1) * 4;
  216. // free the memory for the string
  217. if (m_strings[iIndex])
  218. {
  219. MemFree(m_strings[iIndex]);
  220. m_strings[iIndex] = NULL;
  221. }
  222. // move the other strings down
  223. if (iCopySize)
  224. {
  225. memmove(&(m_strings[iIndex]), &(m_strings[iIndex+1]), iCopySize);
  226. }
  227. // null out the last item in the list and decrement the counter.
  228. m_strings[--m_count] = NULL;
  229. }
  230. /*
  231. CStringList::GetString
  232. Return the pointer to the string at zero based index iIndex.
  233. Return the string at the given index. Note that the TCHAR pointer
  234. is still owned by the string list and should not be deleted.
  235. */
  236. TCHAR *CStringList::GetString(int iIndex)
  237. {
  238. if (iIndex < m_count && iIndex >= 0)
  239. return m_strings[iIndex];
  240. else
  241. return NULL;
  242. }
  243. int __cdecl _CSL_Compare(const void *p1, const void *p2)
  244. {
  245. TCHAR *psz1, *psz2;
  246. psz1 = *((TCHAR **)p1);
  247. psz2 = *((TCHAR **)p2);
  248. return lstrcmpi(psz1, psz2);
  249. }
  250. /*
  251. CStringList::Sort
  252. Sort the strings in the list
  253. */
  254. void CStringList::Sort()
  255. {
  256. qsort(m_strings, m_count, sizeof(TCHAR *), _CSL_Compare);
  257. }
  258. /*
  259. MU_Init
  260. Initialize the memory allocator and make sure that there is
  261. at least one user in the registry.
  262. */
  263. static BOOL g_inited = FALSE;
  264. EXTERN_C void MU_Init()
  265. {
  266. CStringList* pList;
  267. if (!g_inited)
  268. {
  269. pList = MU_GetUsernameList();
  270. if (!pList || pList->GetLength() == 0)
  271. {
  272. _MakeDefaultFirstUser();
  273. }
  274. if (pList)
  275. delete pList;
  276. g_inited = TRUE;
  277. }
  278. }
  279. /*
  280. MU_GetUsernameList
  281. Build a CStringList with all of the names of the users
  282. stored in HKLM
  283. */
  284. #define MAXKEYNAME 256
  285. CStringList* MU_GetUsernameList(void)
  286. {
  287. CStringList* vList = NULL;
  288. HKEY hSourceSubKey;
  289. DWORD dwEnumIndex = 0, dwStatus, dwSize, dwType;
  290. int cb;
  291. TCHAR szKeyNameBuffer[MAXKEYNAME];
  292. DWORD dwIdentityOrdinal = 1;
  293. vList = new CStringList;
  294. Assert(vList);
  295. if (!vList)
  296. goto exit;
  297. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) != ERROR_SUCCESS)
  298. {
  299. AssertSz(FALSE, "Couldn't open user profiles root Key");
  300. goto exit;
  301. }
  302. dwSize = sizeof(dwIdentityOrdinal);
  303. RegQueryValueEx(hSourceSubKey, c_szIdentityOrdinal, NULL, &dwType, (LPBYTE)&dwIdentityOrdinal, &dwSize);
  304. while (TRUE)
  305. {
  306. DWORD dwOrdinal;
  307. HKEY hkUserKey;
  308. if (RegEnumKey(hSourceSubKey, dwEnumIndex++, szKeyNameBuffer,MAXKEYNAME)!= ERROR_SUCCESS)
  309. break;
  310. cb = lstrlen(szKeyNameBuffer);
  311. if (RegOpenKey(hSourceSubKey, szKeyNameBuffer, &hkUserKey) == ERROR_SUCCESS)
  312. {
  313. dwSize = sizeof(szKeyNameBuffer);
  314. dwStatus = RegQueryValueEx(hkUserKey, c_szUsername, NULL, &dwType, (LPBYTE)&szKeyNameBuffer, &dwSize);
  315. Assert(ERROR_SUCCESS == dwStatus);
  316. Assert(*szKeyNameBuffer != 0);
  317. //filter names that begin with _ to hide things like "_Outlook News"
  318. if (ERROR_SUCCESS == dwStatus && *szKeyNameBuffer != '_')
  319. vList->AddString(szKeyNameBuffer);
  320. dwSize = sizeof(dwOrdinal);
  321. dwStatus = RegQueryValueEx(hkUserKey, c_szIdentityOrdinal, NULL, &dwType, (LPBYTE)&dwOrdinal, &dwSize);
  322. if (dwStatus==ERROR_SUCCESS)
  323. {
  324. if (dwOrdinal>=dwIdentityOrdinal)
  325. {
  326. dwIdentityOrdinal = dwOrdinal+1;
  327. AssertSz(FALSE, "MaxOrdinal is smaller than this identity. Why?");
  328. }
  329. }
  330. else
  331. {
  332. dwStatus = RegSetValueEx(hkUserKey, c_szIdentityOrdinal, NULL, REG_DWORD, (LPBYTE)&dwIdentityOrdinal, dwSize);
  333. dwIdentityOrdinal++;
  334. }
  335. Assert(ERROR_SUCCESS == dwStatus);
  336. RegCloseKey(hkUserKey);
  337. }
  338. else
  339. AssertSz(FALSE, "Couldn't open user's Key");
  340. }
  341. dwSize = sizeof(dwIdentityOrdinal);
  342. if (RegSetValueEx(hSourceSubKey, c_szIdentityOrdinal, 0, REG_DWORD, (LPBYTE)&dwIdentityOrdinal, dwSize)!=ERROR_SUCCESS)
  343. {
  344. AssertSz(FALSE, "Couldn't set the identity ordinal");
  345. }
  346. RegCloseKey(hSourceSubKey);
  347. exit:
  348. return vList;
  349. }
  350. /*
  351. MU_UsernameToUserId
  352. Given a username, find its user id and return it. Returns E_FAIL if it can't
  353. find the given username.
  354. */
  355. HRESULT MU_UsernameToUserId(TCHAR *lpszUsername, GUID *puidID)
  356. {
  357. HKEY hSourceSubKey;
  358. ULONG ulEnumIndex = 0;
  359. DWORD dwStatus, dwSize, dwType;
  360. TCHAR szKeyNameBuffer[MAXKEYNAME];
  361. BOOL fFound = FALSE;
  362. TCHAR szUid[255];
  363. ZeroMemory(puidID, sizeof(GUID));
  364. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) == ERROR_SUCCESS)
  365. {
  366. while (!fFound)
  367. {
  368. HKEY hkUserKey;
  369. if (RegEnumKey(hSourceSubKey, ulEnumIndex++, szKeyNameBuffer,MAXKEYNAME)
  370. != ERROR_SUCCESS)
  371. break;
  372. if (RegOpenKey(hSourceSubKey, szKeyNameBuffer, &hkUserKey) == ERROR_SUCCESS)
  373. {
  374. dwSize = sizeof(szKeyNameBuffer);
  375. dwStatus = RegQueryValueEx(hkUserKey, c_szUsername, NULL, &dwType, (LPBYTE)&szKeyNameBuffer, &dwSize);
  376. if (ERROR_SUCCESS == dwStatus && lstrcmpi(lpszUsername, szKeyNameBuffer) == 0)
  377. {
  378. dwSize = sizeof(szUid);
  379. dwStatus = RegQueryValueEx(hkUserKey, c_szUserID, NULL, &dwType, (LPBYTE)&szUid, &dwSize);
  380. fFound = (dwStatus == ERROR_SUCCESS);
  381. if (fFound)
  382. fFound = SUCCEEDED(GUIDFromAString(szUid, puidID));
  383. }
  384. RegCloseKey(hkUserKey);
  385. }
  386. }
  387. RegCloseKey(hSourceSubKey);
  388. }
  389. return (fFound ? S_OK : E_FAIL);
  390. }
  391. /*
  392. MU_GetPasswordForUsername
  393. Get the password for the provided user and return it in szOutPassword. Return in
  394. pfUsePassword if password is enabled and false if it is disabled.
  395. Function returns true if the password data could be found, false otherwise
  396. */
  397. BOOL MU_GetPasswordForUsername(TCHAR *lpszInUsername, TCHAR *szOutPassword, BOOL *pfUsePassword)
  398. {
  399. #ifdef IDENTITY_PASSWORDS
  400. TCHAR szPath[MAX_PATH];
  401. TCHAR szPassword[255] = "";
  402. HKEY hDestinationSubKey;
  403. DWORD dwSize, dwStatus, dwType;
  404. DWORD dwPWEnabled = 0;
  405. GUID uidUserID;
  406. HRESULT hr;
  407. PASSWORD_STORE pwStore;
  408. hr = MU_UsernameToUserId(lpszInUsername, &uidUserID);
  409. Assert(SUCCEEDED(hr));
  410. if (uidUserID == GUID_NULL)
  411. {
  412. *pfUsePassword = FALSE;
  413. return TRUE;
  414. }
  415. if (SUCCEEDED(hr = ReadIdentityPassword(&uidUserID, &pwStore)))
  416. {
  417. lstrcpy(szOutPassword, pwStore.szPassword);
  418. *pfUsePassword = pwStore.fUsePassword;
  419. return TRUE;
  420. }
  421. else
  422. {
  423. BOOL fFoundPassword = FALSE;
  424. //build the user level key.
  425. MU_GetRegRootForUserID(&uidUserID, szPath);
  426. if (RegCreateKey(HKEY_CURRENT_USER, szPath, &hDestinationSubKey) == ERROR_SUCCESS)
  427. {
  428. dwSize = sizeof(dwPWEnabled);
  429. dwStatus = RegQueryValueEx(hDestinationSubKey, c_szUsePassword, NULL, &dwType, (LPBYTE)&dwPWEnabled, &dwSize);
  430. if (ERROR_SUCCESS == dwStatus && 0 != dwPWEnabled)
  431. {
  432. dwSize = sizeof(szPassword);
  433. dwStatus = RegQueryValueEx(hDestinationSubKey, c_szPassword, NULL, &dwType, (LPBYTE)&szPassword, &dwSize);
  434. if (ERROR_SUCCESS == dwStatus)
  435. {
  436. ULONG cbSize;
  437. fFoundPassword = TRUE;
  438. cbSize = dwSize;
  439. if (cbSize > 1)
  440. {
  441. DecodeUserPassword(szPassword, &cbSize);
  442. strcpy(szOutPassword, szPassword);
  443. }
  444. else
  445. {
  446. *szOutPassword = 0;
  447. }
  448. }
  449. }
  450. RegCloseKey(hDestinationSubKey);
  451. }
  452. // Herein lies the pull. We can't count on being able to access any
  453. // given pstore from any given profile on Win9x. If you log on with
  454. // a blank password, or hit escape (not much difference to a user)
  455. // you will have a different pstore. If we store our passwords in the
  456. // registry, they can be whacked pretty simply. If we can't find the
  457. // password, we will disable it for now and say there is none. It
  458. // seems that most people don't put passwords on identities now
  459. // anyway, though this will change.
  460. if (!fFoundPassword)
  461. {
  462. fFoundPassword = TRUE;
  463. dwPWEnabled = 0;
  464. }
  465. // Here ends the pull
  466. *pfUsePassword = (dwPWEnabled != 0);
  467. return fFoundPassword;
  468. }
  469. #else
  470. *pfUsePassword = FALSE;
  471. return TRUE;
  472. #endif //IDENTITY_PASSWORDS
  473. }
  474. /*
  475. _FillListBoxWithUsernames
  476. Fill a listbox with the names of the users, Adds (Default)
  477. to the default user.
  478. */
  479. BOOL _FillListBoxWithUsernames(HWND hListbox)
  480. {
  481. CStringList *lpCStringList;
  482. GUID uidDefault;
  483. GUID uidUser;
  484. lpCStringList = MU_GetUsernameList();
  485. if (lpCStringList)
  486. {
  487. MU_GetDefaultUserID(&uidDefault);
  488. SendMessage(hListbox, LB_RESETCONTENT, 0, 0);
  489. lpCStringList->Sort();
  490. if (lpCStringList)
  491. {
  492. for(int i = 0; i < lpCStringList->GetLength(); i++)
  493. {
  494. if (lpCStringList->GetString(i))
  495. {
  496. SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)lpCStringList->GetString(i));
  497. }
  498. }
  499. delete lpCStringList;
  500. return true;
  501. }
  502. }
  503. return false;
  504. }
  505. BOOL _FillComboBoxWithUsernames(HWND hCombobox, HWND hListbox)
  506. {
  507. TCHAR szRes[128];
  508. DWORD_PTR cIndex, dwCount = SendMessage(hListbox, LB_GETCOUNT, 0, 0);
  509. SendMessage(hCombobox, CB_RESETCONTENT, 0, 0);
  510. for (cIndex = 0; cIndex < dwCount; cIndex++)
  511. {
  512. SendMessage(hListbox, LB_GETTEXT, cIndex, (LPARAM)szRes);
  513. SendMessage(hCombobox, CB_ADDSTRING, 0, (LPARAM)szRes);
  514. }
  515. return true;
  516. }
  517. /*
  518. MU_UsernameExists
  519. Does the given name already exist as a username?
  520. */
  521. BOOL MU_UsernameExists(TCHAR* lpszUsername)
  522. {
  523. GUID uidID;
  524. return SUCCEEDED(MU_UsernameToUserId(lpszUsername, &uidID));
  525. }
  526. /*
  527. MU_GetUserInfo
  528. Fill in the user info structure with current values
  529. */
  530. BOOL MU_GetUserInfo(GUID *puidUserID, LPUSERINFO lpUserInfo)
  531. {
  532. TCHAR szPWBuffer[255];
  533. TCHAR szRegPath[MAX_PATH];
  534. HKEY hKey;
  535. BOOL bResult = false;
  536. LONG lValue;
  537. DWORD dwStatus, dwType, dwSize;
  538. GUID uidUser;
  539. TCHAR szUid[255];
  540. HRESULT hr;
  541. PASSWORD_STORE pwStore;
  542. lpUserInfo->fPasswordValid = FALSE;
  543. if( puidUserID == NULL)
  544. {
  545. MU_GetCurrentUserID(&uidUser);
  546. if (uidUser == GUID_NULL)
  547. return FALSE;
  548. }
  549. else
  550. uidUser = *puidUserID;
  551. MU_GetRegRootForUserID(&uidUser, szRegPath);
  552. if (RegOpenKey(HKEY_CURRENT_USER, szRegPath, &hKey) == ERROR_SUCCESS)
  553. {
  554. *lpUserInfo->szPassword = 0;
  555. lpUserInfo->fUsePassword = false;
  556. ZeroMemory(&lpUserInfo->uidUserID, sizeof(GUID));
  557. dwSize = sizeof(lpUserInfo->szUsername);
  558. if ((dwStatus = RegQueryValueEx(hKey, c_szUsername, NULL, &dwType, (LPBYTE)lpUserInfo->szUsername, &dwSize)) == ERROR_SUCCESS &&
  559. (0 != *lpUserInfo->szUsername))
  560. {
  561. //we have the username, that is the only required part. The others are optional.
  562. bResult = true;
  563. #ifdef IDENTITY_PASSWORDS
  564. lpUserInfo->fPasswordValid = FALSE;
  565. if (SUCCEEDED(hr = ReadIdentityPassword(&uidUser, &pwStore)))
  566. {
  567. lstrcpy(lpUserInfo->szPassword, pwStore.szPassword);
  568. lpUserInfo->fUsePassword = pwStore.fUsePassword;
  569. lpUserInfo->fPasswordValid = TRUE;
  570. }
  571. else
  572. {
  573. dwSize = sizeof(lValue);
  574. if ((dwStatus = RegQueryValueEx(hKey, c_szUsePassword, NULL, &dwType, (LPBYTE)&lValue, &dwSize)) == ERROR_SUCCESS)
  575. {
  576. lpUserInfo->fUsePassword = (lValue != 0);
  577. }
  578. dwSize = sizeof(szPWBuffer);
  579. dwStatus = RegQueryValueEx(hKey, c_szPassword, NULL, &dwType, (LPBYTE)szPWBuffer, &dwSize);
  580. ULONG cbSize;
  581. lpUserInfo->fPasswordValid = (ERROR_SUCCESS == dwStatus);
  582. // Herein lies the pull (Volume 2). We can't count on being able to access any
  583. // given pstore from any given profile on Win9x. If you log on with
  584. // a blank password, or hit escape (not much difference to a user)
  585. // you will have a different pstore. If we store our passwords in the
  586. // registry, they can be whacked pretty simply. If we can't find the
  587. // password, we will disable it for now and say there is none. It
  588. // seems that most people don't put passwords on identities now
  589. // anyway, though this will change.
  590. if (!lpUserInfo->fPasswordValid)
  591. {
  592. lpUserInfo->fPasswordValid = TRUE;
  593. lpUserInfo->fUsePassword = FALSE;
  594. }
  595. // Here ends the pull
  596. cbSize = dwSize;
  597. if (ERROR_SUCCESS == dwStatus && cbSize > 1)
  598. {
  599. DecodeUserPassword(szPWBuffer, &cbSize);
  600. strcpy(lpUserInfo->szPassword, szPWBuffer);
  601. }
  602. else
  603. *lpUserInfo->szPassword = 0;
  604. }
  605. #endif
  606. dwSize = sizeof(szUid);
  607. if ((dwStatus = RegQueryValueEx(hKey, c_szUserID, NULL, &dwType, (LPBYTE)&szUid, &dwSize)) == ERROR_SUCCESS)
  608. {
  609. hr = GUIDFromAString(szUid, &lpUserInfo->uidUserID);
  610. Assert(hr);
  611. }
  612. }
  613. RegCloseKey(hKey);
  614. }
  615. return bResult;
  616. }
  617. /*
  618. MU_SetUserInfo
  619. Save the user info structure with the user values
  620. */
  621. BOOL MU_SetUserInfo(LPUSERINFO lpUserInfo)
  622. {
  623. DWORD dwType, dwSize, dwValue, dwStatus;
  624. HKEY hkCurrUser;
  625. TCHAR szPath[MAX_PATH];
  626. WCHAR szwPath[MAX_PATH];
  627. TCHAR szUid[255];
  628. BOOL fNewIdentity = FALSE;
  629. PASSWORD_STORE pwStore;
  630. HRESULT hr;
  631. MU_GetRegRootForUserID(&lpUserInfo->uidUserID, szPath);
  632. Assert(pszRegPath && *pszRegPath);
  633. Assert(lpUserInfo->uidUserID != GUID_NULL);
  634. if ((dwStatus = RegCreateKey(HKEY_CURRENT_USER, szPath, &hkCurrUser)) == ERROR_SUCCESS)
  635. {
  636. ULONG cbSize;
  637. TCHAR szBuffer[255];
  638. // write out the correct values
  639. dwType = REG_SZ;
  640. dwSize = lstrlen(lpUserInfo->szUsername) + 1;
  641. RegSetValueEx(hkCurrUser, c_szUsername, 0, dwType, (LPBYTE)lpUserInfo->szUsername, dwSize);
  642. dwSize = sizeof(DWORD);
  643. if ((dwStatus = RegQueryValueEx(hkCurrUser, c_szDirName, NULL, &dwType, (LPBYTE)&dwValue, &dwSize)) != ERROR_SUCCESS)
  644. {
  645. dwValue = MU_GenerateDirectoryNameForIdentity(&lpUserInfo->uidUserID);
  646. dwType = REG_DWORD;
  647. dwSize = sizeof(dwValue);
  648. RegSetValueEx(hkCurrUser, c_szDirName, 0, dwType, (LPBYTE)&dwValue, dwSize);
  649. fNewIdentity = TRUE;
  650. }
  651. #ifdef IDENTITY_PASSWORDS
  652. lstrcpy(pwStore.szPassword, lpUserInfo->szPassword);
  653. pwStore.fUsePassword = lpUserInfo->fUsePassword;
  654. if (FAILED(hr = WriteIdentityPassword(&lpUserInfo->uidUserID, &pwStore)))
  655. {
  656. dwType = REG_BINARY ;
  657. cbSize = strlen(lpUserInfo->szPassword) + 1;
  658. lstrcpy(szBuffer, lpUserInfo->szPassword);
  659. EncodeUserPassword(szBuffer, &cbSize);
  660. dwSize = cbSize;
  661. RegSetValueEx(hkCurrUser, c_szPassword, 0, dwType, (LPBYTE)szBuffer, dwSize);
  662. dwType = REG_DWORD;
  663. dwValue = (lpUserInfo->fUsePassword ? 1 : 0);
  664. dwSize = sizeof(dwValue);
  665. RegSetValueEx(hkCurrUser, c_szUsePassword, 0, dwType, (LPBYTE)&dwValue, dwSize);
  666. }
  667. else
  668. {
  669. //don't keep the registry values if we could save it to the pstore.
  670. RegDeleteValue(hkCurrUser, c_szPassword);
  671. RegDeleteValue(hkCurrUser, c_szUsePassword);
  672. }
  673. #endif //IDENTITY_PASSWORDS
  674. Assert(lpUserInfo->uidUserID != GUID_NULL);
  675. AStringFromGUID(&lpUserInfo->uidUserID, szUid, ARRAYSIZE(szUid));
  676. dwType = REG_SZ;
  677. dwSize = lstrlen(szUid) + 1;
  678. RegSetValueEx(hkCurrUser, c_szUserID, 0, dwType, (LPBYTE)&szUid, dwSize);
  679. RegCloseKey(hkCurrUser);
  680. if (fNewIdentity)
  681. {
  682. if (SUCCEEDED(MU_GetUserDirectoryRoot(&lpUserInfo->uidUserID, GIF_ROAMING_FOLDER, szwPath, MAX_PATH)))
  683. {
  684. if (!CreateDirectoryWrapW(szwPath,NULL))
  685. {
  686. _CreateIdentitiesFolder();
  687. CreateDirectoryWrapW(szwPath,NULL);
  688. }
  689. }
  690. if (SUCCEEDED(MU_GetUserDirectoryRoot(&lpUserInfo->uidUserID, GIF_NON_ROAMING_FOLDER, szwPath, MAX_PATH)))
  691. {
  692. if (!CreateDirectoryWrapW(szwPath,NULL))
  693. {
  694. _CreateIdentitiesFolder();
  695. CreateDirectoryWrapW(szwPath,NULL);
  696. }
  697. }
  698. }
  699. return TRUE;
  700. }
  701. return FALSE;
  702. }
  703. /*
  704. MU_SwitchToUser
  705. Currently, this just saves the last user's info.
  706. */
  707. HRESULT MU_SwitchToUser(TCHAR *lpszUsername)
  708. {
  709. GUID uidUserID;
  710. TCHAR szUid[255];
  711. HRESULT hr;
  712. Assert(lpszUsername);
  713. if (*lpszUsername == 0) // null string means null guid
  714. {
  715. uidUserID = GUID_NULL;
  716. }
  717. else
  718. {
  719. hr = MU_UsernameToUserId(lpszUsername, &uidUserID);
  720. if (FAILED(hr))
  721. return hr;
  722. }
  723. AStringFromGUID(&uidUserID, szUid, ARRAYSIZE(szUid));
  724. Assert(uidUserID != GUID_NULL || (*lpszUsername == 0));
  725. wsprintf(g_szRegRoot, "%.100s\\%.40s", c_szRegRoot, szUid);
  726. // remember who we last switched to
  727. HKEY hkey;
  728. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS)
  729. {
  730. DWORD dwType, dwSize;
  731. dwType = REG_SZ;
  732. dwSize = lstrlen(lpszUsername) + 1;
  733. RegSetValueEx(hkey, c_szLastUserName, 0, dwType, (LPBYTE)lpszUsername, dwSize);
  734. dwType = REG_SZ;
  735. dwSize = lstrlen(szUid) + 1;
  736. RegSetValueEx(hkey, c_szLastUserID, 0, dwType, (LPBYTE)szUid, dwSize);
  737. RegCloseKey(hkey);
  738. }
  739. return S_OK;
  740. }
  741. /*
  742. MU_SwitchToLastUser
  743. Makes the last user current, if there is no
  744. last user, it switches to the first user it can
  745. find. If there are no users, it creates a
  746. user called "Main User"
  747. */
  748. void MU_SwitchToLastUser()
  749. {
  750. HKEY hkey;
  751. TCHAR szUserUid[255];
  752. TCHAR szUsername[CCH_USERNAME_MAX_LENGTH + 1];
  753. BOOL fSwitched = FALSE;
  754. GUID uidUserId;
  755. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS)
  756. {
  757. DWORD dwType, dwStatus, dwSize;
  758. dwSize = sizeof(szUserUid);
  759. dwStatus = RegQueryValueEx(hkey, c_szLastUserID, NULL, &dwType, (LPBYTE)szUserUid, &dwSize);
  760. RegCloseKey(hkey);
  761. if (ERROR_SUCCESS == dwStatus && SUCCEEDED(GUIDFromAString(szUserUid, &uidUserId)) &&
  762. SUCCEEDED(MU_UserIdToUsername(&uidUserId, szUsername, CCH_USERNAME_MAX_LENGTH)))
  763. {
  764. MU_SwitchToUser(szUsername);
  765. fSwitched = true;
  766. }
  767. }
  768. if (!fSwitched)
  769. {
  770. LPSTR pszName;
  771. CStringList* pList = MU_GetUsernameList();
  772. if (pList)
  773. {
  774. DWORD dwIndex, dwLen = pList->GetLength();
  775. // find the first non hidden user and switch to them
  776. for (dwIndex = 0; dwIndex < dwLen; dwIndex++)
  777. {
  778. pszName = pList->GetString(dwIndex);
  779. if (pszName && *pszName && *pszName != '_')
  780. {
  781. MU_SwitchToUser(pszName);
  782. fSwitched = TRUE;
  783. break;
  784. }
  785. }
  786. delete pList;
  787. }
  788. }
  789. if (!fSwitched)
  790. {
  791. _MakeDefaultFirstUser();
  792. CStringList* pList = MU_GetUsernameList();
  793. if (pList && pList->GetLength() > 0)
  794. MU_SwitchToUser(pList->GetString(0));
  795. if (pList)
  796. delete pList;
  797. }
  798. }
  799. /*
  800. _CreateIdentitiesFolder
  801. Create the parent folder of all of the identities folders.
  802. */
  803. static void _CreateIdentitiesFolder()
  804. {
  805. HRESULT hr;
  806. TCHAR szAppDir[MAX_PATH], szSubDir[MAX_PATH], *psz;
  807. DWORD dw, type;
  808. hr = E_FAIL;
  809. dw = MAX_PATH;
  810. if (ERROR_SUCCESS == _SHGetValueA(HKEY_CURRENT_USER, c_szRegFolders, c_szValueAppData, &type, (LPBYTE)szAppDir, &dw))
  811. {
  812. lstrcpy(szSubDir, c_szIdentitiesFolderName);
  813. psz = _PathAddBackslash(szSubDir);
  814. if (psz)
  815. {
  816. psz = _PathAddBackslash(szAppDir);
  817. if (psz)
  818. {
  819. lstrcpy(psz, szSubDir);
  820. psz = _PathAddBackslash(szAppDir);
  821. CreateDirectory(szAppDir, NULL);
  822. }
  823. }
  824. }
  825. }
  826. /*
  827. MU_GetCurrentUserDirectoryRoot
  828. Return the path to the top of the current user's root directory.
  829. This is the directory where the mail store should be located.
  830. It is in a subfolder the App Data folder.
  831. lpszUserRoot is a pointer to a character buffer that is cch chars
  832. in size.
  833. */
  834. HRESULT MU_GetUserDirectoryRoot(GUID *uidUserID, DWORD dwFlags, WCHAR *lpszwUserRoot, int cch)
  835. {
  836. HRESULT hr;
  837. WCHAR szwSubDir[MAX_PATH], *pszw, szwUid[255];
  838. int cb;
  839. DWORD type, dwDirId;
  840. LPITEMIDLIST pidl = NULL;
  841. IShellFolder *psf = NULL;
  842. STRRET str;
  843. IMalloc *pMalloc = NULL;
  844. BOOL fNeedHelp = FALSE;
  845. Assert(lpszUserRoot != NULL);
  846. Assert(uidUserID);
  847. Assert(cch >= MAX_PATH);
  848. Assert((dwFlags & (GIF_NON_ROAMING_FOLDER | GIF_ROAMING_FOLDER)));
  849. hr = MU_GetDirectoryIdForIdentity(uidUserID, &dwDirId);
  850. StringFromGUID2(*uidUserID, szwUid, ARRAYSIZE(szwUid));
  851. if (FAILED(hr))
  852. return hr;
  853. hr = SHGetMalloc(&pMalloc);
  854. Assert(pMalloc);
  855. if (!pMalloc)
  856. return E_OUTOFMEMORY;
  857. hr = E_FAIL;
  858. if (!!(dwFlags & GIF_NON_ROAMING_FOLDER))
  859. {
  860. hr = SHGetSpecialFolderLocation(GetDesktopWindow(), CSIDL_LOCAL_APPDATA, &pidl);
  861. if (FAILED(hr) || pidl == 0)
  862. hr = SHGetSpecialFolderLocation(GetDesktopWindow(), CSIDL_APPDATA, &pidl);
  863. if (FAILED(hr))
  864. fNeedHelp = TRUE;
  865. }
  866. else if (!!(dwFlags & GIF_ROAMING_FOLDER))
  867. {
  868. hr = SHGetSpecialFolderLocation(GetDesktopWindow(), CSIDL_APPDATA, &pidl);
  869. if (FAILED(hr))
  870. fNeedHelp = TRUE;
  871. }
  872. else
  873. hr = E_INVALIDARG;
  874. *lpszwUserRoot = 0;
  875. if (SUCCEEDED(hr) && pidl)
  876. {
  877. if (FAILED(hr = SHGetDesktopFolder(&psf)))
  878. goto exit;
  879. if (FAILED(hr = psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)))
  880. goto exit;
  881. switch(str.uType)
  882. {
  883. case STRRET_WSTR:
  884. lstrcpyW(lpszwUserRoot, str.pOleStr);
  885. pMalloc->Free(str.pOleStr);
  886. break;
  887. case STRRET_OFFSET:
  888. MultiByteToWideChar(CP_ACP, 0, (LPSTR)pidl+str.uOffset, -1, lpszwUserRoot, cch-11);
  889. break;
  890. case STRRET_CSTR:
  891. MultiByteToWideChar(CP_ACP, 0, (LPSTR)str.cStr, -1, lpszwUserRoot, cch-11);
  892. break;
  893. default:
  894. Assert(FALSE);
  895. goto exit;
  896. }
  897. pszw = PathAddBackslashW(lpszwUserRoot);
  898. if (lstrlenW(lpszwUserRoot) < cch - 10)
  899. {
  900. StrCatW(pszw, L"Identities\\");
  901. StrCatW(pszw, szwUid);
  902. StrCatW(pszw, L"\\");
  903. }
  904. else
  905. {
  906. hr = E_OUTOFMEMORY;
  907. *lpszwUserRoot = 0;
  908. }
  909. }
  910. else if (fNeedHelp)
  911. {
  912. // $$$Review: NEIL QFE
  913. // SHGetSpecialFolderLocation(GetDesktopWindow(), CSIDL_APPDATA, &pidl) fails on non-SI OSR2.
  914. HKEY hkeySrc;
  915. DWORD cb;
  916. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
  917. 0, KEY_QUERY_VALUE, &hkeySrc))
  918. {
  919. // -1 for the backslash we may add
  920. cb = cch - 1;
  921. if (ERROR_SUCCESS == RegQueryValueExWrapW(hkeySrc, L"AppData", 0, NULL, (LPBYTE)lpszwUserRoot, &cb))
  922. {
  923. pszw = PathAddBackslashW(lpszwUserRoot);
  924. if (lstrlenW(lpszwUserRoot) < cch - 10)
  925. {
  926. StrCatW(pszw, L"Identities\\");
  927. StrCatW(pszw, szwUid);
  928. StrCatW(pszw, L"\\");
  929. hr = S_OK;
  930. }
  931. else
  932. {
  933. hr = E_OUTOFMEMORY;
  934. *lpszwUserRoot = 0;
  935. }
  936. }
  937. RegCloseKey(hkeySrc);
  938. }
  939. }
  940. exit:
  941. Assert(lstrlenW(lpszwUserRoot) > 0);
  942. SafeRelease(psf);
  943. pMalloc->Free(pidl);
  944. SafeRelease(pMalloc);
  945. return hr;
  946. }
  947. /*
  948. _ClaimNextUserId
  949. Get the next available user id. Currently this means starting
  950. with the CURRENT_USER GUID and changing the first DWORD of it
  951. until it is unique.
  952. */
  953. HRESULT _ClaimNextUserId(GUID *puidId)
  954. {
  955. ULONG ulValue = 1;
  956. DWORD dwType, dwSize, dwStatus;
  957. HKEY hkeyProfiles;
  958. TCHAR szUsername[CCH_USERNAME_MAX_LENGTH+1];
  959. GUID uid;
  960. FILETIME ft;
  961. if (FAILED(CoCreateGuid(&uid)))
  962. {
  963. uid = UID_GIBC_CURRENT_USER;
  964. GetSystemTimeAsFileTime(&ft);
  965. uid.Data1 = ft.dwLowDateTime;
  966. //make sure it hasn't been used
  967. while (MU_UserIdToUsername(&uid, szUsername, CCH_USERNAME_MAX_LENGTH))
  968. uid.Data1 ++;
  969. }
  970. *puidId = uid;
  971. return S_OK;
  972. }
  973. BOOL MU_GetCurrentUserID(GUID *puidUserID)
  974. {
  975. BOOL fFound = FALSE;
  976. HKEY hkey;
  977. GUID uidUserId;
  978. TCHAR szUid[255];
  979. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS)
  980. {
  981. DWORD dwSize;
  982. dwSize = 255;
  983. fFound = (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szLastUserID, 0, NULL, (LPBYTE)szUid, &dwSize));
  984. if (fFound)
  985. fFound = SUCCEEDED(GUIDFromAString(szUid, puidUserID));
  986. if (fFound && *puidUserID == GUID_NULL)
  987. fFound = false;
  988. RegCloseKey(hkey);
  989. }
  990. #ifdef DEBUG
  991. TCHAR szUsername[CCH_USERNAME_MAX_LENGTH+1];
  992. Assert(MU_UserIdToUsername(puidUserID, szUsername, CCH_USERNAME_MAX_LENGTH));
  993. #endif
  994. return fFound;
  995. }
  996. /*
  997. MU_UserIdToUsername
  998. Return the user name for the user whose user id is passed in. Returns
  999. whether or not the user was found.
  1000. */
  1001. BOOL MU_UserIdToUsername(GUID *puidUserID, TCHAR *lpszUsername, ULONG cch)
  1002. {
  1003. HKEY hkey;
  1004. TCHAR szPath[MAX_PATH];
  1005. BOOL fFound = FALSE;
  1006. Assert(lpszUsername);
  1007. lpszUsername[0] = 0;
  1008. MU_GetRegRootForUserID(puidUserID, szPath);
  1009. Assert(*szPath);
  1010. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, szPath, 0, KEY_QUERY_VALUE, &hkey))
  1011. {
  1012. fFound = (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szUsername, 0, NULL, (LPBYTE)lpszUsername, &cch));
  1013. RegCloseKey(hkey);
  1014. }
  1015. return fFound;
  1016. }
  1017. /*
  1018. MU_CountUsers
  1019. Returns the number of users currently configured.
  1020. */
  1021. ULONG MU_CountUsers(void)
  1022. {
  1023. CStringList *psList;
  1024. ULONG ulCount = 0;
  1025. psList = MU_GetUsernameList();
  1026. if (psList)
  1027. {
  1028. ulCount = psList->GetLength();
  1029. delete psList;
  1030. }
  1031. return ulCount;
  1032. }
  1033. /*
  1034. MU_GetRegRootForUserid
  1035. Get the reg root path for a given user id.
  1036. */
  1037. HRESULT MU_GetRegRootForUserID(GUID *puidUserID, LPSTR pszPath)
  1038. {
  1039. TCHAR szUid[255];
  1040. Assert(pszPath);
  1041. Assert(puidUserID);
  1042. AStringFromGUID(puidUserID, szUid, ARRAYSIZE(szUid));
  1043. wsprintf(pszPath, "%.100s\\%.40s", c_szRegRoot, szUid);
  1044. return S_OK;
  1045. }
  1046. /*
  1047. MU_GetDefaultUserID
  1048. Get the user id for the user who is currently marked as the default user.
  1049. Returns true if the proper user was found, false if not.
  1050. */
  1051. BOOL MU_GetDefaultUserID(GUID *puidUserID)
  1052. {
  1053. BOOL fFound = FALSE;
  1054. HKEY hkey;
  1055. TCHAR szUid[255];
  1056. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS)
  1057. {
  1058. DWORD dwSize;
  1059. dwSize = sizeof(szUid);
  1060. fFound = (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szDefaultUserID, 0, NULL, (LPBYTE)szUid, &dwSize));
  1061. if (fFound)
  1062. fFound = SUCCEEDED(GUIDFromAString(szUid, puidUserID));
  1063. RegCloseKey(hkey);
  1064. }
  1065. #ifdef DEBUG
  1066. TCHAR szUsername[CCH_USERNAME_MAX_LENGTH+1];
  1067. Assert(MU_UserIdToUsername(ulUserID, szUsername, CCH_USERNAME_MAX_LENGTH));
  1068. #endif
  1069. return fFound;
  1070. }
  1071. /*
  1072. MU_MakeDefaultUser
  1073. Set the user referenced by id ulUserID to be the default user.
  1074. The default user is referenced by certain applications which
  1075. can only deal with one user. MS Phone is a good example.
  1076. */
  1077. HRESULT MU_MakeDefaultUser(GUID *puidUserID)
  1078. {
  1079. HRESULT hr = E_FAIL;
  1080. TCHAR szUid[255];
  1081. // make sure the user exists and get their name to put in the
  1082. // Default Username reg key
  1083. if (*puidUserID==GUID_NULL)
  1084. {
  1085. // We don't have a current user. So we'll have to figure out a new default user.
  1086. LPSTR pszName;
  1087. CStringList* pList = MU_GetUsernameList();
  1088. if (pList)
  1089. {
  1090. DWORD dwIndex, dwLen = pList->GetLength();
  1091. // find the first non hidden user and switch to them
  1092. for (dwIndex = 0; dwIndex < dwLen; dwIndex++)
  1093. {
  1094. pszName = pList->GetString(dwIndex);
  1095. if (pszName && *pszName && *pszName != '_')
  1096. {
  1097. break;
  1098. }
  1099. }
  1100. if (dwIndex==dwLen)
  1101. {
  1102. // Or, just create one
  1103. delete pList;
  1104. _MakeDefaultFirstUser();
  1105. return S_OK;
  1106. }
  1107. MU_SwitchToUser(pszName);
  1108. GUID guid;
  1109. hr = MU_UsernameToUserId(pszName, &guid);
  1110. if (SUCCEEDED(hr))
  1111. {
  1112. AStringFromGUID(&guid, szUid, ARRAYSIZE(szUid));
  1113. }
  1114. delete pList;
  1115. }
  1116. }
  1117. else
  1118. {
  1119. TCHAR szUsername[CCH_USERNAME_MAX_LENGTH+1];
  1120. AStringFromGUID(puidUserID, szUid, ARRAYSIZE(szUid));
  1121. if (MU_UserIdToUsername(puidUserID, szUsername, CCH_USERNAME_MAX_LENGTH))
  1122. hr = S_OK;
  1123. }
  1124. if (SUCCEEDED(hr))
  1125. {
  1126. HKEY hkey;
  1127. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS)
  1128. {
  1129. DWORD dwType, dwSize;
  1130. LONG lError;
  1131. dwType = REG_SZ;
  1132. dwSize = lstrlen(szUid) + 1;
  1133. lError = RegSetValueEx(hkey, c_szDefaultUserID, 0, dwType, (LPBYTE)szUid, dwSize);
  1134. if (lError)
  1135. {
  1136. hr = E_FAIL;
  1137. goto error;
  1138. }
  1139. hr = S_OK;
  1140. error:
  1141. RegCloseKey(hkey);
  1142. }
  1143. }
  1144. return hr;
  1145. }
  1146. /*
  1147. MU_DeleteUser
  1148. Remove a user from the registry. This does not delete
  1149. anything in the user's folder, but it does blow away
  1150. their reg settings.
  1151. */
  1152. HRESULT MU_DeleteUser(GUID *puidUserID)
  1153. {
  1154. GUID uidDefault, uidCurrent;
  1155. TCHAR szPath[MAX_PATH];
  1156. MU_GetCurrentUserID(&uidCurrent);
  1157. MU_GetDefaultUserID(&uidDefault);
  1158. // Can't delete the current user
  1159. if (*puidUserID == uidCurrent)
  1160. return E_FAIL;
  1161. // Delete the registry settings
  1162. MU_GetRegRootForUserID(puidUserID, szPath);
  1163. _DeleteKeyRecursively(HKEY_CURRENT_USER, szPath);
  1164. // If we had a default user, we'll have to find a new one now
  1165. if (*puidUserID == uidDefault)
  1166. MU_MakeDefaultUser(&uidCurrent);
  1167. // don't delete the directory since the user may need
  1168. // data out of it.
  1169. PostMessage(HWND_BROADCAST, WM_IDENTITY_INFO_CHANGED, 0, IIC_IDENTITY_DELETED);
  1170. return S_OK;
  1171. }
  1172. /*
  1173. MU_CreateUser
  1174. Create a user with the user info passed in. This includes
  1175. creating their spot in the registry and their directory in the
  1176. identities folder.
  1177. */
  1178. HRESULT MU_CreateUser(LPUSERINFO lpUserInfo)
  1179. {
  1180. TCHAR szPath[MAX_PATH], szBuffer[MAX_PATH], szUid[255];
  1181. WCHAR szwPath[MAX_PATH];
  1182. HKEY hkey;
  1183. HRESULT hr = S_OK;
  1184. DWORD dwType, dwSize, cbSize, dwValue;
  1185. PASSWORD_STORE pwStore;
  1186. MU_GetRegRootForUserID(&lpUserInfo->uidUserID, szPath);
  1187. Assert(*szPath && *szAcctPath);
  1188. AStringFromGUID(&lpUserInfo->uidUserID, szUid, ARRAYSIZE(szUid));
  1189. Assert(lpUserInfo->uidUserID != GUID_NULL);
  1190. if (RegCreateKey(HKEY_CURRENT_USER, szPath, &hkey) == ERROR_SUCCESS)
  1191. {
  1192. // write out the correct values
  1193. dwType = REG_SZ;
  1194. dwSize = lstrlen(lpUserInfo->szUsername) + 1;
  1195. RegSetValueEx(hkey, c_szUsername, 0, dwType, (LPBYTE)lpUserInfo->szUsername, dwSize);
  1196. #ifdef IDENTITY_PASSWORDS
  1197. lstrcpy(pwStore.szPassword, lpUserInfo->szPassword);
  1198. pwStore.fUsePassword = lpUserInfo->fUsePassword;
  1199. if (FAILED(hr = WriteIdentityPassword(&lpUserInfo->uidUserID, &pwStore)))
  1200. {
  1201. dwType = REG_BINARY ;
  1202. cbSize = strlen(lpUserInfo->szPassword) + 1;
  1203. lstrcpy(szBuffer, lpUserInfo->szPassword);
  1204. EncodeUserPassword(szBuffer, &cbSize);
  1205. dwSize = cbSize;
  1206. RegSetValueEx(hkey, c_szPassword, 0, dwType, (LPBYTE)szBuffer, dwSize);
  1207. dwType = REG_DWORD;
  1208. dwValue = (lpUserInfo->fUsePassword ? 1 : 0);
  1209. dwSize = sizeof(dwValue);
  1210. RegSetValueEx(hkey, c_szUsePassword, 0, dwType, (LPBYTE)&dwValue, dwSize);
  1211. }
  1212. #endif //IDENTITY_PASSWORDS
  1213. dwType = REG_SZ;
  1214. dwSize = lstrlen(szUid) + 1;
  1215. RegSetValueEx(hkey, c_szUserID, 0, dwType, (LPBYTE)&szUid, dwSize);
  1216. RegCloseKey(hkey);
  1217. if (SUCCEEDED(MU_GetUserDirectoryRoot(&lpUserInfo->uidUserID, GIF_ROAMING_FOLDER, szwPath, MAX_PATH)))
  1218. if (!CreateDirectoryWrapW(szwPath,NULL))
  1219. {
  1220. _CreateIdentitiesFolder();
  1221. CreateDirectoryWrapW(szwPath,NULL);
  1222. }
  1223. if (SUCCEEDED(MU_GetUserDirectoryRoot(&lpUserInfo->uidUserID, GIF_NON_ROAMING_FOLDER, szwPath, MAX_PATH)))
  1224. if (!CreateDirectoryWrapW(szwPath,NULL))
  1225. {
  1226. _CreateIdentitiesFolder();
  1227. CreateDirectoryWrapW(szwPath,NULL);
  1228. }
  1229. }
  1230. else
  1231. hr = E_FAIL;
  1232. return hr;
  1233. }
  1234. /*
  1235. MU_GetRegRoot
  1236. Returns a pointer to a string containing the location
  1237. in HKEY_CURRENT_USER for the current user.
  1238. */
  1239. LPCTSTR MU_GetRegRoot()
  1240. {
  1241. if (*g_szRegRoot)
  1242. return g_szRegRoot;
  1243. else
  1244. {
  1245. TCHAR szUsername[CCH_USERNAME_MAX_LENGTH + 1];
  1246. if (MU_Login(NULL, 0, szUsername))
  1247. {
  1248. GUID uidUserId;
  1249. TCHAR szUid[255];
  1250. MU_UsernameToUserId(szUsername, &uidUserId);
  1251. AStringFromGUID(&uidUserId, szUid, ARRAYSIZE(szUid));
  1252. wsprintf(g_szRegRoot, "%.100s\\%.40s", c_szRegRoot, szUid);
  1253. return g_szRegRoot;
  1254. }
  1255. else
  1256. {
  1257. Assert(FALSE);
  1258. }
  1259. }
  1260. return NULL;
  1261. }
  1262. void _MakeDefaultFirstUser()
  1263. {
  1264. USERINFO nuInfo;
  1265. TCHAR szUid[255];
  1266. MLLoadStringA(idsMainUser, nuInfo.szUsername, CCH_USERNAME_MAX_LENGTH);
  1267. if (nuInfo.szUsername[0] == 0)
  1268. {
  1269. lstrcpy(nuInfo.szUsername, TEXT("Main Identity"));
  1270. }
  1271. *nuInfo.szPassword = 0;
  1272. nuInfo.fUsePassword = false;
  1273. nuInfo.fPasswordValid = true;
  1274. _ClaimNextUserId(&nuInfo.uidUserID);
  1275. MU_CreateUser(&nuInfo);
  1276. MU_MakeDefaultUser(&nuInfo.uidUserID);
  1277. MU_SwitchToUser(nuInfo.szUsername);
  1278. AStringFromGUID(&nuInfo.uidUserID, szUid, ARRAYSIZE(szUid));
  1279. wsprintf(g_szRegRoot, "%.100s\\%.40s", c_szRegRoot, szUid);
  1280. }
  1281. void FixMissingIdentityNames()
  1282. {
  1283. HKEY hSourceSubKey;
  1284. ULONG ulEnumIndex = 0;
  1285. DWORD dwStatus, dwSize, dwType, dwValue;
  1286. BOOL fFound = FALSE;
  1287. TCHAR szKeyNameBuffer[MAX_PATH];
  1288. TCHAR szUsername[CCH_USERNAME_MAX_LENGTH];
  1289. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) == ERROR_SUCCESS)
  1290. {
  1291. while (!fFound)
  1292. {
  1293. HKEY hkUserKey;
  1294. if (RegEnumKey(hSourceSubKey, ulEnumIndex++, szKeyNameBuffer,MAXKEYNAME)
  1295. != ERROR_SUCCESS)
  1296. break;
  1297. if (RegOpenKey(hSourceSubKey, szKeyNameBuffer, &hkUserKey) == ERROR_SUCCESS)
  1298. {
  1299. dwSize = sizeof(szUsername);
  1300. dwStatus = RegQueryValueEx(hkUserKey, c_szUsername, NULL, &dwType, (LPBYTE)szUsername, &dwSize);
  1301. if (ERROR_SUCCESS != dwStatus || 0 == szUsername[0])
  1302. {
  1303. lstrcpy(szUsername, "Main Identity");
  1304. dwStatus = RegSetValueEx(hkUserKey, c_szUsername, 0, REG_SZ, (LPBYTE)szUsername, lstrlen(szUsername)+1);
  1305. }
  1306. RegCloseKey(hkUserKey);
  1307. }
  1308. }
  1309. RegCloseKey(hSourceSubKey);
  1310. }
  1311. }
  1312. typedef DWORD (STDAPICALLTYPE *PNetWkstaUserGetInfo)
  1313. (LPWSTR reserved, DWORD level, LPBYTE *bufptr);
  1314. #if 0
  1315. /*
  1316. _DomainControllerPresent
  1317. Identities are disabled when the machine they are running on is part of a domain, unless
  1318. there is a policy to explicitly allow them. This function checks to see if the machine
  1319. is joined to a domain.
  1320. */
  1321. BOOL _DomainControllerPresent()
  1322. {
  1323. static BOOL fInDomain = FALSE;
  1324. static BOOL fValid = FALSE;
  1325. HINSTANCE hInst;
  1326. PNetWkstaUserGetInfo pNetWkstaUserGetInfo;
  1327. _WKSTA_USER_INFO_1 *pwui1;
  1328. if (!fValid)
  1329. {
  1330. fValid = TRUE;
  1331. hInst = LoadLibrary(TEXT("NETAPI32.DLL"));
  1332. if (hInst)
  1333. {
  1334. pNetWkstaUserGetInfo = (PNetWkstaUserGetInfo)GetProcAddress(hInst, TEXT("NetWkstaUserGetInfo"));
  1335. if (pNetWkstaUserGetInfo && (pNetWkstaUserGetInfo(NULL, 1, (LPBYTE*)&pwui1) == NOERROR))
  1336. {
  1337. if (pwui1->wkui1_logon_domain && pwui1->wkui1_logon_server && lstrcmpW(pwui1->wkui1_logon_server, pwui1->wkui1_logon_domain) != 0)
  1338. {
  1339. fInDomain = TRUE;
  1340. }
  1341. }
  1342. FreeLibrary(hInst);
  1343. }
  1344. }
  1345. return fInDomain;
  1346. }
  1347. #endif
  1348. /*
  1349. MU_IdentitiesDisabled
  1350. Returns if identities is disabled due to a policy
  1351. or whatever.
  1352. */
  1353. BOOL MU_IdentitiesDisabled()
  1354. {
  1355. #ifndef _WIN64
  1356. TCHAR szPolicyPath[] = "Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Identities";
  1357. HKEY hkey;
  1358. DWORD dwValue, dwSize;
  1359. BOOL fLockedDown = FALSE;
  1360. if (RegOpenKey(HKEY_LOCAL_MACHINE, szPolicyPath, &hkey) == ERROR_SUCCESS)
  1361. {
  1362. dwSize = sizeof(DWORD);
  1363. if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue)
  1364. fLockedDown = TRUE;
  1365. RegCloseKey(hkey);
  1366. }
  1367. if (!fLockedDown && RegOpenKey(HKEY_CURRENT_USER, szPolicyPath, &hkey) == ERROR_SUCCESS)
  1368. {
  1369. dwSize = sizeof(DWORD);
  1370. if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue)
  1371. fLockedDown = TRUE;
  1372. RegCloseKey(hkey);
  1373. }
  1374. #ifdef DISABIDENT
  1375. if (!fLockedDown && RegOpenKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS)
  1376. {
  1377. dwSize = sizeof(DWORD);
  1378. if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue)
  1379. fLockedDown = TRUE;
  1380. RegCloseKey(hkey);
  1381. }
  1382. #endif //DISABIDENT
  1383. #if 0
  1384. // turned off for now, pending determination of whether we even want to
  1385. // have this policy
  1386. if (!fLockedDown && _DomainControllerPresent())
  1387. {
  1388. fLockedDown = TRUE;
  1389. if (RegOpenKey(HKEY_LOCAL_MACHINE, szPolicyPath, &hkey) == ERROR_SUCCESS)
  1390. {
  1391. dwSize = sizeof(DWORD);
  1392. if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szEnableDCPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue)
  1393. fLockedDown = FALSE;
  1394. RegCloseKey(hkey);
  1395. }
  1396. if (fLockedDown && RegOpenKey(HKEY_CURRENT_USER, szPolicyPath, &hkey) == ERROR_SUCCESS)
  1397. {
  1398. dwSize = sizeof(DWORD);
  1399. if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szEnableDCPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue)
  1400. fLockedDown = FALSE;
  1401. RegCloseKey(hkey);
  1402. }
  1403. }
  1404. #endif
  1405. return fLockedDown;
  1406. #else // _WIN64
  1407. return(TRUE);
  1408. #endif // _WIN64
  1409. }
  1410. static GUID g_uidLoginOption;
  1411. static BOOLEAN g_uidLoginOptionSet;
  1412. void _ResetRememberedLoginOption(void)
  1413. {
  1414. g_uidLoginOption = GUID_NULL;
  1415. g_uidLoginOptionSet = FALSE;
  1416. }
  1417. void _RememberLoginOption(HWND hwndCombo)
  1418. {
  1419. LRESULT dFoundItem;
  1420. TCHAR szUsername[CCH_IDENTITY_NAME_MAX_LENGTH * 2];
  1421. GUID uidUser;
  1422. *szUsername = 0;
  1423. g_uidLoginOptionSet = TRUE;
  1424. dFoundItem = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
  1425. SendMessage(hwndCombo, CB_GETLBTEXT, dFoundItem, (LPARAM)szUsername);
  1426. if (FAILED(MU_UsernameToUserId(szUsername, &uidUser)))
  1427. g_uidLoginOption = GUID_NULL;
  1428. else
  1429. g_uidLoginOption = uidUser;
  1430. }
  1431. DWORD MU_GetDefaultOptionIndex(HWND hwndCombo)
  1432. {
  1433. GUID uidStart, uidDefault;
  1434. USERINFO uiDefault;
  1435. DWORD dwResult = 0;
  1436. if (MU_GetDefaultUserID(&uidDefault))
  1437. {
  1438. MU_GetUserInfo(&uidDefault, &uiDefault);
  1439. if (uiDefault.szUsername[0])
  1440. {
  1441. dwResult = (DWORD)SendMessage(hwndCombo, CB_FINDSTRING, 0, (LPARAM)uiDefault.szUsername);
  1442. }
  1443. }
  1444. return dwResult;
  1445. }
  1446. DWORD MU_GetLoginOptionIndex(HWND hwndCombo)
  1447. {
  1448. GUID uidStart, uidDefault;
  1449. USERINFO uiLogin;
  1450. DWORD dwResult = ASK_BEFORE_LOGIN;
  1451. if (GUID_NULL == g_uidLoginOption)
  1452. {
  1453. if (g_uidLoginOptionSet)
  1454. goto exit;
  1455. MU_GetLoginOption(&uidStart);
  1456. }
  1457. else
  1458. uidStart = g_uidLoginOption;
  1459. if (uidStart == GUID_NULL)
  1460. goto exit;
  1461. if(!MU_GetUserInfo(&uidStart, &uiLogin))
  1462. goto exit;
  1463. dwResult = (DWORD)SendMessage(hwndCombo, CB_FINDSTRING, 0, (LPARAM)uiLogin.szUsername);
  1464. exit:
  1465. return dwResult;
  1466. }
  1467. /*
  1468. MU_GetLoginOption
  1469. return the user's choice for what should happen when there is no current
  1470. user
  1471. */
  1472. void MU_GetLoginOption(GUID *puidStartAs)
  1473. {
  1474. HKEY hkey;
  1475. DWORD dwSize;
  1476. TCHAR szUid[255];
  1477. GUID uidUser;
  1478. ZeroMemory(puidStartAs, sizeof(GUID));
  1479. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS)
  1480. {
  1481. dwSize = sizeof(szUid);
  1482. if (ERROR_SUCCESS != RegQueryValueEx(hkey, c_szLoginAs, 0, NULL, (LPBYTE)szUid, &dwSize))
  1483. MU_GetDefaultUserID(puidStartAs);
  1484. else
  1485. GUIDFromAString(szUid, puidStartAs);
  1486. RegCloseKey(hkey);
  1487. }
  1488. }
  1489. /*
  1490. MU_SetLoginOption
  1491. return the user's choice for what should happen when there is no current
  1492. user
  1493. */
  1494. BOOL MU_SetLoginOption(HWND hwndCombo, LRESULT dOption)
  1495. {
  1496. HKEY hkey;
  1497. BOOL fResult = FALSE;
  1498. TCHAR szUsername[CCH_IDENTITY_NAME_MAX_LENGTH * 2];
  1499. TCHAR szUid[255];
  1500. GUID uidUser;
  1501. SendMessage(hwndCombo, CB_GETLBTEXT, dOption, (LPARAM)szUsername);
  1502. if (dOption == (LRESULT)ASK_BEFORE_LOGIN || FAILED(MU_UsernameToUserId(szUsername, &uidUser)))
  1503. {
  1504. ZeroMemory(&uidUser, sizeof(uidUser));
  1505. }
  1506. AStringFromGUID(&uidUser, szUid, sizeof(szUid));
  1507. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS)
  1508. {
  1509. fResult = (ERROR_SUCCESS == RegSetValueEx(hkey, c_szLoginAs, 0, REG_SZ, (LPBYTE)szUid, lstrlen(szUid)+1));
  1510. RegCloseKey(hkey);
  1511. }
  1512. return TRUE;
  1513. }
  1514. /*
  1515. MU_CanEditIdentity
  1516. Is the current identity allowed to edit the indicated identity's settings?
  1517. */
  1518. BOOL MU_CanEditIdentity(HWND hwndParent, GUID *puidIdentityId)
  1519. {
  1520. #ifndef IDENTITY_PASSWORDS
  1521. return TRUE;
  1522. #else
  1523. USERINFO uiCurrent, uiQuery;
  1524. TCHAR szBuffer[255]; // really ought to be big enough
  1525. TCHAR szString[255+CCH_USERNAME_MAX_LENGTH];
  1526. BOOL fResult = FALSE;
  1527. PASSWORD_STORE pwStore;
  1528. ZeroMemory(&uiQuery, sizeof(USERINFO));
  1529. if (MU_GetUserInfo(puidIdentityId, &uiQuery))
  1530. {
  1531. if (!uiQuery.fPasswordValid)
  1532. {
  1533. MU_ShowErrorMessage(hwndParent, idsPwdNotFound, idsPwdError);
  1534. return FALSE;
  1535. }
  1536. if (uiQuery.szPassword[0] == 0)
  1537. {
  1538. return TRUE;
  1539. }
  1540. if (MU_GetUserInfo(NULL, &uiCurrent))
  1541. {
  1542. if (uiCurrent.uidUserID == uiQuery.uidUserID)
  1543. return TRUE;
  1544. }
  1545. }
  1546. else
  1547. return FALSE;
  1548. MLLoadStringA(idsConfirmEdit, szBuffer, sizeof(szBuffer));
  1549. wsprintf(szString, szBuffer, uiQuery.szUsername);
  1550. fResult = MU_ConfirmUserPassword(hwndParent, szString, uiQuery.szPassword);
  1551. return fResult;
  1552. #endif //IDENTITY_PASSWORDS
  1553. }
  1554. static BOOL _DirectoryIdInUse(DWORD dwId)
  1555. {
  1556. HKEY hSourceSubKey;
  1557. ULONG ulEnumIndex = 0;
  1558. DWORD dwStatus, dwSize, dwType, dwValue;
  1559. BOOL fFound = FALSE;
  1560. TCHAR szKeyNameBuffer[MAX_PATH];
  1561. if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) == ERROR_SUCCESS)
  1562. {
  1563. while (!fFound)
  1564. {
  1565. HKEY hkUserKey;
  1566. if (RegEnumKey(hSourceSubKey, ulEnumIndex++, szKeyNameBuffer,MAXKEYNAME)
  1567. != ERROR_SUCCESS)
  1568. break;
  1569. if (RegOpenKey(hSourceSubKey, szKeyNameBuffer, &hkUserKey) == ERROR_SUCCESS)
  1570. {
  1571. dwSize = sizeof(dwValue);
  1572. dwStatus = RegQueryValueEx(hkUserKey, c_szDirName, NULL, &dwType, (LPBYTE)&dwValue, &dwSize);
  1573. if (ERROR_SUCCESS == dwStatus && dwValue == dwId)
  1574. {
  1575. fFound = TRUE;
  1576. RegCloseKey(hkUserKey);
  1577. break;
  1578. }
  1579. RegCloseKey(hkUserKey);
  1580. }
  1581. }
  1582. RegCloseKey(hSourceSubKey);
  1583. }
  1584. return fFound;
  1585. }
  1586. DWORD MU_GenerateDirectoryNameForIdentity(GUID *puidIdentityId)
  1587. {
  1588. DWORD dwId, dwRegValue;
  1589. dwId = puidIdentityId->Data1;
  1590. while (_DirectoryIdInUse(dwId))
  1591. dwId++;
  1592. return dwId;
  1593. }
  1594. HRESULT MU_GetDirectoryIdForIdentity(GUID *puidIdentityId, DWORD *pdwDirId)
  1595. {
  1596. TCHAR szRegPath[MAX_PATH];
  1597. HKEY hkey;
  1598. HRESULT hr = E_FAIL;
  1599. DWORD dwSize, dwStatus, dwValue, dwType;
  1600. MU_GetRegRootForUserID(puidIdentityId, szRegPath);
  1601. if (RegOpenKey(HKEY_CURRENT_USER, szRegPath, &hkey) == ERROR_SUCCESS)
  1602. {
  1603. dwSize = sizeof(dwValue);
  1604. dwStatus = RegQueryValueEx(hkey, c_szDirName, NULL, &dwType, (LPBYTE)&dwValue, &dwSize);
  1605. if (ERROR_SUCCESS == dwStatus)
  1606. {
  1607. *pdwDirId = dwValue;
  1608. hr = S_OK;
  1609. }
  1610. else
  1611. {
  1612. // try to generate one
  1613. dwValue = MU_GenerateDirectoryNameForIdentity(puidIdentityId);
  1614. dwType = REG_DWORD;
  1615. dwSize = sizeof(dwValue);
  1616. dwStatus = RegSetValueEx(hkey, c_szDirName, 0, dwType, (LPBYTE)&dwValue, dwSize);
  1617. if (ERROR_SUCCESS == dwStatus)
  1618. {
  1619. *pdwDirId = dwValue;
  1620. hr = S_OK;
  1621. }
  1622. }
  1623. RegCloseKey(hkey);
  1624. }
  1625. return hr;
  1626. }
  1627. void _MigratePasswords()
  1628. {
  1629. CStringList *psList;
  1630. int i, iCount = 0;
  1631. USERINFO uiUser;
  1632. DWORD dwStatus, dwValue, dwType, dwSize;
  1633. dwType = REG_DWORD;
  1634. dwSize = sizeof(DWORD);
  1635. dwStatus = SHGetValue(HKEY_CURRENT_USER, c_szRegRoot, c_szMigrated5, &dwType, &dwValue, &dwSize);
  1636. if (dwStatus == ERROR_SUCCESS && dwValue == 1)
  1637. return;
  1638. psList = MU_GetUsernameList();
  1639. if (psList)
  1640. {
  1641. iCount = psList->GetLength();
  1642. for (i = 0; i < iCount; i++)
  1643. {
  1644. GUID uidUser;
  1645. if (SUCCEEDED(MU_UsernameToUserId(psList->GetString(i), &uidUser))
  1646. && MU_GetUserInfo(&uidUser, &uiUser))
  1647. {
  1648. if (!uiUser.fPasswordValid)
  1649. {
  1650. uiUser.fUsePassword = false;
  1651. *uiUser.szPassword = 0;
  1652. MU_SetUserInfo(&uiUser);
  1653. }
  1654. }
  1655. }
  1656. delete psList;
  1657. }
  1658. dwValue = 1;
  1659. SHSetValue(HKEY_CURRENT_USER, c_szRegRoot, c_szMigrated5, REG_DWORD, &dwValue, sizeof(DWORD));
  1660. }