2060 lines
55 KiB
C++
2060 lines
55 KiB
C++
|
/*******************************************************
|
||
|
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 <assert.h>
|
||
|
#include "multiutl.h"
|
||
|
#include "strconst.h"
|
||
|
#include "Shlwapi.h"
|
||
|
#include "multiui.h"
|
||
|
#include <shlobj.h>
|
||
|
#include "mluisup.h"
|
||
|
#include <lmwksta.h>
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
|