/******************************************************* MultiUsr.cpp Code for handling multiple user functionality in IE and friends Initially by Christopher Evans (cevans) 4/28/98 ********************************************************/ #define DONT_WANT_SHELLDEBUG #include "private.h" #include "resource.h" #include "multiusr.h" #include #include "multiutl.h" #include "strconst.h" #include "Shlwapi.h" #include "multiui.h" #include #include "mluisup.h" #include TCHAR g_szRegRoot[MAX_PATH] = ""; extern HINSTANCE g_hInst; static void _CreateIdentitiesFolder(); // add a backslash to a qualified path // // in: // lpszPath path (A:, C:\foo, etc) // // out: // lpszPath A:\, C:\foo\ ; // // returns: // pointer to the NULL that terminates the path // this is here to avoid a dependancy on shlwapi.dll #define CH_WHACK TEXT('\\') STDAPI_(LPTSTR) _PathAddBackslash( LPTSTR lpszPath) { LPTSTR lpszEnd; // perf: avoid lstrlen call for guys who pass in ptr to end // of buffer (or rather, EOB - 1). // note that such callers need to check for overflow themselves. int ichPath = (*lpszPath && !*(lpszPath + 1)) ? 1 : lstrlen(lpszPath); // try to keep us from tromping over MAX_PATH in size. // if we find these cases, return NULL. Note: We need to // check those places that call us to handle their GP fault // if they try to use the NULL! if (ichPath >= (MAX_PATH - 1)) { Assert(FALSE); // Let the caller know! return(NULL); } lpszEnd = lpszPath + ichPath; // this is really an error, caller shouldn't pass // an empty string if (!*lpszPath) return lpszEnd; /* Get the end of the source directory */ switch(*CharPrev(lpszPath, lpszEnd)) { case CH_WHACK: break; default: *lpszEnd++ = CH_WHACK; *lpszEnd = TEXT('\0'); } return lpszEnd; } STDAPI_(DWORD) _SHGetValueA( IN HKEY hkey, IN LPCSTR pszSubKey, OPTIONAL IN LPCSTR pszValue, OPTIONAL OUT LPDWORD pdwType, OPTIONAL OUT LPVOID pvData, OPTIONAL OUT LPDWORD pcbData) OPTIONAL { DWORD dwRet; HKEY hkeyNew; dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, KEY_QUERY_VALUE, &hkeyNew); if (NO_ERROR == dwRet) { dwRet = RegQueryValueEx(hkeyNew, pszValue, NULL, pdwType, (LPBYTE)pvData, pcbData); RegCloseKey(hkeyNew); } else if (pcbData) *pcbData = 0; return dwRet; } /*---------------------------------------------------------- Purpose: Recursively delete the key, including all child values and keys. Mimics what RegDeleteKey does in Win95. Returns: Cond: -- */ DWORD _DeleteKeyRecursively( IN HKEY hkey, IN LPCSTR pszSubKey) { DWORD dwRet; HKEY hkSubKey; // Open the subkey so we can enumerate any children dwRet = RegOpenKeyExA(hkey, pszSubKey, 0, MAXIMUM_ALLOWED, &hkSubKey); if (ERROR_SUCCESS == dwRet) { DWORD dwIndex; CHAR szSubKeyName[MAX_PATH + 1]; DWORD cchSubKeyName = ARRAYSIZE(szSubKeyName); CHAR szClass[MAX_PATH]; DWORD cbClass = ARRAYSIZE(szClass); // I can't just call RegEnumKey with an ever-increasing index, because // I'm deleting the subkeys as I go, which alters the indices of the // remaining subkeys in an implementation-dependent way. In order to // be safe, I have to count backwards while deleting the subkeys. // Find out how many subkeys there are dwRet = RegQueryInfoKeyA(hkSubKey, szClass, &cbClass, NULL, &dwIndex, // The # of subkeys -- all we need NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (NO_ERROR == dwRet) { // dwIndex is now the count of subkeys, but it needs to be // zero-based for RegEnumKey, so I'll pre-decrement, rather // than post-decrement. while (ERROR_SUCCESS == RegEnumKeyA(hkSubKey, --dwIndex, szSubKeyName, cchSubKeyName)) { _DeleteKeyRecursively(hkSubKey, szSubKeyName); } } RegCloseKey(hkSubKey); dwRet = RegDeleteKeyA(hkey, pszSubKey); } return dwRet; } // **************************************************************************************************** // C S T R I N G L I S T C L A S S // // A really basic string list class. Actually, its a string array class, but you don't need to know // that. It could do so much more, but for now, it only maintains an array of C strings. // CStringList::CStringList() { m_count = 0; m_ptrCount = 0; m_strings = NULL; } /* CStringList::~CStringList Clean up any memory that was allocated in the CStringList object */ CStringList::~CStringList() { if (m_strings) { for (int i = 0; i < m_count; i++) { if (m_strings[i]) { MemFree(m_strings[i]); m_strings[i] = NULL; } } MemFree(m_strings); m_strings = NULL; m_count = 0; } } /* CStringList::AddString Add a string to the end of the string list. */ void CStringList::AddString(TCHAR* lpszInString) { // make more room for pointers, if necessary if (m_ptrCount == m_count) { m_ptrCount += 5; if (!MemRealloc((void **)&m_strings, sizeof(TCHAR *) * m_ptrCount)) { m_ptrCount -= 5; Assert(false); return; } // initialize the new strings to nil for (int i = m_count; i < m_ptrCount; i++) m_strings[i] = NULL; } //now put the string in the next location int iNewIndex = m_count++; if(MemAlloc((void **)&m_strings[iNewIndex], sizeof(TCHAR) * lstrlen(lpszInString)+1)) { lstrcpy(m_strings[iNewIndex], lpszInString); } else { // couldn't allocate space for the string. Don't count that spot as filled m_count--; } } /* CStringList::RemoveString Remove a string at zero based index iIndex */ void CStringList::RemoveString(int iIndex) { int iCopySize; iCopySize = ((m_count - iIndex) - 1) * 4; // free the memory for the string if (m_strings[iIndex]) { MemFree(m_strings[iIndex]); m_strings[iIndex] = NULL; } // move the other strings down if (iCopySize) { memmove(&(m_strings[iIndex]), &(m_strings[iIndex+1]), iCopySize); } // null out the last item in the list and decrement the counter. m_strings[--m_count] = NULL; } /* CStringList::GetString Return the pointer to the string at zero based index iIndex. Return the string at the given index. Note that the TCHAR pointer is still owned by the string list and should not be deleted. */ TCHAR *CStringList::GetString(int iIndex) { if (iIndex < m_count && iIndex >= 0) return m_strings[iIndex]; else return NULL; } int __cdecl _CSL_Compare(const void *p1, const void *p2) { TCHAR *psz1, *psz2; psz1 = *((TCHAR **)p1); psz2 = *((TCHAR **)p2); return lstrcmpi(psz1, psz2); } /* CStringList::Sort Sort the strings in the list */ void CStringList::Sort() { qsort(m_strings, m_count, sizeof(TCHAR *), _CSL_Compare); } /* MU_Init Initialize the memory allocator and make sure that there is at least one user in the registry. */ static BOOL g_inited = FALSE; EXTERN_C void MU_Init() { CStringList* pList; if (!g_inited) { pList = MU_GetUsernameList(); if (!pList || pList->GetLength() == 0) { _MakeDefaultFirstUser(); } if (pList) delete pList; g_inited = TRUE; } } /* MU_GetUsernameList Build a CStringList with all of the names of the users stored in HKLM */ #define MAXKEYNAME 256 CStringList* MU_GetUsernameList(void) { CStringList* vList = NULL; HKEY hSourceSubKey; DWORD dwEnumIndex = 0, dwStatus, dwSize, dwType; int cb; TCHAR szKeyNameBuffer[MAXKEYNAME]; DWORD dwIdentityOrdinal = 1; vList = new CStringList; Assert(vList); if (!vList) goto exit; if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) != ERROR_SUCCESS) { AssertSz(FALSE, "Couldn't open user profiles root Key"); goto exit; } dwSize = sizeof(dwIdentityOrdinal); RegQueryValueEx(hSourceSubKey, c_szIdentityOrdinal, NULL, &dwType, (LPBYTE)&dwIdentityOrdinal, &dwSize); while (TRUE) { DWORD dwOrdinal; HKEY hkUserKey; if (RegEnumKey(hSourceSubKey, dwEnumIndex++, szKeyNameBuffer,MAXKEYNAME)!= ERROR_SUCCESS) break; cb = lstrlen(szKeyNameBuffer); if (RegOpenKey(hSourceSubKey, szKeyNameBuffer, &hkUserKey) == ERROR_SUCCESS) { dwSize = sizeof(szKeyNameBuffer); dwStatus = RegQueryValueEx(hkUserKey, c_szUsername, NULL, &dwType, (LPBYTE)&szKeyNameBuffer, &dwSize); Assert(ERROR_SUCCESS == dwStatus); Assert(*szKeyNameBuffer != 0); //filter names that begin with _ to hide things like "_Outlook News" if (ERROR_SUCCESS == dwStatus && *szKeyNameBuffer != '_') vList->AddString(szKeyNameBuffer); dwSize = sizeof(dwOrdinal); dwStatus = RegQueryValueEx(hkUserKey, c_szIdentityOrdinal, NULL, &dwType, (LPBYTE)&dwOrdinal, &dwSize); if (dwStatus==ERROR_SUCCESS) { if (dwOrdinal>=dwIdentityOrdinal) { dwIdentityOrdinal = dwOrdinal+1; AssertSz(FALSE, "MaxOrdinal is smaller than this identity. Why?"); } } else { dwStatus = RegSetValueEx(hkUserKey, c_szIdentityOrdinal, NULL, REG_DWORD, (LPBYTE)&dwIdentityOrdinal, dwSize); dwIdentityOrdinal++; } Assert(ERROR_SUCCESS == dwStatus); RegCloseKey(hkUserKey); } else AssertSz(FALSE, "Couldn't open user's Key"); } dwSize = sizeof(dwIdentityOrdinal); if (RegSetValueEx(hSourceSubKey, c_szIdentityOrdinal, 0, REG_DWORD, (LPBYTE)&dwIdentityOrdinal, dwSize)!=ERROR_SUCCESS) { AssertSz(FALSE, "Couldn't set the identity ordinal"); } RegCloseKey(hSourceSubKey); exit: return vList; } /* MU_UsernameToUserId Given a username, find its user id and return it. Returns E_FAIL if it can't find the given username. */ HRESULT MU_UsernameToUserId(TCHAR *lpszUsername, GUID *puidID) { HKEY hSourceSubKey; ULONG ulEnumIndex = 0; DWORD dwStatus, dwSize, dwType; TCHAR szKeyNameBuffer[MAXKEYNAME]; BOOL fFound = FALSE; TCHAR szUid[255]; ZeroMemory(puidID, sizeof(GUID)); if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) == ERROR_SUCCESS) { while (!fFound) { HKEY hkUserKey; if (RegEnumKey(hSourceSubKey, ulEnumIndex++, szKeyNameBuffer,MAXKEYNAME) != ERROR_SUCCESS) break; if (RegOpenKey(hSourceSubKey, szKeyNameBuffer, &hkUserKey) == ERROR_SUCCESS) { dwSize = sizeof(szKeyNameBuffer); dwStatus = RegQueryValueEx(hkUserKey, c_szUsername, NULL, &dwType, (LPBYTE)&szKeyNameBuffer, &dwSize); if (ERROR_SUCCESS == dwStatus && lstrcmpi(lpszUsername, szKeyNameBuffer) == 0) { dwSize = sizeof(szUid); dwStatus = RegQueryValueEx(hkUserKey, c_szUserID, NULL, &dwType, (LPBYTE)&szUid, &dwSize); fFound = (dwStatus == ERROR_SUCCESS); if (fFound) fFound = SUCCEEDED(GUIDFromAString(szUid, puidID)); } RegCloseKey(hkUserKey); } } RegCloseKey(hSourceSubKey); } return (fFound ? S_OK : E_FAIL); } /* MU_GetPasswordForUsername Get the password for the provided user and return it in szOutPassword. Return in pfUsePassword if password is enabled and false if it is disabled. Function returns true if the password data could be found, false otherwise */ BOOL MU_GetPasswordForUsername(TCHAR *lpszInUsername, TCHAR *szOutPassword, BOOL *pfUsePassword) { #ifdef IDENTITY_PASSWORDS TCHAR szPath[MAX_PATH]; TCHAR szPassword[255] = ""; HKEY hDestinationSubKey; DWORD dwSize, dwStatus, dwType; DWORD dwPWEnabled = 0; GUID uidUserID; HRESULT hr; PASSWORD_STORE pwStore; hr = MU_UsernameToUserId(lpszInUsername, &uidUserID); Assert(SUCCEEDED(hr)); if (uidUserID == GUID_NULL) { *pfUsePassword = FALSE; return TRUE; } if (SUCCEEDED(hr = ReadIdentityPassword(&uidUserID, &pwStore))) { lstrcpy(szOutPassword, pwStore.szPassword); *pfUsePassword = pwStore.fUsePassword; return TRUE; } else { BOOL fFoundPassword = FALSE; //build the user level key. MU_GetRegRootForUserID(&uidUserID, szPath); if (RegCreateKey(HKEY_CURRENT_USER, szPath, &hDestinationSubKey) == ERROR_SUCCESS) { dwSize = sizeof(dwPWEnabled); dwStatus = RegQueryValueEx(hDestinationSubKey, c_szUsePassword, NULL, &dwType, (LPBYTE)&dwPWEnabled, &dwSize); if (ERROR_SUCCESS == dwStatus && 0 != dwPWEnabled) { dwSize = sizeof(szPassword); dwStatus = RegQueryValueEx(hDestinationSubKey, c_szPassword, NULL, &dwType, (LPBYTE)&szPassword, &dwSize); if (ERROR_SUCCESS == dwStatus) { ULONG cbSize; fFoundPassword = TRUE; cbSize = dwSize; if (cbSize > 1) { DecodeUserPassword(szPassword, &cbSize); strcpy(szOutPassword, szPassword); } else { *szOutPassword = 0; } } } RegCloseKey(hDestinationSubKey); } // Herein lies the pull. We can't count on being able to access any // given pstore from any given profile on Win9x. If you log on with // a blank password, or hit escape (not much difference to a user) // you will have a different pstore. If we store our passwords in the // registry, they can be whacked pretty simply. If we can't find the // password, we will disable it for now and say there is none. It // seems that most people don't put passwords on identities now // anyway, though this will change. if (!fFoundPassword) { fFoundPassword = TRUE; dwPWEnabled = 0; } // Here ends the pull *pfUsePassword = (dwPWEnabled != 0); return fFoundPassword; } #else *pfUsePassword = FALSE; return TRUE; #endif //IDENTITY_PASSWORDS } /* _FillListBoxWithUsernames Fill a listbox with the names of the users, Adds (Default) to the default user. */ BOOL _FillListBoxWithUsernames(HWND hListbox) { CStringList *lpCStringList; GUID uidDefault; GUID uidUser; lpCStringList = MU_GetUsernameList(); if (lpCStringList) { MU_GetDefaultUserID(&uidDefault); SendMessage(hListbox, LB_RESETCONTENT, 0, 0); lpCStringList->Sort(); if (lpCStringList) { for(int i = 0; i < lpCStringList->GetLength(); i++) { if (lpCStringList->GetString(i)) { SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)lpCStringList->GetString(i)); } } delete lpCStringList; return true; } } return false; } BOOL _FillComboBoxWithUsernames(HWND hCombobox, HWND hListbox) { TCHAR szRes[128]; DWORD_PTR cIndex, dwCount = SendMessage(hListbox, LB_GETCOUNT, 0, 0); SendMessage(hCombobox, CB_RESETCONTENT, 0, 0); for (cIndex = 0; cIndex < dwCount; cIndex++) { SendMessage(hListbox, LB_GETTEXT, cIndex, (LPARAM)szRes); SendMessage(hCombobox, CB_ADDSTRING, 0, (LPARAM)szRes); } return true; } /* MU_UsernameExists Does the given name already exist as a username? */ BOOL MU_UsernameExists(TCHAR* lpszUsername) { GUID uidID; return SUCCEEDED(MU_UsernameToUserId(lpszUsername, &uidID)); } /* MU_GetUserInfo Fill in the user info structure with current values */ BOOL MU_GetUserInfo(GUID *puidUserID, LPUSERINFO lpUserInfo) { TCHAR szPWBuffer[255]; TCHAR szRegPath[MAX_PATH]; HKEY hKey; BOOL bResult = false; LONG lValue; DWORD dwStatus, dwType, dwSize; GUID uidUser; TCHAR szUid[255]; HRESULT hr; PASSWORD_STORE pwStore; lpUserInfo->fPasswordValid = FALSE; if( puidUserID == NULL) { MU_GetCurrentUserID(&uidUser); if (uidUser == GUID_NULL) return FALSE; } else uidUser = *puidUserID; MU_GetRegRootForUserID(&uidUser, szRegPath); if (RegOpenKey(HKEY_CURRENT_USER, szRegPath, &hKey) == ERROR_SUCCESS) { *lpUserInfo->szPassword = 0; lpUserInfo->fUsePassword = false; ZeroMemory(&lpUserInfo->uidUserID, sizeof(GUID)); dwSize = sizeof(lpUserInfo->szUsername); if ((dwStatus = RegQueryValueEx(hKey, c_szUsername, NULL, &dwType, (LPBYTE)lpUserInfo->szUsername, &dwSize)) == ERROR_SUCCESS && (0 != *lpUserInfo->szUsername)) { //we have the username, that is the only required part. The others are optional. bResult = true; #ifdef IDENTITY_PASSWORDS lpUserInfo->fPasswordValid = FALSE; if (SUCCEEDED(hr = ReadIdentityPassword(&uidUser, &pwStore))) { lstrcpy(lpUserInfo->szPassword, pwStore.szPassword); lpUserInfo->fUsePassword = pwStore.fUsePassword; lpUserInfo->fPasswordValid = TRUE; } else { dwSize = sizeof(lValue); if ((dwStatus = RegQueryValueEx(hKey, c_szUsePassword, NULL, &dwType, (LPBYTE)&lValue, &dwSize)) == ERROR_SUCCESS) { lpUserInfo->fUsePassword = (lValue != 0); } dwSize = sizeof(szPWBuffer); dwStatus = RegQueryValueEx(hKey, c_szPassword, NULL, &dwType, (LPBYTE)szPWBuffer, &dwSize); ULONG cbSize; lpUserInfo->fPasswordValid = (ERROR_SUCCESS == dwStatus); // Herein lies the pull (Volume 2). We can't count on being able to access any // given pstore from any given profile on Win9x. If you log on with // a blank password, or hit escape (not much difference to a user) // you will have a different pstore. If we store our passwords in the // registry, they can be whacked pretty simply. If we can't find the // password, we will disable it for now and say there is none. It // seems that most people don't put passwords on identities now // anyway, though this will change. if (!lpUserInfo->fPasswordValid) { lpUserInfo->fPasswordValid = TRUE; lpUserInfo->fUsePassword = FALSE; } // Here ends the pull cbSize = dwSize; if (ERROR_SUCCESS == dwStatus && cbSize > 1) { DecodeUserPassword(szPWBuffer, &cbSize); strcpy(lpUserInfo->szPassword, szPWBuffer); } else *lpUserInfo->szPassword = 0; } #endif dwSize = sizeof(szUid); if ((dwStatus = RegQueryValueEx(hKey, c_szUserID, NULL, &dwType, (LPBYTE)&szUid, &dwSize)) == ERROR_SUCCESS) { hr = GUIDFromAString(szUid, &lpUserInfo->uidUserID); Assert(hr); } } RegCloseKey(hKey); } return bResult; } /* MU_SetUserInfo Save the user info structure with the user values */ BOOL MU_SetUserInfo(LPUSERINFO lpUserInfo) { DWORD dwType, dwSize, dwValue, dwStatus; HKEY hkCurrUser; TCHAR szPath[MAX_PATH]; WCHAR szwPath[MAX_PATH]; TCHAR szUid[255]; BOOL fNewIdentity = FALSE; PASSWORD_STORE pwStore; HRESULT hr; MU_GetRegRootForUserID(&lpUserInfo->uidUserID, szPath); Assert(pszRegPath && *pszRegPath); Assert(lpUserInfo->uidUserID != GUID_NULL); if ((dwStatus = RegCreateKey(HKEY_CURRENT_USER, szPath, &hkCurrUser)) == ERROR_SUCCESS) { ULONG cbSize; TCHAR szBuffer[255]; // write out the correct values dwType = REG_SZ; dwSize = lstrlen(lpUserInfo->szUsername) + 1; RegSetValueEx(hkCurrUser, c_szUsername, 0, dwType, (LPBYTE)lpUserInfo->szUsername, dwSize); dwSize = sizeof(DWORD); if ((dwStatus = RegQueryValueEx(hkCurrUser, c_szDirName, NULL, &dwType, (LPBYTE)&dwValue, &dwSize)) != ERROR_SUCCESS) { dwValue = MU_GenerateDirectoryNameForIdentity(&lpUserInfo->uidUserID); dwType = REG_DWORD; dwSize = sizeof(dwValue); RegSetValueEx(hkCurrUser, c_szDirName, 0, dwType, (LPBYTE)&dwValue, dwSize); fNewIdentity = TRUE; } #ifdef IDENTITY_PASSWORDS lstrcpy(pwStore.szPassword, lpUserInfo->szPassword); pwStore.fUsePassword = lpUserInfo->fUsePassword; if (FAILED(hr = WriteIdentityPassword(&lpUserInfo->uidUserID, &pwStore))) { dwType = REG_BINARY ; cbSize = strlen(lpUserInfo->szPassword) + 1; lstrcpy(szBuffer, lpUserInfo->szPassword); EncodeUserPassword(szBuffer, &cbSize); dwSize = cbSize; RegSetValueEx(hkCurrUser, c_szPassword, 0, dwType, (LPBYTE)szBuffer, dwSize); dwType = REG_DWORD; dwValue = (lpUserInfo->fUsePassword ? 1 : 0); dwSize = sizeof(dwValue); RegSetValueEx(hkCurrUser, c_szUsePassword, 0, dwType, (LPBYTE)&dwValue, dwSize); } else { //don't keep the registry values if we could save it to the pstore. RegDeleteValue(hkCurrUser, c_szPassword); RegDeleteValue(hkCurrUser, c_szUsePassword); } #endif //IDENTITY_PASSWORDS Assert(lpUserInfo->uidUserID != GUID_NULL); AStringFromGUID(&lpUserInfo->uidUserID, szUid, ARRAYSIZE(szUid)); dwType = REG_SZ; dwSize = lstrlen(szUid) + 1; RegSetValueEx(hkCurrUser, c_szUserID, 0, dwType, (LPBYTE)&szUid, dwSize); RegCloseKey(hkCurrUser); if (fNewIdentity) { if (SUCCEEDED(MU_GetUserDirectoryRoot(&lpUserInfo->uidUserID, GIF_ROAMING_FOLDER, szwPath, MAX_PATH))) { if (!CreateDirectoryWrapW(szwPath,NULL)) { _CreateIdentitiesFolder(); CreateDirectoryWrapW(szwPath,NULL); } } if (SUCCEEDED(MU_GetUserDirectoryRoot(&lpUserInfo->uidUserID, GIF_NON_ROAMING_FOLDER, szwPath, MAX_PATH))) { if (!CreateDirectoryWrapW(szwPath,NULL)) { _CreateIdentitiesFolder(); CreateDirectoryWrapW(szwPath,NULL); } } } return TRUE; } return FALSE; } /* MU_SwitchToUser Currently, this just saves the last user's info. */ HRESULT MU_SwitchToUser(TCHAR *lpszUsername) { GUID uidUserID; TCHAR szUid[255]; HRESULT hr; Assert(lpszUsername); if (*lpszUsername == 0) // null string means null guid { uidUserID = GUID_NULL; } else { hr = MU_UsernameToUserId(lpszUsername, &uidUserID); if (FAILED(hr)) return hr; } AStringFromGUID(&uidUserID, szUid, ARRAYSIZE(szUid)); Assert(uidUserID != GUID_NULL || (*lpszUsername == 0)); wsprintf(g_szRegRoot, "%.100s\\%.40s", c_szRegRoot, szUid); // remember who we last switched to HKEY hkey; if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS) { DWORD dwType, dwSize; dwType = REG_SZ; dwSize = lstrlen(lpszUsername) + 1; RegSetValueEx(hkey, c_szLastUserName, 0, dwType, (LPBYTE)lpszUsername, dwSize); dwType = REG_SZ; dwSize = lstrlen(szUid) + 1; RegSetValueEx(hkey, c_szLastUserID, 0, dwType, (LPBYTE)szUid, dwSize); RegCloseKey(hkey); } return S_OK; } /* MU_SwitchToLastUser Makes the last user current, if there is no last user, it switches to the first user it can find. If there are no users, it creates a user called "Main User" */ void MU_SwitchToLastUser() { HKEY hkey; TCHAR szUserUid[255]; TCHAR szUsername[CCH_USERNAME_MAX_LENGTH + 1]; BOOL fSwitched = FALSE; GUID uidUserId; if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS) { DWORD dwType, dwStatus, dwSize; dwSize = sizeof(szUserUid); dwStatus = RegQueryValueEx(hkey, c_szLastUserID, NULL, &dwType, (LPBYTE)szUserUid, &dwSize); RegCloseKey(hkey); if (ERROR_SUCCESS == dwStatus && SUCCEEDED(GUIDFromAString(szUserUid, &uidUserId)) && SUCCEEDED(MU_UserIdToUsername(&uidUserId, szUsername, CCH_USERNAME_MAX_LENGTH))) { MU_SwitchToUser(szUsername); fSwitched = true; } } if (!fSwitched) { LPSTR pszName; CStringList* pList = MU_GetUsernameList(); if (pList) { DWORD dwIndex, dwLen = pList->GetLength(); // find the first non hidden user and switch to them for (dwIndex = 0; dwIndex < dwLen; dwIndex++) { pszName = pList->GetString(dwIndex); if (pszName && *pszName && *pszName != '_') { MU_SwitchToUser(pszName); fSwitched = TRUE; break; } } delete pList; } } if (!fSwitched) { _MakeDefaultFirstUser(); CStringList* pList = MU_GetUsernameList(); if (pList && pList->GetLength() > 0) MU_SwitchToUser(pList->GetString(0)); if (pList) delete pList; } } /* _CreateIdentitiesFolder Create the parent folder of all of the identities folders. */ static void _CreateIdentitiesFolder() { HRESULT hr; TCHAR szAppDir[MAX_PATH], szSubDir[MAX_PATH], *psz; DWORD dw, type; hr = E_FAIL; dw = MAX_PATH; if (ERROR_SUCCESS == _SHGetValueA(HKEY_CURRENT_USER, c_szRegFolders, c_szValueAppData, &type, (LPBYTE)szAppDir, &dw)) { lstrcpy(szSubDir, c_szIdentitiesFolderName); psz = _PathAddBackslash(szSubDir); if (psz) { psz = _PathAddBackslash(szAppDir); if (psz) { lstrcpy(psz, szSubDir); psz = _PathAddBackslash(szAppDir); CreateDirectory(szAppDir, NULL); } } } } /* MU_GetCurrentUserDirectoryRoot Return the path to the top of the current user's root directory. This is the directory where the mail store should be located. It is in a subfolder the App Data folder. lpszUserRoot is a pointer to a character buffer that is cch chars in size. */ HRESULT MU_GetUserDirectoryRoot(GUID *uidUserID, DWORD dwFlags, WCHAR *lpszwUserRoot, int cch) { HRESULT hr; WCHAR szwSubDir[MAX_PATH], *pszw, szwUid[255]; int cb; DWORD type, dwDirId; LPITEMIDLIST pidl = NULL; IShellFolder *psf = NULL; STRRET str; IMalloc *pMalloc = NULL; BOOL fNeedHelp = FALSE; Assert(lpszUserRoot != NULL); Assert(uidUserID); Assert(cch >= MAX_PATH); Assert((dwFlags & (GIF_NON_ROAMING_FOLDER | GIF_ROAMING_FOLDER))); hr = MU_GetDirectoryIdForIdentity(uidUserID, &dwDirId); StringFromGUID2(*uidUserID, szwUid, ARRAYSIZE(szwUid)); if (FAILED(hr)) return hr; hr = SHGetMalloc(&pMalloc); Assert(pMalloc); if (!pMalloc) return E_OUTOFMEMORY; hr = E_FAIL; if (!!(dwFlags & GIF_NON_ROAMING_FOLDER)) { hr = SHGetSpecialFolderLocation(GetDesktopWindow(), CSIDL_LOCAL_APPDATA, &pidl); if (FAILED(hr) || pidl == 0) hr = SHGetSpecialFolderLocation(GetDesktopWindow(), CSIDL_APPDATA, &pidl); if (FAILED(hr)) fNeedHelp = TRUE; } else if (!!(dwFlags & GIF_ROAMING_FOLDER)) { hr = SHGetSpecialFolderLocation(GetDesktopWindow(), CSIDL_APPDATA, &pidl); if (FAILED(hr)) fNeedHelp = TRUE; } else hr = E_INVALIDARG; *lpszwUserRoot = 0; if (SUCCEEDED(hr) && pidl) { if (FAILED(hr = SHGetDesktopFolder(&psf))) goto exit; if (FAILED(hr = psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str))) goto exit; switch(str.uType) { case STRRET_WSTR: lstrcpyW(lpszwUserRoot, str.pOleStr); pMalloc->Free(str.pOleStr); break; case STRRET_OFFSET: MultiByteToWideChar(CP_ACP, 0, (LPSTR)pidl+str.uOffset, -1, lpszwUserRoot, cch-11); break; case STRRET_CSTR: MultiByteToWideChar(CP_ACP, 0, (LPSTR)str.cStr, -1, lpszwUserRoot, cch-11); break; default: Assert(FALSE); goto exit; } pszw = PathAddBackslashW(lpszwUserRoot); if (lstrlenW(lpszwUserRoot) < cch - 10) { StrCatW(pszw, L"Identities\\"); StrCatW(pszw, szwUid); StrCatW(pszw, L"\\"); } else { hr = E_OUTOFMEMORY; *lpszwUserRoot = 0; } } else if (fNeedHelp) { // $$$Review: NEIL QFE // SHGetSpecialFolderLocation(GetDesktopWindow(), CSIDL_APPDATA, &pidl) fails on non-SI OSR2. HKEY hkeySrc; DWORD cb; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_QUERY_VALUE, &hkeySrc)) { // -1 for the backslash we may add cb = cch - 1; if (ERROR_SUCCESS == RegQueryValueExWrapW(hkeySrc, L"AppData", 0, NULL, (LPBYTE)lpszwUserRoot, &cb)) { pszw = PathAddBackslashW(lpszwUserRoot); if (lstrlenW(lpszwUserRoot) < cch - 10) { StrCatW(pszw, L"Identities\\"); StrCatW(pszw, szwUid); StrCatW(pszw, L"\\"); hr = S_OK; } else { hr = E_OUTOFMEMORY; *lpszwUserRoot = 0; } } RegCloseKey(hkeySrc); } } exit: Assert(lstrlenW(lpszwUserRoot) > 0); SafeRelease(psf); pMalloc->Free(pidl); SafeRelease(pMalloc); return hr; } /* _ClaimNextUserId Get the next available user id. Currently this means starting with the CURRENT_USER GUID and changing the first DWORD of it until it is unique. */ HRESULT _ClaimNextUserId(GUID *puidId) { ULONG ulValue = 1; DWORD dwType, dwSize, dwStatus; HKEY hkeyProfiles; TCHAR szUsername[CCH_USERNAME_MAX_LENGTH+1]; GUID uid; FILETIME ft; if (FAILED(CoCreateGuid(&uid))) { uid = UID_GIBC_CURRENT_USER; GetSystemTimeAsFileTime(&ft); uid.Data1 = ft.dwLowDateTime; //make sure it hasn't been used while (MU_UserIdToUsername(&uid, szUsername, CCH_USERNAME_MAX_LENGTH)) uid.Data1 ++; } *puidId = uid; return S_OK; } BOOL MU_GetCurrentUserID(GUID *puidUserID) { BOOL fFound = FALSE; HKEY hkey; GUID uidUserId; TCHAR szUid[255]; if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS) { DWORD dwSize; dwSize = 255; fFound = (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szLastUserID, 0, NULL, (LPBYTE)szUid, &dwSize)); if (fFound) fFound = SUCCEEDED(GUIDFromAString(szUid, puidUserID)); if (fFound && *puidUserID == GUID_NULL) fFound = false; RegCloseKey(hkey); } #ifdef DEBUG TCHAR szUsername[CCH_USERNAME_MAX_LENGTH+1]; Assert(MU_UserIdToUsername(puidUserID, szUsername, CCH_USERNAME_MAX_LENGTH)); #endif return fFound; } /* MU_UserIdToUsername Return the user name for the user whose user id is passed in. Returns whether or not the user was found. */ BOOL MU_UserIdToUsername(GUID *puidUserID, TCHAR *lpszUsername, ULONG cch) { HKEY hkey; TCHAR szPath[MAX_PATH]; BOOL fFound = FALSE; Assert(lpszUsername); lpszUsername[0] = 0; MU_GetRegRootForUserID(puidUserID, szPath); Assert(*szPath); if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, szPath, 0, KEY_QUERY_VALUE, &hkey)) { fFound = (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szUsername, 0, NULL, (LPBYTE)lpszUsername, &cch)); RegCloseKey(hkey); } return fFound; } /* MU_CountUsers Returns the number of users currently configured. */ ULONG MU_CountUsers(void) { CStringList *psList; ULONG ulCount = 0; psList = MU_GetUsernameList(); if (psList) { ulCount = psList->GetLength(); delete psList; } return ulCount; } /* MU_GetRegRootForUserid Get the reg root path for a given user id. */ HRESULT MU_GetRegRootForUserID(GUID *puidUserID, LPSTR pszPath) { TCHAR szUid[255]; Assert(pszPath); Assert(puidUserID); AStringFromGUID(puidUserID, szUid, ARRAYSIZE(szUid)); wsprintf(pszPath, "%.100s\\%.40s", c_szRegRoot, szUid); return S_OK; } /* MU_GetDefaultUserID Get the user id for the user who is currently marked as the default user. Returns true if the proper user was found, false if not. */ BOOL MU_GetDefaultUserID(GUID *puidUserID) { BOOL fFound = FALSE; HKEY hkey; TCHAR szUid[255]; if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS) { DWORD dwSize; dwSize = sizeof(szUid); fFound = (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szDefaultUserID, 0, NULL, (LPBYTE)szUid, &dwSize)); if (fFound) fFound = SUCCEEDED(GUIDFromAString(szUid, puidUserID)); RegCloseKey(hkey); } #ifdef DEBUG TCHAR szUsername[CCH_USERNAME_MAX_LENGTH+1]; Assert(MU_UserIdToUsername(ulUserID, szUsername, CCH_USERNAME_MAX_LENGTH)); #endif return fFound; } /* MU_MakeDefaultUser Set the user referenced by id ulUserID to be the default user. The default user is referenced by certain applications which can only deal with one user. MS Phone is a good example. */ HRESULT MU_MakeDefaultUser(GUID *puidUserID) { HRESULT hr = E_FAIL; TCHAR szUid[255]; // make sure the user exists and get their name to put in the // Default Username reg key if (*puidUserID==GUID_NULL) { // We don't have a current user. So we'll have to figure out a new default user. LPSTR pszName; CStringList* pList = MU_GetUsernameList(); if (pList) { DWORD dwIndex, dwLen = pList->GetLength(); // find the first non hidden user and switch to them for (dwIndex = 0; dwIndex < dwLen; dwIndex++) { pszName = pList->GetString(dwIndex); if (pszName && *pszName && *pszName != '_') { break; } } if (dwIndex==dwLen) { // Or, just create one delete pList; _MakeDefaultFirstUser(); return S_OK; } MU_SwitchToUser(pszName); GUID guid; hr = MU_UsernameToUserId(pszName, &guid); if (SUCCEEDED(hr)) { AStringFromGUID(&guid, szUid, ARRAYSIZE(szUid)); } delete pList; } } else { TCHAR szUsername[CCH_USERNAME_MAX_LENGTH+1]; AStringFromGUID(puidUserID, szUid, ARRAYSIZE(szUid)); if (MU_UserIdToUsername(puidUserID, szUsername, CCH_USERNAME_MAX_LENGTH)) hr = S_OK; } if (SUCCEEDED(hr)) { HKEY hkey; if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS) { DWORD dwType, dwSize; LONG lError; dwType = REG_SZ; dwSize = lstrlen(szUid) + 1; lError = RegSetValueEx(hkey, c_szDefaultUserID, 0, dwType, (LPBYTE)szUid, dwSize); if (lError) { hr = E_FAIL; goto error; } hr = S_OK; error: RegCloseKey(hkey); } } return hr; } /* MU_DeleteUser Remove a user from the registry. This does not delete anything in the user's folder, but it does blow away their reg settings. */ HRESULT MU_DeleteUser(GUID *puidUserID) { GUID uidDefault, uidCurrent; TCHAR szPath[MAX_PATH]; MU_GetCurrentUserID(&uidCurrent); MU_GetDefaultUserID(&uidDefault); // Can't delete the current user if (*puidUserID == uidCurrent) return E_FAIL; // Delete the registry settings MU_GetRegRootForUserID(puidUserID, szPath); _DeleteKeyRecursively(HKEY_CURRENT_USER, szPath); // If we had a default user, we'll have to find a new one now if (*puidUserID == uidDefault) MU_MakeDefaultUser(&uidCurrent); // don't delete the directory since the user may need // data out of it. PostMessage(HWND_BROADCAST, WM_IDENTITY_INFO_CHANGED, 0, IIC_IDENTITY_DELETED); return S_OK; } /* MU_CreateUser Create a user with the user info passed in. This includes creating their spot in the registry and their directory in the identities folder. */ HRESULT MU_CreateUser(LPUSERINFO lpUserInfo) { TCHAR szPath[MAX_PATH], szBuffer[MAX_PATH], szUid[255]; WCHAR szwPath[MAX_PATH]; HKEY hkey; HRESULT hr = S_OK; DWORD dwType, dwSize, cbSize, dwValue; PASSWORD_STORE pwStore; MU_GetRegRootForUserID(&lpUserInfo->uidUserID, szPath); Assert(*szPath && *szAcctPath); AStringFromGUID(&lpUserInfo->uidUserID, szUid, ARRAYSIZE(szUid)); Assert(lpUserInfo->uidUserID != GUID_NULL); if (RegCreateKey(HKEY_CURRENT_USER, szPath, &hkey) == ERROR_SUCCESS) { // write out the correct values dwType = REG_SZ; dwSize = lstrlen(lpUserInfo->szUsername) + 1; RegSetValueEx(hkey, c_szUsername, 0, dwType, (LPBYTE)lpUserInfo->szUsername, dwSize); #ifdef IDENTITY_PASSWORDS lstrcpy(pwStore.szPassword, lpUserInfo->szPassword); pwStore.fUsePassword = lpUserInfo->fUsePassword; if (FAILED(hr = WriteIdentityPassword(&lpUserInfo->uidUserID, &pwStore))) { dwType = REG_BINARY ; cbSize = strlen(lpUserInfo->szPassword) + 1; lstrcpy(szBuffer, lpUserInfo->szPassword); EncodeUserPassword(szBuffer, &cbSize); dwSize = cbSize; RegSetValueEx(hkey, c_szPassword, 0, dwType, (LPBYTE)szBuffer, dwSize); dwType = REG_DWORD; dwValue = (lpUserInfo->fUsePassword ? 1 : 0); dwSize = sizeof(dwValue); RegSetValueEx(hkey, c_szUsePassword, 0, dwType, (LPBYTE)&dwValue, dwSize); } #endif //IDENTITY_PASSWORDS dwType = REG_SZ; dwSize = lstrlen(szUid) + 1; RegSetValueEx(hkey, c_szUserID, 0, dwType, (LPBYTE)&szUid, dwSize); RegCloseKey(hkey); if (SUCCEEDED(MU_GetUserDirectoryRoot(&lpUserInfo->uidUserID, GIF_ROAMING_FOLDER, szwPath, MAX_PATH))) if (!CreateDirectoryWrapW(szwPath,NULL)) { _CreateIdentitiesFolder(); CreateDirectoryWrapW(szwPath,NULL); } if (SUCCEEDED(MU_GetUserDirectoryRoot(&lpUserInfo->uidUserID, GIF_NON_ROAMING_FOLDER, szwPath, MAX_PATH))) if (!CreateDirectoryWrapW(szwPath,NULL)) { _CreateIdentitiesFolder(); CreateDirectoryWrapW(szwPath,NULL); } } else hr = E_FAIL; return hr; } /* MU_GetRegRoot Returns a pointer to a string containing the location in HKEY_CURRENT_USER for the current user. */ LPCTSTR MU_GetRegRoot() { if (*g_szRegRoot) return g_szRegRoot; else { TCHAR szUsername[CCH_USERNAME_MAX_LENGTH + 1]; if (MU_Login(NULL, 0, szUsername)) { GUID uidUserId; TCHAR szUid[255]; MU_UsernameToUserId(szUsername, &uidUserId); AStringFromGUID(&uidUserId, szUid, ARRAYSIZE(szUid)); wsprintf(g_szRegRoot, "%.100s\\%.40s", c_szRegRoot, szUid); return g_szRegRoot; } else { Assert(FALSE); } } return NULL; } void _MakeDefaultFirstUser() { USERINFO nuInfo; TCHAR szUid[255]; MLLoadStringA(idsMainUser, nuInfo.szUsername, CCH_USERNAME_MAX_LENGTH); if (nuInfo.szUsername[0] == 0) { lstrcpy(nuInfo.szUsername, TEXT("Main Identity")); } *nuInfo.szPassword = 0; nuInfo.fUsePassword = false; nuInfo.fPasswordValid = true; _ClaimNextUserId(&nuInfo.uidUserID); MU_CreateUser(&nuInfo); MU_MakeDefaultUser(&nuInfo.uidUserID); MU_SwitchToUser(nuInfo.szUsername); AStringFromGUID(&nuInfo.uidUserID, szUid, ARRAYSIZE(szUid)); wsprintf(g_szRegRoot, "%.100s\\%.40s", c_szRegRoot, szUid); } void FixMissingIdentityNames() { HKEY hSourceSubKey; ULONG ulEnumIndex = 0; DWORD dwStatus, dwSize, dwType, dwValue; BOOL fFound = FALSE; TCHAR szKeyNameBuffer[MAX_PATH]; TCHAR szUsername[CCH_USERNAME_MAX_LENGTH]; if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) == ERROR_SUCCESS) { while (!fFound) { HKEY hkUserKey; if (RegEnumKey(hSourceSubKey, ulEnumIndex++, szKeyNameBuffer,MAXKEYNAME) != ERROR_SUCCESS) break; if (RegOpenKey(hSourceSubKey, szKeyNameBuffer, &hkUserKey) == ERROR_SUCCESS) { dwSize = sizeof(szUsername); dwStatus = RegQueryValueEx(hkUserKey, c_szUsername, NULL, &dwType, (LPBYTE)szUsername, &dwSize); if (ERROR_SUCCESS != dwStatus || 0 == szUsername[0]) { lstrcpy(szUsername, "Main Identity"); dwStatus = RegSetValueEx(hkUserKey, c_szUsername, 0, REG_SZ, (LPBYTE)szUsername, lstrlen(szUsername)+1); } RegCloseKey(hkUserKey); } } RegCloseKey(hSourceSubKey); } } typedef DWORD (STDAPICALLTYPE *PNetWkstaUserGetInfo) (LPWSTR reserved, DWORD level, LPBYTE *bufptr); #if 0 /* _DomainControllerPresent Identities are disabled when the machine they are running on is part of a domain, unless there is a policy to explicitly allow them. This function checks to see if the machine is joined to a domain. */ BOOL _DomainControllerPresent() { static BOOL fInDomain = FALSE; static BOOL fValid = FALSE; HINSTANCE hInst; PNetWkstaUserGetInfo pNetWkstaUserGetInfo; _WKSTA_USER_INFO_1 *pwui1; if (!fValid) { fValid = TRUE; hInst = LoadLibrary(TEXT("NETAPI32.DLL")); if (hInst) { pNetWkstaUserGetInfo = (PNetWkstaUserGetInfo)GetProcAddress(hInst, TEXT("NetWkstaUserGetInfo")); if (pNetWkstaUserGetInfo && (pNetWkstaUserGetInfo(NULL, 1, (LPBYTE*)&pwui1) == NOERROR)) { if (pwui1->wkui1_logon_domain && pwui1->wkui1_logon_server && lstrcmpW(pwui1->wkui1_logon_server, pwui1->wkui1_logon_domain) != 0) { fInDomain = TRUE; } } FreeLibrary(hInst); } } return fInDomain; } #endif /* MU_IdentitiesDisabled Returns if identities is disabled due to a policy or whatever. */ BOOL MU_IdentitiesDisabled() { #ifndef _WIN64 TCHAR szPolicyPath[] = "Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Identities"; HKEY hkey; DWORD dwValue, dwSize; BOOL fLockedDown = FALSE; if (RegOpenKey(HKEY_LOCAL_MACHINE, szPolicyPath, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(DWORD); if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue) fLockedDown = TRUE; RegCloseKey(hkey); } if (!fLockedDown && RegOpenKey(HKEY_CURRENT_USER, szPolicyPath, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(DWORD); if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue) fLockedDown = TRUE; RegCloseKey(hkey); } #ifdef DISABIDENT if (!fLockedDown && RegOpenKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(DWORD); if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue) fLockedDown = TRUE; RegCloseKey(hkey); } #endif //DISABIDENT #if 0 // turned off for now, pending determination of whether we even want to // have this policy if (!fLockedDown && _DomainControllerPresent()) { fLockedDown = TRUE; if (RegOpenKey(HKEY_LOCAL_MACHINE, szPolicyPath, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(DWORD); if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szEnableDCPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue) fLockedDown = FALSE; RegCloseKey(hkey); } if (fLockedDown && RegOpenKey(HKEY_CURRENT_USER, szPolicyPath, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(DWORD); if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_szEnableDCPolicyKey, 0, NULL, (LPBYTE)&dwValue, &dwSize) && 1 == dwValue) fLockedDown = FALSE; RegCloseKey(hkey); } } #endif return fLockedDown; #else // _WIN64 return(TRUE); #endif // _WIN64 } static GUID g_uidLoginOption; static BOOLEAN g_uidLoginOptionSet; void _ResetRememberedLoginOption(void) { g_uidLoginOption = GUID_NULL; g_uidLoginOptionSet = FALSE; } void _RememberLoginOption(HWND hwndCombo) { LRESULT dFoundItem; TCHAR szUsername[CCH_IDENTITY_NAME_MAX_LENGTH * 2]; GUID uidUser; *szUsername = 0; g_uidLoginOptionSet = TRUE; dFoundItem = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0); SendMessage(hwndCombo, CB_GETLBTEXT, dFoundItem, (LPARAM)szUsername); if (FAILED(MU_UsernameToUserId(szUsername, &uidUser))) g_uidLoginOption = GUID_NULL; else g_uidLoginOption = uidUser; } DWORD MU_GetDefaultOptionIndex(HWND hwndCombo) { GUID uidStart, uidDefault; USERINFO uiDefault; DWORD dwResult = 0; if (MU_GetDefaultUserID(&uidDefault)) { MU_GetUserInfo(&uidDefault, &uiDefault); if (uiDefault.szUsername[0]) { dwResult = (DWORD)SendMessage(hwndCombo, CB_FINDSTRING, 0, (LPARAM)uiDefault.szUsername); } } return dwResult; } DWORD MU_GetLoginOptionIndex(HWND hwndCombo) { GUID uidStart, uidDefault; USERINFO uiLogin; DWORD dwResult = ASK_BEFORE_LOGIN; if (GUID_NULL == g_uidLoginOption) { if (g_uidLoginOptionSet) goto exit; MU_GetLoginOption(&uidStart); } else uidStart = g_uidLoginOption; if (uidStart == GUID_NULL) goto exit; if(!MU_GetUserInfo(&uidStart, &uiLogin)) goto exit; dwResult = (DWORD)SendMessage(hwndCombo, CB_FINDSTRING, 0, (LPARAM)uiLogin.szUsername); exit: return dwResult; } /* MU_GetLoginOption return the user's choice for what should happen when there is no current user */ void MU_GetLoginOption(GUID *puidStartAs) { HKEY hkey; DWORD dwSize; TCHAR szUid[255]; GUID uidUser; ZeroMemory(puidStartAs, sizeof(GUID)); if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(szUid); if (ERROR_SUCCESS != RegQueryValueEx(hkey, c_szLoginAs, 0, NULL, (LPBYTE)szUid, &dwSize)) MU_GetDefaultUserID(puidStartAs); else GUIDFromAString(szUid, puidStartAs); RegCloseKey(hkey); } } /* MU_SetLoginOption return the user's choice for what should happen when there is no current user */ BOOL MU_SetLoginOption(HWND hwndCombo, LRESULT dOption) { HKEY hkey; BOOL fResult = FALSE; TCHAR szUsername[CCH_IDENTITY_NAME_MAX_LENGTH * 2]; TCHAR szUid[255]; GUID uidUser; SendMessage(hwndCombo, CB_GETLBTEXT, dOption, (LPARAM)szUsername); if (dOption == (LRESULT)ASK_BEFORE_LOGIN || FAILED(MU_UsernameToUserId(szUsername, &uidUser))) { ZeroMemory(&uidUser, sizeof(uidUser)); } AStringFromGUID(&uidUser, szUid, sizeof(szUid)); if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hkey) == ERROR_SUCCESS) { fResult = (ERROR_SUCCESS == RegSetValueEx(hkey, c_szLoginAs, 0, REG_SZ, (LPBYTE)szUid, lstrlen(szUid)+1)); RegCloseKey(hkey); } return TRUE; } /* MU_CanEditIdentity Is the current identity allowed to edit the indicated identity's settings? */ BOOL MU_CanEditIdentity(HWND hwndParent, GUID *puidIdentityId) { #ifndef IDENTITY_PASSWORDS return TRUE; #else USERINFO uiCurrent, uiQuery; TCHAR szBuffer[255]; // really ought to be big enough TCHAR szString[255+CCH_USERNAME_MAX_LENGTH]; BOOL fResult = FALSE; PASSWORD_STORE pwStore; ZeroMemory(&uiQuery, sizeof(USERINFO)); if (MU_GetUserInfo(puidIdentityId, &uiQuery)) { if (!uiQuery.fPasswordValid) { MU_ShowErrorMessage(hwndParent, idsPwdNotFound, idsPwdError); return FALSE; } if (uiQuery.szPassword[0] == 0) { return TRUE; } if (MU_GetUserInfo(NULL, &uiCurrent)) { if (uiCurrent.uidUserID == uiQuery.uidUserID) return TRUE; } } else return FALSE; MLLoadStringA(idsConfirmEdit, szBuffer, sizeof(szBuffer)); wsprintf(szString, szBuffer, uiQuery.szUsername); fResult = MU_ConfirmUserPassword(hwndParent, szString, uiQuery.szPassword); return fResult; #endif //IDENTITY_PASSWORDS } static BOOL _DirectoryIdInUse(DWORD dwId) { HKEY hSourceSubKey; ULONG ulEnumIndex = 0; DWORD dwStatus, dwSize, dwType, dwValue; BOOL fFound = FALSE; TCHAR szKeyNameBuffer[MAX_PATH]; if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) == ERROR_SUCCESS) { while (!fFound) { HKEY hkUserKey; if (RegEnumKey(hSourceSubKey, ulEnumIndex++, szKeyNameBuffer,MAXKEYNAME) != ERROR_SUCCESS) break; if (RegOpenKey(hSourceSubKey, szKeyNameBuffer, &hkUserKey) == ERROR_SUCCESS) { dwSize = sizeof(dwValue); dwStatus = RegQueryValueEx(hkUserKey, c_szDirName, NULL, &dwType, (LPBYTE)&dwValue, &dwSize); if (ERROR_SUCCESS == dwStatus && dwValue == dwId) { fFound = TRUE; RegCloseKey(hkUserKey); break; } RegCloseKey(hkUserKey); } } RegCloseKey(hSourceSubKey); } return fFound; } DWORD MU_GenerateDirectoryNameForIdentity(GUID *puidIdentityId) { DWORD dwId, dwRegValue; dwId = puidIdentityId->Data1; while (_DirectoryIdInUse(dwId)) dwId++; return dwId; } HRESULT MU_GetDirectoryIdForIdentity(GUID *puidIdentityId, DWORD *pdwDirId) { TCHAR szRegPath[MAX_PATH]; HKEY hkey; HRESULT hr = E_FAIL; DWORD dwSize, dwStatus, dwValue, dwType; MU_GetRegRootForUserID(puidIdentityId, szRegPath); if (RegOpenKey(HKEY_CURRENT_USER, szRegPath, &hkey) == ERROR_SUCCESS) { dwSize = sizeof(dwValue); dwStatus = RegQueryValueEx(hkey, c_szDirName, NULL, &dwType, (LPBYTE)&dwValue, &dwSize); if (ERROR_SUCCESS == dwStatus) { *pdwDirId = dwValue; hr = S_OK; } else { // try to generate one dwValue = MU_GenerateDirectoryNameForIdentity(puidIdentityId); dwType = REG_DWORD; dwSize = sizeof(dwValue); dwStatus = RegSetValueEx(hkey, c_szDirName, 0, dwType, (LPBYTE)&dwValue, dwSize); if (ERROR_SUCCESS == dwStatus) { *pdwDirId = dwValue; hr = S_OK; } } RegCloseKey(hkey); } return hr; } void _MigratePasswords() { CStringList *psList; int i, iCount = 0; USERINFO uiUser; DWORD dwStatus, dwValue, dwType, dwSize; dwType = REG_DWORD; dwSize = sizeof(DWORD); dwStatus = SHGetValue(HKEY_CURRENT_USER, c_szRegRoot, c_szMigrated5, &dwType, &dwValue, &dwSize); if (dwStatus == ERROR_SUCCESS && dwValue == 1) return; psList = MU_GetUsernameList(); if (psList) { iCount = psList->GetLength(); for (i = 0; i < iCount; i++) { GUID uidUser; if (SUCCEEDED(MU_UsernameToUserId(psList->GetString(i), &uidUser)) && MU_GetUserInfo(&uidUser, &uiUser)) { if (!uiUser.fPasswordValid) { uiUser.fUsePassword = false; *uiUser.szPassword = 0; MU_SetUserInfo(&uiUser); } } } delete psList; } dwValue = 1; SHSetValue(HKEY_CURRENT_USER, c_szRegRoot, c_szMigrated5, REG_DWORD, &dwValue, sizeof(DWORD)); }