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.

3737 lines
103 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Windows NT Directory Service Property Pages
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1999
  7. //
  8. // File: group.cxx
  9. //
  10. // Contents: CDsGroupGenObjPage, the class that implements the group object
  11. // general property page, CDsGrpMembersPage for the group
  12. // membership page, and CDsGrpShlGenPage for the shell group
  13. // general page.
  14. //
  15. // History: 10-April-97 EricB created
  16. //
  17. //-----------------------------------------------------------------------------
  18. #include "pch.h"
  19. #include "proppage.h"
  20. #include "group.h"
  21. #include "qrybase.h"
  22. #define BULK_ADD 1
  23. #ifdef DSADMIN
  24. #define DESCR_IDX 0
  25. #define SAMNAME_IDX 1
  26. #define EMAIL_IDX 2
  27. #define COMMENT_IDX 3
  28. //+----------------------------------------------------------------------------
  29. //
  30. // Member: CDsGroupGenObjPage::CDsGroupGenObjPage
  31. //
  32. //-----------------------------------------------------------------------------
  33. CDsGroupGenObjPage::CDsGroupGenObjPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj,
  34. HWND hNotifyObj, DWORD dwFlags) :
  35. m_pCIcon(NULL),
  36. m_fMixed(TRUE),
  37. m_dwType(0),
  38. m_fTypeWritable(FALSE),
  39. m_fDescrWritable(FALSE),
  40. m_fSamNameWritable(FALSE),
  41. m_fEmailWritable(FALSE),
  42. m_fCommentWritable(FALSE),
  43. m_fTypeDirty(FALSE),
  44. m_fDescrDirty(FALSE),
  45. m_fSamNameDirty(FALSE),
  46. m_fEmailDirty(FALSE),
  47. m_fCommentDirty(FALSE),
  48. CDsPropPageBase(pDsPage, pDataObj, hNotifyObj, dwFlags)
  49. {
  50. TRACE(CDsGroupGenObjPage,CDsGroupGenObjPage);
  51. #ifdef _DEBUG
  52. strcpy(szClass, "CDsGroupGenObjPage");
  53. #endif
  54. }
  55. //+----------------------------------------------------------------------------
  56. //
  57. // Member: CDsGroupGenObjPage::~CDsGroupGenObjPage
  58. //
  59. //-----------------------------------------------------------------------------
  60. CDsGroupGenObjPage::~CDsGroupGenObjPage()
  61. {
  62. TRACE(CDsGroupGenObjPage,~CDsGroupGenObjPage);
  63. }
  64. //+----------------------------------------------------------------------------
  65. //
  66. // Function: CreateGroupGenObjPage
  67. //
  68. // Synopsis: Creates an instance of a page window.
  69. //
  70. //-----------------------------------------------------------------------------
  71. HRESULT
  72. CreateGroupGenObjPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj,
  73. PWSTR pwzADsPath, PWSTR pwzClass, HWND hNotifyObj,
  74. DWORD dwFlags, CDSBasePathsInfo* pBasePathsInfo,
  75. HPROPSHEETPAGE * phPage)
  76. {
  77. TRACE_FUNCTION(CreateGroupGenObjPage);
  78. CDsGroupGenObjPage * pPageObj = new CDsGroupGenObjPage(pDsPage, pDataObj,
  79. hNotifyObj, dwFlags);
  80. CHECK_NULL(pPageObj, return E_OUTOFMEMORY);
  81. pPageObj->Init(pwzADsPath, pwzClass, pBasePathsInfo);
  82. return pPageObj->CreatePage(phPage);
  83. }
  84. //+----------------------------------------------------------------------------
  85. //
  86. // Method: CDsPropPageBase::DlgProc
  87. //
  88. // Synopsis: per-instance dialog proc
  89. //
  90. //-----------------------------------------------------------------------------
  91. LRESULT
  92. CDsGroupGenObjPage::DlgProc(HWND, UINT uMsg, WPARAM wParam, LPARAM lParam)
  93. {
  94. if (uMsg == g_uChangeMsg)
  95. {
  96. OnAttrChanged(wParam);
  97. return TRUE;
  98. }
  99. switch (uMsg)
  100. {
  101. case WM_INITDIALOG:
  102. return InitDlg(lParam);
  103. case WM_NOTIFY:
  104. return OnNotify(wParam, lParam);
  105. case PSM_QUERYSIBLINGS:
  106. OnQuerySiblings(wParam, lParam);
  107. break;
  108. case WM_SHOWWINDOW:
  109. return OnShowWindow();
  110. case WM_SETFOCUS:
  111. return OnSetFocus((HWND)wParam);
  112. case WM_HELP:
  113. return OnHelp((LPHELPINFO)lParam);
  114. case WM_COMMAND:
  115. if (m_fInInit)
  116. {
  117. return TRUE;
  118. }
  119. return(OnCommand(GET_WM_COMMAND_ID(wParam, lParam),
  120. GET_WM_COMMAND_HWND(wParam, lParam),
  121. GET_WM_COMMAND_CMD(wParam, lParam)));
  122. case WM_DESTROY:
  123. return OnDestroy();
  124. default:
  125. return FALSE;
  126. }
  127. return TRUE;
  128. }
  129. //+----------------------------------------------------------------------------
  130. //
  131. // Method: CDsGroupGenObjPage::OnInitDialog
  132. //
  133. // Synopsis: Set the initial control values from the corresponding DS
  134. // attributes.
  135. //
  136. //-----------------------------------------------------------------------------
  137. HRESULT CDsGroupGenObjPage::OnInitDialog(LPARAM)
  138. {
  139. TRACE(CDsGroupGenObjPage,OnInitDialog);
  140. HRESULT hr;
  141. PADS_ATTR_INFO pAttrs = NULL;
  142. DWORD cAttrs = 0;
  143. CWaitCursor Wait;
  144. if (!ADsPropSetHwnd(m_hNotifyObj, m_hPage))
  145. {
  146. m_pWritableAttrs = NULL;
  147. }
  148. PTSTR ptzRDN;
  149. if (!UnicodeToTchar(m_pwszRDName, &ptzRDN))
  150. {
  151. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  152. return S_OK;
  153. }
  154. SetDlgItemText(m_hPage, IDC_CN, ptzRDN);
  155. delete ptzRDN;
  156. //
  157. // Get the icon from the DS and put it on the page.
  158. //
  159. ATTR_DATA ad = {0, 0};
  160. hr = GeneralPageIcon(this, &GenIcon, NULL, 0, &ad, fInit);
  161. CHECK_HRESULT_REPORT(hr, m_hPage, return S_OK);
  162. m_pCIcon = (CDsIconCtrl *)ad.pVoid;
  163. m_fTypeWritable = CheckIfWritable(g_wzGroupType);
  164. m_fDescrWritable = CheckIfWritable(m_rgpAttrMap[DESCR_IDX]->AttrInfo.pszAttrName);
  165. m_fSamNameWritable = CheckIfWritable(m_rgpAttrMap[SAMNAME_IDX]->AttrInfo.pszAttrName);
  166. m_fEmailWritable = CheckIfWritable(m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName);
  167. m_fCommentWritable = CheckIfWritable(m_rgpAttrMap[COMMENT_IDX]->AttrInfo.pszAttrName);
  168. //
  169. // Get description, SAM name, email address, and comment attributes.
  170. //
  171. SendDlgItemMessage(m_hPage, m_rgpAttrMap[DESCR_IDX]->nCtrlID, EM_LIMITTEXT,
  172. m_rgpAttrMap[DESCR_IDX]->nSizeLimit, 0);
  173. SendDlgItemMessage(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, EM_LIMITTEXT,
  174. m_rgpAttrMap[SAMNAME_IDX]->nSizeLimit, 0);
  175. SendDlgItemMessage(m_hPage, m_rgpAttrMap[EMAIL_IDX]->nCtrlID, EM_LIMITTEXT,
  176. m_rgpAttrMap[EMAIL_IDX]->nSizeLimit, 0);
  177. SendDlgItemMessage(m_hPage, m_rgpAttrMap[COMMENT_IDX]->nCtrlID, EM_LIMITTEXT,
  178. m_rgpAttrMap[COMMENT_IDX]->nSizeLimit, 0);
  179. PWSTR rgpwzAttrNames[] = {m_rgpAttrMap[DESCR_IDX]->AttrInfo.pszAttrName,
  180. m_rgpAttrMap[SAMNAME_IDX]->AttrInfo.pszAttrName,
  181. m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName,
  182. m_rgpAttrMap[COMMENT_IDX]->AttrInfo.pszAttrName,
  183. g_wzGroupType};
  184. hr = m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 5, &pAttrs, &cAttrs);
  185. if (!CHECK_ADS_HR_IGNORE_UNFOUND_ATTR(&hr, m_hPage))
  186. {
  187. return S_OK;
  188. }
  189. for (DWORD i = 0; i < cAttrs; i++)
  190. {
  191. dspAssert(pAttrs);
  192. dspAssert(pAttrs[i].pADsValues);
  193. PTSTR ptz;
  194. if (_wcsicmp(pAttrs[i].pszAttrName, m_rgpAttrMap[DESCR_IDX]->AttrInfo.pszAttrName) == 0)
  195. {
  196. // description.
  197. //
  198. if (!UnicodeToTchar(pAttrs[i].pADsValues->CaseIgnoreString, &ptz))
  199. {
  200. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  201. FreeADsMem(pAttrs);
  202. return S_OK;
  203. }
  204. SetDlgItemText(m_hPage, m_rgpAttrMap[DESCR_IDX]->nCtrlID, ptz);
  205. delete ptz;
  206. }
  207. if (_wcsicmp(pAttrs[i].pszAttrName, m_rgpAttrMap[SAMNAME_IDX]->AttrInfo.pszAttrName) == 0)
  208. {
  209. // SAM name.
  210. //
  211. if (!UnicodeToTchar(pAttrs[i].pADsValues->CaseIgnoreString, &ptz))
  212. {
  213. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  214. FreeADsMem(pAttrs);
  215. return S_OK;
  216. }
  217. SetDlgItemText(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, ptz);
  218. delete ptz;
  219. }
  220. if (_wcsicmp(pAttrs[i].pszAttrName, m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName) == 0)
  221. {
  222. // email address.
  223. //
  224. if (!UnicodeToTchar(pAttrs[i].pADsValues->CaseIgnoreString, &ptz))
  225. {
  226. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  227. FreeADsMem(pAttrs);
  228. return S_OK;
  229. }
  230. SetDlgItemText(m_hPage, m_rgpAttrMap[EMAIL_IDX]->nCtrlID, ptz);
  231. delete ptz;
  232. }
  233. if (_wcsicmp(pAttrs[i].pszAttrName, m_rgpAttrMap[COMMENT_IDX]->AttrInfo.pszAttrName) == 0)
  234. {
  235. // comment.
  236. //
  237. if (!UnicodeToTchar(pAttrs[i].pADsValues->CaseIgnoreString, &ptz))
  238. {
  239. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  240. FreeADsMem(pAttrs);
  241. return S_OK;
  242. }
  243. SetDlgItemText(m_hPage, m_rgpAttrMap[COMMENT_IDX]->nCtrlID, ptz);
  244. delete ptz;
  245. }
  246. if (_wcsicmp(pAttrs[i].pszAttrName, g_wzGroupType) == 0)
  247. {
  248. // group type.
  249. //
  250. m_dwType = pAttrs[i].pADsValues->Integer;
  251. }
  252. }
  253. if (pAttrs)
  254. {
  255. FreeADsMem(pAttrs);
  256. }
  257. //
  258. // Get the domain type and set the buttons accordingly.
  259. //
  260. GetDomainMode(this, &m_fMixed);
  261. BOOL Sec = m_dwType & GROUP_TYPE_SECURITY_ENABLED;
  262. CheckDlgButton(m_hPage,
  263. (Sec) ? IDC_RADIO_SEC_ENABLED :
  264. IDC_RADIO_SEC_DISABLED,
  265. BST_CHECKED);
  266. if (m_fMixed)
  267. {
  268. EnableWindow(GetDlgItem(m_hPage, (Sec) ? IDC_RADIO_SEC_DISABLED :
  269. IDC_RADIO_SEC_ENABLED), FALSE);
  270. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_UNIVERSAL), FALSE);
  271. }
  272. UINT id;
  273. if (m_dwType & GROUP_TYPE_ACCOUNT_GROUP)
  274. {
  275. id = IDC_RADIO_ACCOUNT;
  276. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_RESOURCE), FALSE);
  277. }
  278. else
  279. if (m_dwType & GROUP_TYPE_RESOURCE_GROUP)
  280. {
  281. id = IDC_RADIO_RESOURCE;
  282. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_ACCOUNT), FALSE);
  283. if (m_dwType & GROUP_TYPE_BUILTIN_LOCAL_GROUP)
  284. {
  285. TCHAR szLabel[100];
  286. if (!LoadStringReport(IDS_BUILTIN_GROUP, szLabel, 100, m_hPage))
  287. {
  288. hr = E_OUTOFMEMORY;
  289. return S_OK;
  290. }
  291. SetWindowText(GetDlgItem(m_hPage, IDC_RADIO_RESOURCE), szLabel);
  292. }
  293. }
  294. else
  295. if (m_dwType & GROUP_TYPE_UNIVERSAL_GROUP)
  296. {
  297. id = IDC_RADIO_UNIVERSAL;
  298. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_ACCOUNT), m_fMixed ? FALSE : TRUE);
  299. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_RESOURCE), m_fMixed ? FALSE : TRUE);
  300. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_UNIVERSAL), TRUE);
  301. }
  302. else
  303. {
  304. //
  305. // Probably a default but we should never get here anyway
  306. //
  307. id = IDC_RADIO_ACCOUNT;
  308. #if DBG == 1
  309. dspAssert(FALSE && "Unknown group type!");
  310. #endif
  311. }
  312. CheckDlgButton(m_hPage, id, BST_CHECKED);
  313. bool fIsSpecialAccount = false;
  314. IsSpecialAccount (fIsSpecialAccount);
  315. if (!m_fTypeWritable ||
  316. (m_dwType & GROUP_TYPE_BUILTIN_LOCAL_GROUP) ||
  317. fIsSpecialAccount)
  318. {
  319. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_ACCOUNT), FALSE);
  320. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_RESOURCE), FALSE);
  321. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_UNIVERSAL), FALSE);
  322. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_SEC_ENABLED), FALSE);
  323. EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_SEC_DISABLED), FALSE);
  324. }
  325. if (!m_fDescrWritable)
  326. {
  327. SendDlgItemMessage(m_hPage, m_rgpAttrMap[DESCR_IDX]->nCtrlID, EM_SETREADONLY, (WPARAM)TRUE, 0);
  328. }
  329. if (!m_fSamNameWritable)
  330. {
  331. SendDlgItemMessage(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, EM_SETREADONLY, (WPARAM)TRUE, 0);
  332. }
  333. if (!m_fEmailWritable)
  334. {
  335. SendDlgItemMessage(m_hPage, m_rgpAttrMap[EMAIL_IDX]->nCtrlID, EM_SETREADONLY, (WPARAM)TRUE, 0);
  336. }
  337. if (!m_fCommentWritable)
  338. {
  339. SendDlgItemMessage(m_hPage, m_rgpAttrMap[COMMENT_IDX]->nCtrlID, EM_SETREADONLY, (WPARAM)TRUE, 0);
  340. }
  341. return S_OK;
  342. }
  343. //+----------------------------------------------------------------------------
  344. //
  345. // Method: CDsGroupGenObjPage::IsSpecialAccount
  346. //
  347. // Synopsis: Returns true if group RID indicates a special account
  348. //
  349. //-----------------------------------------------------------------------------
  350. HRESULT CDsGroupGenObjPage::IsSpecialAccount(bool& fIsSpecialAccount)
  351. {
  352. //
  353. // Get the group SID. This is a required attribute so bail if not found.
  354. //
  355. PWSTR rgpwzAttrNames[] = {g_wzObjectSID};
  356. PADS_ATTR_INFO pAttrs = NULL;
  357. DWORD cAttrs = 0;
  358. CWaitCursor Wait;
  359. HRESULT hr = m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs);
  360. if (!CHECK_ADS_HR(&hr, m_hPage))
  361. {
  362. return hr;
  363. }
  364. dspAssert(cAttrs);
  365. if (cAttrs != 1)
  366. {
  367. return E_FAIL;
  368. }
  369. dspAssert(pAttrs);
  370. PUCHAR saCount = GetSidSubAuthorityCount(pAttrs->pADsValues->OctetString.lpValue);
  371. DWORD dwGroupRID = *GetSidSubAuthority(pAttrs->pADsValues->OctetString.lpValue, (ULONG)*saCount - 1);
  372. dspDebugOut((DEB_ITRACE, "Group RID = %d\n", dwGroupRID));
  373. // This is the highest special account RID or alias in ntseapi.h
  374. if ( dwGroupRID <= DOMAIN_ALIAS_RID_RAS_SERVERS )
  375. fIsSpecialAccount = true;
  376. FreeADsMem(pAttrs);
  377. return hr;
  378. }
  379. //+----------------------------------------------------------------------------
  380. //
  381. // Method: CDsGroupGenObjPage::OnApply
  382. //
  383. // Synopsis: Handles the Apply notification.
  384. //
  385. //-----------------------------------------------------------------------------
  386. LRESULT
  387. CDsGroupGenObjPage::OnApply(void)
  388. {
  389. TRACE(CDsGroupGenObjPage,OnApply);
  390. HRESULT hr = S_OK;
  391. ADSVALUE ADsValueType = {ADSTYPE_INTEGER, 0};
  392. ADS_ATTR_INFO AttrInfoDesc = m_rgpAttrMap[DESCR_IDX]->AttrInfo;
  393. ADS_ATTR_INFO AttrInfoSAMn = m_rgpAttrMap[SAMNAME_IDX]->AttrInfo;
  394. ADS_ATTR_INFO AttrInfoMail = m_rgpAttrMap[EMAIL_IDX]->AttrInfo;
  395. ADS_ATTR_INFO AttrInfoComm = m_rgpAttrMap[COMMENT_IDX]->AttrInfo;
  396. ADS_ATTR_INFO AttrInfoType = {g_wzGroupType, ADS_ATTR_UPDATE,
  397. ADSTYPE_INTEGER, &ADsValueType, 1};
  398. ADS_ATTR_INFO rgAttrs[5];
  399. DWORD cAttrs = 0;
  400. ADSVALUE ADsValueDesc = {m_rgpAttrMap[DESCR_IDX]->AttrInfo.dwADsType, NULL};
  401. ADSVALUE ADsValueSAMname = {m_rgpAttrMap[SAMNAME_IDX]->AttrInfo.dwADsType, NULL};
  402. ADSVALUE ADsValueComm = {m_rgpAttrMap[COMMENT_IDX]->AttrInfo.dwADsType, NULL};
  403. ADSVALUE ADsValueMail = {m_rgpAttrMap[EMAIL_IDX]->AttrInfo.dwADsType, NULL};
  404. //
  405. // Description.
  406. //
  407. AttrInfoDesc.pADsValues = &ADsValueDesc;
  408. AttrInfoDesc.dwNumValues = 1;
  409. LPTSTR ptsz;
  410. if (m_fDescrDirty)
  411. {
  412. dspAssert(m_fDescrWritable);
  413. ptsz = new TCHAR[m_rgpAttrMap[DESCR_IDX]->nSizeLimit + 1];
  414. CHECK_NULL(ptsz, return -1);
  415. if (GetDlgItemText(m_hPage, m_rgpAttrMap[DESCR_IDX]->nCtrlID,
  416. ptsz, m_rgpAttrMap[DESCR_IDX]->nSizeLimit + 1) == 0)
  417. {
  418. // An empty control means remove the attribute value from the
  419. // object.
  420. //
  421. AttrInfoDesc.dwNumValues = 0;
  422. AttrInfoDesc.pADsValues = NULL;
  423. AttrInfoDesc.dwControlCode = ADS_ATTR_CLEAR;
  424. }
  425. else
  426. {
  427. if (!TcharToUnicode(ptsz, &ADsValueDesc.CaseIgnoreString))
  428. {
  429. delete[] ptsz;
  430. return -1;
  431. }
  432. }
  433. delete[] ptsz;
  434. rgAttrs[cAttrs++] = AttrInfoDesc;
  435. }
  436. //
  437. // SAM name.
  438. //
  439. AttrInfoSAMn.pADsValues = &ADsValueSAMname;
  440. AttrInfoSAMn.dwNumValues = 1;
  441. if (m_fSamNameDirty)
  442. {
  443. dspAssert(m_fSamNameWritable);
  444. ptsz = new TCHAR[m_rgpAttrMap[SAMNAME_IDX]->nSizeLimit + 1];
  445. if (ptsz == NULL)
  446. {
  447. DO_DEL(ADsValueDesc.CaseExactString)
  448. return -1;
  449. }
  450. if (GetDlgItemText(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID,
  451. ptsz, m_rgpAttrMap[SAMNAME_IDX]->nSizeLimit + 1) == 0)
  452. {
  453. ErrMsg (IDS_ERR_DNLEVELNAME_MISSING, m_hPage);
  454. delete[] ptsz;
  455. hr = E_FAIL;
  456. goto Cleanup;
  457. }
  458. else
  459. {
  460. CStr csSAMName = ptsz;
  461. //
  462. // Now check for illegal characters
  463. //
  464. bool bSAMNameChanged = false;
  465. int iFind = csSAMName.FindOneOf(INVALID_ACCOUNT_NAME_CHARS);
  466. if (iFind != -1 && !csSAMName.IsEmpty())
  467. {
  468. PVOID apv[1] = {(LPWSTR)(LPCWSTR)csSAMName};
  469. if (IDYES == SuperMsgBox(m_hPage,
  470. IDS_GROUP_SAMNAME_ILLEGAL,
  471. 0,
  472. MB_YESNO | MB_ICONWARNING,
  473. S_OK,
  474. apv,
  475. 1,
  476. FALSE,
  477. __FILE__,
  478. __LINE__))
  479. {
  480. while (iFind != -1)
  481. {
  482. csSAMName.SetAt(iFind, L'_');
  483. iFind = csSAMName.FindOneOf(INVALID_ACCOUNT_NAME_CHARS);
  484. bSAMNameChanged = true;
  485. }
  486. }
  487. else
  488. {
  489. //
  490. // Set the focus to the edit box and select the text
  491. //
  492. SetFocus(GetDlgItem(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID));
  493. SendDlgItemMessage(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, EM_SETSEL, 0, -1);
  494. delete[] ptsz;
  495. hr = E_FAIL;
  496. goto Cleanup;
  497. }
  498. }
  499. if (bSAMNameChanged)
  500. {
  501. //
  502. // Write the change back to the control
  503. //
  504. SetDlgItemText(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, const_cast<PWSTR>((LPCWSTR)csSAMName));
  505. }
  506. if (!AllocWStr((PWSTR)(PCWSTR)csSAMName, &ADsValueSAMname.CaseIgnoreString))
  507. {
  508. delete[] ptsz;
  509. DO_DEL(ADsValueDesc.CaseExactString)
  510. return -1;
  511. }
  512. }
  513. delete[] ptsz;
  514. rgAttrs[cAttrs++] = AttrInfoSAMn;
  515. }
  516. //
  517. // Email Address.
  518. //
  519. AttrInfoMail.pADsValues = &ADsValueMail;
  520. AttrInfoMail.dwNumValues = 1;
  521. if (m_fEmailWritable)
  522. {
  523. if (!m_fEmailDirty)
  524. {
  525. SendMessage(GetParent(GetHWnd()), PSM_QUERYSIBLINGS,
  526. (WPARAM)m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName,
  527. (LPARAM)GetHWnd());
  528. }
  529. // SendMessage is syncronous. If the sibling page has an updated email
  530. // attribute value, it will get written to this page's edit control
  531. // and the dirty state member will be set. So, check it now rather than
  532. // use a 'else' clause after the above 'if' clause.
  533. //
  534. if (m_fEmailDirty)
  535. {
  536. ptsz = new TCHAR[m_rgpAttrMap[EMAIL_IDX]->nSizeLimit + 1];
  537. if (ptsz == NULL)
  538. {
  539. DO_DEL(ADsValueDesc.CaseExactString)
  540. DO_DEL(ADsValueSAMname.CaseIgnoreString);
  541. return -1;
  542. }
  543. if (GetDlgItemText(m_hPage, m_rgpAttrMap[EMAIL_IDX]->nCtrlID,
  544. ptsz, m_rgpAttrMap[EMAIL_IDX]->nSizeLimit + 1) == 0)
  545. {
  546. AttrInfoMail.dwNumValues = 0;
  547. AttrInfoMail.pADsValues = NULL;
  548. AttrInfoMail.dwControlCode = ADS_ATTR_CLEAR;
  549. }
  550. else
  551. {
  552. if (!TcharToUnicode(ptsz, &ADsValueMail.CaseIgnoreString))
  553. {
  554. delete[] ptsz;
  555. hr = E_OUTOFMEMORY;
  556. goto Cleanup;
  557. }
  558. if (!FValidSMTPAddress(ADsValueMail.CaseIgnoreString))
  559. {
  560. ErrMsg(IDS_INVALID_MAIL_ADDR, GetHWnd());
  561. delete [] ptsz;
  562. hr = E_FAIL;
  563. goto Cleanup;
  564. }
  565. }
  566. delete[] ptsz;
  567. rgAttrs[cAttrs++] = AttrInfoMail;
  568. }
  569. }
  570. //
  571. // Comment.
  572. //
  573. AttrInfoComm.pADsValues = &ADsValueComm;
  574. AttrInfoComm.dwNumValues = 1;
  575. if (m_fCommentDirty)
  576. {
  577. dspAssert(m_fCommentWritable);
  578. ptsz = new TCHAR[m_rgpAttrMap[COMMENT_IDX]->nSizeLimit + 1];
  579. if (ptsz == NULL)
  580. {
  581. DO_DEL(ADsValueDesc.CaseExactString)
  582. DO_DEL(ADsValueSAMname.CaseIgnoreString);
  583. DO_DEL(ADsValueMail.CaseExactString)
  584. return -1;
  585. }
  586. if (GetDlgItemText(m_hPage, m_rgpAttrMap[COMMENT_IDX]->nCtrlID,
  587. ptsz, m_rgpAttrMap[COMMENT_IDX]->nSizeLimit + 1) == 0)
  588. {
  589. AttrInfoComm.dwNumValues = 0;
  590. AttrInfoComm.pADsValues = NULL;
  591. AttrInfoComm.dwControlCode = ADS_ATTR_CLEAR;
  592. }
  593. else
  594. {
  595. if (!TcharToUnicode(ptsz, &ADsValueComm.CaseIgnoreString))
  596. {
  597. delete[] ptsz;
  598. DO_DEL(ADsValueDesc.CaseExactString)
  599. DO_DEL(ADsValueSAMname.CaseIgnoreString);
  600. DO_DEL(ADsValueMail.CaseExactString)
  601. return -1;
  602. }
  603. }
  604. delete[] ptsz;
  605. rgAttrs[cAttrs++] = AttrInfoComm;
  606. }
  607. //
  608. // set the group type flags
  609. //
  610. if (m_fTypeDirty)
  611. {
  612. dspAssert(m_fTypeWritable);
  613. BOOL Account = (IsDlgButtonChecked (m_hPage,IDC_RADIO_ACCOUNT)
  614. == BST_CHECKED);
  615. BOOL Resource = (IsDlgButtonChecked (m_hPage,IDC_RADIO_RESOURCE)
  616. == BST_CHECKED);
  617. BOOL Security = (IsDlgButtonChecked (m_hPage, IDC_RADIO_SEC_ENABLED)
  618. == BST_CHECKED);
  619. if (Security)
  620. {
  621. ADsValueType.Integer = GROUP_TYPE_SECURITY_ENABLED;
  622. }
  623. else
  624. {
  625. if (m_dwType & GROUP_TYPE_SECURITY_ENABLED)
  626. {
  627. TCHAR szTitle[80], szMessage[512];
  628. if (!LoadStringReport(IDS_MSG_TITLE, szTitle, 80, m_hPage))
  629. {
  630. hr = E_OUTOFMEMORY;
  631. goto Cleanup;
  632. }
  633. if (!LoadStringReport(IDS_MSG_DISABLING_SECURITY, szMessage, 512, m_hPage))
  634. {
  635. hr = E_OUTOFMEMORY;
  636. goto Cleanup;
  637. }
  638. LONG iRet = MessageBox(m_hPage, szMessage, szTitle, MB_YESNO |
  639. MB_ICONWARNING);
  640. if (iRet == IDNO)
  641. {
  642. //
  643. // The user declined, so go back to prop sheet.
  644. //
  645. hr = S_FALSE;
  646. goto Cleanup;
  647. }
  648. }
  649. ADsValueType.Integer = 0;
  650. }
  651. if (Resource)
  652. {
  653. ADsValueType.Integer |= GROUP_TYPE_RESOURCE_GROUP;
  654. }
  655. else
  656. {
  657. if (Account)
  658. {
  659. ADsValueType.Integer |= GROUP_TYPE_ACCOUNT_GROUP;
  660. }
  661. else
  662. {
  663. ADsValueType.Integer |= GROUP_TYPE_UNIVERSAL_GROUP;
  664. }
  665. }
  666. rgAttrs[cAttrs++] = AttrInfoType;
  667. }
  668. //
  669. // Write the description, and group type.
  670. //
  671. DWORD cModified;
  672. hr = m_pDsObj->SetObjectAttributes(rgAttrs, cAttrs, &cModified);
  673. if (!CHECK_ADS_HR(&hr, m_hPage))
  674. {
  675. goto Cleanup;
  676. }
  677. m_fTypeDirty = m_fDescrDirty = m_fSamNameDirty = m_fEmailDirty =
  678. m_fCommentDirty = FALSE;
  679. Cleanup:
  680. DO_DEL(ADsValueDesc.CaseExactString)
  681. DO_DEL(ADsValueSAMname.CaseIgnoreString);
  682. DO_DEL(ADsValueMail.CaseExactString)
  683. DO_DEL(ADsValueComm.CaseExactString)
  684. if (hr == S_FALSE)
  685. return PSNRET_INVALID_NOCHANGEPAGE;
  686. else
  687. return (SUCCEEDED(hr)) ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE;
  688. }
  689. //+----------------------------------------------------------------------------
  690. //
  691. // Method: CDsGroupGenObjPage::OnCommand
  692. //
  693. // Synopsis: Handle control notifications.
  694. //
  695. //-----------------------------------------------------------------------------
  696. LRESULT
  697. CDsGroupGenObjPage::OnCommand(int id, HWND hwndCtl, UINT codeNotify)
  698. {
  699. if (m_fInInit)
  700. {
  701. return 0;
  702. }
  703. switch (codeNotify)
  704. {
  705. case BN_CLICKED:
  706. if ((id == IDC_RADIO_UNIVERSAL) ||
  707. (id == IDC_RADIO_RESOURCE) ||
  708. (id == IDC_RADIO_ACCOUNT))
  709. {
  710. int iCheck1, iCheck2;
  711. switch (id)
  712. {
  713. case IDC_RADIO_UNIVERSAL:
  714. iCheck1 = IDC_RADIO_RESOURCE;
  715. iCheck2 = IDC_RADIO_ACCOUNT;
  716. break;
  717. case IDC_RADIO_RESOURCE:
  718. iCheck1 = IDC_RADIO_UNIVERSAL;
  719. iCheck2 = IDC_RADIO_ACCOUNT;
  720. break;
  721. case IDC_RADIO_ACCOUNT:
  722. iCheck1 = IDC_RADIO_UNIVERSAL;
  723. iCheck2 = IDC_RADIO_RESOURCE;
  724. break;
  725. default:
  726. dspAssert(FALSE);
  727. iCheck1 = IDC_RADIO_RESOURCE;
  728. iCheck2 = IDC_RADIO_ACCOUNT;
  729. break;
  730. }
  731. CheckDlgButton(m_hPage, iCheck1, BST_UNCHECKED);
  732. CheckDlgButton(m_hPage, iCheck2, BST_UNCHECKED);
  733. m_fTypeDirty = TRUE;
  734. SetDirty();
  735. }
  736. if ((id == IDC_RADIO_SEC_ENABLED) ||
  737. (id == IDC_RADIO_SEC_DISABLED))
  738. {
  739. CheckDlgButton(m_hPage,
  740. (id == IDC_RADIO_SEC_ENABLED) ?
  741. IDC_RADIO_SEC_DISABLED : IDC_RADIO_SEC_ENABLED,
  742. BST_UNCHECKED);
  743. m_fTypeDirty = TRUE;
  744. SetDirty();
  745. }
  746. break;
  747. case EN_CHANGE:
  748. switch (id)
  749. {
  750. case IDC_EMAIL_EDIT:
  751. m_fEmailDirty = TRUE;
  752. break;
  753. case IDC_DESCRIPTION_EDIT:
  754. m_fDescrDirty = TRUE;
  755. break;
  756. case IDC_SAM_NAME_EDIT:
  757. m_fSamNameDirty = TRUE;
  758. break;
  759. case IDC_EDIT_COMMENT:
  760. m_fCommentDirty = TRUE;
  761. break;
  762. }
  763. break;
  764. }
  765. return CDsPropPageBase::OnCommand(id, hwndCtl, codeNotify);
  766. }
  767. //+----------------------------------------------------------------------------
  768. //
  769. // Method: CDsGroupGenObjPage::OnNotify
  770. //
  771. // Synopsis: Handles list notification messages
  772. //
  773. //-----------------------------------------------------------------------------
  774. LRESULT
  775. CDsGroupGenObjPage::OnNotify(WPARAM wParam, LPARAM lParam)
  776. {
  777. if (m_fInInit)
  778. {
  779. return 0;
  780. }
  781. if (((LPNMHDR)lParam)->code == PSN_SETACTIVE)
  782. {
  783. dspDebugOut((DEB_ITRACE,
  784. "(HWND: %08x) got PSN_SETACTIVE, sending PSM_QUERYSIBLINGS.\n",
  785. GetHWnd()));
  786. SendMessage(GetParent(GetHWnd()), PSM_QUERYSIBLINGS,
  787. (WPARAM)m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName,
  788. (LPARAM)GetHWnd());
  789. }
  790. return CDsPropPageBase::OnNotify(wParam, lParam);
  791. }
  792. //+----------------------------------------------------------------------------
  793. //
  794. // Method: CDsGroupGenObjPage::OnQuerySiblings
  795. //
  796. // Synopsis: Inter-page communications for shared attributes.
  797. //
  798. // lParam == the HWND of the sending window.
  799. // wParam == the name of the attribute whose status is sought.
  800. //
  801. //-----------------------------------------------------------------------------
  802. void
  803. CDsGroupGenObjPage::OnQuerySiblings(WPARAM wParam, LPARAM lParam)
  804. {
  805. PWSTR pwz = NULL;
  806. int cch;
  807. #if DBG == 1
  808. char szBuf[100];
  809. strcpy(szBuf, "(HWND: %08x) got PSM_QUERYSIBLINGS for '%ws'");
  810. #endif
  811. if ((HWND)lParam != GetHWnd())
  812. {
  813. if (m_fEmailDirty && wParam &&
  814. _wcsicmp((PWSTR)wParam,
  815. m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName) == 0)
  816. {
  817. #if DBG == 1
  818. strcat(szBuf, " sending DSPROP_ATTRCHANGED_MSG");
  819. #endif
  820. ADS_ATTR_INFO Attr;
  821. ADSVALUE ADsValue;
  822. cch = (int)SendDlgItemMessage(GetHWnd(),
  823. m_rgpAttrMap[EMAIL_IDX]->nCtrlID,
  824. WM_GETTEXTLENGTH, 0, 0);
  825. pwz = new WCHAR[++cch];
  826. CHECK_NULL_REPORT(pwz, GetHWnd(), return);
  827. Attr.dwNumValues = 1;
  828. Attr.pszAttrName = m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName;
  829. Attr.pADsValues = &ADsValue;
  830. Attr.pADsValues->dwType = m_rgpAttrMap[EMAIL_IDX]->AttrInfo.dwADsType;
  831. Attr.pADsValues->CaseIgnoreString = pwz;
  832. GetDlgItemText(GetHWnd(), m_rgpAttrMap[EMAIL_IDX]->nCtrlID,
  833. Attr.pADsValues->CaseIgnoreString, cch);
  834. SendMessage((HWND)lParam, g_uChangeMsg, (WPARAM)&Attr, 0);
  835. delete pwz;
  836. }
  837. }
  838. #if DBG == 1
  839. else
  840. {
  841. strcat(szBuf, " (it was sent by this page!)");
  842. }
  843. strcat(szBuf, "\n");
  844. dspDebugOut((DEB_ITRACE, szBuf, GetHWnd(), wParam));
  845. #endif
  846. }
  847. //+----------------------------------------------------------------------------
  848. //
  849. // Method: CDsGroupGenObjPage::OnAttrChanged
  850. //
  851. // Synopsis: Inter-page communications for shared attributes.
  852. //
  853. // wParam == the PADS_ATTR_INFO struct for the changed attribute.
  854. //
  855. //-----------------------------------------------------------------------------
  856. void
  857. CDsGroupGenObjPage::OnAttrChanged(WPARAM wParam)
  858. {
  859. PADS_ATTR_INFO pAttrInfo = (PADS_ATTR_INFO)wParam;
  860. dspAssert(pAttrInfo && pAttrInfo->pszAttrName && pAttrInfo->pADsValues &&
  861. pAttrInfo->pADsValues->CaseIgnoreString);
  862. dspDebugOut((DEB_ITRACE,
  863. "(HWND: %08x) got DSPROP_ATTRCHANGED_MSG for '%ws'.\n",
  864. GetHWnd(), pAttrInfo->pszAttrName));
  865. if (_wcsicmp(pAttrInfo->pszAttrName, m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName) == 0)
  866. {
  867. SetDlgItemText(GetHWnd(), m_rgpAttrMap[EMAIL_IDX]->nCtrlID,
  868. pAttrInfo->pADsValues->CaseIgnoreString);
  869. }
  870. }
  871. //+----------------------------------------------------------------------------
  872. //
  873. // Method: CDsGroupGenObjPage::OnDestroy
  874. //
  875. // Synopsis: Exit cleanup
  876. //
  877. //-----------------------------------------------------------------------------
  878. LRESULT
  879. CDsGroupGenObjPage::OnDestroy(void)
  880. {
  881. ATTR_DATA ad = {0, (LPARAM)m_pCIcon};
  882. GeneralPageIcon(this, &GenIcon, NULL, 0, &ad, fOnDestroy);
  883. CDsPropPageBase::OnDestroy();
  884. // If an application processes this message, it should return zero.
  885. return 0;
  886. }
  887. #endif // DSADMIN
  888. //+----------------------------------------------------------------------------
  889. //
  890. // Member: CDsGrpMembersPage::CDsGrpMembersPage
  891. //
  892. //-----------------------------------------------------------------------------
  893. CDsGrpMembersPage::CDsGrpMembersPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj,
  894. HWND hNotifyObj, DWORD dwFlags) :
  895. m_pList(NULL),
  896. m_fMixed(TRUE),
  897. m_dwType(0),
  898. m_fMemberWritable(FALSE),
  899. m_dwGroupRID(0),
  900. m_fShowIcons(FALSE),
  901. m_pszSecurityGroupExtraClasses(NULL),
  902. m_dwSecurityGroupExtraClassesCount(0),
  903. m_pszNonSecurityGroupExtraClasses(NULL),
  904. m_dwNonSecurityGroupExtraClassesCount(0),
  905. m_hwndObjPicker(NULL),
  906. m_pInitInfo(NULL),
  907. CDsPropPageBase(pDsPage, pDataObj, hNotifyObj, dwFlags)
  908. {
  909. TRACE(CDsGrpMembersPage,CDsGrpMembersPage);
  910. #ifdef _DEBUG
  911. strcpy(szClass, "CDsGrpMembersPage");
  912. #endif
  913. }
  914. //+----------------------------------------------------------------------------
  915. //
  916. // Member: CDsGrpMembersPage::~CDsGrpMembersPage
  917. //
  918. //-----------------------------------------------------------------------------
  919. CDsGrpMembersPage::~CDsGrpMembersPage()
  920. {
  921. TRACE(CDsGrpMembersPage,~CDsGrpMembersPage);
  922. DO_DEL(m_pList);
  923. if (m_pszSecurityGroupExtraClasses != NULL)
  924. {
  925. for (DWORD idx = 0; idx < m_dwSecurityGroupExtraClassesCount; idx++)
  926. {
  927. if (m_pszSecurityGroupExtraClasses[idx] != NULL)
  928. {
  929. delete[] m_pszSecurityGroupExtraClasses[idx];
  930. m_pszSecurityGroupExtraClasses[idx] = NULL;
  931. }
  932. }
  933. delete[] m_pszSecurityGroupExtraClasses;
  934. }
  935. if (m_pszNonSecurityGroupExtraClasses != NULL)
  936. {
  937. for (DWORD idx = 0; idx < m_dwNonSecurityGroupExtraClassesCount; idx++)
  938. {
  939. if (m_pszNonSecurityGroupExtraClasses[idx] != NULL)
  940. {
  941. delete[] m_pszNonSecurityGroupExtraClasses[idx];
  942. m_pszNonSecurityGroupExtraClasses[idx] = NULL;
  943. }
  944. }
  945. delete[] m_pszNonSecurityGroupExtraClasses;
  946. }
  947. }
  948. //+----------------------------------------------------------------------------
  949. //
  950. // Member: CDsGrpMembersPage::IUnknown::QueryInterface
  951. //
  952. // Synopsis: Returns requested interface pointer
  953. //
  954. //-----------------------------------------------------------------------------
  955. STDMETHODIMP
  956. CDsGrpMembersPage::QueryInterface(REFIID riid, void ** ppvObject)
  957. {
  958. TRACE2(CDsGrpMembersPage,QueryInterface);
  959. if (IID_ICustomizeDsBrowser == riid)
  960. {
  961. *ppvObject = (ICustomizeDsBrowser*)this;
  962. }
  963. else
  964. {
  965. return CDsPropPageBase::QueryInterface(riid, ppvObject);
  966. }
  967. AddRef();
  968. return S_OK;
  969. }
  970. //+----------------------------------------------------------------------------
  971. //
  972. // Member: CDsGrpMembersPage::IUnknown::AddRef
  973. //
  974. // Synopsis: increments reference count
  975. //
  976. // Returns: the reference count
  977. //
  978. //-----------------------------------------------------------------------------
  979. STDMETHODIMP_(ULONG)
  980. CDsGrpMembersPage::AddRef(void)
  981. {
  982. dspDebugOut((DEB_USER2, "CDsGrpMembersPage::AddRef refcount going in %d\n", m_uRefs));
  983. return CDsPropPageBase::AddRef();
  984. }
  985. //+----------------------------------------------------------------------------
  986. //
  987. // Member: CDsGrpMembersPage::IUnknown::Release
  988. //
  989. // Synopsis: Decrements the object's reference count and frees it when
  990. // no longer referenced.
  991. //
  992. // Returns: zero if the reference count is zero or non-zero otherwise
  993. //
  994. //-----------------------------------------------------------------------------
  995. STDMETHODIMP_(ULONG)
  996. CDsGrpMembersPage::Release(void)
  997. {
  998. dspDebugOut((DEB_USER2, "CDsGrpMembersPage::Release ref count going in %d\n", m_uRefs));
  999. return CDsPropPageBase::Release();
  1000. }
  1001. //+----------------------------------------------------------------------------
  1002. //
  1003. // Method: CDsGrpMembersPage::Initialize
  1004. //
  1005. // Synopsis: Initializes the ICustomizeDsBrowser interface
  1006. //
  1007. //-----------------------------------------------------------------------------
  1008. HRESULT CDsGrpMembersPage::Initialize(HWND hwnd,
  1009. PCDSOP_INIT_INFO pInitInfo,
  1010. IBindHelper *pBindHelper)
  1011. {
  1012. HRESULT hr = S_OK;
  1013. dspAssert(IsWindow(hwnd));
  1014. dspAssert(pBindHelper);
  1015. m_hwndObjPicker = hwnd;
  1016. m_pInitInfo = pInitInfo;
  1017. m_pBinder = pBindHelper;
  1018. return hr;
  1019. }
  1020. //+----------------------------------------------------------------------------
  1021. //
  1022. // Method: CDsGrpMembersPage::BuildQueryString
  1023. //
  1024. // Synopsis: Used to build the query string from the securityGroupExtraClasses
  1025. // and nonSecurityGroupExtraClasses in the DisplaySpecifiers
  1026. //
  1027. //-----------------------------------------------------------------------------
  1028. HRESULT CDsGrpMembersPage::BuildQueryString(PWSTR* ppszFilterString)
  1029. {
  1030. CStrW szFilterString = L"(|";
  1031. BOOL bSecurityGroup = (m_dwType & GROUP_TYPE_SECURITY_ENABLED) ? TRUE : FALSE;
  1032. if (bSecurityGroup)
  1033. {
  1034. if (m_dwSecurityGroupExtraClassesCount == 0)
  1035. {
  1036. return S_FALSE;
  1037. }
  1038. for (DWORD idx = 0; idx < m_dwSecurityGroupExtraClassesCount; idx++)
  1039. {
  1040. if (m_pszSecurityGroupExtraClasses[idx] != NULL)
  1041. {
  1042. szFilterString += L"(objectClass=";
  1043. szFilterString += m_pszSecurityGroupExtraClasses[idx];
  1044. szFilterString += L")";
  1045. }
  1046. }
  1047. }
  1048. else
  1049. {
  1050. if (m_dwNonSecurityGroupExtraClassesCount == 0)
  1051. {
  1052. return S_FALSE;
  1053. }
  1054. for (DWORD idx = 0; idx < m_dwNonSecurityGroupExtraClassesCount; idx++)
  1055. {
  1056. if (m_pszNonSecurityGroupExtraClasses[idx] != NULL)
  1057. {
  1058. szFilterString += L"(objectClass=";
  1059. szFilterString += m_pszNonSecurityGroupExtraClasses[idx];
  1060. szFilterString += L")";
  1061. }
  1062. }
  1063. }
  1064. szFilterString += L")";
  1065. *ppszFilterString = new WCHAR[szFilterString.GetLength() + 1];
  1066. CHECK_NULL_REPORT(*ppszFilterString, GetHWnd(), return E_OUTOFMEMORY);
  1067. wcscpy(*ppszFilterString, szFilterString);
  1068. return S_OK;
  1069. }
  1070. //+----------------------------------------------------------------------------
  1071. //
  1072. // Method: CDsSelectionListWrapper::CreateSelectionList
  1073. //
  1074. // Synopsis: Used to convert a CDsSelectionListWrapper to a PDS_SELECTION_LIST
  1075. //
  1076. //-----------------------------------------------------------------------------
  1077. PDS_SELECTION_LIST CDsSelectionListWrapper::CreateSelectionList(CDsSelectionListWrapper* pHead)
  1078. {
  1079. if (pHead == NULL)
  1080. {
  1081. return NULL;
  1082. }
  1083. PDS_SELECTION_LIST pSelectionList = NULL;
  1084. UINT nCount = CDsSelectionListWrapper::GetCount(pHead);
  1085. if (nCount > 0)
  1086. {
  1087. pSelectionList = (PDS_SELECTION_LIST)malloc(sizeof(DS_SELECTION_LIST) +
  1088. (sizeof(DS_SELECTION) * (nCount - 1)));
  1089. if (pSelectionList != NULL)
  1090. {
  1091. memset(pSelectionList, 0, sizeof(DS_SELECTION_LIST) + (sizeof(DS_SELECTION) * (nCount - 1)));
  1092. pSelectionList->cItems = nCount;
  1093. pSelectionList->cFetchedAttributes = 0;
  1094. //
  1095. // Now fill in the selection list by walking the wrapper list
  1096. //
  1097. UINT idx = 0;
  1098. CDsSelectionListWrapper* pCurrentItem = pHead;
  1099. while (pCurrentItem != NULL)
  1100. {
  1101. memcpy(&(pSelectionList->aDsSelection[idx]), pCurrentItem->m_pSelection, sizeof(DS_SELECTION));
  1102. pCurrentItem = pCurrentItem->m_pNext;
  1103. idx++;
  1104. }
  1105. }
  1106. }
  1107. return pSelectionList;
  1108. }
  1109. //+----------------------------------------------------------------------------
  1110. //
  1111. // Method: CDsSelectionListWrapper::CreateSelectionList
  1112. //
  1113. // Synopsis: Counts the number of items in the CDsSelectionListWrapper
  1114. //
  1115. //-----------------------------------------------------------------------------
  1116. UINT CDsSelectionListWrapper::GetCount(CDsSelectionListWrapper* pHead)
  1117. {
  1118. CDsSelectionListWrapper* pCurrentItem = pHead;
  1119. UINT nCount = 0;
  1120. while (pCurrentItem != NULL)
  1121. {
  1122. nCount++;
  1123. pCurrentItem = pCurrentItem->m_pNext;
  1124. }
  1125. return nCount;
  1126. }
  1127. //+----------------------------------------------------------------------------
  1128. //
  1129. // Method: CDsSelectionListWrapper::DetachItemsAndDeleteList
  1130. //
  1131. // Synopsis: Counts the number of items in the CDsSelectionListWrapper
  1132. //
  1133. //-----------------------------------------------------------------------------
  1134. void CDsSelectionListWrapper::DetachItemsAndDeleteList(CDsSelectionListWrapper* pHead)
  1135. {
  1136. CDsSelectionListWrapper* pNextItem = pHead;
  1137. CDsSelectionListWrapper* pDeleteItem = NULL;
  1138. while (pNextItem != NULL)
  1139. {
  1140. pDeleteItem = pNextItem;
  1141. pNextItem = pNextItem->m_pNext;
  1142. delete pDeleteItem->m_pSelection;
  1143. delete pDeleteItem;
  1144. }
  1145. }
  1146. //+----------------------------------------------------------------------------
  1147. //
  1148. // Method: CDsGrpMembersPage::CollectDsObjects
  1149. //
  1150. // Synopsis: Used by AddObjects and PrefixSearch to retrieve the dataobject
  1151. // of the additional objects
  1152. //
  1153. //-----------------------------------------------------------------------------
  1154. HRESULT CDsGrpMembersPage::CollectDsObjects(PWSTR pszFilter,
  1155. IDsObjectPickerScope *pDsScope,
  1156. CDsPropDataObj *pdo)
  1157. {
  1158. HRESULT hr = S_OK;
  1159. dspAssert(pdo != NULL);
  1160. if (pdo == NULL)
  1161. {
  1162. return E_POINTER;
  1163. }
  1164. //
  1165. // Prepare the search object
  1166. //
  1167. PWSTR pszScopePath = NULL;
  1168. hr = pDsScope->GetADsPath(&pszScopePath);
  1169. CHECK_HRESULT(hr, return hr;);
  1170. CDSSearch searchObj;
  1171. hr = searchObj.Init(pszScopePath);
  1172. CHECK_HRESULT(hr, return hr;);
  1173. PWSTR pszAttributes[] = { g_wzADsPath };
  1174. hr = searchObj.SetAttributeList(pszAttributes, 1);
  1175. CHECK_HRESULT(hr, return hr);
  1176. dspAssert(pszFilter != NULL);
  1177. if (pszFilter == NULL)
  1178. {
  1179. return E_INVALIDARG;
  1180. }
  1181. hr = searchObj.SetFilterString(pszFilter);
  1182. CHECK_HRESULT(hr, return hr);
  1183. hr = searchObj.SetSearchScope(ADS_SCOPE_SUBTREE);
  1184. CHECK_HRESULT(hr, return hr);
  1185. //
  1186. // Prepare the linked list for temporary storage of DS_SELECTION items
  1187. //
  1188. CDsSelectionListWrapper* pListHead = NULL;
  1189. CDsSelectionListWrapper* pCurrentListItem = NULL;
  1190. //
  1191. // Get the path cracker
  1192. //
  1193. CComPtr<IADsPathname> spPathCracker;
  1194. hr = GetADsPathname(spPathCracker);
  1195. CHECK_HRESULT(hr, return hr);
  1196. //
  1197. // Execute the query
  1198. //
  1199. hr = searchObj.DoQuery();
  1200. while (SUCCEEDED(hr))
  1201. {
  1202. hr = searchObj.GetNextRow();
  1203. if (S_ADS_NOMORE_ROWS == hr)
  1204. {
  1205. hr = S_OK;
  1206. break;
  1207. }
  1208. if (SUCCEEDED(hr))
  1209. {
  1210. ADS_SEARCH_COLUMN PathColumn, ClassColumn;
  1211. ::ZeroMemory( &PathColumn, sizeof(PathColumn) );
  1212. ::ZeroMemory(&ClassColumn, sizeof(ClassColumn));
  1213. //
  1214. // Get the ADsPath
  1215. //
  1216. hr = searchObj.GetColumn(pszAttributes[0], &PathColumn);
  1217. CHECK_HRESULT(hr, continue);
  1218. dspAssert(PathColumn.pADsValues->dwType == ADSTYPE_CASE_IGNORE_STRING);
  1219. //
  1220. // Get the objectClass
  1221. //
  1222. CComPtr<IDirectoryObject> spDirObject;
  1223. hr = ADsOpenObject(PathColumn.pADsValues->CaseIgnoreString,
  1224. NULL,
  1225. NULL,
  1226. ADS_SECURE_AUTHENTICATION,
  1227. IID_IDirectoryObject,
  1228. (PVOID*)&spDirObject);
  1229. CHECK_HRESULT(hr, continue);
  1230. //
  1231. // Get the object info
  1232. //
  1233. ADS_OBJECT_INFO* pADsObjectInfo = NULL;
  1234. hr = spDirObject->GetObjectInformation(&pADsObjectInfo);
  1235. CHECK_HRESULT(hr, continue);
  1236. dspAssert(pADsObjectInfo != NULL);
  1237. PDS_SELECTION pSelection = new DS_SELECTION;
  1238. CHECK_NULL(pSelection, return E_OUTOFMEMORY);
  1239. ::ZeroMemory(pSelection, sizeof(DS_SELECTION));
  1240. if (!AllocWStr(PathColumn.pADsValues->CaseIgnoreString, &(pSelection->pwzADsPath)))
  1241. {
  1242. CHECK_HRESULT(E_OUTOFMEMORY, return E_OUTOFMEMORY);
  1243. }
  1244. //
  1245. // Assume that the class we are interested in is the first in the multivalued attribute
  1246. //
  1247. if (!AllocWStr(pADsObjectInfo->pszClassName, &(pSelection->pwzClass)))
  1248. {
  1249. CHECK_HRESULT(E_OUTOFMEMORY, return E_OUTOFMEMORY);
  1250. }
  1251. hr = spPathCracker->Set(PathColumn.pADsValues->CaseIgnoreString, ADS_SETTYPE_FULL);
  1252. CHECK_HRESULT(hr, continue);
  1253. hr = spPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  1254. CHECK_HRESULT(hr, continue);
  1255. // CODEWORK 122531 Should we be turning off escaped mode here?
  1256. CComBSTR bstrName;
  1257. hr = spPathCracker->Retrieve(ADS_FORMAT_LEAF, &bstrName);
  1258. CHECK_HRESULT(hr, continue);
  1259. //
  1260. // Return the display to full
  1261. //
  1262. hr = spPathCracker->SetDisplayType(ADS_DISPLAY_FULL);
  1263. dspAssert(SUCCEEDED(hr));
  1264. if (!AllocWStr(bstrName, &(pSelection->pwzName)))
  1265. {
  1266. CHECK_HRESULT(E_OUTOFMEMORY, return E_OUTOFMEMORY);
  1267. }
  1268. CDsSelectionListWrapper* pNewItem = new CDsSelectionListWrapper;
  1269. CHECK_NULL(pNewItem, return E_OUTOFMEMORY);
  1270. pNewItem->m_pSelection = pSelection;
  1271. //
  1272. // Add selection item to list
  1273. //
  1274. if (pListHead == NULL)
  1275. {
  1276. pListHead = pNewItem;
  1277. pCurrentListItem = pNewItem;
  1278. }
  1279. else
  1280. {
  1281. pCurrentListItem->m_pNext = pNewItem;
  1282. pCurrentListItem = pNewItem;
  1283. }
  1284. searchObj.FreeColumn(&PathColumn);
  1285. searchObj.FreeColumn(&ClassColumn);
  1286. }
  1287. }
  1288. if (pListHead != NULL)
  1289. {
  1290. PDS_SELECTION_LIST pSelectionList = CDsSelectionListWrapper::CreateSelectionList(pListHead);
  1291. if (pSelectionList != NULL)
  1292. {
  1293. hr = pdo->Init(pSelectionList);
  1294. CHECK_HRESULT(hr, return hr);
  1295. }
  1296. CDsSelectionListWrapper::DetachItemsAndDeleteList(pListHead);
  1297. }
  1298. else
  1299. {
  1300. hr = S_FALSE;
  1301. }
  1302. return hr;
  1303. }
  1304. //+----------------------------------------------------------------------------
  1305. //
  1306. // Method: CDsGrpMembersPage::AddObjects
  1307. //
  1308. // Synopsis: Called by the Object Picker UI to add additional objects to the UI
  1309. //
  1310. //-----------------------------------------------------------------------------
  1311. HRESULT CDsGrpMembersPage::AddObjects(IDsObjectPickerScope *pDsScope,
  1312. IDataObject **ppdo)
  1313. {
  1314. HRESULT hr = S_OK;
  1315. //
  1316. // Prepare the data object
  1317. //
  1318. CDsPropDataObj* pDataObj = new CDsPropDataObj(GetHWnd(), m_fReadOnly);
  1319. CHECK_NULL(pDataObj, return E_OUTOFMEMORY);
  1320. *ppdo = pDataObj;
  1321. PWSTR pszFilter = NULL;
  1322. hr = BuildQueryString(&pszFilter);
  1323. if (FAILED(hr) ||
  1324. !pszFilter ||
  1325. hr == S_FALSE)
  1326. {
  1327. delete pDataObj;
  1328. pDataObj = NULL;
  1329. *ppdo = NULL;
  1330. hr = S_FALSE;
  1331. }
  1332. else
  1333. {
  1334. hr = CollectDsObjects(pszFilter, pDsScope, pDataObj);
  1335. }
  1336. if (pszFilter != NULL)
  1337. {
  1338. delete[] pszFilter;
  1339. pszFilter = NULL;
  1340. }
  1341. return hr;
  1342. }
  1343. //+----------------------------------------------------------------------------
  1344. //
  1345. // Method: CDsGrpMembersPage::GetQueryInfoByScope
  1346. //
  1347. // Synopsis: Called by the Object Picker UI
  1348. //
  1349. //-----------------------------------------------------------------------------
  1350. HRESULT CDsGrpMembersPage::GetQueryInfoByScope(IDsObjectPickerScope*,
  1351. PDSQUERYINFO *ppdsqi)
  1352. {
  1353. return E_NOTIMPL;
  1354. }
  1355. //+----------------------------------------------------------------------------
  1356. //
  1357. // Method: CDsGrpMembersPage::PrefixSearch
  1358. //
  1359. // Synopsis: Called by the Object Picker UI to get additional objects starting
  1360. // with a specific string
  1361. //
  1362. //-----------------------------------------------------------------------------
  1363. HRESULT CDsGrpMembersPage::PrefixSearch(IDsObjectPickerScope *pDsScope,
  1364. PCWSTR pwzSearchFor,
  1365. IDataObject **ppdo)
  1366. {
  1367. HRESULT hr = S_OK;
  1368. //
  1369. // Prepare the data object
  1370. //
  1371. CDsPropDataObj* pDataObj = new CDsPropDataObj(GetHWnd(), m_fReadOnly);
  1372. CHECK_NULL(pDataObj, return E_OUTOFMEMORY);
  1373. *ppdo = pDataObj;
  1374. CStrW szFilter;
  1375. PWSTR pszFilter = NULL;
  1376. hr = BuildQueryString(&pszFilter);
  1377. if (FAILED(hr) ||
  1378. hr == S_FALSE ||
  1379. pszFilter == NULL)
  1380. {
  1381. delete pDataObj;
  1382. pDataObj = NULL;
  1383. *ppdo = NULL;
  1384. if (pszFilter)
  1385. {
  1386. delete[] pszFilter;
  1387. pszFilter = NULL;
  1388. }
  1389. hr = S_FALSE;
  1390. }
  1391. else
  1392. {
  1393. szFilter = pszFilter;
  1394. CStrW szPrefix;
  1395. szPrefix = L"(&(name=";
  1396. szPrefix += pwzSearchFor;
  1397. szPrefix += L"*)";
  1398. szFilter = szPrefix + szFilter + L")";
  1399. hr = CollectDsObjects(szFilter.GetBuffer(szFilter.GetLength() + 1), pDsScope, pDataObj);
  1400. }
  1401. return hr;
  1402. }
  1403. //+----------------------------------------------------------------------------
  1404. //
  1405. // Function: CreateGroupMembersPage
  1406. //
  1407. // Synopsis: Creates an instance of the group membership page window.
  1408. //
  1409. //-----------------------------------------------------------------------------
  1410. HRESULT
  1411. CreateGroupMembersPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj,
  1412. PWSTR pwzADsPath, PWSTR pwzClass, HWND hNotifyObj,
  1413. DWORD dwFlags, CDSBasePathsInfo* pBasePathsInfo,
  1414. HPROPSHEETPAGE * phPage)
  1415. {
  1416. TRACE_FUNCTION(CreateGroupMembersPage);
  1417. CDsGrpMembersPage * pPageObj = new CDsGrpMembersPage(pDsPage, pDataObj,
  1418. hNotifyObj, dwFlags);
  1419. CHECK_NULL(pPageObj, return E_OUTOFMEMORY);
  1420. pPageObj->Init(pwzADsPath, pwzClass, pBasePathsInfo);
  1421. return pPageObj->CreatePage(phPage);
  1422. }
  1423. //+----------------------------------------------------------------------------
  1424. //
  1425. // Method: CDsGrpMembersPage::DlgProc
  1426. //
  1427. // Synopsis: per-instance dialog proc
  1428. //
  1429. //-----------------------------------------------------------------------------
  1430. LRESULT
  1431. CDsGrpMembersPage::DlgProc(HWND, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1432. {
  1433. switch (uMsg)
  1434. {
  1435. case WM_INITDIALOG:
  1436. return InitDlg(lParam);
  1437. case WM_NOTIFY:
  1438. return OnNotify(wParam, lParam);
  1439. case WM_SHOWWINDOW:
  1440. return OnShowWindow();
  1441. case WM_SETFOCUS:
  1442. return OnSetFocus((HWND)wParam);
  1443. case WM_HELP:
  1444. return OnHelp((LPHELPINFO)lParam);
  1445. case WM_COMMAND:
  1446. if (m_fInInit)
  1447. {
  1448. return TRUE;
  1449. }
  1450. return(OnCommand(GET_WM_COMMAND_ID(wParam, lParam),
  1451. GET_WM_COMMAND_HWND(wParam, lParam),
  1452. GET_WM_COMMAND_CMD(wParam, lParam)));
  1453. case WM_DESTROY:
  1454. return OnDestroy();
  1455. default:
  1456. return(FALSE);
  1457. }
  1458. return(TRUE);
  1459. }
  1460. //+----------------------------------------------------------------------------
  1461. //
  1462. // Method: CDsGrpMembersPage::OnInitDialog
  1463. //
  1464. // Synopsis: Set the initial control values from the corresponding DS
  1465. // attributes.
  1466. //
  1467. //-----------------------------------------------------------------------------
  1468. HRESULT
  1469. CDsGrpMembersPage::OnInitDialog(LPARAM lParam)
  1470. {
  1471. return OnInitDialog(lParam, TRUE);
  1472. }
  1473. HRESULT CDsGrpMembersPage::OnInitDialog(LPARAM, BOOL fShowIcons)
  1474. {
  1475. TRACE(CDsGrpMembersPage,OnInitDialog);
  1476. HRESULT hr;
  1477. PADS_ATTR_INFO pAttrs = NULL;
  1478. DWORD cAttrs = 0;
  1479. m_fShowIcons = (0 != g_ulMemberFilterCount) ? fShowIcons : FALSE;
  1480. CWaitCursor Wait;
  1481. if (!ADsPropSetHwnd(m_hNotifyObj, m_hPage))
  1482. {
  1483. m_pWritableAttrs = NULL;
  1484. }
  1485. PTSTR ptzRDN;
  1486. if (!UnicodeToTchar(m_pwszRDName, &ptzRDN))
  1487. {
  1488. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  1489. return S_OK;
  1490. }
  1491. SetDlgItemText(m_hPage, IDC_CN, ptzRDN);
  1492. delete ptzRDN;
  1493. GetDomainMode(this, &m_fMixed);
  1494. //
  1495. // Get the group RID.
  1496. //
  1497. PWSTR rgpwzAttrNames[] = {L"primaryGroupToken"};
  1498. hr = m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs);
  1499. if (!CHECK_ADS_HR(&hr, m_hPage))
  1500. {
  1501. return S_OK;
  1502. }
  1503. dspAssert(cAttrs);
  1504. if (cAttrs == 1)
  1505. {
  1506. dspAssert(pAttrs);
  1507. m_dwGroupRID = pAttrs->pADsValues->Integer;
  1508. FreeADsMem(pAttrs);
  1509. }
  1510. GetGroupType(this, &m_dwType);
  1511. dspDebugOut((DEB_ITRACE, "Group Type = 0x%x\n", m_dwType));
  1512. //
  1513. // Get the membership list and fill the listview control.
  1514. //
  1515. m_pList = new CDsMembershipList(m_hPage, IDC_MEMBER_LIST);
  1516. CHECK_NULL_REPORT(m_pList, m_hPage, return S_OK);
  1517. hr = m_pList->Init(m_fShowIcons);
  1518. CHECK_HRESULT(hr, return S_OK);
  1519. hr = FillGroupList();
  1520. CHECK_HRESULT(hr, return S_OK);
  1521. m_fMemberWritable = CheckIfWritable(g_wzMemberAttr);
  1522. if (!m_fMemberWritable)
  1523. {
  1524. EnableWindow(GetDlgItem(m_hPage, IDC_ADD_BTN), FALSE);
  1525. EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), FALSE);
  1526. }
  1527. //Set the focus on first item in the list
  1528. ListView_SetItemState(GetDlgItem(m_hPage, IDC_MEMBER_LIST), 0,
  1529. LVIS_SELECTED, LVIS_SELECTED);
  1530. BOOL bSecurityGroup = (m_dwType & GROUP_TYPE_SECURITY_ENABLED) ? TRUE : FALSE;
  1531. hr = LoadGroupExtraClasses(bSecurityGroup);
  1532. return S_OK;
  1533. }
  1534. //+----------------------------------------------------------------------------
  1535. //
  1536. // Method: CDsGrpMembersPage::OnApply
  1537. //
  1538. // Synopsis: Handles the Apply notification.
  1539. //
  1540. //-----------------------------------------------------------------------------
  1541. LRESULT
  1542. CDsGrpMembersPage::OnApply(void)
  1543. {
  1544. TRACE(CDsGrpMembersPage,OnApply);
  1545. HRESULT hr = S_OK;
  1546. ADS_ATTR_INFO AttrInfo;
  1547. PADS_ATTR_INFO pAttrs = &AttrInfo;
  1548. AttrInfo.pszAttrName = g_wzMemberAttr;
  1549. AttrInfo.dwADsType = ADSTYPE_DN_STRING;
  1550. //
  1551. // Read the list of members and do additions.
  1552. //
  1553. DWORD cModified;
  1554. int i, cMembers = m_pList->GetCount();
  1555. if (cMembers > 0)
  1556. {
  1557. AttrInfo.dwControlCode = ADS_ATTR_APPEND;
  1558. PADSVALUE rgADsValues;
  1559. rgADsValues = new ADSVALUE[cMembers];
  1560. CHECK_NULL(rgADsValues, return PSNRET_INVALID_NOCHANGEPAGE);
  1561. memset(rgADsValues, 0, cMembers * sizeof(ADSVALUE));
  1562. pAttrs->pADsValues = rgADsValues;
  1563. pAttrs->dwNumValues = 0;
  1564. CMemberListItem * pItem;
  1565. for (i = 0; i < cMembers; i++)
  1566. {
  1567. if (FAILED(m_pList->GetItem(i, &pItem)))
  1568. {
  1569. dspAssert(FALSE && "List Error");
  1570. return PSNRET_INVALID_NOCHANGEPAGE;
  1571. }
  1572. if (!pItem)
  1573. {
  1574. dspAssert(pItem);
  1575. return PSNRET_INVALID_NOCHANGEPAGE;
  1576. }
  1577. if (pItem->m_fIsAlreadyMember)
  1578. {
  1579. continue;
  1580. }
  1581. dspAssert(pItem->m_pwzDN);
  1582. rgADsValues[pAttrs->dwNumValues].DNString = pItem->m_pwzDN;
  1583. rgADsValues[pAttrs->dwNumValues].dwType = ADSTYPE_DN_STRING;
  1584. pItem->m_fIsAlreadyMember = TRUE;
  1585. pAttrs->dwNumValues++;
  1586. }
  1587. if (pAttrs->dwNumValues)
  1588. {
  1589. hr = m_pDsObj->SetObjectAttributes(pAttrs, 1, &cModified);
  1590. if (!CheckGroupUpdate(hr, m_hPage))
  1591. {
  1592. delete rgADsValues;
  1593. return PSNRET_INVALID_NOCHANGEPAGE;
  1594. }
  1595. }
  1596. delete rgADsValues;
  1597. }
  1598. //
  1599. // Do removals.
  1600. //
  1601. DWORD cDelItems = m_DelList.GetItemCount();
  1602. if (cDelItems)
  1603. {
  1604. AttrInfo.dwControlCode = ADS_ATTR_DELETE;
  1605. PADSVALUE rgADsValues;
  1606. rgADsValues = new ADSVALUE[cDelItems];
  1607. CHECK_NULL(rgADsValues, return PSNRET_INVALID_NOCHANGEPAGE);
  1608. memset(rgADsValues, 0, cDelItems * sizeof(ADSVALUE));
  1609. pAttrs->pADsValues = rgADsValues;
  1610. pAttrs->dwNumValues = 0;
  1611. CMemberListItem * pDelItem = m_DelList.RemoveFirstItem();
  1612. while (pDelItem)
  1613. {
  1614. if (pDelItem->m_fIsExternal)
  1615. {
  1616. hr = GetRealDN(pDelItem);
  1617. CHECK_HRESULT_REPORT(hr, m_hPage, continue);
  1618. }
  1619. dspAssert(pDelItem->m_pwzDN);
  1620. rgADsValues[pAttrs->dwNumValues].DNString = pDelItem->m_pwzDN;
  1621. rgADsValues[pAttrs->dwNumValues].dwType = ADSTYPE_DN_STRING;
  1622. pAttrs->dwNumValues++;
  1623. pDelItem = m_DelList.RemoveFirstItem();
  1624. }
  1625. if (pAttrs->dwNumValues)
  1626. {
  1627. hr = m_pDsObj->SetObjectAttributes(pAttrs, 1, &cModified);
  1628. if (!CheckGroupUpdate(hr, m_hPage, FALSE))
  1629. {
  1630. delete rgADsValues;
  1631. return PSNRET_INVALID_NOCHANGEPAGE;
  1632. }
  1633. }
  1634. delete rgADsValues;
  1635. }
  1636. return (SUCCEEDED(hr)) ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE;
  1637. }
  1638. //+----------------------------------------------------------------------------
  1639. //
  1640. // Method: CDsGrpMembersPage::OnCommand
  1641. //
  1642. // Synopsis: Handle control notifications.
  1643. //
  1644. //-----------------------------------------------------------------------------
  1645. LRESULT
  1646. CDsGrpMembersPage::OnCommand(int id, HWND hwndCtl, UINT codeNotify)
  1647. {
  1648. if (m_fInInit)
  1649. {
  1650. return 0;
  1651. }
  1652. switch (codeNotify)
  1653. {
  1654. case BN_CLICKED:
  1655. TRACE(CDsGrpMembersPage,OnCommand);
  1656. if (id == IDC_ADD_BTN)
  1657. {
  1658. InvokeUserQuery();
  1659. }
  1660. if (id == IDC_REMOVE_BTN)
  1661. {
  1662. RemoveMember();
  1663. }
  1664. break;
  1665. }
  1666. return CDsPropPageBase::OnCommand(id, hwndCtl, codeNotify);
  1667. }
  1668. //+----------------------------------------------------------------------------
  1669. //
  1670. // Method: CDsGrpMembersPage::OnNotify
  1671. //
  1672. // Synopsis: Handles list notification messages
  1673. //
  1674. //-----------------------------------------------------------------------------
  1675. LRESULT
  1676. CDsGrpMembersPage::OnNotify(WPARAM wParam, LPARAM lParam)
  1677. {
  1678. if (m_fInInit)
  1679. {
  1680. return 0;
  1681. }
  1682. switch (((LPNMHDR)lParam)->code)
  1683. {
  1684. case LVN_ITEMCHANGED:
  1685. if (m_fMemberWritable)
  1686. {
  1687. EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), TRUE);
  1688. }
  1689. break;
  1690. case NM_DBLCLK:
  1691. //
  1692. // Display properties for the selected item. First, find out
  1693. // which item is selected.
  1694. //
  1695. CMemberListItem * pItem;
  1696. if (!m_pList->GetCurListItem(NULL, NULL, &pItem))
  1697. {
  1698. break;
  1699. }
  1700. dspAssert(pItem);
  1701. if (pItem->m_fIsExternal)
  1702. {
  1703. HRESULT hr = GetRealDN(pItem);
  1704. if (hr == HRESULT_FROM_WIN32(ERROR_DS_OBJ_NOT_FOUND))
  1705. {
  1706. MsgBox(IDS_CANT_VIEW_EXTERNAL, m_hPage);
  1707. break;
  1708. }
  1709. CHECK_HRESULT_REPORT(hr, m_hPage, break);
  1710. }
  1711. if (pItem->m_ulScopeType & DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN)
  1712. {
  1713. //
  1714. // We cannot show the properties for downlevel users
  1715. //
  1716. // Put a useful message up
  1717. PTSTR ptzTitle, ptzMsg;
  1718. if (!LoadStringToTchar(IDS_MSG_NO_DOWNLEVEL_PROPERTIES, &ptzMsg))
  1719. {
  1720. break;
  1721. }
  1722. if (!LoadStringToTchar(IDS_MSG_TITLE, &ptzTitle))
  1723. {
  1724. break;
  1725. }
  1726. MessageBox(m_hPage, ptzMsg, ptzTitle, MB_OK | MB_ICONEXCLAMATION);
  1727. delete[] ptzTitle;
  1728. delete[] ptzMsg;
  1729. break;
  1730. }
  1731. PostPropSheet(pItem->m_pwzDN, this);
  1732. break;
  1733. }
  1734. return CDsPropPageBase::OnNotify(wParam, lParam);
  1735. }
  1736. //+----------------------------------------------------------------------------
  1737. //
  1738. // Method: CDsGrpMembersPage::InvokeUserQuery
  1739. //
  1740. // Synopsis: Bring up the query dialog to search for users and groups.
  1741. //
  1742. //-----------------------------------------------------------------------------
  1743. void
  1744. CDsGrpMembersPage::InvokeUserQuery(void)
  1745. {
  1746. TRACE(CDsGrpMembersPage,InvokeUserQuery);
  1747. HRESULT hr;
  1748. UINT i;
  1749. CWaitCursor WaitCursor;
  1750. CSmartWStr cstrCleanDN;
  1751. IDsObjectPicker * pObjSel;
  1752. BOOL fIsObjSelInited, fNativeModeUSG = FALSE;
  1753. CStr strExternMemberList;
  1754. hr = GetObjSel(&pObjSel, &fIsObjSelInited);
  1755. CHECK_HRESULT(hr, return);
  1756. if (!fIsObjSelInited)
  1757. {
  1758. CStrW cstrDC;
  1759. hr = GetLdapServerName(m_pDsObj, cstrDC);
  1760. CHECK_HRESULT_REPORT(hr, m_hPage, return);
  1761. dspDebugOut((DEB_ITRACE, "ObjSel targetted to %ws\n", (LPCWSTR)cstrDC));
  1762. DSOP_SCOPE_INIT_INFO rgScopes[5];
  1763. DSOP_INIT_INFO InitInfo;
  1764. ZeroMemory(rgScopes, sizeof(rgScopes));
  1765. ZeroMemory(&InitInfo, sizeof(InitInfo));
  1766. // The first scope is the local domain. All group types can contain
  1767. // users, computers, and contacts from the local domain.
  1768. //
  1769. rgScopes[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  1770. rgScopes[0].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN;
  1771. rgScopes[0].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE |
  1772. DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
  1773. DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
  1774. rgScopes[0].pwzDcName = cstrDC;
  1775. rgScopes[0].FilterFlags.Uplevel.flBothModes =
  1776. DSOP_FILTER_USERS | DSOP_FILTER_CONTACTS | DSOP_FILTER_COMPUTERS;
  1777. // The second scope is the local forest.
  1778. //
  1779. rgScopes[1].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  1780. rgScopes[1].flType = DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN;
  1781. rgScopes[1].flScope = DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
  1782. DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
  1783. // The third scope is the GC.
  1784. //
  1785. rgScopes[2].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  1786. rgScopes[2].flType = DSOP_SCOPE_TYPE_GLOBAL_CATALOG;
  1787. rgScopes[2].flScope = DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
  1788. DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
  1789. // The fourth scope is uplevel external trusted domains.
  1790. //
  1791. rgScopes[3].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  1792. rgScopes[3].flType = DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN;
  1793. rgScopes[3].flScope = DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
  1794. DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
  1795. // The fifth scope is downlevel external trusted domains.
  1796. //
  1797. rgScopes[4].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  1798. rgScopes[4].flType = DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN;
  1799. rgScopes[4].flScope = DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS |
  1800. DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
  1801. if (m_dwType & GROUP_TYPE_ACCOUNT_GROUP) // Global group
  1802. {
  1803. if (!(m_fMixed && (m_dwType & GROUP_TYPE_SECURITY_ENABLED)))
  1804. {
  1805. // if it is not mixed-mode, security-enabled, add global.
  1806. //
  1807. rgScopes[0].FilterFlags.Uplevel.flBothModes |=
  1808. DSOP_FILTER_GLOBAL_GROUPS_DL |
  1809. DSOP_FILTER_GLOBAL_GROUPS_SE;
  1810. }
  1811. rgScopes[1].FilterFlags.Uplevel.flBothModes =
  1812. rgScopes[2].FilterFlags.Uplevel.flBothModes =
  1813. DSOP_FILTER_CONTACTS;
  1814. InitInfo.cDsScopeInfos = 3; // Enterprise scope.
  1815. }
  1816. else if (m_dwType & GROUP_TYPE_RESOURCE_GROUP) // Local group.
  1817. {
  1818. rgScopes[0].FilterFlags.Uplevel.flBothModes |=
  1819. DSOP_FILTER_UNIVERSAL_GROUPS_DL |
  1820. DSOP_FILTER_UNIVERSAL_GROUPS_SE |
  1821. DSOP_FILTER_GLOBAL_GROUPS_DL |
  1822. DSOP_FILTER_GLOBAL_GROUPS_SE;
  1823. if (!(m_fMixed && (m_dwType & GROUP_TYPE_SECURITY_ENABLED)) &&
  1824. !(m_dwType & GROUP_TYPE_BUILTIN_LOCAL_GROUP))
  1825. {
  1826. // If this is not a mixed-mode security-enabled local group
  1827. // or a builtin group, then add local groups.
  1828. //
  1829. rgScopes[0].FilterFlags.Uplevel.flBothModes |=
  1830. DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL |
  1831. DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE;
  1832. }
  1833. //bug 37724
  1834. if( m_dwType & GROUP_TYPE_BUILTIN_LOCAL_GROUP )
  1835. rgScopes[0].FilterFlags.Uplevel.flBothModes |=
  1836. DSOP_FILTER_WELL_KNOWN_PRINCIPALS;
  1837. rgScopes[1].FilterFlags.Uplevel.flBothModes =
  1838. rgScopes[2].FilterFlags.Uplevel.flBothModes =
  1839. DSOP_FILTER_USERS | DSOP_FILTER_CONTACTS |
  1840. DSOP_FILTER_COMPUTERS |
  1841. DSOP_FILTER_UNIVERSAL_GROUPS_DL |
  1842. DSOP_FILTER_UNIVERSAL_GROUPS_SE |
  1843. DSOP_FILTER_GLOBAL_GROUPS_DL |
  1844. DSOP_FILTER_GLOBAL_GROUPS_SE;
  1845. //
  1846. // Uplevel external domains:
  1847. //
  1848. rgScopes[3].FilterFlags.Uplevel.flBothModes =
  1849. DSOP_FILTER_USERS | DSOP_FILTER_COMPUTERS |
  1850. DSOP_FILTER_UNIVERSAL_GROUPS_DL |
  1851. DSOP_FILTER_UNIVERSAL_GROUPS_SE |
  1852. DSOP_FILTER_GLOBAL_GROUPS_DL |
  1853. DSOP_FILTER_GLOBAL_GROUPS_SE;
  1854. //
  1855. // Downlevel external domains:
  1856. //
  1857. rgScopes[4].FilterFlags.flDownlevel =
  1858. DSOP_DOWNLEVEL_FILTER_USERS |
  1859. DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS;
  1860. InitInfo.cDsScopeInfos = 5; // Any trusted domain.
  1861. }
  1862. else if (m_dwType & GROUP_TYPE_UNIVERSAL_GROUP)
  1863. {
  1864. rgScopes[0].FilterFlags.Uplevel.flBothModes =
  1865. rgScopes[1].FilterFlags.Uplevel.flBothModes =
  1866. rgScopes[2].FilterFlags.Uplevel.flBothModes =
  1867. DSOP_FILTER_USERS | DSOP_FILTER_CONTACTS |
  1868. DSOP_FILTER_COMPUTERS |
  1869. DSOP_FILTER_UNIVERSAL_GROUPS_DL |
  1870. DSOP_FILTER_UNIVERSAL_GROUPS_SE |
  1871. DSOP_FILTER_GLOBAL_GROUPS_DL |
  1872. DSOP_FILTER_GLOBAL_GROUPS_SE;
  1873. InitInfo.cDsScopeInfos = 3; // Enterprise scope.
  1874. }
  1875. InitInfo.cbSize = sizeof(DSOP_INIT_INFO);
  1876. InitInfo.aDsScopeInfos = rgScopes;
  1877. InitInfo.pwzTargetComputer = cstrDC;
  1878. InitInfo.flOptions = DSOP_FLAG_MULTISELECT;
  1879. InitInfo.cAttributesToFetch = 2;
  1880. LPCWSTR rgAttrNames[] = {g_wzObjectSID,
  1881. g_wzUserAccountControl};
  1882. InitInfo.apwzAttributeNames = rgAttrNames;
  1883. hr = pObjSel->Initialize(&InitInfo);
  1884. CHECK_HRESULT_REPORT(hr, m_hPage, return);
  1885. ObjSelInited();
  1886. }
  1887. IDataObject * pdoSelections = NULL;
  1888. CComPtr<IDsObjectPickerEx> spObjPickerEx;
  1889. hr = pObjSel->QueryInterface(IID_IDsObjectPickerEx, (void**)&spObjPickerEx);
  1890. CHECK_HRESULT_REPORT(hr, m_hPage, return);
  1891. hr = spObjPickerEx->InvokeDialogEx(m_hPage, this, &pdoSelections);
  1892. // hr = pObjSel->InvokeDialog(m_hPage, &pdoSelections);
  1893. CHECK_HRESULT_REPORT(hr, m_hPage, return);
  1894. if (hr == S_FALSE || !pdoSelections)
  1895. {
  1896. return;
  1897. }
  1898. // Security enabled universal groups shouldn't contain members
  1899. // from mixed-mode domains BUT, we have to allow it for Exchange's
  1900. // non-standard public folder security model.
  1901. //
  1902. if (!m_fMixed && (m_dwType & GROUP_TYPE_UNIVERSAL_GROUP) &&
  1903. (m_dwType & GROUP_TYPE_SECURITY_ENABLED))
  1904. {
  1905. fNativeModeUSG = TRUE;
  1906. }
  1907. m_MixedModeMembers.Init(this);
  1908. CSmartWStr cstrCleanGroup;
  1909. FORMATETC fmte = {g_cfDsSelList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1910. STGMEDIUM medium = {TYMED_NULL, NULL, NULL};
  1911. hr = pdoSelections->GetData(&fmte, &medium);
  1912. CHECK_HRESULT_REPORT(hr, m_hPage, return);
  1913. PDS_SELECTION_LIST pSelList = (PDS_SELECTION_LIST)GlobalLock(medium.hGlobal);
  1914. if (!pSelList)
  1915. {
  1916. goto ExitCleanup;
  1917. }
  1918. WaitCursor.SetWait();
  1919. // Clean the group name so it can be compared with those returned by the
  1920. // user's selection.
  1921. //
  1922. hr = SkipPrefix(m_pwszObjPathName, &cstrCleanGroup);
  1923. CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
  1924. //
  1925. // Insert the returned items into the group.
  1926. //
  1927. for (i = 0; i < pSelList->cItems; i++)
  1928. {
  1929. CMemberListItem * pItemInDelList = NULL;
  1930. PSID pSid = NULL;
  1931. if (!pSelList->aDsSelection[i].pwzADsPath) continue;
  1932. // Check for an object from an external trusted domain. These objects
  1933. // have a path of the form "LDAP://<SID=01050xxxx>". SAM will create
  1934. // an FSPO for this member and will then store that DN rather than the
  1935. // above path. We won't know this DN until after the member is added,
  1936. // so use its object-SID to identify it.
  1937. //
  1938. if ((pSelList->aDsSelection[i].flScopeType == DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN) ||
  1939. (pSelList->aDsSelection[i].flScopeType == DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN))
  1940. {
  1941. dspAssert(pSelList->aDsSelection[i].pvarFetchedAttributes);
  1942. if (pSelList->aDsSelection[i].pvarFetchedAttributes[0].vt != (VT_ARRAY | VT_UI1))
  1943. {
  1944. REPORT_ERROR(ERROR_DATATYPE_MISMATCH, m_hPage);
  1945. continue;
  1946. }
  1947. pSid = pSelList->aDsSelection[i].pvarFetchedAttributes[0].parray->pvData;
  1948. dspAssert(IsValidSid(pSid));
  1949. //
  1950. // Check if the item is in the delete list, if so remove it.
  1951. //
  1952. pItemInDelList = m_DelList.FindItemRemove(pSid);
  1953. }
  1954. else
  1955. {
  1956. hr = SkipPrefix(pSelList->aDsSelection[i].pwzADsPath, &cstrCleanDN);
  1957. CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
  1958. // See if the user is trying to add the group to itself.
  1959. //
  1960. if (_wcsicmp(cstrCleanDN, cstrCleanGroup) == 0)
  1961. {
  1962. if (pSelList->cItems == 1)
  1963. {
  1964. ErrMsg(IDS_ERROR_GRP_SELF, m_hPage);
  1965. goto ExitCleanup;
  1966. }
  1967. continue;
  1968. }
  1969. // Check if the item is in the delete list, if so remove it.
  1970. //
  1971. pItemInDelList = m_DelList.FindItemRemove(cstrCleanDN);
  1972. }
  1973. if (pItemInDelList)
  1974. {
  1975. hr = m_pList->InsertIntoList(pItemInDelList);
  1976. }
  1977. else
  1978. {
  1979. if (pSid)
  1980. {
  1981. CComPtr<IADsPathname> spPathCracker;
  1982. hr = GetADsPathname(spPathCracker);
  1983. CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
  1984. hr = spPathCracker->Set(pSelList->aDsSelection[i].pwzADsPath,
  1985. ADS_SETTYPE_FULL);
  1986. CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
  1987. PWSTR pwzName;
  1988. BSTR bstr;
  1989. hr = spPathCracker->Retrieve(ADS_FORMAT_PROVIDER, &bstr);
  1990. CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
  1991. if (_wcsicmp(bstr, L"LDAP") == 0)
  1992. {
  1993. SysFreeString(bstr);
  1994. hr = SkipPrefix(pSelList->aDsSelection[i].pwzADsPath, &cstrCleanDN);
  1995. CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
  1996. hr = CrackName(cstrCleanDN, &pwzName, GET_OBJ_CAN_NAME_EX, m_hPage);
  1997. CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
  1998. hr = m_pList->InsertIntoList(pSid, pwzName);
  1999. LocalFreeStringW(&pwzName);
  2000. }
  2001. else
  2002. {
  2003. SysFreeString(bstr);
  2004. hr = spPathCracker->Retrieve(ADS_FORMAT_WINDOWS_DN, &bstr);
  2005. CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
  2006. hr = m_pList->InsertIntoList(pSid, bstr);
  2007. SysFreeString(bstr);
  2008. }
  2009. }
  2010. else
  2011. {
  2012. int iIcon = -1;
  2013. if (m_fShowIcons)
  2014. {
  2015. BOOL fDisabled = FALSE;
  2016. if (pSelList->aDsSelection[i].pvarFetchedAttributes[1].vt == VT_I4)
  2017. {
  2018. fDisabled = pSelList->aDsSelection[i].pvarFetchedAttributes[1].lVal & UF_ACCOUNTDISABLE;
  2019. }
  2020. iIcon = g_ClassIconCache.GetClassIconIndex(pSelList->aDsSelection[i].pwzClass,
  2021. fDisabled);
  2022. if (iIcon == -1)
  2023. {
  2024. iIcon = g_ClassIconCache.AddClassIcon(pSelList->aDsSelection[i].pwzClass,
  2025. fDisabled);
  2026. }
  2027. }
  2028. if (fNativeModeUSG &&
  2029. ((DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN == pSelList->aDsSelection[i].flScopeType) ||
  2030. (DSOP_SCOPE_TYPE_GLOBAL_CATALOG == pSelList->aDsSelection[i].flScopeType)))
  2031. {
  2032. // member from domain in forest is DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN
  2033. // member from the same domain is DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN
  2034. //
  2035. m_MixedModeMembers.CheckMember(cstrCleanDN);
  2036. }
  2037. dspDebugOut((DEB_ITRACE, "New member scope is 0x%x\n", pSelList->aDsSelection[i].flScopeType));
  2038. hr = m_pList->InsertIntoList(cstrCleanDN, iIcon);
  2039. }
  2040. }
  2041. if (hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS))
  2042. {
  2043. continue;
  2044. }
  2045. CHECK_HRESULT(hr, goto ExitCleanup);
  2046. }
  2047. m_MixedModeMembers.ListExternalMembers(strExternMemberList);
  2048. if (!strExternMemberList.IsEmpty())
  2049. {
  2050. CStr strMessage, strFormat;
  2051. strFormat.LoadString(g_hInstance, IDS_USG_MIXED_WARNING);
  2052. strMessage.Format(strFormat, strExternMemberList);
  2053. ReportErrorWorker(m_hPage, (LPTSTR)(LPCTSTR)strMessage);
  2054. }
  2055. SetDirty();
  2056. ExitCleanup:
  2057. GlobalUnlock(medium.hGlobal);
  2058. ReleaseStgMedium(&medium);
  2059. pdoSelections->Release();
  2060. }
  2061. //+----------------------------------------------------------------------------
  2062. //
  2063. // Method: CDsGrpMembersPage::FillGroupList
  2064. //
  2065. // Synopsis: Fill the list box with the names of the group members.
  2066. //
  2067. //-----------------------------------------------------------------------------
  2068. HRESULT
  2069. CDsGrpMembersPage::FillGroupList(void)
  2070. {
  2071. TRACE(CDsGrpMembersPage,FillGroupList);
  2072. return ::FillGroupList(this, m_pList, m_dwGroupRID);
  2073. }
  2074. //+----------------------------------------------------------------------------
  2075. //
  2076. // Method: CDsGrpMembersPage::RemoveMember
  2077. //
  2078. // Synopsis: Removes the selected users.
  2079. //
  2080. //-----------------------------------------------------------------------------
  2081. void
  2082. CDsGrpMembersPage::RemoveMember(void)
  2083. {
  2084. TRACE(CDsGrpMembersPage,RemoveMember);
  2085. if (!m_pList)
  2086. {
  2087. return;
  2088. }
  2089. int* pIndex = NULL;
  2090. CMemberListItem ** ppItem;
  2091. int nNumSelected = 0;
  2092. //
  2093. // Compose the confirmation message and post it.
  2094. //
  2095. TCHAR szMsg[160];
  2096. if (!LoadStringReport(IDS_RM_MBR_MSG, szMsg, 160, m_hPage))
  2097. {
  2098. return;
  2099. }
  2100. TCHAR szTitle[80];
  2101. if (!LoadStringReport(IDS_MSG_TITLE, szTitle, 80, m_hPage))
  2102. {
  2103. return;
  2104. }
  2105. LONG iRet = MessageBox(m_hPage, szMsg, szTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
  2106. if (iRet == IDNO)
  2107. {
  2108. // The user declined, so go home.
  2109. //
  2110. return;
  2111. }
  2112. CWaitCursor cWait;
  2113. if (!m_pList->GetCurListItems(&pIndex, NULL, &ppItem, &nNumSelected))
  2114. {
  2115. return;
  2116. }
  2117. for (int idx = 0; idx < nNumSelected; idx++)
  2118. {
  2119. if (!ppItem[idx])
  2120. {
  2121. if (pIndex != NULL)
  2122. {
  2123. delete[] pIndex;
  2124. pIndex = 0;
  2125. }
  2126. delete[] ppItem;
  2127. return;
  2128. }
  2129. if (ppItem[idx]->m_fIsPrimary)
  2130. {
  2131. ErrMsg(IDS_RM_USR_PRI_GRP, m_hPage);
  2132. continue;
  2133. }
  2134. //
  2135. // Put the item into the delete list and remove it from the list box.
  2136. //
  2137. if (!m_DelList.AddItem(ppItem[idx]))
  2138. {
  2139. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  2140. if (pIndex != NULL)
  2141. {
  2142. delete[] pIndex;
  2143. pIndex = 0;
  2144. }
  2145. delete[] ppItem;
  2146. return;
  2147. }
  2148. m_pList->RemoveListItem(pIndex[idx]);
  2149. for (int idx2 = idx; idx2 < nNumSelected; idx2++)
  2150. {
  2151. if (pIndex[idx2] > pIndex[idx])
  2152. {
  2153. pIndex[idx2]--;
  2154. }
  2155. }
  2156. SetDirty();
  2157. }
  2158. //
  2159. // Disable the Remove button, since nothing in the list box should have
  2160. // the selection at this point.
  2161. //
  2162. //Since Remove Button has focus now, set focus to add button
  2163. //before disabling
  2164. SetFocus(GetDlgItem(m_hPage,IDC_ADD_BTN));
  2165. EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), FALSE);
  2166. if (pIndex != NULL)
  2167. {
  2168. delete[] pIndex;
  2169. pIndex = 0;
  2170. }
  2171. delete[] ppItem;
  2172. return;
  2173. }
  2174. //+----------------------------------------------------------------------------
  2175. //
  2176. // Method: CDsGrpMembersPage::GetRealDN
  2177. //
  2178. // Synopsis: If a member from an external domain that was added to the
  2179. // group during this instance of the page, we won't yet have the
  2180. // path to the FPO as the DN. So, search for the FPO using the
  2181. // object-SID.
  2182. //
  2183. //-----------------------------------------------------------------------------
  2184. HRESULT
  2185. CDsGrpMembersPage::GetRealDN(CMemberListItem * pItem)
  2186. {
  2187. return ::GetRealDN(this, pItem);
  2188. }
  2189. //+----------------------------------------------------------------------------
  2190. //
  2191. // Method: CDsGrpMembersPage::OnDestroy
  2192. //
  2193. // Synopsis: Exit cleanup
  2194. //
  2195. //-----------------------------------------------------------------------------
  2196. LRESULT
  2197. CDsGrpMembersPage::OnDestroy(void)
  2198. {
  2199. if (m_pList)
  2200. {
  2201. m_pList->ClearList();
  2202. }
  2203. CDsPropPageBase::OnDestroy();
  2204. // If an application processes this message, it should return zero.
  2205. return 0;
  2206. }
  2207. HRESULT HrVariantToStringArray(const CComVariant& refvar, PWSTR** pppszStringArray, DWORD* pdwCount)
  2208. {
  2209. HRESULT hr = S_OK;
  2210. long start, end, current;
  2211. *pdwCount = 0;
  2212. *pppszStringArray = NULL;
  2213. if (V_VT(&refvar) == VT_BSTR)
  2214. {
  2215. CComBSTR bstrVal = V_BSTR(&refvar);
  2216. *pppszStringArray = new PWSTR[1];
  2217. if (*pppszStringArray != NULL)
  2218. {
  2219. size_t length = wcslen(bstrVal);
  2220. PWSTR pszVal = new WCHAR[length + 1];
  2221. if (pszVal != NULL)
  2222. {
  2223. wcscpy(pszVal, bstrVal);
  2224. (*pppszStringArray)[0] = pszVal;
  2225. }
  2226. else
  2227. {
  2228. delete[] *pppszStringArray;
  2229. *pppszStringArray = NULL;
  2230. *pdwCount = 0;
  2231. return E_OUTOFMEMORY;
  2232. }
  2233. }
  2234. *pdwCount = 1;
  2235. return S_OK;
  2236. }
  2237. //
  2238. // Check the VARIANT to make sure we have
  2239. // an array of variants.
  2240. //
  2241. if ( V_VT(&refvar) != ( VT_ARRAY | VT_VARIANT ) )
  2242. {
  2243. dspAssert(FALSE);
  2244. return E_UNEXPECTED;
  2245. }
  2246. SAFEARRAY *saAttributes = V_ARRAY( &refvar );
  2247. //
  2248. // Figure out the dimensions of the array.
  2249. //
  2250. hr = SafeArrayGetLBound( saAttributes, 1, &start );
  2251. if( FAILED(hr) )
  2252. return hr;
  2253. hr = SafeArrayGetUBound( saAttributes, 1, &end );
  2254. if( FAILED(hr) )
  2255. return hr;
  2256. CComVariant SingleResult;
  2257. //
  2258. // Process the array elements.
  2259. //
  2260. *pppszStringArray = new PWSTR[(end - start) + 1];
  2261. if (*pppszStringArray != NULL)
  2262. {
  2263. for ( current = start; current <= end; current++)
  2264. {
  2265. hr = SafeArrayGetElement( saAttributes, &current, &SingleResult );
  2266. if( FAILED(hr) )
  2267. return hr;
  2268. if ( V_VT(&SingleResult) != VT_BSTR )
  2269. return E_UNEXPECTED;
  2270. CComBSTR bstrVal = V_BSTR(&SingleResult);
  2271. size_t length = wcslen(bstrVal);
  2272. PWSTR pszVal = new WCHAR[length + 1];
  2273. if (pszVal != NULL)
  2274. {
  2275. wcscpy(pszVal, bstrVal);
  2276. long lCount = static_cast<long>(*pdwCount);
  2277. if (lCount < (end - start) + 1)
  2278. {
  2279. (*pppszStringArray)[(*pdwCount)++] = pszVal;
  2280. }
  2281. }
  2282. else
  2283. {
  2284. return E_OUTOFMEMORY;
  2285. }
  2286. }
  2287. }
  2288. else
  2289. {
  2290. hr = E_OUTOFMEMORY;
  2291. *pdwCount = 0;
  2292. }
  2293. return hr;
  2294. }
  2295. //+----------------------------------------------------------------------------
  2296. //
  2297. // Method: CDsGrpMembersPage::LoadGroupExtraClasses
  2298. //
  2299. // Synopsis: Read the extra classes that need to be displayed from the
  2300. // DisplaySpecifiers
  2301. //
  2302. //-----------------------------------------------------------------------------
  2303. HRESULT CDsGrpMembersPage::LoadGroupExtraClasses(BOOL bSecurity)
  2304. {
  2305. HRESULT hr = S_OK;
  2306. dspAssert(m_pDsObj != NULL);
  2307. if (m_pDsObj == NULL)
  2308. {
  2309. return E_INVALIDARG;
  2310. }
  2311. static LPCWSTR lpszSettingsObjectClass = L"dsUISettings";
  2312. static LPCWSTR lpszSettingsObject = L"cn=DS-UI-Default-Settings";
  2313. static LPCWSTR lpszSecurityGroupProperty = L"msDS-Security-Group-Extra-Classes";
  2314. static LPCWSTR lpszNonSecurityGroupProperty = L"msDS-Non-Security-Group-Extra-Classes";
  2315. //
  2316. // Not AddRef'd so don't use a smart pointer
  2317. //
  2318. IDsDisplaySpecifier* pDispSpec;
  2319. hr = GetIDispSpec(&pDispSpec);
  2320. CHECK_HRESULT_REPORT(hr, GetHWnd(), return hr);
  2321. //
  2322. // get the display specifiers locale container (e.g. 409)
  2323. //
  2324. CComPtr<IADsContainer> spLocaleContainer;
  2325. hr = pDispSpec->GetDisplaySpecifier(NULL, IID_IADsContainer, (void**)&spLocaleContainer);
  2326. if (FAILED(hr))
  2327. {
  2328. return hr;
  2329. }
  2330. //
  2331. // bind to the settings object
  2332. //
  2333. CComPtr<IDispatch> spIDispatchObject;
  2334. hr = spLocaleContainer->GetObject((LPWSTR)lpszSettingsObjectClass,
  2335. (LPWSTR)lpszSettingsObject,
  2336. &spIDispatchObject);
  2337. if (FAILED(hr))
  2338. {
  2339. return hr;
  2340. }
  2341. CComPtr<IADs> spSettingsObject;
  2342. hr = spIDispatchObject->QueryInterface(IID_IADs, (void**)&spSettingsObject);
  2343. if (FAILED(hr))
  2344. {
  2345. return hr;
  2346. }
  2347. if (bSecurity)
  2348. {
  2349. //
  2350. // get the security group extra classes as a CStringList
  2351. //
  2352. CComVariant var;
  2353. hr = spSettingsObject->Get((LPWSTR)lpszSecurityGroupProperty, &var);
  2354. if (SUCCEEDED(hr))
  2355. {
  2356. hr = HrVariantToStringArray(var, &m_pszSecurityGroupExtraClasses, &m_dwSecurityGroupExtraClassesCount);
  2357. }
  2358. }
  2359. else
  2360. {
  2361. //
  2362. // get the non-security group extra classes as a CStringList
  2363. //
  2364. CComVariant var;
  2365. hr = spSettingsObject->Get((LPWSTR)lpszNonSecurityGroupProperty, &var);
  2366. if (SUCCEEDED(hr))
  2367. {
  2368. hr = HrVariantToStringArray(var, &m_pszNonSecurityGroupExtraClasses, &m_dwNonSecurityGroupExtraClassesCount);
  2369. }
  2370. }
  2371. return hr;
  2372. }
  2373. //+----------------------------------------------------------------------------
  2374. //
  2375. // Function: GetDomainMode
  2376. //
  2377. // Synopsis: Is the domain to which the indicated object belongs in mixed
  2378. // or native mode?
  2379. //
  2380. //-----------------------------------------------------------------------------
  2381. HRESULT
  2382. GetDomainMode(CDsPropPageBase * pObj, PBOOL pfMixed)
  2383. {
  2384. HRESULT hr;
  2385. CComBSTR cbstrDomain;
  2386. hr = GetDomainScope(pObj, &cbstrDomain);
  2387. CHECK_HRESULT_REPORT(hr, pObj->GetHWnd(), return hr);
  2388. return GetDomainMode(cbstrDomain, pObj->GetHWnd(), pfMixed);
  2389. }
  2390. HRESULT
  2391. GetDomainMode(PWSTR pwzDomain, HWND hWnd, PBOOL pfMixed)
  2392. {
  2393. HRESULT hr;
  2394. WCHAR wzMixedAttr[] = L"nTMixedDomain";
  2395. PWSTR rgpwzAttrNames[] = {wzMixedAttr};
  2396. CComPtr <IDirectoryObject> pDomObj;
  2397. PADS_ATTR_INFO pAttrs = NULL;
  2398. DWORD cAttrs = 0;
  2399. dspDebugOut((DEB_ITRACE, "GetDomainMode targetted to %ws\n", pwzDomain));
  2400. hr = ADsOpenObject(pwzDomain, NULL, NULL, ADS_SECURE_AUTHENTICATION,
  2401. IID_IDirectoryObject, (void **)&pDomObj);
  2402. CHECK_HRESULT_REPORT(hr, hWnd, return hr);
  2403. hr = pDomObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs);
  2404. CHECK_HRESULT_REPORT(hr, hWnd, return hr);
  2405. if (cAttrs && pAttrs && (_wcsicmp(pAttrs->pszAttrName, wzMixedAttr) == 0))
  2406. {
  2407. *pfMixed = (BOOL)pAttrs->pADsValues->Integer;
  2408. FreeADsMem(pAttrs);
  2409. }
  2410. else
  2411. {
  2412. *pfMixed = 0;
  2413. }
  2414. return hr;
  2415. }
  2416. //+----------------------------------------------------------------------------
  2417. //
  2418. // Function: GetGroupType
  2419. //
  2420. //-----------------------------------------------------------------------------
  2421. HRESULT
  2422. GetGroupType(CDsPropPageBase * pObj, DWORD * pdwType)
  2423. {
  2424. HRESULT hr;
  2425. PWSTR rgpwzAttrNames[] = {g_wzGroupType};
  2426. PADS_ATTR_INFO pAttrs = NULL;
  2427. DWORD cAttrs = 0;
  2428. hr = pObj->m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs);
  2429. CHECK_HRESULT_REPORT(hr, pObj->GetHWnd(), return hr);
  2430. if (cAttrs && pAttrs && (_wcsicmp(pAttrs->pszAttrName, g_wzGroupType) == 0))
  2431. {
  2432. *pdwType = pAttrs->pADsValues->Integer;
  2433. FreeADsMem(pAttrs);
  2434. }
  2435. else
  2436. {
  2437. *pdwType = 0;
  2438. }
  2439. return hr;
  2440. }
  2441. //+----------------------------------------------------------------------------
  2442. //
  2443. // Function: FillGroupList
  2444. //
  2445. // Synopsis: Fill the list box with the names of the group members.
  2446. //
  2447. //-----------------------------------------------------------------------------
  2448. HRESULT
  2449. FillGroupList(CDsPropPageBase * pPage, CDsMembershipList * pList,
  2450. DWORD dwGroupRID)
  2451. {
  2452. TRACE_FUNCTION(FillGroupList);
  2453. HRESULT hr = S_OK;
  2454. Smart_PADS_ATTR_INFO spAttrs;
  2455. DWORD i, cAttrs = 0;
  2456. WCHAR wzMemberAttr[MAX_PATH] = L"member;range=0-*";
  2457. const WCHAR wcSep = L'-';
  2458. const WCHAR wcEnd = L'*';
  2459. const WCHAR wzFormat[] = L"member;range=%ld-*";
  2460. PWSTR pwzAttrName[] = {wzMemberAttr}, pwzPath;
  2461. BOOL fMoreRemain = FALSE, fNameNotMapped = FALSE;
  2462. CComPtr <IDirectorySearch> spDsSearch;
  2463. //
  2464. // Read the membership list from the object using range (incremental)
  2465. // retrieval.
  2466. //
  2467. do
  2468. {
  2469. hr = pPage->m_pDsObj->GetObjectAttributes(pwzAttrName, 1, &spAttrs, &cAttrs);
  2470. if (!CHECK_ADS_HR_IGNORE_UNFOUND_ATTR(&hr, pPage->GetHWnd()))
  2471. {
  2472. return hr;
  2473. }
  2474. if (cAttrs > 0 && spAttrs != NULL)
  2475. {
  2476. for (i = 0; i < spAttrs->dwNumValues; i++)
  2477. {
  2478. hr = pList->InsertIntoNewList(spAttrs->pADsValues[i].CaseIgnoreString);
  2479. if (DS_NAME_ERROR_NO_MAPPING == HRESULT_CODE(hr))
  2480. {
  2481. fNameNotMapped = TRUE;
  2482. hr = S_OK;
  2483. }
  2484. else
  2485. {
  2486. CHECK_HRESULT(hr, return hr);
  2487. }
  2488. }
  2489. //
  2490. // Check to see if there is more data. If the last char of the
  2491. // attribute name string is an asterisk, then we have everything.
  2492. //
  2493. size_t cchEnd = wcslen(spAttrs->pszAttrName);
  2494. fMoreRemain = spAttrs->pszAttrName[cchEnd - 1] != wcEnd;
  2495. if (fMoreRemain)
  2496. {
  2497. PWSTR pwz = wcsrchr(spAttrs->pszAttrName, wcSep);
  2498. if (!pwz)
  2499. {
  2500. dspAssert(FALSE && spAttrs->pszAttrName);
  2501. fMoreRemain = FALSE;
  2502. }
  2503. else
  2504. {
  2505. pwz++; // move past the hyphen to the range end value.
  2506. dspAssert(*pwz);
  2507. long lEnd = _wtol(pwz);
  2508. lEnd++; // start with the next value.
  2509. wsprintfW(wzMemberAttr, wzFormat, lEnd);
  2510. dspDebugOut((DEB_ITRACE,
  2511. "Range returned is %ws, now asking for %ws\n",
  2512. spAttrs->pszAttrName, wzMemberAttr));
  2513. }
  2514. }
  2515. }
  2516. } while (fMoreRemain);
  2517. //
  2518. // Query for all users/computers who have this as their primary group.
  2519. //
  2520. // Filter out interdomain-trust accounts (0x30000002).
  2521. // This value is defined in ds\src\dsamain\include\mappings.h
  2522. //
  2523. WCHAR wzSearchFormat[] = L"(&(primaryGroupID=%u)(sAMAccountType<=805306369))";
  2524. CStrW strSearchFilter;
  2525. strSearchFilter.Format(wzSearchFormat, dwGroupRID);
  2526. BSTR bstrDomain;
  2527. hr = GetDomainScope(pPage, &bstrDomain);
  2528. CHECK_HRESULT(hr, return hr);
  2529. pwzAttrName[0] = g_wzADsPath;
  2530. CDSSearch Search;
  2531. hr = Search.Init((LPCWSTR)bstrDomain);
  2532. SysFreeString(bstrDomain);
  2533. CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr);
  2534. Search.SetFilterString(const_cast<LPWSTR>((LPCWSTR)strSearchFilter));
  2535. Search.SetAttributeList(pwzAttrName, 1);
  2536. Search.SetSearchScope(ADS_SCOPE_SUBTREE);
  2537. hr = Search.DoQuery();
  2538. while (SUCCEEDED(hr))
  2539. {
  2540. hr = Search.GetNextRow();
  2541. if (hr == S_ADS_NOMORE_ROWS)
  2542. {
  2543. hr = S_OK;
  2544. break;
  2545. }
  2546. CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr);
  2547. ADS_SEARCH_COLUMN Column = {0};
  2548. hr = Search.GetColumn(g_wzADsPath, &Column);
  2549. CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr);
  2550. hr = pPage->SkipPrefix(Column.pADsValues->CaseIgnoreString, &pwzPath);
  2551. Search.FreeColumn(&Column);
  2552. CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr);
  2553. hr = pList->InsertIntoNewList(pwzPath, TRUE);
  2554. delete pwzPath;
  2555. CHECK_HRESULT(hr, return hr);
  2556. }
  2557. if (pList->GetCount() < 1)
  2558. {
  2559. EnableWindow(GetDlgItem(pPage->GetHWnd(), IDC_REMOVE_BTN), FALSE);
  2560. }
  2561. else if (((CDsGrpMembersPage *)pPage)->m_fShowIcons)
  2562. {
  2563. // Get class and userAccountControl for the group members and use
  2564. // those values to select icons.
  2565. //
  2566. pList->SetMemberIcons(pPage);
  2567. }
  2568. if (fNameNotMapped)
  2569. {
  2570. MsgBox(IDS_GRP_NO_NAME_MAPPING, pPage->GetHWnd());
  2571. }
  2572. return hr;
  2573. }
  2574. //+----------------------------------------------------------------------------
  2575. //
  2576. // Function: GetRealDN
  2577. //
  2578. // Synopsis: If a member from an external domain that was added to the
  2579. // group during this instance of the page, we won't yet have the
  2580. // path to the FPO as the DN. So, search for the FPO using the
  2581. // object-SID.
  2582. //
  2583. //-----------------------------------------------------------------------------
  2584. HRESULT
  2585. GetRealDN(CDsPropPageBase * pPage, CMemberListItem * pItem)
  2586. {
  2587. HRESULT hr = S_OK;
  2588. if (!pItem->GetSid())
  2589. {
  2590. return E_FAIL;
  2591. }
  2592. CComBSTR cbstrDomain;
  2593. hr = GetDomainScope(pPage, &cbstrDomain);
  2594. CHECK_HRESULT(hr, return hr);
  2595. CStrW strDN;
  2596. hr = FindFPO(pItem->GetSid(), cbstrDomain, strDN);
  2597. //Don't show this eror here
  2598. if(FAILED(hr))
  2599. return hr;
  2600. PWSTR pwzOldDN = pItem->m_pwzDN;
  2601. if (!AllocWStr(const_cast<PWSTR>((LPCWSTR)strDN), &pItem->m_pwzDN))
  2602. {
  2603. REPORT_ERROR(E_OUTOFMEMORY, pPage->GetHWnd());
  2604. return E_OUTOFMEMORY;
  2605. }
  2606. DO_DEL(pwzOldDN);
  2607. pItem->m_fIsExternal = FALSE;
  2608. return S_OK;
  2609. }
  2610. //+----------------------------------------------------------------------------
  2611. //
  2612. // Member: CDsGrpShlGenPage::CDsGrpShlGenPage
  2613. //
  2614. //-----------------------------------------------------------------------------
  2615. CDsGrpShlGenPage::CDsGrpShlGenPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj,
  2616. HWND hNotifyObj, DWORD dwFlags) :
  2617. m_pCIcon(NULL),
  2618. m_fDescrWritable(FALSE),
  2619. m_fDescrDirty(FALSE),
  2620. CDsGrpMembersPage(pDsPage, pDataObj, hNotifyObj, dwFlags)
  2621. {
  2622. TRACE(CDsGrpShlGenPage,CDsGrpShlGenPage);
  2623. #ifdef _DEBUG
  2624. strcpy(szClass, "CDsGrpShlGenPage");
  2625. #endif
  2626. }
  2627. //+----------------------------------------------------------------------------
  2628. //
  2629. // Member: CDsGrpShlGenPage::~CDsGrpShlGenPage
  2630. //
  2631. //-----------------------------------------------------------------------------
  2632. CDsGrpShlGenPage::~CDsGrpShlGenPage()
  2633. {
  2634. TRACE(CDsGrpShlGenPage,~CDsGrpShlGenPage);
  2635. }
  2636. //+----------------------------------------------------------------------------
  2637. //
  2638. // Function: CreateGrpShlGenPage
  2639. //
  2640. // Synopsis: Creates an instance of the group shell general page window.
  2641. //
  2642. //-----------------------------------------------------------------------------
  2643. HRESULT
  2644. CreateGrpShlGenPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj,
  2645. PWSTR pwzADsPath, PWSTR pwzClass, HWND hNotifyObj,
  2646. DWORD dwFlags, CDSBasePathsInfo* pBasePathsInfo,
  2647. HPROPSHEETPAGE * phPage)
  2648. {
  2649. TRACE_FUNCTION(CreateGroupMembersPage);
  2650. CDsGrpShlGenPage * pPageObj = new CDsGrpShlGenPage(pDsPage, pDataObj,
  2651. hNotifyObj, dwFlags);
  2652. CHECK_NULL(pPageObj, return E_OUTOFMEMORY);
  2653. pPageObj->Init(pwzADsPath, pwzClass, pBasePathsInfo);
  2654. return pPageObj->CreatePage(phPage);
  2655. }
  2656. //+----------------------------------------------------------------------------
  2657. //
  2658. // Method: CDsGrpShlGenPage::OnInitDialog
  2659. //
  2660. // Synopsis: Set the initial control values from the corresponding DS
  2661. // attributes.
  2662. //
  2663. //-----------------------------------------------------------------------------
  2664. HRESULT CDsGrpShlGenPage::OnInitDialog(LPARAM lParam)
  2665. {
  2666. TRACE(CDsGrpShlGenPage,OnInitDialog);
  2667. HRESULT hr;
  2668. Smart_PADS_ATTR_INFO spAttrs;
  2669. DWORD cAttrs = 0;
  2670. CWaitCursor Wait;
  2671. //
  2672. // Get the icon from the DS and put it on the page.
  2673. //
  2674. ATTR_DATA ad = {0, 0};
  2675. hr = GeneralPageIcon(this, &GenIcon, NULL, 0, &ad, fInit);
  2676. CHECK_HRESULT_REPORT(hr, m_hPage, return S_OK);
  2677. m_pCIcon = (CDsIconCtrl *)ad.pVoid;
  2678. //
  2679. // Get the name.
  2680. //
  2681. LPTSTR ptz;
  2682. if (!UnicodeToTchar(m_pwszRDName, &ptz))
  2683. {
  2684. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  2685. return S_OK;
  2686. }
  2687. SetDlgItemText(m_hPage, IDC_CN, ptz);
  2688. delete [] ptz;
  2689. m_fDescrWritable = CheckIfWritable(g_wzDescription);
  2690. //
  2691. // Get the description
  2692. //
  2693. PWSTR rgpwzAttrNames[] = {g_wzDescription};
  2694. hr = m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 1, &spAttrs, &cAttrs);
  2695. if (!CHECK_ADS_HR_IGNORE_UNFOUND_ATTR(&hr, m_hPage))
  2696. {
  2697. return S_OK;
  2698. }
  2699. if (1 == cAttrs)
  2700. {
  2701. dspAssert(spAttrs);
  2702. if (!UnicodeToTchar(spAttrs->pADsValues->CaseIgnoreString, &ptz))
  2703. {
  2704. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  2705. return S_OK;
  2706. }
  2707. SetDlgItemText(m_hPage, IDC_DESCRIPTION_EDIT, ptz);
  2708. delete [] ptz;
  2709. }
  2710. if (m_fDescrWritable)
  2711. {
  2712. SendDlgItemMessage(m_hPage, IDC_DESCRIPTION_EDIT, EM_LIMITTEXT, DSPROP_DESCRIPTION_RANGE_UPPER, 0);
  2713. }
  2714. else
  2715. {
  2716. SendDlgItemMessage(m_hPage, IDC_DESCRIPTION_EDIT, EM_SETREADONLY, (WPARAM)TRUE, 0);
  2717. }
  2718. HRESULT hRes = CDsGrpMembersPage::OnInitDialog(lParam, FALSE);
  2719. #if !defined(DSADMIN)
  2720. // in the Win95 shell, we do not want to have the buttons
  2721. // because we do not have object picker
  2722. MakeNotWritable();
  2723. EnableWindow(GetDlgItem(m_hPage, IDC_ADD_BTN), FALSE);
  2724. #endif
  2725. EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), FALSE);
  2726. return hRes;
  2727. }
  2728. //+----------------------------------------------------------------------------
  2729. //
  2730. // Method: CDsGrpShlGenPage::OnCommand
  2731. //
  2732. // Synopsis: Handle control notifications.
  2733. //
  2734. //-----------------------------------------------------------------------------
  2735. LRESULT
  2736. CDsGrpShlGenPage::OnCommand(int id, HWND hwndCtl, UINT codeNotify)
  2737. {
  2738. if (m_fInInit)
  2739. {
  2740. return 0;
  2741. }
  2742. if (EN_CHANGE == codeNotify && IDC_DESCRIPTION_EDIT == id)
  2743. {
  2744. m_fDescrDirty = TRUE;
  2745. }
  2746. TRACE(CDsGrpShlGenPage,OnCommand);
  2747. return CDsGrpMembersPage::OnCommand(id, hwndCtl, codeNotify);
  2748. }
  2749. //+----------------------------------------------------------------------------
  2750. //
  2751. // Method: CDsGrpShlGenPage::OnApply
  2752. //
  2753. // Synopsis: Write changes
  2754. //
  2755. //-----------------------------------------------------------------------------
  2756. LRESULT CDsGrpShlGenPage::OnApply(void)
  2757. {
  2758. TRACE(CDsGrpShlGenPage,OnApply);
  2759. ADS_ATTR_INFO AttrInfoDesc = {g_wzDescription, ADS_ATTR_UPDATE,
  2760. ADSTYPE_CASE_IGNORE_STRING, NULL, 0};
  2761. ADSVALUE ADsValueDesc = {ADSTYPE_CASE_IGNORE_STRING, NULL};
  2762. AttrInfoDesc.pADsValues = &ADsValueDesc;
  2763. AttrInfoDesc.dwNumValues = 1;
  2764. LPTSTR ptsz;
  2765. if (m_fDescrDirty)
  2766. {
  2767. dspAssert(m_fDescrWritable);
  2768. ptsz = new TCHAR[DSPROP_DESCRIPTION_RANGE_UPPER + 1];
  2769. CHECK_NULL_REPORT(ptsz, m_hPage, return -1);
  2770. if (GetDlgItemText(m_hPage, IDC_DESCRIPTION_EDIT, ptsz, DSPROP_DESCRIPTION_RANGE_UPPER + 1) == 0)
  2771. {
  2772. // An empty control means remove the attribute value from the
  2773. // object.
  2774. //
  2775. AttrInfoDesc.dwNumValues = 0;
  2776. AttrInfoDesc.pADsValues = NULL;
  2777. AttrInfoDesc.dwControlCode = ADS_ATTR_CLEAR;
  2778. }
  2779. else
  2780. {
  2781. if (!TcharToUnicode(ptsz, &ADsValueDesc.CaseIgnoreString))
  2782. {
  2783. REPORT_ERROR(E_OUTOFMEMORY, m_hPage);
  2784. delete ptsz;
  2785. return -1;
  2786. }
  2787. }
  2788. delete ptsz;
  2789. DWORD cModified;
  2790. HRESULT hr = m_pDsObj->SetObjectAttributes(&AttrInfoDesc, 1, &cModified);
  2791. if (!CHECK_ADS_HR(&hr, m_hPage))
  2792. {
  2793. goto Cleanup;
  2794. }
  2795. m_fDescrDirty = FALSE;
  2796. }
  2797. Cleanup:
  2798. DO_DEL(ADsValueDesc.CaseExactString);
  2799. return CDsGrpMembersPage::OnApply();
  2800. }
  2801. //+----------------------------------------------------------------------------
  2802. //
  2803. // Method: CDsGrpShlGenPage::OnDestroy
  2804. //
  2805. // Synopsis: Exit cleanup
  2806. //
  2807. //-----------------------------------------------------------------------------
  2808. LRESULT
  2809. CDsGrpShlGenPage::OnDestroy(void)
  2810. {
  2811. ATTR_DATA ad = {0, (LPARAM)m_pCIcon};
  2812. GeneralPageIcon(this, &GenIcon, NULL, 0, &ad, fOnDestroy);
  2813. CDsGrpMembersPage::OnDestroy();
  2814. // If an application processes this message, it should return zero.
  2815. return 0;
  2816. }
  2817. //+----------------------------------------------------------------------------
  2818. //
  2819. // Function: CheckGroupUpdate
  2820. //
  2821. // Synopsis: Checks the result code to see if a group-specific error has
  2822. // occured.
  2823. //
  2824. //-----------------------------------------------------------------------------
  2825. BOOL
  2826. CheckGroupUpdate(HRESULT hr, HWND hPage, BOOL fAdd, PWSTR pwzDN)
  2827. {
  2828. if (SUCCEEDED(hr))
  2829. {
  2830. return TRUE;
  2831. }
  2832. if (hPage == NULL)
  2833. {
  2834. hPage = GetDesktopWindow();
  2835. }
  2836. DWORD dwErr = 0;
  2837. WCHAR wszErrBuf[MAX_PATH+1];
  2838. WCHAR wszNameBuf[MAX_PATH+1];
  2839. ADsGetLastError(&dwErr, wszErrBuf, MAX_PATH, wszNameBuf, MAX_PATH);
  2840. //
  2841. // ERROR_DS_CONSTRAINT_VIOLATION is the error returned for
  2842. // duplicate name.
  2843. //
  2844. if ((LDAP_RETCODE)dwErr == LDAP_CONSTRAINT_VIOLATION ||
  2845. hr == HRESULT_FROM_WIN32(ERROR_DS_CONSTRAINT_VIOLATION))
  2846. {
  2847. PTSTR ptzTitle, ptzMsg;
  2848. if (!LoadStringToTchar(IDS_MSG_TITLE, &ptzTitle))
  2849. {
  2850. goto FatalError;
  2851. }
  2852. if (!LoadStringToTchar((fAdd) ? IDS_ERRMSG_GROUP_CONSTRAINT :
  2853. IDS_ERRMSG_GROUP_DELETE, &ptzMsg))
  2854. {
  2855. delete ptzTitle;
  2856. goto FatalError;
  2857. }
  2858. MessageBox(hPage, ptzMsg, ptzTitle, MB_OK | MB_ICONEXCLAMATION);
  2859. delete [] ptzTitle;
  2860. delete [] ptzMsg;
  2861. }
  2862. else if (HRESULT_CODE(hr) == ERROR_DS_NO_SUCH_OBJECT && fAdd)
  2863. {
  2864. // Put a useful message up
  2865. PTSTR ptzTitle = 0, ptzMsg = 0;
  2866. if (!LoadStringToTchar(IDS_MSG_USER_NOT_PRESENT, &ptzMsg))
  2867. {
  2868. goto FatalError;
  2869. }
  2870. if (!LoadStringToTchar(IDS_MSG_TITLE, &ptzTitle))
  2871. {
  2872. goto FatalError;
  2873. }
  2874. MessageBox(hPage, ptzMsg, ptzTitle, MB_OK | MB_ICONEXCLAMATION);
  2875. delete[] ptzTitle;
  2876. delete[] ptzMsg;
  2877. }
  2878. else if (HRESULT_CODE(dwErr) == ERROR_DS_NO_ATTRIBUTE_OR_VALUE && !fAdd)
  2879. {
  2880. // No message needed
  2881. return FALSE;
  2882. }
  2883. else if (HRESULT_CODE(dwErr) == ERROR_MEMBER_NOT_IN_ALIAS && !fAdd)
  2884. {
  2885. // Put a useful message up
  2886. bool bShowGenericMessage = true;
  2887. if (pwzDN)
  2888. {
  2889. //
  2890. // Crack the DN into the name
  2891. //
  2892. CComPtr<IADsPathname> spPathcracker;
  2893. hr = CoCreateInstance(CLSID_Pathname,
  2894. NULL,
  2895. CLSCTX_INPROC_SERVER,
  2896. IID_IADsPathname,
  2897. (PVOID *)&spPathcracker);
  2898. if (SUCCEEDED(hr))
  2899. {
  2900. hr = spPathcracker->Set(pwzDN, ADS_SETTYPE_DN);
  2901. if (SUCCEEDED(hr))
  2902. {
  2903. hr = spPathcracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  2904. if (SUCCEEDED(hr))
  2905. {
  2906. CComBSTR sbstrName;
  2907. hr = spPathcracker->Retrieve(ADS_FORMAT_LEAF, &sbstrName);
  2908. if (SUCCEEDED(hr))
  2909. {
  2910. ErrMsgParam(IDS_MSG_MEMBER_ALREADY_GONE, (LPARAM)(PWSTR)sbstrName, hPage);
  2911. bShowGenericMessage = false;
  2912. }
  2913. }
  2914. }
  2915. }
  2916. }
  2917. if (bShowGenericMessage)
  2918. {
  2919. PTSTR ptzTitle = 0, ptzMsg = 0;
  2920. if (!LoadStringToTchar(IDS_MSG_MEMBER_ALREADY_GONE2, &ptzMsg))
  2921. {
  2922. goto FatalError;
  2923. }
  2924. if (!LoadStringToTchar(IDS_MSG_TITLE, &ptzTitle))
  2925. {
  2926. goto FatalError;
  2927. }
  2928. MessageBox(hPage, ptzMsg, ptzTitle, MB_OK | MB_ICONEXCLAMATION);
  2929. delete[] ptzTitle;
  2930. delete[] ptzMsg;
  2931. }
  2932. }
  2933. else
  2934. {
  2935. if (dwErr)
  2936. {
  2937. dspDebugOut((DEB_ERROR,
  2938. "Extended Error 0x%x: %ws %ws.\n", dwErr,
  2939. wszErrBuf, wszNameBuf));
  2940. ReportError(dwErr, IDS_ADS_ERROR_FORMAT, hPage);
  2941. }
  2942. else
  2943. {
  2944. dspDebugOut((DEB_ERROR, "Error %08lx\n", hr));
  2945. ReportError(hr, IDS_ADS_ERROR_FORMAT, hPage);
  2946. }
  2947. }
  2948. return FALSE;
  2949. FatalError:
  2950. MessageBoxA(hPage, "A Fatal Error has occured!", "Active Directory Service",
  2951. MB_OK | MB_ICONEXCLAMATION);
  2952. return FALSE;
  2953. }
  2954. //+----------------------------------------------------------------------------
  2955. //
  2956. // Function: FindFPO
  2957. //
  2958. // Synopsis: Given a SID, look for a corresponding FPO.
  2959. //
  2960. //-----------------------------------------------------------------------------
  2961. HRESULT
  2962. FindFPO(PSID pSid, PWSTR pwzDomain, CStrW & strFPODN)
  2963. {
  2964. HRESULT hr;
  2965. CDSSearch Srch;
  2966. hr = Srch.Init(pwzDomain);
  2967. CHECK_HRESULT(hr, return hr);
  2968. PWSTR rgpwzAttrNames[] = {g_wzDN};
  2969. hr = Srch.SetAttributeList(rgpwzAttrNames, 1);
  2970. CHECK_HRESULT(hr, return hr);
  2971. Srch.SetSearchScope(ADS_SCOPE_SUBTREE);
  2972. WCHAR wzSearchFormat[] = L"(&(objectCategory=foreignSecurityPrincipal)(objectSid=%s))";
  2973. PWSTR pwzSID;
  2974. CStrW strSearchFilter;
  2975. hr = ADsEncodeBinaryData((PBYTE)pSid,
  2976. GetLengthSid(pSid),
  2977. &pwzSID);
  2978. CHECK_HRESULT(hr, return hr);
  2979. strSearchFilter.Format(wzSearchFormat, pwzSID);
  2980. FreeADsMem(pwzSID);
  2981. Srch.SetFilterString(const_cast<LPWSTR>((LPCWSTR)strSearchFilter));
  2982. hr = Srch.DoQuery();
  2983. CHECK_HRESULT(hr, return hr);
  2984. hr = Srch.GetNextRow();
  2985. if (hr == S_ADS_NOMORE_ROWS)
  2986. {
  2987. // No object has a matching SID, the FPO must have been deleted.
  2988. //
  2989. return HRESULT_FROM_WIN32(ERROR_DS_OBJ_NOT_FOUND);
  2990. }
  2991. CHECK_HRESULT(hr, return hr);
  2992. ADS_SEARCH_COLUMN Column;
  2993. hr = Srch.GetColumn(g_wzDN, &Column);
  2994. CHECK_HRESULT(hr, return hr);
  2995. strFPODN = Column.pADsValues->CaseIgnoreString;
  2996. if (strFPODN.IsEmpty())
  2997. {
  2998. Srch.FreeColumn(&Column);
  2999. return E_OUTOFMEMORY;
  3000. }
  3001. Srch.FreeColumn(&Column);
  3002. return S_OK;
  3003. }
  3004. //+----------------------------------------------------------------------------
  3005. //
  3006. // Class: CMemberDomainMode
  3007. //
  3008. // Purpose: Maintains a list of all domains in the enterprise from which
  3009. // members have been added along with those domains' mode. Keeps
  3010. // a second list of members who have been added from mixed-mode
  3011. // domains.
  3012. //
  3013. //-----------------------------------------------------------------------------
  3014. void
  3015. CMemberDomainMode::Init(CDsPropPageBase * pPage)
  3016. {
  3017. m_pPage = pPage;
  3018. m_MemberList.Clear();
  3019. }
  3020. HRESULT
  3021. CMemberDomainMode::CheckMember(PWSTR pwzMemberDN)
  3022. {
  3023. HRESULT hr;
  3024. CComBSTR cbstrDomain;
  3025. BOOL fMixed = FALSE;
  3026. hr = GetObjectsDomain(m_pPage, pwzMemberDN, &cbstrDomain);
  3027. if (SUCCEEDED(hr) && cbstrDomain)
  3028. {
  3029. if (!m_DomainList.Find(cbstrDomain, &fMixed))
  3030. {
  3031. // The member's domain is not already in the list. Read the domain
  3032. // mode and then add it.
  3033. //
  3034. hr = GetDomainMode(cbstrDomain, m_pPage->GetHWnd(), &fMixed);
  3035. CHECK_HRESULT(hr, return hr);
  3036. hr = m_DomainList.Insert(cbstrDomain, fMixed);
  3037. CHECK_HRESULT_REPORT(hr, m_pPage->GetHWnd(), return hr);
  3038. }
  3039. }
  3040. if (fMixed)
  3041. {
  3042. PWSTR pwzCanEx;
  3043. PTSTR ptzCanEx;
  3044. CStr strName;
  3045. hr = CrackName(pwzMemberDN, &pwzCanEx, GET_OBJ_CAN_NAME_EX, m_pPage->GetHWnd());
  3046. CHECK_HRESULT(hr, return hr);
  3047. if (!UnicodeToTchar(pwzCanEx, &ptzCanEx))
  3048. {
  3049. LocalFreeStringW(&pwzCanEx);
  3050. REPORT_ERROR(E_OUTOFMEMORY, m_pPage->GetHWnd());
  3051. return E_OUTOFMEMORY;
  3052. }
  3053. LocalFreeStringW(&pwzCanEx);
  3054. CStr cstrFolder;
  3055. GetNameParts(ptzCanEx, cstrFolder, strName);
  3056. DO_DEL(ptzCanEx);
  3057. hr = m_MemberList.Insert(strName);
  3058. CHECK_HRESULT_REPORT(hr, m_pPage->GetHWnd(), return hr);
  3059. }
  3060. return S_OK;
  3061. }
  3062. HRESULT
  3063. CMemberDomainMode::ListExternalMembers(CStr & strList)
  3064. {
  3065. m_MemberList.GetList(strList);
  3066. return S_OK;
  3067. }
  3068. //+----------------------------------------------------------------------------
  3069. //
  3070. // CMemberDomainMode helper classes
  3071. //
  3072. //-----------------------------------------------------------------------------
  3073. HRESULT
  3074. CMMMemberList::Insert(LPCTSTR ptzName)
  3075. {
  3076. CMMMemberListItem * pItem = new CMMMemberListItem;
  3077. if (!pItem)
  3078. {
  3079. return E_OUTOFMEMORY;
  3080. }
  3081. pItem->m_strName = ptzName;
  3082. if (m_pListHead == NULL)
  3083. {
  3084. m_pListHead = pItem;
  3085. }
  3086. else
  3087. {
  3088. pItem->LinkAfter(m_pListHead);
  3089. }
  3090. return S_OK;
  3091. }
  3092. #define MAX_MMMLISTING 25
  3093. void
  3094. CMMMemberList::GetList(CStr & strList)
  3095. {
  3096. int nCount = 0;
  3097. strList.Empty();
  3098. CMMMemberListItem * pItem = m_pListHead;
  3099. while (pItem)
  3100. {
  3101. strList += pItem->m_strName;
  3102. nCount++;
  3103. pItem = pItem->Next();
  3104. if (pItem)
  3105. {
  3106. if (nCount > MAX_MMMLISTING)
  3107. {
  3108. strList += TEXT("...");
  3109. return;
  3110. }
  3111. else
  3112. {
  3113. strList += TEXT(", ");
  3114. }
  3115. }
  3116. }
  3117. }
  3118. void
  3119. CMMMemberList::Clear(void)
  3120. {
  3121. CMMMemberListItem * pItem = m_pListHead, * pNext;
  3122. while (pItem)
  3123. {
  3124. pNext = pItem->Next();
  3125. delete pItem;
  3126. pItem = pNext;
  3127. }
  3128. m_pListHead = NULL;
  3129. }
  3130. CDomainModeList::~CDomainModeList(void)
  3131. {
  3132. CDomainModeListItem * pItem = m_pListHead, * pNext;
  3133. while (pItem)
  3134. {
  3135. pNext = pItem->Next();
  3136. delete pItem;
  3137. pItem = pNext;
  3138. }
  3139. }
  3140. HRESULT
  3141. CDomainModeList::Insert(PWSTR pwzDomain, BOOL fMixed)
  3142. {
  3143. CDomainModeListItem * pItem = new CDomainModeListItem;
  3144. if (!pItem)
  3145. {
  3146. return E_OUTOFMEMORY;
  3147. }
  3148. pItem->m_strName = pwzDomain;
  3149. pItem->m_fMixed = fMixed;
  3150. if (m_pListHead == NULL)
  3151. {
  3152. m_pListHead = pItem;
  3153. }
  3154. else
  3155. {
  3156. pItem->LinkAfter(m_pListHead);
  3157. }
  3158. return S_OK;
  3159. }
  3160. BOOL
  3161. CDomainModeList::Find(LPCWSTR pwzDomain, PBOOL pfMixed)
  3162. {
  3163. CDomainModeListItem * pItem = m_pListHead;
  3164. while (pItem)
  3165. {
  3166. if (_wcsicmp(pwzDomain, pItem->m_strName) == 0)
  3167. {
  3168. *pfMixed = pItem->m_fMixed;
  3169. return TRUE;
  3170. }
  3171. pItem = pItem->Next();
  3172. }
  3173. return FALSE;
  3174. }