You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
780 lines
25 KiB
780 lines
25 KiB
#include "stdafx.h"
|
|
#include "userinfo.h"
|
|
#pragma hdrstop
|
|
|
|
/*******************************************************************
|
|
CUserInfo implementation
|
|
*******************************************************************/
|
|
|
|
CUserInfo::CUserInfo()
|
|
{
|
|
m_fHaveExtraUserInfo = FALSE;
|
|
m_psid = NULL;
|
|
}
|
|
|
|
CUserInfo::~CUserInfo()
|
|
{
|
|
if (m_psid != NULL)
|
|
LocalFree(m_psid);
|
|
|
|
ZeroPassword();
|
|
}
|
|
|
|
HRESULT CUserInfo::Reload(BOOL fLoadExtraInfo /* = NULL */)
|
|
{
|
|
// Initialize the structure and add it to the head of the list
|
|
DWORD cchUsername = ARRAYSIZE(m_szUsername);
|
|
DWORD cchDomain = ARRAYSIZE(m_szDomain);
|
|
|
|
if (LookupAccountSid(NULL, m_psid, m_szUsername, &cchUsername, m_szDomain, &cchDomain, &m_sUse))
|
|
{
|
|
m_fHaveExtraUserInfo = FALSE;
|
|
if (fLoadExtraInfo)
|
|
GetExtraUserInfo();
|
|
|
|
SetUserType();
|
|
SetAccountDisabled();
|
|
return SetLocalGroups();
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CUserInfo::SetLocalGroups()
|
|
{
|
|
TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
|
|
::MakeDomainUserString(m_szDomain, m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
|
|
|
|
DWORD dwEntriesRead;
|
|
DWORD dwTotalEntries;
|
|
BOOL fMore = TRUE;
|
|
DWORD iNextGroupName = 0;
|
|
BOOL fAddElipses = FALSE;
|
|
|
|
HRESULT hr = S_OK;
|
|
while (fMore)
|
|
{
|
|
LOCALGROUP_USERS_INFO_0* prglgrui0;
|
|
NET_API_STATUS status = NetUserGetLocalGroups(NULL, szDomainUser, 0, 0,
|
|
(BYTE**) &prglgrui0, 2048,
|
|
&dwEntriesRead, &dwTotalEntries);
|
|
|
|
if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
|
|
{
|
|
for (DWORD i = 0; i < dwEntriesRead; i++)
|
|
{
|
|
DWORD iThisGroupName = iNextGroupName;
|
|
iNextGroupName += lstrlen(prglgrui0[i].lgrui0_name) + 2;
|
|
|
|
if (iNextGroupName < (ARRAYSIZE(m_szGroups) - 1))
|
|
{
|
|
lstrcpy(&m_szGroups[iThisGroupName], prglgrui0[i].lgrui0_name);
|
|
lstrcpy(&m_szGroups[iNextGroupName - 2], TEXT("; "));
|
|
}
|
|
else
|
|
{
|
|
fAddElipses = TRUE;
|
|
if (iThisGroupName + 3 >= (ARRAYSIZE(m_szGroups)))
|
|
iThisGroupName -= 3;
|
|
|
|
lstrcpy(&m_szGroups[iThisGroupName], TEXT("..."));
|
|
|
|
// No need to read more; we're out o' buffer
|
|
fMore = FALSE;
|
|
}
|
|
}
|
|
NetApiBufferFree((void*) prglgrui0);
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (status != ERROR_MORE_DATA)
|
|
{
|
|
fMore = FALSE;
|
|
}
|
|
}
|
|
|
|
// There is an extra ';' at the end. Nuke it
|
|
if (!fAddElipses && ((iNextGroupName - 2) < (ARRAYSIZE(m_szGroups))))
|
|
{
|
|
m_szGroups[iNextGroupName - 2] = TEXT('\0');
|
|
}
|
|
|
|
// Absolutely guarantee the string ends in a null
|
|
m_szGroups[ARRAYSIZE(m_szGroups) - 1] = TEXT('\0');
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CUserInfo::Load(PSID psid, BOOL fLoadExtraInfo /* = NULL */)
|
|
{
|
|
CUserInfo(); // Nuke the record first
|
|
|
|
// Make a copy of the SID
|
|
DWORD cbSid = GetLengthSid(psid);
|
|
m_psid = (PSID) LocalAlloc(NULL, cbSid);
|
|
if (!m_psid)
|
|
return E_OUTOFMEMORY;
|
|
|
|
CopySid(cbSid, m_psid, psid);
|
|
return Reload(fLoadExtraInfo);
|
|
}
|
|
|
|
HRESULT CUserInfo::Create(HWND hwndError, GROUPPSEUDONYM grouppseudonym)
|
|
{
|
|
NET_API_STATUS status = NERR_Success;
|
|
|
|
CWaitCursor cur;
|
|
|
|
HRESULT hr = E_FAIL;
|
|
if (m_userType == CUserInfo::LOCALUSER)
|
|
{
|
|
// Fill in the big, ugly structure containing information about our new user
|
|
USER_INFO_2 usri2 = {0};
|
|
usri2.usri2_name = T2W(m_szUsername);
|
|
|
|
// Reveal the password
|
|
RevealPassword();
|
|
|
|
usri2.usri2_password = T2W(m_szPasswordBuffer);
|
|
usri2.usri2_priv = USER_PRIV_USER;
|
|
usri2.usri2_comment = T2W(m_szComment);
|
|
|
|
if (m_szPasswordBuffer[0] == TEXT('\0'))
|
|
usri2.usri2_flags = UF_NORMAL_ACCOUNT | UF_SCRIPT | UF_PASSWD_NOTREQD;
|
|
else
|
|
usri2.usri2_flags = UF_NORMAL_ACCOUNT | UF_SCRIPT;
|
|
|
|
usri2.usri2_full_name = T2W(m_szFullName);
|
|
usri2.usri2_acct_expires = TIMEQ_FOREVER;
|
|
usri2.usri2_max_storage = USER_MAXSTORAGE_UNLIMITED;
|
|
|
|
TCHAR szCountryCode[7];
|
|
if (0 < GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ICOUNTRY, szCountryCode, ARRAYSIZE(szCountryCode)))
|
|
{
|
|
usri2.usri2_country_code = (DWORD) StrToLong(szCountryCode);
|
|
}
|
|
|
|
usri2.usri2_code_page = GetACP();
|
|
|
|
// Create the user
|
|
status = NetUserAdd(NULL, 2, (BYTE*) &usri2, NULL);
|
|
|
|
// Hide the password
|
|
HidePassword();
|
|
|
|
switch (status)
|
|
{
|
|
case NERR_Success:
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case NERR_PasswordTooShort:
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
|
|
IDS_USR_CREATE_PASSWORDTOOSHORT_ERROR, MB_ICONERROR | MB_OK);
|
|
|
|
break;
|
|
case NERR_GroupExists:
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
|
|
IDS_USR_CREATE_GROUPEXISTS_ERROR, MB_ICONERROR | MB_OK);
|
|
break;
|
|
|
|
case NERR_UserExists:
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
|
|
IDS_USR_CREATE_USEREXISTS_ERROR, MB_ICONERROR | MB_OK,
|
|
m_szUsername);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
TCHAR szMessage[512];
|
|
|
|
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) status, 0, szMessage, ARRAYSIZE(szMessage), NULL))
|
|
LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage));
|
|
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION, IDS_USERCREATE_GENERICERROR, MB_ICONERROR | MB_OK, szMessage);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK; // m_userType == DOMAINUSER or GROUP
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ChangeLocalGroups(hwndError, grouppseudonym);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// User type may have been updated by ChangeLocalGroups - // relect that!
|
|
SetUserType();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CUserInfo::Remove()
|
|
{
|
|
CWaitCursor cur;
|
|
if (m_userType == CUserInfo::LOCALUSER)
|
|
{
|
|
// Try to actually remove this local user (this may fail!)
|
|
|
|
NET_API_STATUS status = NetUserDel(NULL, m_szUsername);
|
|
if (status != NERR_Success)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We can only delete local users. For all others the best we can do is
|
|
// remove them from all local groups
|
|
|
|
return RemoveFromLocalGroups();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserInfo::InitializeForNewUser()
|
|
{
|
|
CUserInfo(); // Nuke the record first
|
|
|
|
m_fHaveExtraUserInfo = TRUE;
|
|
m_sUse = SidTypeUser;
|
|
m_userType = LOCALUSER;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserInfo::RemoveFromLocalGroups()
|
|
{
|
|
// Create a data structure we'll need to pass to NetLocalGroupxxx functions
|
|
TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2];
|
|
::MakeDomainUserString(m_szDomain, m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
|
|
LOCALGROUP_MEMBERS_INFO_3 rglgrmi3[] = {{szDomainUser}};
|
|
|
|
// Try and remove the user/group from ALL local groups. The reason
|
|
// for this is the NetUserGetLocalGroups won't work for groups, even
|
|
// well-known ones. For instance, it will fail for "Everyone" even
|
|
// though "Everyone" may very well belong to local groups.
|
|
|
|
DWORD_PTR dwResumeHandle = 0;
|
|
|
|
BOOL fMoreData = TRUE;
|
|
while (fMoreData)
|
|
{
|
|
DWORD dwEntriesRead;
|
|
DWORD dwTotalEntries;
|
|
LOCALGROUP_INFO_0* plgrpi0 = NULL;
|
|
|
|
NET_API_STATUS status = NetLocalGroupEnum(NULL, 0, (BYTE**)&plgrpi0, 8192,
|
|
&dwEntriesRead, &dwTotalEntries, &dwResumeHandle);
|
|
|
|
if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
|
|
{
|
|
for (DWORD i = 0; i < dwEntriesRead; i ++)
|
|
{
|
|
status = NetLocalGroupDelMembers(NULL, plgrpi0[i].lgrpi0_name, 3,
|
|
(BYTE*) rglgrmi3, ARRAYSIZE(rglgrmi3));
|
|
}
|
|
|
|
if (dwEntriesRead == dwTotalEntries)
|
|
{
|
|
fMoreData = FALSE;
|
|
}
|
|
|
|
NetApiBufferFree(plgrpi0);
|
|
}
|
|
else
|
|
{
|
|
fMoreData = FALSE;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserInfo::SetUserType()
|
|
{
|
|
TCHAR szComputerName[MAX_COMPUTERNAME + 1];
|
|
DWORD cchComputerName = ARRAYSIZE(szComputerName);
|
|
::GetComputerName(szComputerName, &cchComputerName);
|
|
|
|
// Figure out what type of user we're talking about
|
|
|
|
if ((m_sUse == SidTypeWellKnownGroup) || (m_sUse == SidTypeGroup))
|
|
{
|
|
m_userType = GROUP;
|
|
}
|
|
else
|
|
{
|
|
// User type - see if this user is a local one
|
|
if ((m_szDomain[0] == TEXT('\0')) ||
|
|
(StrCmpI(m_szDomain, szComputerName) == 0))
|
|
{
|
|
m_userType = LOCALUSER; // Local user
|
|
}
|
|
else
|
|
{
|
|
m_userType = DOMAINUSER; // User is a network one
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserInfo::SetAccountDisabled()
|
|
{
|
|
m_fAccountDisabled = FALSE;
|
|
|
|
USER_INFO_1* pusri1 = NULL;
|
|
NET_API_STATUS status = NetUserGetInfo(NULL, T2W(m_szUsername), 1, (BYTE**)&pusri1);
|
|
if (NERR_Success == status)
|
|
{
|
|
if (pusri1->usri1_flags & UF_ACCOUNTDISABLE)
|
|
{
|
|
m_fAccountDisabled = TRUE;
|
|
}
|
|
|
|
NetApiBufferFree(pusri1);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserInfo::GetExtraUserInfo()
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
CWaitCursor cur;
|
|
|
|
if (!m_fHaveExtraUserInfo)
|
|
{
|
|
NET_API_STATUS status;
|
|
USER_INFO_11* pusri11 = NULL;
|
|
|
|
// Even if we fail to the info, we only want to try once since it may take a long time
|
|
m_fHaveExtraUserInfo = TRUE;
|
|
|
|
// Get the name of the domain's DC if we aren't talking about a local user
|
|
#ifdef _0 // Turns out this is REALLY slow to fail if the DsGetDcName call fails
|
|
if (m_userType != LOCALUSER)
|
|
{
|
|
|
|
DOMAIN_CONTROLLER_INFO* pDCInfo;
|
|
DWORD dwErr = DsGetDcName(NULL, m_szDomain, NULL, NULL, DS_IS_FLAT_NAME, &pDCInfo);
|
|
if (dwErr != NO_ERROR)
|
|
return E_FAIL;
|
|
|
|
// Get the user's detailed information (we really need full name and comment)
|
|
// Need to use level 11 here since this allows a domain user to query their
|
|
// information
|
|
|
|
status = NetUserGetInfo(T2W(pDCInfo->DomainControllerName), T2W(m_szUsername), 11, (BYTE**)&pusri11);
|
|
NetApiBufferFree(pDCInfo);
|
|
}
|
|
else
|
|
#endif //0
|
|
{
|
|
status = NetUserGetInfo(NULL, T2W(m_szUsername), 11, (BYTE**)&pusri11);
|
|
}
|
|
|
|
if (status != NERR_Success)
|
|
return E_FAIL;
|
|
|
|
StrCpyN(m_szComment, W2T(pusri11->usri11_comment), ARRAYSIZE(m_szComment));
|
|
StrCpyN(m_szFullName, W2T(pusri11->usri11_full_name), ARRAYSIZE(m_szFullName));
|
|
|
|
NetApiBufferFree(pusri11);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// ChangeLocalGroups
|
|
// Removes the specified user from all current local groups and adds them to the
|
|
// SINGLE local group specified in pUserInfo->szGroups
|
|
HRESULT CUserInfo::ChangeLocalGroups(HWND hwndError, GROUPPSEUDONYM grouppseudonym)
|
|
{
|
|
// First, remove the user from all existing local groups
|
|
HRESULT hr = RemoveFromLocalGroups();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szDomainAndUser[MAX_USER + MAX_DOMAIN + 2];
|
|
::MakeDomainUserString(m_szDomain, m_szUsername, szDomainAndUser, ARRAYSIZE(szDomainAndUser));
|
|
|
|
// Create a data structure we'll need to pass to NetLocalGroupxxx functions
|
|
LOCALGROUP_MEMBERS_INFO_3 rglgrmi3[] = {{szDomainAndUser}};
|
|
|
|
// Now add the user to the SINGLE localgroup that should be specified in
|
|
// m_szGroups; Assert this is the case!
|
|
NET_API_STATUS status = NetLocalGroupAddMembers(NULL, T2W(m_szGroups), 3,
|
|
(BYTE*) rglgrmi3, ARRAYSIZE(rglgrmi3));
|
|
if (status == NERR_Success)
|
|
{
|
|
// We may now need to get the user's SID. This happens if we are
|
|
// changing local groups for a domain user and we couldn't read their
|
|
// SID since they weren't in the local SAM.
|
|
|
|
DWORD cchDomain = ARRAYSIZE(m_szDomain);
|
|
hr = ::AttemptLookupAccountName(szDomainAndUser, &m_psid, m_szDomain, &cchDomain, &m_sUse);
|
|
if (FAILED(hr))
|
|
{
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
|
|
IDS_USR_CREATE_MISC_ERROR, MB_ICONERROR | MB_OK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(status)
|
|
{
|
|
case ERROR_NO_SUCH_MEMBER:
|
|
{
|
|
switch (grouppseudonym)
|
|
{
|
|
case RESTRICTED:
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
|
|
IDS_BADRESTRICTEDUSER, MB_ICONERROR | MB_OK, szDomainAndUser);
|
|
break;
|
|
|
|
case STANDARD:
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
|
|
IDS_BADSTANDARDUSER, MB_ICONERROR | MB_OK, szDomainAndUser);
|
|
break;
|
|
|
|
case USEGROUPNAME:
|
|
default:
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
|
|
IDS_USR_CHANGEGROUP_ERR, MB_ICONERROR | MB_OK, szDomainAndUser, m_szGroups);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
TCHAR szMessage[512];
|
|
|
|
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) status, 0, szMessage, ARRAYSIZE(szMessage), NULL))
|
|
LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage));
|
|
|
|
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION, IDS_ERR_ADDUSER, MB_ICONERROR | MB_OK, szMessage);
|
|
}
|
|
}
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CUserInfo::UpdateUsername(LPTSTR pszNewUsername)
|
|
{
|
|
CWaitCursor cur;
|
|
|
|
USER_INFO_0 usri0;
|
|
usri0.usri0_name = T2W(pszNewUsername);
|
|
|
|
DWORD dwErr;
|
|
NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 0, (BYTE*) &usri0, &dwErr);
|
|
if (status != NERR_Success)
|
|
return E_FAIL;
|
|
|
|
StrCpyN(m_szUsername, pszNewUsername, ARRAYSIZE(m_szUsername));
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserInfo::UpdateFullName(LPTSTR pszFullName)
|
|
{
|
|
CWaitCursor cur;
|
|
|
|
USER_INFO_1011 usri1011;
|
|
usri1011.usri1011_full_name = T2W(pszFullName);
|
|
DWORD dwErr;
|
|
|
|
NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1011, (BYTE*) &usri1011, &dwErr);
|
|
if (status != NERR_Success)
|
|
return E_FAIL;
|
|
|
|
StrCpyN(m_szFullName, pszFullName, ARRAYSIZE(m_szFullName));
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserInfo::UpdatePassword(BOOL* pfBadPWFormat)
|
|
{
|
|
CWaitCursor cur;
|
|
|
|
RevealPassword();
|
|
|
|
USER_INFO_1003 usri1003;
|
|
usri1003.usri1003_password = T2W(m_szPasswordBuffer);
|
|
|
|
DWORD dwErr;
|
|
NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1003, (BYTE*)&usri1003, &dwErr);
|
|
|
|
ZeroPassword(); // Kill the password
|
|
|
|
if (pfBadPWFormat != NULL)
|
|
*pfBadPWFormat = (status == NERR_PasswordTooShort);
|
|
|
|
return (status == NERR_Success) ? S_OK:E_FAIL;
|
|
}
|
|
|
|
HRESULT CUserInfo::UpdateGroup(HWND hwndError, LPTSTR pszGroup, GROUPPSEUDONYM grouppseudonym)
|
|
{
|
|
CWaitCursor cur;
|
|
|
|
// Save the old group before we change it
|
|
TCHAR szOldGroups[MAX_GROUP * 2 + 3];
|
|
StrCpyN(szOldGroups, m_szGroups, ARRAYSIZE(szOldGroups));
|
|
|
|
// Try to change the local group
|
|
StrCpyN(m_szGroups, pszGroup, ARRAYSIZE(m_szGroups));
|
|
HRESULT hr = ChangeLocalGroups(hwndError, grouppseudonym);
|
|
|
|
if (FAILED(hr))
|
|
StrCpyN(m_szGroups, szOldGroups, ARRAYSIZE(m_szGroups)); // Restore the old group in case of failure
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CUserInfo::UpdateDescription(LPTSTR pszDescription)
|
|
{
|
|
CWaitCursor cur;
|
|
|
|
USER_INFO_1007 usri1007;
|
|
usri1007.usri1007_comment = T2W(pszDescription);
|
|
|
|
DWORD dwErr;
|
|
NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1007, (BYTE*) &usri1007, &dwErr);
|
|
|
|
if (status != NERR_Success)
|
|
return E_FAIL;
|
|
|
|
StrCpyN(m_szComment, pszDescription, ARRAYSIZE(m_szComment));
|
|
return S_OK;
|
|
}
|
|
|
|
void CUserInfo::HidePassword()
|
|
{
|
|
m_Seed = 0;
|
|
RtlInitUnicodeString(&m_Password, m_szPasswordBuffer);
|
|
RtlRunEncodeUnicodeString(&m_Seed, &m_Password);
|
|
}
|
|
|
|
void CUserInfo::RevealPassword()
|
|
{
|
|
RtlRunDecodeUnicodeString(m_Seed, &m_Password);
|
|
}
|
|
|
|
void CUserInfo::ZeroPassword()
|
|
{
|
|
SecureZeroMemory(m_szPasswordBuffer, ARRAYSIZE(m_szPasswordBuffer));
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
CUserListLoader implementation
|
|
*******************************************************************/
|
|
|
|
CUserListLoader::CUserListLoader()
|
|
{
|
|
m_hInitDoneEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
}
|
|
|
|
CUserListLoader::~CUserListLoader()
|
|
{
|
|
EndInitNow();
|
|
WaitForSingleObject(m_hInitDoneEvent, INFINITE);
|
|
}
|
|
|
|
BOOL CUserListLoader::HasUserBeenAdded(PSID psid)
|
|
{
|
|
// Walk the user list looking for a given username and domain
|
|
CUserInfo* pUserInfo = NULL;
|
|
BOOL fFound = FALSE;
|
|
for (int i = 0; i < m_dpaAddedUsers.GetPtrCount(); i ++)
|
|
{
|
|
pUserInfo = m_dpaAddedUsers.GetPtr(i);
|
|
if (pUserInfo->m_psid && psid && EqualSid(pUserInfo->m_psid, psid))
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
return fFound;
|
|
}
|
|
|
|
HRESULT CUserListLoader::Initialize(HWND hwndUserListPage)
|
|
{
|
|
if (!m_hInitDoneEvent)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Tell any existing init thread to exit and wait for it to do so
|
|
m_fEndInitNow = TRUE;
|
|
WaitForSingleObject(m_hInitDoneEvent, INFINITE);
|
|
ResetEvent(m_hInitDoneEvent);
|
|
|
|
m_fEndInitNow = FALSE;
|
|
m_hwndUserListPage = hwndUserListPage;
|
|
|
|
// Launch the initialize thread
|
|
DWORD InitThreadId;
|
|
HANDLE hInitThread = CreateThread(NULL, 0, CUserListLoader::InitializeThread, (LPVOID) this, 0, &InitThreadId);
|
|
if (hInitThread == NULL)
|
|
return E_FAIL;
|
|
|
|
CloseHandle(hInitThread); // Let this thread go about his/her merry way
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserListLoader::UpdateFromLocalGroup(LPWSTR szLocalGroup)
|
|
{
|
|
USES_CONVERSION;
|
|
DWORD_PTR dwResumeHandle = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL fBreakLoop = FALSE;
|
|
while(!fBreakLoop)
|
|
{
|
|
LOCALGROUP_MEMBERS_INFO_0* prgMembersInfo;
|
|
DWORD dwEntriesRead = 0;
|
|
DWORD dwTotalEntries = 0;
|
|
|
|
NET_API_STATUS status = NetLocalGroupGetMembers(NULL, szLocalGroup, 0, (BYTE**) &prgMembersInfo,
|
|
8192, &dwEntriesRead,
|
|
&dwTotalEntries, &dwResumeHandle);
|
|
|
|
if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
|
|
{
|
|
// for all the members in the structure, lets add them
|
|
DWORD iMember;
|
|
for (iMember = 0; ((iMember < dwEntriesRead) && (!m_fEndInitNow)); iMember ++)
|
|
{
|
|
hr = AddUserInformation(prgMembersInfo[iMember].lgrmi0_sid);
|
|
}
|
|
|
|
NetApiBufferFree((BYTE*) prgMembersInfo);
|
|
|
|
// See if we can avoid calling NetLocalGroupGetMembers again
|
|
fBreakLoop = ((dwEntriesRead == dwTotalEntries) || m_fEndInitNow);
|
|
}
|
|
else
|
|
{
|
|
fBreakLoop = TRUE;
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CUserListLoader::AddUserInformation(PSID psid)
|
|
{
|
|
// Only add this user if we haven't already
|
|
if (!HasUserBeenAdded(psid))
|
|
{
|
|
CUserInfo *pUserInfo = new CUserInfo;
|
|
if (!pUserInfo)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (SUCCEEDED(pUserInfo->Load(psid, FALSE)))
|
|
{
|
|
PostMessage(m_hwndUserListPage, WM_ADDUSERTOLIST, (WPARAM) FALSE, (LPARAM)pUserInfo);
|
|
m_dpaAddedUsers.AppendPtr(pUserInfo); // Remember we've added this user
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD CUserListLoader::InitializeThread(LPVOID pvoid)
|
|
{
|
|
CUserListLoader *pthis = (CUserListLoader*)pvoid;
|
|
|
|
// First delete any old list
|
|
PostMessage(GetDlgItem(pthis->m_hwndUserListPage, IDC_USER_LIST), LVM_DELETEALLITEMS, 0, 0);
|
|
|
|
// Create a list of adready-added users so we don't add a user twice
|
|
// if they're in multiple local groups
|
|
if (pthis->m_dpaAddedUsers.Create(8))
|
|
{
|
|
// Read each local group
|
|
DWORD_PTR dwResumeHandle = 0;
|
|
|
|
BOOL fBreakLoop = FALSE;
|
|
while (!fBreakLoop)
|
|
{
|
|
DWORD dwEntriesRead = 0;
|
|
DWORD dwTotalEntries = 0;
|
|
LOCALGROUP_INFO_1* prgGroupInfo;
|
|
NET_API_STATUS status = NetLocalGroupEnum(NULL, 1, (BYTE**) &prgGroupInfo,
|
|
8192, &dwEntriesRead, &dwTotalEntries,
|
|
&dwResumeHandle);
|
|
|
|
if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
|
|
{
|
|
// We got some local groups - add information for all users in these local groups to our list
|
|
DWORD iGroup;
|
|
for (iGroup = 0; ((iGroup < dwEntriesRead) && (!pthis->m_fEndInitNow)); iGroup ++)
|
|
{
|
|
pthis->UpdateFromLocalGroup(prgGroupInfo[iGroup].lgrpi1_name);
|
|
}
|
|
|
|
NetApiBufferFree((BYTE*) prgGroupInfo);
|
|
|
|
// Maybe we don't have to try NetLocalGroupEnum again (if we got all the groups)
|
|
fBreakLoop = ((dwEntriesRead == dwTotalEntries) || pthis->m_fEndInitNow);
|
|
}
|
|
else
|
|
{
|
|
fBreakLoop = TRUE;
|
|
}
|
|
}
|
|
|
|
// Its okay to orphan any CUserInfo pointers stored here; they'll be
|
|
// released when the ulistpg exits or reinits.
|
|
pthis->m_dpaAddedUsers.Destroy();
|
|
}
|
|
|
|
SetEvent(pthis->m_hInitDoneEvent);
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
return 0;
|
|
}
|
|
|
|
// User utility functions
|
|
|
|
BOOL UserAlreadyHasPermission(CUserInfo* pUserInfo, HWND hwndMsgParent)
|
|
{
|
|
TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
|
|
::MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
|
|
|
|
BOOL fHasPermission = FALSE;
|
|
|
|
// See if this user is already in local groups on this machine
|
|
DWORD dwEntriesRead, dwIgnore2;
|
|
LOCALGROUP_USERS_INFO_0* plgrui0 = NULL;
|
|
if (NERR_Success == NetUserGetLocalGroups(NULL, szDomainUser, 0, 0,
|
|
(LPBYTE*)&plgrui0, 8192,
|
|
&dwEntriesRead, &dwIgnore2))
|
|
{
|
|
fHasPermission = (0 != dwEntriesRead);
|
|
NetApiBufferFree((LPVOID) plgrui0);
|
|
}
|
|
|
|
if ((NULL != hwndMsgParent) && (fHasPermission))
|
|
{
|
|
// Display an error; the user doesn't have permission
|
|
TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
|
|
MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername,
|
|
szDomainUser, ARRAYSIZE(szDomainUser));
|
|
|
|
DisplayFormatMessage(hwndMsgParent, IDS_USR_NEWUSERWIZARD_CAPTION,
|
|
IDS_USR_CREATE_USEREXISTS_ERROR, MB_OK | MB_ICONINFORMATION,
|
|
szDomainUser);
|
|
}
|
|
|
|
return fHasPermission;
|
|
}
|