Source code of Windows XP (NT5)
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.

777 lines
24 KiB

  1. #include "stdafx.h"
  2. #include "userinfo.h"
  3. #pragma hdrstop
  4. /*******************************************************************
  5. CUserInfo implementation
  6. *******************************************************************/
  7. CUserInfo::CUserInfo()
  8. {
  9. m_fHaveExtraUserInfo = FALSE;
  10. m_psid = NULL;
  11. }
  12. CUserInfo::~CUserInfo()
  13. {
  14. if (m_psid != NULL)
  15. LocalFree(m_psid);
  16. ZeroPassword();
  17. }
  18. HRESULT CUserInfo::Reload(BOOL fLoadExtraInfo /* = NULL */)
  19. {
  20. // Initialize the structure and add it to the head of the list
  21. DWORD cchUsername = ARRAYSIZE(m_szUsername);
  22. DWORD cchDomain = ARRAYSIZE(m_szDomain);
  23. if (LookupAccountSid(NULL, m_psid, m_szUsername, &cchUsername, m_szDomain, &cchDomain, &m_sUse))
  24. {
  25. m_fHaveExtraUserInfo = FALSE;
  26. if (fLoadExtraInfo)
  27. GetExtraUserInfo();
  28. SetUserType();
  29. SetAccountDisabled();
  30. return SetLocalGroups();
  31. }
  32. return E_FAIL;
  33. }
  34. HRESULT CUserInfo::SetLocalGroups()
  35. {
  36. TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
  37. ::MakeDomainUserString(m_szDomain, m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
  38. DWORD dwEntriesRead;
  39. DWORD dwTotalEntries;
  40. BOOL fMore = TRUE;
  41. DWORD iNextGroupName = 0;
  42. BOOL fAddElipses = FALSE;
  43. HRESULT hr = S_OK;
  44. while (fMore)
  45. {
  46. LOCALGROUP_USERS_INFO_0* prglgrui0;
  47. NET_API_STATUS status = NetUserGetLocalGroups(NULL, szDomainUser, 0, 0,
  48. (BYTE**) &prglgrui0, 2048,
  49. &dwEntriesRead, &dwTotalEntries);
  50. if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
  51. {
  52. for (DWORD i = 0; i < dwEntriesRead; i++)
  53. {
  54. DWORD iThisGroupName = iNextGroupName;
  55. iNextGroupName += lstrlen(prglgrui0[i].lgrui0_name) + 2;
  56. if (iNextGroupName < (ARRAYSIZE(m_szGroups) - 1))
  57. {
  58. lstrcpy(&m_szGroups[iThisGroupName], prglgrui0[i].lgrui0_name);
  59. lstrcpy(&m_szGroups[iNextGroupName - 2], TEXT("; "));
  60. }
  61. else
  62. {
  63. fAddElipses = TRUE;
  64. if (iThisGroupName + 3 >= (ARRAYSIZE(m_szGroups)))
  65. iThisGroupName -= 3;
  66. lstrcpy(&m_szGroups[iThisGroupName], TEXT("..."));
  67. // No need to read more; we're out o' buffer
  68. fMore = FALSE;
  69. }
  70. }
  71. NetApiBufferFree((void*) prglgrui0);
  72. }
  73. else
  74. {
  75. hr = E_FAIL;
  76. }
  77. if (status != ERROR_MORE_DATA)
  78. {
  79. fMore = FALSE;
  80. }
  81. }
  82. // There is an extra ';' at the end. Nuke it
  83. if (!fAddElipses && ((iNextGroupName - 2) < (ARRAYSIZE(m_szGroups))))
  84. {
  85. m_szGroups[iNextGroupName - 2] = TEXT('\0');
  86. }
  87. // Absolutely guarantee the string ends in a null
  88. m_szGroups[ARRAYSIZE(m_szGroups) - 1] = TEXT('\0');
  89. return hr;
  90. }
  91. HRESULT CUserInfo::Load(PSID psid, BOOL fLoadExtraInfo /* = NULL */)
  92. {
  93. CUserInfo(); // Nuke the record first
  94. // Make a copy of the SID
  95. DWORD cbSid = GetLengthSid(psid);
  96. m_psid = (PSID) LocalAlloc(NULL, cbSid);
  97. if (!m_psid)
  98. return E_OUTOFMEMORY;
  99. CopySid(cbSid, m_psid, psid);
  100. return Reload(fLoadExtraInfo);
  101. }
  102. HRESULT CUserInfo::Create(HWND hwndError, GROUPPSEUDONYM grouppseudonym)
  103. {
  104. NET_API_STATUS status = NERR_Success;
  105. CWaitCursor cur;
  106. HRESULT hr = E_FAIL;
  107. if (m_userType == CUserInfo::LOCALUSER)
  108. {
  109. // Fill in the big, ugly structure containing information about our new user
  110. USER_INFO_2 usri2 = {0};
  111. usri2.usri2_name = T2W(m_szUsername);
  112. // Reveal the password
  113. RevealPassword();
  114. usri2.usri2_password = T2W(m_szPasswordBuffer);
  115. usri2.usri2_priv = USER_PRIV_USER;
  116. usri2.usri2_comment = T2W(m_szComment);
  117. if (m_szPasswordBuffer[0] == TEXT('\0'))
  118. usri2.usri2_flags = UF_NORMAL_ACCOUNT | UF_SCRIPT | UF_PASSWD_NOTREQD;
  119. else
  120. usri2.usri2_flags = UF_NORMAL_ACCOUNT | UF_SCRIPT;
  121. usri2.usri2_full_name = T2W(m_szFullName);
  122. usri2.usri2_acct_expires = TIMEQ_FOREVER;
  123. usri2.usri2_max_storage = USER_MAXSTORAGE_UNLIMITED;
  124. TCHAR szCountryCode[7];
  125. if (0 < GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ICOUNTRY, szCountryCode, ARRAYSIZE(szCountryCode)))
  126. {
  127. usri2.usri2_country_code = (DWORD) StrToLong(szCountryCode);
  128. }
  129. usri2.usri2_code_page = GetACP();
  130. // Create the user
  131. status = NetUserAdd(NULL, 2, (BYTE*) &usri2, NULL);
  132. // Hide the password
  133. HidePassword();
  134. switch (status)
  135. {
  136. case NERR_Success:
  137. hr = S_OK;
  138. break;
  139. case NERR_PasswordTooShort:
  140. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
  141. IDS_USR_CREATE_PASSWORDTOOSHORT_ERROR, MB_ICONERROR | MB_OK);
  142. break;
  143. case NERR_GroupExists:
  144. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
  145. IDS_USR_CREATE_GROUPEXISTS_ERROR, MB_ICONERROR | MB_OK);
  146. break;
  147. case NERR_UserExists:
  148. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
  149. IDS_USR_CREATE_USEREXISTS_ERROR, MB_ICONERROR | MB_OK,
  150. m_szUsername);
  151. break;
  152. default:
  153. {
  154. TCHAR szMessage[512];
  155. if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) status, 0, szMessage, ARRAYSIZE(szMessage), NULL))
  156. LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage));
  157. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION, IDS_USERCREATE_GENERICERROR, MB_ICONERROR | MB_OK, szMessage);
  158. break;
  159. }
  160. }
  161. }
  162. else
  163. {
  164. hr = S_OK; // m_userType == DOMAINUSER or GROUP
  165. }
  166. if (SUCCEEDED(hr))
  167. {
  168. hr = ChangeLocalGroups(hwndError, grouppseudonym);
  169. if (SUCCEEDED(hr))
  170. {
  171. // User type may have been updated by ChangeLocalGroups - // relect that!
  172. SetUserType();
  173. }
  174. }
  175. return hr;
  176. }
  177. HRESULT CUserInfo::Remove()
  178. {
  179. CWaitCursor cur;
  180. if (m_userType == CUserInfo::LOCALUSER)
  181. {
  182. // Try to actually remove this local user (this may fail!)
  183. NET_API_STATUS status = NetUserDel(NULL, m_szUsername);
  184. if (status != NERR_Success)
  185. {
  186. return E_FAIL;
  187. }
  188. }
  189. else
  190. {
  191. // We can only delete local users. For all others the best we can do is
  192. // remove them from all local groups
  193. return RemoveFromLocalGroups();
  194. }
  195. return S_OK;
  196. }
  197. HRESULT CUserInfo::InitializeForNewUser()
  198. {
  199. CUserInfo(); // Nuke the record first
  200. m_fHaveExtraUserInfo = TRUE;
  201. m_sUse = SidTypeUser;
  202. m_userType = LOCALUSER;
  203. return S_OK;
  204. }
  205. HRESULT CUserInfo::RemoveFromLocalGroups()
  206. {
  207. // Create a data structure we'll need to pass to NetLocalGroupxxx functions
  208. TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2];
  209. ::MakeDomainUserString(m_szDomain, m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
  210. LOCALGROUP_MEMBERS_INFO_3 rglgrmi3[] = {{szDomainUser}};
  211. // Try and remove the user/group from ALL local groups. The reason
  212. // for this is the NetUserGetLocalGroups won't work for groups, even
  213. // well-known ones. For instance, it will fail for "Everyone" even
  214. // though "Everyone" may very well belong to local groups.
  215. DWORD_PTR dwResumeHandle = 0;
  216. BOOL fMoreData = TRUE;
  217. while (fMoreData)
  218. {
  219. DWORD dwEntriesRead;
  220. DWORD dwTotalEntries;
  221. LOCALGROUP_INFO_0* plgrpi0 = NULL;
  222. NET_API_STATUS status = NetLocalGroupEnum(NULL, 0, (BYTE**)&plgrpi0, 8192,
  223. &dwEntriesRead, &dwTotalEntries, &dwResumeHandle);
  224. if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
  225. {
  226. for (DWORD i = 0; i < dwEntriesRead; i ++)
  227. {
  228. status = NetLocalGroupDelMembers(NULL, plgrpi0[i].lgrpi0_name, 3,
  229. (BYTE*) rglgrmi3, ARRAYSIZE(rglgrmi3));
  230. }
  231. if (dwEntriesRead == dwTotalEntries)
  232. {
  233. fMoreData = FALSE;
  234. }
  235. NetApiBufferFree(plgrpi0);
  236. }
  237. else
  238. {
  239. fMoreData = FALSE;
  240. }
  241. }
  242. return S_OK;
  243. }
  244. HRESULT CUserInfo::SetUserType()
  245. {
  246. TCHAR szComputerName[MAX_COMPUTERNAME + 1];
  247. DWORD cchComputerName = ARRAYSIZE(szComputerName);
  248. ::GetComputerName(szComputerName, &cchComputerName);
  249. // Figure out what type of user we're talking about
  250. if ((m_sUse == SidTypeWellKnownGroup) || (m_sUse == SidTypeGroup))
  251. {
  252. m_userType = GROUP;
  253. }
  254. else
  255. {
  256. // User type - see if this user is a local one
  257. if ((m_szDomain[0] == TEXT('\0')) ||
  258. (StrCmpI(m_szDomain, szComputerName) == 0))
  259. {
  260. m_userType = LOCALUSER; // Local user
  261. }
  262. else
  263. {
  264. m_userType = DOMAINUSER; // User is a network one
  265. }
  266. }
  267. return S_OK;
  268. }
  269. HRESULT CUserInfo::SetAccountDisabled()
  270. {
  271. m_fAccountDisabled = FALSE;
  272. USER_INFO_1* pusri1 = NULL;
  273. NET_API_STATUS status = NetUserGetInfo(NULL, T2W(m_szUsername), 1, (BYTE**)&pusri1);
  274. if (NERR_Success == status)
  275. {
  276. if (pusri1->usri1_flags & UF_ACCOUNTDISABLE)
  277. {
  278. m_fAccountDisabled = TRUE;
  279. }
  280. NetApiBufferFree(pusri1);
  281. }
  282. return S_OK;
  283. }
  284. HRESULT CUserInfo::GetExtraUserInfo()
  285. {
  286. USES_CONVERSION;
  287. CWaitCursor cur;
  288. if (!m_fHaveExtraUserInfo)
  289. {
  290. NET_API_STATUS status;
  291. USER_INFO_11* pusri11 = NULL;
  292. // Even if we fail to the info, we only want to try once since it may take a long time
  293. m_fHaveExtraUserInfo = TRUE;
  294. // Get the name of the domain's DC if we aren't talking about a local user
  295. #ifdef _0 // Turns out this is REALLY slow to fail if the DsGetDcName call fails
  296. if (m_userType != LOCALUSER)
  297. {
  298. DOMAIN_CONTROLLER_INFO* pDCInfo;
  299. DWORD dwErr = DsGetDcName(NULL, m_szDomain, NULL, NULL, DS_IS_FLAT_NAME, &pDCInfo);
  300. if (dwErr != NO_ERROR)
  301. return E_FAIL;
  302. // Get the user's detailed information (we really need full name and comment)
  303. // Need to use level 11 here since this allows a domain user to query their
  304. // information
  305. status = NetUserGetInfo(T2W(pDCInfo->DomainControllerName), T2W(m_szUsername), 11, (BYTE**)&pusri11);
  306. NetApiBufferFree(pDCInfo);
  307. }
  308. else
  309. #endif //0
  310. {
  311. status = NetUserGetInfo(NULL, T2W(m_szUsername), 11, (BYTE**)&pusri11);
  312. }
  313. if (status != NERR_Success)
  314. return E_FAIL;
  315. lstrcpyn(m_szComment, W2T(pusri11->usri11_comment), ARRAYSIZE(m_szComment));
  316. lstrcpyn(m_szFullName, W2T(pusri11->usri11_full_name), ARRAYSIZE(m_szFullName));
  317. NetApiBufferFree(pusri11);
  318. }
  319. return S_OK;
  320. }
  321. // ChangeLocalGroups
  322. // Removes the specified user from all current local groups and adds them to the
  323. // SINGLE local group specified in pUserInfo->szGroups
  324. HRESULT CUserInfo::ChangeLocalGroups(HWND hwndError, GROUPPSEUDONYM grouppseudonym)
  325. {
  326. // First, remove the user from all existing local groups
  327. HRESULT hr = RemoveFromLocalGroups();
  328. if (SUCCEEDED(hr))
  329. {
  330. TCHAR szDomainAndUser[MAX_USER + MAX_DOMAIN + 2];
  331. ::MakeDomainUserString(m_szDomain, m_szUsername, szDomainAndUser, ARRAYSIZE(szDomainAndUser));
  332. // Create a data structure we'll need to pass to NetLocalGroupxxx functions
  333. LOCALGROUP_MEMBERS_INFO_3 rglgrmi3[] = {{szDomainAndUser}};
  334. // Now add the user to the SINGLE localgroup that should be specified in
  335. // m_szGroups; Assert this is the case!
  336. NET_API_STATUS status = NetLocalGroupAddMembers(NULL, T2W(m_szGroups), 3,
  337. (BYTE*) rglgrmi3, ARRAYSIZE(rglgrmi3));
  338. if (status == NERR_Success)
  339. {
  340. // We may now need to get the user's SID. This happens if we are
  341. // changing local groups for a domain user and we couldn't read their
  342. // SID since they weren't in the local SAM.
  343. DWORD cchDomain = ARRAYSIZE(m_szDomain);
  344. hr = ::AttemptLookupAccountName(szDomainAndUser, &m_psid, m_szDomain, &cchDomain, &m_sUse);
  345. if (FAILED(hr))
  346. {
  347. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
  348. IDS_USR_CREATE_MISC_ERROR, MB_ICONERROR | MB_OK);
  349. }
  350. }
  351. else
  352. {
  353. switch(status)
  354. {
  355. case ERROR_NO_SUCH_MEMBER:
  356. {
  357. switch (grouppseudonym)
  358. {
  359. case RESTRICTED:
  360. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
  361. IDS_BADRESTRICTEDUSER, MB_ICONERROR | MB_OK, szDomainAndUser);
  362. break;
  363. case STANDARD:
  364. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
  365. IDS_BADSTANDARDUSER, MB_ICONERROR | MB_OK, szDomainAndUser);
  366. break;
  367. case USEGROUPNAME:
  368. default:
  369. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
  370. IDS_USR_CHANGEGROUP_ERR, MB_ICONERROR | MB_OK, szDomainAndUser, m_szGroups);
  371. break;
  372. }
  373. break;
  374. }
  375. default:
  376. {
  377. TCHAR szMessage[512];
  378. if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) status, 0, szMessage, ARRAYSIZE(szMessage), NULL))
  379. LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage));
  380. ::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION, IDS_ERR_ADDUSER, MB_ICONERROR | MB_OK, szMessage);
  381. }
  382. }
  383. hr = E_FAIL;
  384. }
  385. }
  386. return hr;;
  387. }
  388. HRESULT CUserInfo::UpdateUsername(LPTSTR pszNewUsername)
  389. {
  390. CWaitCursor cur;
  391. USER_INFO_0 usri0;
  392. usri0.usri0_name = T2W(pszNewUsername);
  393. DWORD dwErr;
  394. NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 0, (BYTE*) &usri0, &dwErr);
  395. if (status != NERR_Success)
  396. return E_FAIL;
  397. lstrcpyn(m_szUsername, pszNewUsername, ARRAYSIZE(m_szUsername));
  398. return S_OK;
  399. }
  400. HRESULT CUserInfo::UpdateFullName(LPTSTR pszFullName)
  401. {
  402. CWaitCursor cur;
  403. USER_INFO_1011 usri1011;
  404. usri1011.usri1011_full_name = T2W(pszFullName);
  405. DWORD dwErr;
  406. NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1011, (BYTE*) &usri1011, &dwErr);
  407. if (status != NERR_Success)
  408. return E_FAIL;
  409. lstrcpyn(m_szFullName, pszFullName, ARRAYSIZE(m_szFullName));
  410. return S_OK;
  411. }
  412. HRESULT CUserInfo::UpdatePassword(BOOL* pfBadPWFormat)
  413. {
  414. CWaitCursor cur;
  415. RevealPassword();
  416. USER_INFO_1003 usri1003;
  417. usri1003.usri1003_password = T2W(m_szPasswordBuffer);
  418. DWORD dwErr;
  419. NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1003, (BYTE*)&usri1003, &dwErr);
  420. ZeroPassword(); // Kill the password
  421. if (pfBadPWFormat != NULL)
  422. *pfBadPWFormat = (status == NERR_PasswordTooShort);
  423. return (status == NERR_Success) ? S_OK:E_FAIL;
  424. }
  425. HRESULT CUserInfo::UpdateGroup(HWND hwndError, LPTSTR pszGroup, GROUPPSEUDONYM grouppseudonym)
  426. {
  427. CWaitCursor cur;
  428. // Save the old group before we change it
  429. TCHAR szOldGroups[MAX_GROUP * 2 + 3];
  430. lstrcpyn(szOldGroups, m_szGroups, ARRAYSIZE(szOldGroups));
  431. // Try to change the local group
  432. lstrcpyn(m_szGroups, pszGroup, ARRAYSIZE(m_szGroups));
  433. HRESULT hr = ChangeLocalGroups(hwndError, grouppseudonym);
  434. if (FAILED(hr))
  435. lstrcpyn(m_szGroups, szOldGroups, ARRAYSIZE(m_szGroups)); // Restore the old group in case of failure
  436. return hr;
  437. }
  438. HRESULT CUserInfo::UpdateDescription(LPTSTR pszDescription)
  439. {
  440. CWaitCursor cur;
  441. USER_INFO_1007 usri1007;
  442. usri1007.usri1007_comment = T2W(pszDescription);
  443. DWORD dwErr;
  444. NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1007, (BYTE*) &usri1007, &dwErr);
  445. if (status != NERR_Success)
  446. return E_FAIL;
  447. lstrcpyn(m_szComment, pszDescription, ARRAYSIZE(m_szComment));
  448. return S_OK;
  449. }
  450. void CUserInfo::HidePassword()
  451. {
  452. m_Seed = 0;
  453. RtlInitUnicodeString(&m_Password, m_szPasswordBuffer);
  454. RtlRunEncodeUnicodeString(&m_Seed, &m_Password);
  455. }
  456. void CUserInfo::RevealPassword()
  457. {
  458. RtlRunDecodeUnicodeString(m_Seed, &m_Password);
  459. }
  460. void CUserInfo::ZeroPassword()
  461. {
  462. ZeroMemory(m_szPasswordBuffer, ARRAYSIZE(m_szPasswordBuffer));
  463. }
  464. /*******************************************************************
  465. CUserListLoader implementation
  466. *******************************************************************/
  467. CUserListLoader::CUserListLoader()
  468. {
  469. m_hInitDoneEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  470. }
  471. CUserListLoader::~CUserListLoader()
  472. {
  473. EndInitNow();
  474. WaitForSingleObject(m_hInitDoneEvent, INFINITE);
  475. }
  476. BOOL CUserListLoader::HasUserBeenAdded(PSID psid)
  477. {
  478. // Walk the user list looking for a given username and domain
  479. CUserInfo* pUserInfo = NULL;
  480. BOOL fFound = FALSE;
  481. for (int i = 0; i < m_dpaAddedUsers.GetPtrCount(); i ++)
  482. {
  483. pUserInfo = m_dpaAddedUsers.GetPtr(i);
  484. if (pUserInfo->m_psid && psid && EqualSid(pUserInfo->m_psid, psid))
  485. {
  486. fFound = TRUE;
  487. break;
  488. }
  489. }
  490. return fFound;
  491. }
  492. HRESULT CUserListLoader::Initialize(HWND hwndUserListPage)
  493. {
  494. USES_CONVERSION;
  495. // Tell any existing init thread to exit and wait for it to do so
  496. m_fEndInitNow = TRUE;
  497. WaitForSingleObject(m_hInitDoneEvent, INFINITE);
  498. ResetEvent(m_hInitDoneEvent);
  499. m_fEndInitNow = FALSE;
  500. m_hwndUserListPage = hwndUserListPage;
  501. // Launch the initialize thread
  502. DWORD InitThreadId;
  503. HANDLE hInitThread = CreateThread(NULL, 0, CUserListLoader::InitializeThread, (LPVOID) this, 0, &InitThreadId);
  504. if (hInitThread == NULL)
  505. return E_FAIL;
  506. CloseHandle(hInitThread); // Let this thread go about his/her merry way
  507. return S_OK;
  508. }
  509. HRESULT CUserListLoader::UpdateFromLocalGroup(LPWSTR szLocalGroup)
  510. {
  511. USES_CONVERSION;
  512. DWORD_PTR dwResumeHandle = 0;
  513. HRESULT hr = S_OK;
  514. BOOL fBreakLoop = FALSE;
  515. while(!fBreakLoop)
  516. {
  517. LOCALGROUP_MEMBERS_INFO_0* prgMembersInfo;
  518. DWORD dwEntriesRead = 0;
  519. DWORD dwTotalEntries = 0;
  520. NET_API_STATUS status = NetLocalGroupGetMembers(NULL, szLocalGroup, 0, (BYTE**) &prgMembersInfo,
  521. 8192, &dwEntriesRead,
  522. &dwTotalEntries, &dwResumeHandle);
  523. if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
  524. {
  525. // for all the members in the structure, lets add them
  526. DWORD iMember;
  527. for (iMember = 0; ((iMember < dwEntriesRead) && (!m_fEndInitNow)); iMember ++)
  528. {
  529. hr = AddUserInformation(prgMembersInfo[iMember].lgrmi0_sid);
  530. }
  531. NetApiBufferFree((BYTE*) prgMembersInfo);
  532. // See if we can avoid calling NetLocalGroupGetMembers again
  533. fBreakLoop = ((dwEntriesRead == dwTotalEntries) || m_fEndInitNow);
  534. }
  535. else
  536. {
  537. fBreakLoop = TRUE;
  538. hr = E_FAIL;
  539. }
  540. }
  541. return hr;
  542. }
  543. HRESULT CUserListLoader::AddUserInformation(PSID psid)
  544. {
  545. // Only add this user if we haven't already
  546. if (!HasUserBeenAdded(psid))
  547. {
  548. CUserInfo *pUserInfo = new CUserInfo;
  549. if (!pUserInfo)
  550. return E_OUTOFMEMORY;
  551. if (SUCCEEDED(pUserInfo->Load(psid, FALSE)))
  552. {
  553. PostMessage(m_hwndUserListPage, WM_ADDUSERTOLIST, (WPARAM) FALSE, (LPARAM)pUserInfo);
  554. m_dpaAddedUsers.AppendPtr(pUserInfo); // Remember we've added this user
  555. }
  556. }
  557. return S_OK;
  558. }
  559. DWORD CUserListLoader::InitializeThread(LPVOID pvoid)
  560. {
  561. CUserListLoader *pthis = (CUserListLoader*)pvoid;
  562. // First delete any old list
  563. PostMessage(GetDlgItem(pthis->m_hwndUserListPage, IDC_USER_LIST), LVM_DELETEALLITEMS, 0, 0);
  564. // Create a list of adready-added users so we don't add a user twice
  565. // if they're in multiple local groups
  566. if (pthis->m_dpaAddedUsers.Create(8))
  567. {
  568. // Read each local group
  569. DWORD_PTR dwResumeHandle = 0;
  570. BOOL fBreakLoop = FALSE;
  571. while (!fBreakLoop)
  572. {
  573. DWORD dwEntriesRead = 0;
  574. DWORD dwTotalEntries = 0;
  575. LOCALGROUP_INFO_1* prgGroupInfo;
  576. NET_API_STATUS status = NetLocalGroupEnum(NULL, 1, (BYTE**) &prgGroupInfo,
  577. 8192, &dwEntriesRead, &dwTotalEntries,
  578. &dwResumeHandle);
  579. if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
  580. {
  581. // We got some local groups - add information for all users in these local groups to our list
  582. DWORD iGroup;
  583. for (iGroup = 0; ((iGroup < dwEntriesRead) && (!pthis->m_fEndInitNow)); iGroup ++)
  584. {
  585. pthis->UpdateFromLocalGroup(prgGroupInfo[iGroup].lgrpi1_name);
  586. }
  587. NetApiBufferFree((BYTE*) prgGroupInfo);
  588. // Maybe we don't have to try NetLocalGroupEnum again (if we got all the groups)
  589. fBreakLoop = ((dwEntriesRead == dwTotalEntries) || pthis->m_fEndInitNow);
  590. }
  591. else
  592. {
  593. fBreakLoop = TRUE;
  594. }
  595. }
  596. // Its okay to orphan any CUserInfo pointers stored here; they'll be
  597. // released when the ulistpg exits or reinits.
  598. pthis->m_dpaAddedUsers.Destroy();
  599. }
  600. SetEvent(pthis->m_hInitDoneEvent);
  601. SetCursor(LoadCursor(NULL, IDC_ARROW));
  602. return 0;
  603. }
  604. // User utility functions
  605. BOOL UserAlreadyHasPermission(CUserInfo* pUserInfo, HWND hwndMsgParent)
  606. {
  607. TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
  608. ::MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
  609. BOOL fHasPermission = FALSE;
  610. // See if this user is already in local groups on this machine
  611. DWORD dwEntriesRead, dwIgnore2;
  612. LOCALGROUP_USERS_INFO_0* plgrui0 = NULL;
  613. if (NERR_Success == NetUserGetLocalGroups(NULL, szDomainUser, 0, 0,
  614. (LPBYTE*)&plgrui0, 8192,
  615. &dwEntriesRead, &dwIgnore2))
  616. {
  617. fHasPermission = (0 != dwEntriesRead);
  618. NetApiBufferFree((LPVOID) plgrui0);
  619. }
  620. if ((NULL != hwndMsgParent) && (fHasPermission))
  621. {
  622. // Display an error; the user doesn't have permission
  623. TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
  624. MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername,
  625. szDomainUser, ARRAYSIZE(szDomainUser));
  626. DisplayFormatMessage(hwndMsgParent, IDS_USR_NEWUSERWIZARD_CAPTION,
  627. IDS_USR_CREATE_USEREXISTS_ERROR, MB_OK | MB_ICONINFORMATION,
  628. szDomainUser);
  629. }
  630. return fHasPermission;
  631. }