multiusr.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 55k
Category:

Windows Kernel

Development Platform:

Visual C++

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