//+---------------------------------------------------------------------------- // // Windows NT Directory Service Property Pages // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1999 // // File: group.cxx // // Contents: CDsGroupGenObjPage, the class that implements the group object // general property page, CDsGrpMembersPage for the group // membership page, and CDsGrpShlGenPage for the shell group // general page. // // History: 10-April-97 EricB created // //----------------------------------------------------------------------------- #include "pch.h" #include "proppage.h" #include "group.h" #include "qrybase.h" #define BULK_ADD 1 #ifdef DSADMIN #define DESCR_IDX 0 #define SAMNAME_IDX 1 #define EMAIL_IDX 2 #define COMMENT_IDX 3 //+---------------------------------------------------------------------------- // // Member: CDsGroupGenObjPage::CDsGroupGenObjPage // //----------------------------------------------------------------------------- CDsGroupGenObjPage::CDsGroupGenObjPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj, HWND hNotifyObj, DWORD dwFlags) : m_pCIcon(NULL), m_fMixed(TRUE), m_dwType(0), m_fTypeWritable(FALSE), m_fDescrWritable(FALSE), m_fSamNameWritable(FALSE), m_fEmailWritable(FALSE), m_fCommentWritable(FALSE), m_fTypeDirty(FALSE), m_fDescrDirty(FALSE), m_fSamNameDirty(FALSE), m_fEmailDirty(FALSE), m_fCommentDirty(FALSE), CDsPropPageBase(pDsPage, pDataObj, hNotifyObj, dwFlags) { TRACE(CDsGroupGenObjPage,CDsGroupGenObjPage); #ifdef _DEBUG strcpy(szClass, "CDsGroupGenObjPage"); #endif } //+---------------------------------------------------------------------------- // // Member: CDsGroupGenObjPage::~CDsGroupGenObjPage // //----------------------------------------------------------------------------- CDsGroupGenObjPage::~CDsGroupGenObjPage() { TRACE(CDsGroupGenObjPage,~CDsGroupGenObjPage); } //+---------------------------------------------------------------------------- // // Function: CreateGroupGenObjPage // // Synopsis: Creates an instance of a page window. // //----------------------------------------------------------------------------- HRESULT CreateGroupGenObjPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj, PWSTR pwzADsPath, PWSTR pwzClass, HWND hNotifyObj, DWORD dwFlags, CDSBasePathsInfo* pBasePathsInfo, HPROPSHEETPAGE * phPage) { TRACE_FUNCTION(CreateGroupGenObjPage); CDsGroupGenObjPage * pPageObj = new CDsGroupGenObjPage(pDsPage, pDataObj, hNotifyObj, dwFlags); CHECK_NULL(pPageObj, return E_OUTOFMEMORY); pPageObj->Init(pwzADsPath, pwzClass, pBasePathsInfo); return pPageObj->CreatePage(phPage); } //+---------------------------------------------------------------------------- // // Method: CDsPropPageBase::DlgProc // // Synopsis: per-instance dialog proc // //----------------------------------------------------------------------------- LRESULT CDsGroupGenObjPage::DlgProc(HWND, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == g_uChangeMsg) { OnAttrChanged(wParam); return TRUE; } switch (uMsg) { case WM_INITDIALOG: return InitDlg(lParam); case WM_NOTIFY: return OnNotify(wParam, lParam); case PSM_QUERYSIBLINGS: OnQuerySiblings(wParam, lParam); break; case WM_SHOWWINDOW: return OnShowWindow(); case WM_SETFOCUS: return OnSetFocus((HWND)wParam); case WM_HELP: return OnHelp((LPHELPINFO)lParam); case WM_COMMAND: if (m_fInInit) { return TRUE; } return(OnCommand(GET_WM_COMMAND_ID(wParam, lParam), GET_WM_COMMAND_HWND(wParam, lParam), GET_WM_COMMAND_CMD(wParam, lParam))); case WM_DESTROY: return OnDestroy(); default: return FALSE; } return TRUE; } //+---------------------------------------------------------------------------- // // Method: CDsGroupGenObjPage::OnInitDialog // // Synopsis: Set the initial control values from the corresponding DS // attributes. // //----------------------------------------------------------------------------- HRESULT CDsGroupGenObjPage::OnInitDialog(LPARAM) { TRACE(CDsGroupGenObjPage,OnInitDialog); HRESULT hr; PADS_ATTR_INFO pAttrs = NULL; DWORD cAttrs = 0; CWaitCursor Wait; if (!ADsPropSetHwnd(m_hNotifyObj, m_hPage)) { m_pWritableAttrs = NULL; } PTSTR ptzRDN; if (!UnicodeToTchar(m_pwszRDName, &ptzRDN)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); return S_OK; } SetDlgItemText(m_hPage, IDC_CN, ptzRDN); delete ptzRDN; // // Get the icon from the DS and put it on the page. // ATTR_DATA ad = {0, 0}; hr = GeneralPageIcon(this, &GenIcon, NULL, 0, &ad, fInit); CHECK_HRESULT_REPORT(hr, m_hPage, return S_OK); m_pCIcon = (CDsIconCtrl *)ad.pVoid; m_fTypeWritable = CheckIfWritable(g_wzGroupType); m_fDescrWritable = CheckIfWritable(m_rgpAttrMap[DESCR_IDX]->AttrInfo.pszAttrName); m_fSamNameWritable = CheckIfWritable(m_rgpAttrMap[SAMNAME_IDX]->AttrInfo.pszAttrName); m_fEmailWritable = CheckIfWritable(m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName); m_fCommentWritable = CheckIfWritable(m_rgpAttrMap[COMMENT_IDX]->AttrInfo.pszAttrName); // // Get description, SAM name, email address, and comment attributes. // SendDlgItemMessage(m_hPage, m_rgpAttrMap[DESCR_IDX]->nCtrlID, EM_LIMITTEXT, m_rgpAttrMap[DESCR_IDX]->nSizeLimit, 0); SendDlgItemMessage(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, EM_LIMITTEXT, m_rgpAttrMap[SAMNAME_IDX]->nSizeLimit, 0); SendDlgItemMessage(m_hPage, m_rgpAttrMap[EMAIL_IDX]->nCtrlID, EM_LIMITTEXT, m_rgpAttrMap[EMAIL_IDX]->nSizeLimit, 0); SendDlgItemMessage(m_hPage, m_rgpAttrMap[COMMENT_IDX]->nCtrlID, EM_LIMITTEXT, m_rgpAttrMap[COMMENT_IDX]->nSizeLimit, 0); PWSTR rgpwzAttrNames[] = {m_rgpAttrMap[DESCR_IDX]->AttrInfo.pszAttrName, m_rgpAttrMap[SAMNAME_IDX]->AttrInfo.pszAttrName, m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName, m_rgpAttrMap[COMMENT_IDX]->AttrInfo.pszAttrName, g_wzGroupType}; hr = m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 5, &pAttrs, &cAttrs); if (!CHECK_ADS_HR_IGNORE_UNFOUND_ATTR(&hr, m_hPage)) { return S_OK; } for (DWORD i = 0; i < cAttrs; i++) { dspAssert(pAttrs); dspAssert(pAttrs[i].pADsValues); PTSTR ptz; if (_wcsicmp(pAttrs[i].pszAttrName, m_rgpAttrMap[DESCR_IDX]->AttrInfo.pszAttrName) == 0) { // description. // if (!UnicodeToTchar(pAttrs[i].pADsValues->CaseIgnoreString, &ptz)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); FreeADsMem(pAttrs); return S_OK; } SetDlgItemText(m_hPage, m_rgpAttrMap[DESCR_IDX]->nCtrlID, ptz); delete ptz; } if (_wcsicmp(pAttrs[i].pszAttrName, m_rgpAttrMap[SAMNAME_IDX]->AttrInfo.pszAttrName) == 0) { // SAM name. // if (!UnicodeToTchar(pAttrs[i].pADsValues->CaseIgnoreString, &ptz)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); FreeADsMem(pAttrs); return S_OK; } SetDlgItemText(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, ptz); delete ptz; } if (_wcsicmp(pAttrs[i].pszAttrName, m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName) == 0) { // email address. // if (!UnicodeToTchar(pAttrs[i].pADsValues->CaseIgnoreString, &ptz)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); FreeADsMem(pAttrs); return S_OK; } SetDlgItemText(m_hPage, m_rgpAttrMap[EMAIL_IDX]->nCtrlID, ptz); delete ptz; } if (_wcsicmp(pAttrs[i].pszAttrName, m_rgpAttrMap[COMMENT_IDX]->AttrInfo.pszAttrName) == 0) { // comment. // if (!UnicodeToTchar(pAttrs[i].pADsValues->CaseIgnoreString, &ptz)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); FreeADsMem(pAttrs); return S_OK; } SetDlgItemText(m_hPage, m_rgpAttrMap[COMMENT_IDX]->nCtrlID, ptz); delete ptz; } if (_wcsicmp(pAttrs[i].pszAttrName, g_wzGroupType) == 0) { // group type. // m_dwType = pAttrs[i].pADsValues->Integer; } } if (pAttrs) { FreeADsMem(pAttrs); } // // Get the domain type and set the buttons accordingly. // GetDomainMode(this, &m_fMixed); BOOL Sec = m_dwType & GROUP_TYPE_SECURITY_ENABLED; CheckDlgButton(m_hPage, (Sec) ? IDC_RADIO_SEC_ENABLED : IDC_RADIO_SEC_DISABLED, BST_CHECKED); if (m_fMixed) { EnableWindow(GetDlgItem(m_hPage, (Sec) ? IDC_RADIO_SEC_DISABLED : IDC_RADIO_SEC_ENABLED), FALSE); EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_UNIVERSAL), FALSE); } UINT id; if (m_dwType & GROUP_TYPE_ACCOUNT_GROUP) { id = IDC_RADIO_ACCOUNT; EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_RESOURCE), FALSE); } else if (m_dwType & GROUP_TYPE_RESOURCE_GROUP) { id = IDC_RADIO_RESOURCE; EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_ACCOUNT), FALSE); if (m_dwType & GROUP_TYPE_BUILTIN_LOCAL_GROUP) { TCHAR szLabel[100]; if (!LoadStringReport(IDS_BUILTIN_GROUP, szLabel, 100, m_hPage)) { hr = E_OUTOFMEMORY; return S_OK; } SetWindowText(GetDlgItem(m_hPage, IDC_RADIO_RESOURCE), szLabel); } } else if (m_dwType & GROUP_TYPE_UNIVERSAL_GROUP) { id = IDC_RADIO_UNIVERSAL; EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_ACCOUNT), m_fMixed ? FALSE : TRUE); EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_RESOURCE), m_fMixed ? FALSE : TRUE); EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_UNIVERSAL), TRUE); } else { // // Probably a default but we should never get here anyway // id = IDC_RADIO_ACCOUNT; #if DBG == 1 dspAssert(FALSE && "Unknown group type!"); #endif } CheckDlgButton(m_hPage, id, BST_CHECKED); bool fIsSpecialAccount = false; IsSpecialAccount (fIsSpecialAccount); if (!m_fTypeWritable || (m_dwType & GROUP_TYPE_BUILTIN_LOCAL_GROUP) || fIsSpecialAccount) { EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_ACCOUNT), FALSE); EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_RESOURCE), FALSE); EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_UNIVERSAL), FALSE); EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_SEC_ENABLED), FALSE); EnableWindow(GetDlgItem(m_hPage, IDC_RADIO_SEC_DISABLED), FALSE); } if (!m_fDescrWritable) { SendDlgItemMessage(m_hPage, m_rgpAttrMap[DESCR_IDX]->nCtrlID, EM_SETREADONLY, (WPARAM)TRUE, 0); } if (!m_fSamNameWritable) { SendDlgItemMessage(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, EM_SETREADONLY, (WPARAM)TRUE, 0); } if (!m_fEmailWritable) { SendDlgItemMessage(m_hPage, m_rgpAttrMap[EMAIL_IDX]->nCtrlID, EM_SETREADONLY, (WPARAM)TRUE, 0); } if (!m_fCommentWritable) { SendDlgItemMessage(m_hPage, m_rgpAttrMap[COMMENT_IDX]->nCtrlID, EM_SETREADONLY, (WPARAM)TRUE, 0); } return S_OK; } //+---------------------------------------------------------------------------- // // Method: CDsGroupGenObjPage::IsSpecialAccount // // Synopsis: Returns true if group RID indicates a special account // //----------------------------------------------------------------------------- HRESULT CDsGroupGenObjPage::IsSpecialAccount(bool& fIsSpecialAccount) { // // Get the group SID. This is a required attribute so bail if not found. // PWSTR rgpwzAttrNames[] = {g_wzObjectSID}; PADS_ATTR_INFO pAttrs = NULL; DWORD cAttrs = 0; CWaitCursor Wait; HRESULT hr = m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs); if (!CHECK_ADS_HR(&hr, m_hPage)) { return hr; } dspAssert(cAttrs); if (cAttrs != 1) { return E_FAIL; } dspAssert(pAttrs); PUCHAR saCount = GetSidSubAuthorityCount(pAttrs->pADsValues->OctetString.lpValue); DWORD dwGroupRID = *GetSidSubAuthority(pAttrs->pADsValues->OctetString.lpValue, (ULONG)*saCount - 1); dspDebugOut((DEB_ITRACE, "Group RID = %d\n", dwGroupRID)); // This is the highest special account RID or alias in ntseapi.h if ( dwGroupRID <= DOMAIN_ALIAS_RID_RAS_SERVERS ) fIsSpecialAccount = true; FreeADsMem(pAttrs); return hr; } //+---------------------------------------------------------------------------- // // Method: CDsGroupGenObjPage::OnApply // // Synopsis: Handles the Apply notification. // //----------------------------------------------------------------------------- LRESULT CDsGroupGenObjPage::OnApply(void) { TRACE(CDsGroupGenObjPage,OnApply); HRESULT hr = S_OK; ADSVALUE ADsValueType = {ADSTYPE_INTEGER, 0}; ADS_ATTR_INFO AttrInfoDesc = m_rgpAttrMap[DESCR_IDX]->AttrInfo; ADS_ATTR_INFO AttrInfoSAMn = m_rgpAttrMap[SAMNAME_IDX]->AttrInfo; ADS_ATTR_INFO AttrInfoMail = m_rgpAttrMap[EMAIL_IDX]->AttrInfo; ADS_ATTR_INFO AttrInfoComm = m_rgpAttrMap[COMMENT_IDX]->AttrInfo; ADS_ATTR_INFO AttrInfoType = {g_wzGroupType, ADS_ATTR_UPDATE, ADSTYPE_INTEGER, &ADsValueType, 1}; ADS_ATTR_INFO rgAttrs[5]; DWORD cAttrs = 0; ADSVALUE ADsValueDesc = {m_rgpAttrMap[DESCR_IDX]->AttrInfo.dwADsType, NULL}; ADSVALUE ADsValueSAMname = {m_rgpAttrMap[SAMNAME_IDX]->AttrInfo.dwADsType, NULL}; ADSVALUE ADsValueComm = {m_rgpAttrMap[COMMENT_IDX]->AttrInfo.dwADsType, NULL}; ADSVALUE ADsValueMail = {m_rgpAttrMap[EMAIL_IDX]->AttrInfo.dwADsType, NULL}; // // Description. // AttrInfoDesc.pADsValues = &ADsValueDesc; AttrInfoDesc.dwNumValues = 1; LPTSTR ptsz; if (m_fDescrDirty) { dspAssert(m_fDescrWritable); ptsz = new TCHAR[m_rgpAttrMap[DESCR_IDX]->nSizeLimit + 1]; CHECK_NULL(ptsz, return -1); if (GetDlgItemText(m_hPage, m_rgpAttrMap[DESCR_IDX]->nCtrlID, ptsz, m_rgpAttrMap[DESCR_IDX]->nSizeLimit + 1) == 0) { // An empty control means remove the attribute value from the // object. // AttrInfoDesc.dwNumValues = 0; AttrInfoDesc.pADsValues = NULL; AttrInfoDesc.dwControlCode = ADS_ATTR_CLEAR; } else { if (!TcharToUnicode(ptsz, &ADsValueDesc.CaseIgnoreString)) { delete[] ptsz; return -1; } } delete[] ptsz; rgAttrs[cAttrs++] = AttrInfoDesc; } // // SAM name. // AttrInfoSAMn.pADsValues = &ADsValueSAMname; AttrInfoSAMn.dwNumValues = 1; if (m_fSamNameDirty) { dspAssert(m_fSamNameWritable); ptsz = new TCHAR[m_rgpAttrMap[SAMNAME_IDX]->nSizeLimit + 1]; if (ptsz == NULL) { DO_DEL(ADsValueDesc.CaseExactString) return -1; } if (GetDlgItemText(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, ptsz, m_rgpAttrMap[SAMNAME_IDX]->nSizeLimit + 1) == 0) { ErrMsg (IDS_ERR_DNLEVELNAME_MISSING, m_hPage); delete[] ptsz; hr = E_FAIL; goto Cleanup; } else { CStr csSAMName = ptsz; // // Now check for illegal characters // bool bSAMNameChanged = false; int iFind = csSAMName.FindOneOf(INVALID_ACCOUNT_NAME_CHARS); if (iFind != -1 && !csSAMName.IsEmpty()) { PVOID apv[1] = {(LPWSTR)(LPCWSTR)csSAMName}; if (IDYES == SuperMsgBox(m_hPage, IDS_GROUP_SAMNAME_ILLEGAL, 0, MB_YESNO | MB_ICONWARNING, S_OK, apv, 1, FALSE, __FILE__, __LINE__)) { while (iFind != -1) { csSAMName.SetAt(iFind, L'_'); iFind = csSAMName.FindOneOf(INVALID_ACCOUNT_NAME_CHARS); bSAMNameChanged = true; } } else { // // Set the focus to the edit box and select the text // SetFocus(GetDlgItem(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID)); SendDlgItemMessage(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, EM_SETSEL, 0, -1); delete[] ptsz; hr = E_FAIL; goto Cleanup; } } if (bSAMNameChanged) { // // Write the change back to the control // SetDlgItemText(m_hPage, m_rgpAttrMap[SAMNAME_IDX]->nCtrlID, const_cast((LPCWSTR)csSAMName)); } if (!AllocWStr((PWSTR)(PCWSTR)csSAMName, &ADsValueSAMname.CaseIgnoreString)) { delete[] ptsz; DO_DEL(ADsValueDesc.CaseExactString) return -1; } } delete[] ptsz; rgAttrs[cAttrs++] = AttrInfoSAMn; } // // Email Address. // AttrInfoMail.pADsValues = &ADsValueMail; AttrInfoMail.dwNumValues = 1; if (m_fEmailWritable) { if (!m_fEmailDirty) { SendMessage(GetParent(GetHWnd()), PSM_QUERYSIBLINGS, (WPARAM)m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName, (LPARAM)GetHWnd()); } // SendMessage is syncronous. If the sibling page has an updated email // attribute value, it will get written to this page's edit control // and the dirty state member will be set. So, check it now rather than // use a 'else' clause after the above 'if' clause. // if (m_fEmailDirty) { ptsz = new TCHAR[m_rgpAttrMap[EMAIL_IDX]->nSizeLimit + 1]; if (ptsz == NULL) { DO_DEL(ADsValueDesc.CaseExactString) DO_DEL(ADsValueSAMname.CaseIgnoreString); return -1; } if (GetDlgItemText(m_hPage, m_rgpAttrMap[EMAIL_IDX]->nCtrlID, ptsz, m_rgpAttrMap[EMAIL_IDX]->nSizeLimit + 1) == 0) { AttrInfoMail.dwNumValues = 0; AttrInfoMail.pADsValues = NULL; AttrInfoMail.dwControlCode = ADS_ATTR_CLEAR; } else { if (!TcharToUnicode(ptsz, &ADsValueMail.CaseIgnoreString)) { delete[] ptsz; hr = E_OUTOFMEMORY; goto Cleanup; } if (!FValidSMTPAddress(ADsValueMail.CaseIgnoreString)) { ErrMsg(IDS_INVALID_MAIL_ADDR, GetHWnd()); delete [] ptsz; hr = E_FAIL; goto Cleanup; } } delete[] ptsz; rgAttrs[cAttrs++] = AttrInfoMail; } } // // Comment. // AttrInfoComm.pADsValues = &ADsValueComm; AttrInfoComm.dwNumValues = 1; if (m_fCommentDirty) { dspAssert(m_fCommentWritable); ptsz = new TCHAR[m_rgpAttrMap[COMMENT_IDX]->nSizeLimit + 1]; if (ptsz == NULL) { DO_DEL(ADsValueDesc.CaseExactString) DO_DEL(ADsValueSAMname.CaseIgnoreString); DO_DEL(ADsValueMail.CaseExactString) return -1; } if (GetDlgItemText(m_hPage, m_rgpAttrMap[COMMENT_IDX]->nCtrlID, ptsz, m_rgpAttrMap[COMMENT_IDX]->nSizeLimit + 1) == 0) { AttrInfoComm.dwNumValues = 0; AttrInfoComm.pADsValues = NULL; AttrInfoComm.dwControlCode = ADS_ATTR_CLEAR; } else { if (!TcharToUnicode(ptsz, &ADsValueComm.CaseIgnoreString)) { delete[] ptsz; DO_DEL(ADsValueDesc.CaseExactString) DO_DEL(ADsValueSAMname.CaseIgnoreString); DO_DEL(ADsValueMail.CaseExactString) return -1; } } delete[] ptsz; rgAttrs[cAttrs++] = AttrInfoComm; } // // set the group type flags // if (m_fTypeDirty) { dspAssert(m_fTypeWritable); BOOL Account = (IsDlgButtonChecked (m_hPage,IDC_RADIO_ACCOUNT) == BST_CHECKED); BOOL Resource = (IsDlgButtonChecked (m_hPage,IDC_RADIO_RESOURCE) == BST_CHECKED); BOOL Security = (IsDlgButtonChecked (m_hPage, IDC_RADIO_SEC_ENABLED) == BST_CHECKED); if (Security) { ADsValueType.Integer = GROUP_TYPE_SECURITY_ENABLED; } else { if (m_dwType & GROUP_TYPE_SECURITY_ENABLED) { TCHAR szTitle[80], szMessage[512]; if (!LoadStringReport(IDS_MSG_TITLE, szTitle, 80, m_hPage)) { hr = E_OUTOFMEMORY; goto Cleanup; } if (!LoadStringReport(IDS_MSG_DISABLING_SECURITY, szMessage, 512, m_hPage)) { hr = E_OUTOFMEMORY; goto Cleanup; } LONG iRet = MessageBox(m_hPage, szMessage, szTitle, MB_YESNO | MB_ICONWARNING); if (iRet == IDNO) { // // The user declined, so go back to prop sheet. // hr = S_FALSE; goto Cleanup; } } ADsValueType.Integer = 0; } if (Resource) { ADsValueType.Integer |= GROUP_TYPE_RESOURCE_GROUP; } else { if (Account) { ADsValueType.Integer |= GROUP_TYPE_ACCOUNT_GROUP; } else { ADsValueType.Integer |= GROUP_TYPE_UNIVERSAL_GROUP; } } rgAttrs[cAttrs++] = AttrInfoType; } // // Write the description, and group type. // DWORD cModified; hr = m_pDsObj->SetObjectAttributes(rgAttrs, cAttrs, &cModified); if (!CHECK_ADS_HR(&hr, m_hPage)) { goto Cleanup; } m_fTypeDirty = m_fDescrDirty = m_fSamNameDirty = m_fEmailDirty = m_fCommentDirty = FALSE; Cleanup: DO_DEL(ADsValueDesc.CaseExactString) DO_DEL(ADsValueSAMname.CaseIgnoreString); DO_DEL(ADsValueMail.CaseExactString) DO_DEL(ADsValueComm.CaseExactString) if (hr == S_FALSE) return PSNRET_INVALID_NOCHANGEPAGE; else return (SUCCEEDED(hr)) ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE; } //+---------------------------------------------------------------------------- // // Method: CDsGroupGenObjPage::OnCommand // // Synopsis: Handle control notifications. // //----------------------------------------------------------------------------- LRESULT CDsGroupGenObjPage::OnCommand(int id, HWND hwndCtl, UINT codeNotify) { if (m_fInInit) { return 0; } switch (codeNotify) { case BN_CLICKED: if ((id == IDC_RADIO_UNIVERSAL) || (id == IDC_RADIO_RESOURCE) || (id == IDC_RADIO_ACCOUNT)) { int iCheck1, iCheck2; switch (id) { case IDC_RADIO_UNIVERSAL: iCheck1 = IDC_RADIO_RESOURCE; iCheck2 = IDC_RADIO_ACCOUNT; break; case IDC_RADIO_RESOURCE: iCheck1 = IDC_RADIO_UNIVERSAL; iCheck2 = IDC_RADIO_ACCOUNT; break; case IDC_RADIO_ACCOUNT: iCheck1 = IDC_RADIO_UNIVERSAL; iCheck2 = IDC_RADIO_RESOURCE; break; default: dspAssert(FALSE); iCheck1 = IDC_RADIO_RESOURCE; iCheck2 = IDC_RADIO_ACCOUNT; break; } CheckDlgButton(m_hPage, iCheck1, BST_UNCHECKED); CheckDlgButton(m_hPage, iCheck2, BST_UNCHECKED); m_fTypeDirty = TRUE; SetDirty(); } if ((id == IDC_RADIO_SEC_ENABLED) || (id == IDC_RADIO_SEC_DISABLED)) { CheckDlgButton(m_hPage, (id == IDC_RADIO_SEC_ENABLED) ? IDC_RADIO_SEC_DISABLED : IDC_RADIO_SEC_ENABLED, BST_UNCHECKED); m_fTypeDirty = TRUE; SetDirty(); } break; case EN_CHANGE: switch (id) { case IDC_EMAIL_EDIT: m_fEmailDirty = TRUE; break; case IDC_DESCRIPTION_EDIT: m_fDescrDirty = TRUE; break; case IDC_SAM_NAME_EDIT: m_fSamNameDirty = TRUE; break; case IDC_EDIT_COMMENT: m_fCommentDirty = TRUE; break; } break; } return CDsPropPageBase::OnCommand(id, hwndCtl, codeNotify); } //+---------------------------------------------------------------------------- // // Method: CDsGroupGenObjPage::OnNotify // // Synopsis: Handles list notification messages // //----------------------------------------------------------------------------- LRESULT CDsGroupGenObjPage::OnNotify(WPARAM wParam, LPARAM lParam) { if (m_fInInit) { return 0; } if (((LPNMHDR)lParam)->code == PSN_SETACTIVE) { dspDebugOut((DEB_ITRACE, "(HWND: %08x) got PSN_SETACTIVE, sending PSM_QUERYSIBLINGS.\n", GetHWnd())); SendMessage(GetParent(GetHWnd()), PSM_QUERYSIBLINGS, (WPARAM)m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName, (LPARAM)GetHWnd()); } return CDsPropPageBase::OnNotify(wParam, lParam); } //+---------------------------------------------------------------------------- // // Method: CDsGroupGenObjPage::OnQuerySiblings // // Synopsis: Inter-page communications for shared attributes. // // lParam == the HWND of the sending window. // wParam == the name of the attribute whose status is sought. // //----------------------------------------------------------------------------- void CDsGroupGenObjPage::OnQuerySiblings(WPARAM wParam, LPARAM lParam) { PWSTR pwz = NULL; int cch; #if DBG == 1 char szBuf[100]; strcpy(szBuf, "(HWND: %08x) got PSM_QUERYSIBLINGS for '%ws'"); #endif if ((HWND)lParam != GetHWnd()) { if (m_fEmailDirty && wParam && _wcsicmp((PWSTR)wParam, m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName) == 0) { #if DBG == 1 strcat(szBuf, " sending DSPROP_ATTRCHANGED_MSG"); #endif ADS_ATTR_INFO Attr; ADSVALUE ADsValue; cch = (int)SendDlgItemMessage(GetHWnd(), m_rgpAttrMap[EMAIL_IDX]->nCtrlID, WM_GETTEXTLENGTH, 0, 0); pwz = new WCHAR[++cch]; CHECK_NULL_REPORT(pwz, GetHWnd(), return); Attr.dwNumValues = 1; Attr.pszAttrName = m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName; Attr.pADsValues = &ADsValue; Attr.pADsValues->dwType = m_rgpAttrMap[EMAIL_IDX]->AttrInfo.dwADsType; Attr.pADsValues->CaseIgnoreString = pwz; GetDlgItemText(GetHWnd(), m_rgpAttrMap[EMAIL_IDX]->nCtrlID, Attr.pADsValues->CaseIgnoreString, cch); SendMessage((HWND)lParam, g_uChangeMsg, (WPARAM)&Attr, 0); delete pwz; } } #if DBG == 1 else { strcat(szBuf, " (it was sent by this page!)"); } strcat(szBuf, "\n"); dspDebugOut((DEB_ITRACE, szBuf, GetHWnd(), wParam)); #endif } //+---------------------------------------------------------------------------- // // Method: CDsGroupGenObjPage::OnAttrChanged // // Synopsis: Inter-page communications for shared attributes. // // wParam == the PADS_ATTR_INFO struct for the changed attribute. // //----------------------------------------------------------------------------- void CDsGroupGenObjPage::OnAttrChanged(WPARAM wParam) { PADS_ATTR_INFO pAttrInfo = (PADS_ATTR_INFO)wParam; dspAssert(pAttrInfo && pAttrInfo->pszAttrName && pAttrInfo->pADsValues && pAttrInfo->pADsValues->CaseIgnoreString); dspDebugOut((DEB_ITRACE, "(HWND: %08x) got DSPROP_ATTRCHANGED_MSG for '%ws'.\n", GetHWnd(), pAttrInfo->pszAttrName)); if (_wcsicmp(pAttrInfo->pszAttrName, m_rgpAttrMap[EMAIL_IDX]->AttrInfo.pszAttrName) == 0) { SetDlgItemText(GetHWnd(), m_rgpAttrMap[EMAIL_IDX]->nCtrlID, pAttrInfo->pADsValues->CaseIgnoreString); } } //+---------------------------------------------------------------------------- // // Method: CDsGroupGenObjPage::OnDestroy // // Synopsis: Exit cleanup // //----------------------------------------------------------------------------- LRESULT CDsGroupGenObjPage::OnDestroy(void) { ATTR_DATA ad = {0, (LPARAM)m_pCIcon}; GeneralPageIcon(this, &GenIcon, NULL, 0, &ad, fOnDestroy); CDsPropPageBase::OnDestroy(); // If an application processes this message, it should return zero. return 0; } #endif // DSADMIN //+---------------------------------------------------------------------------- // // Member: CDsGrpMembersPage::CDsGrpMembersPage // //----------------------------------------------------------------------------- CDsGrpMembersPage::CDsGrpMembersPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj, HWND hNotifyObj, DWORD dwFlags) : m_pList(NULL), m_fMixed(TRUE), m_dwType(0), m_fMemberWritable(FALSE), m_dwGroupRID(0), m_fShowIcons(FALSE), m_pszSecurityGroupExtraClasses(NULL), m_dwSecurityGroupExtraClassesCount(0), m_pszNonSecurityGroupExtraClasses(NULL), m_dwNonSecurityGroupExtraClassesCount(0), m_hwndObjPicker(NULL), m_pInitInfo(NULL), CDsPropPageBase(pDsPage, pDataObj, hNotifyObj, dwFlags) { TRACE(CDsGrpMembersPage,CDsGrpMembersPage); #ifdef _DEBUG strcpy(szClass, "CDsGrpMembersPage"); #endif } //+---------------------------------------------------------------------------- // // Member: CDsGrpMembersPage::~CDsGrpMembersPage // //----------------------------------------------------------------------------- CDsGrpMembersPage::~CDsGrpMembersPage() { TRACE(CDsGrpMembersPage,~CDsGrpMembersPage); DO_DEL(m_pList); if (m_pszSecurityGroupExtraClasses != NULL) { for (DWORD idx = 0; idx < m_dwSecurityGroupExtraClassesCount; idx++) { if (m_pszSecurityGroupExtraClasses[idx] != NULL) { delete[] m_pszSecurityGroupExtraClasses[idx]; m_pszSecurityGroupExtraClasses[idx] = NULL; } } delete[] m_pszSecurityGroupExtraClasses; } if (m_pszNonSecurityGroupExtraClasses != NULL) { for (DWORD idx = 0; idx < m_dwNonSecurityGroupExtraClassesCount; idx++) { if (m_pszNonSecurityGroupExtraClasses[idx] != NULL) { delete[] m_pszNonSecurityGroupExtraClasses[idx]; m_pszNonSecurityGroupExtraClasses[idx] = NULL; } } delete[] m_pszNonSecurityGroupExtraClasses; } } //+---------------------------------------------------------------------------- // // Member: CDsGrpMembersPage::IUnknown::QueryInterface // // Synopsis: Returns requested interface pointer // //----------------------------------------------------------------------------- STDMETHODIMP CDsGrpMembersPage::QueryInterface(REFIID riid, void ** ppvObject) { TRACE2(CDsGrpMembersPage,QueryInterface); if (IID_ICustomizeDsBrowser == riid) { *ppvObject = (ICustomizeDsBrowser*)this; } else { return CDsPropPageBase::QueryInterface(riid, ppvObject); } AddRef(); return S_OK; } //+---------------------------------------------------------------------------- // // Member: CDsGrpMembersPage::IUnknown::AddRef // // Synopsis: increments reference count // // Returns: the reference count // //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDsGrpMembersPage::AddRef(void) { dspDebugOut((DEB_USER2, "CDsGrpMembersPage::AddRef refcount going in %d\n", m_uRefs)); return CDsPropPageBase::AddRef(); } //+---------------------------------------------------------------------------- // // Member: CDsGrpMembersPage::IUnknown::Release // // Synopsis: Decrements the object's reference count and frees it when // no longer referenced. // // Returns: zero if the reference count is zero or non-zero otherwise // //----------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CDsGrpMembersPage::Release(void) { dspDebugOut((DEB_USER2, "CDsGrpMembersPage::Release ref count going in %d\n", m_uRefs)); return CDsPropPageBase::Release(); } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::Initialize // // Synopsis: Initializes the ICustomizeDsBrowser interface // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::Initialize(HWND hwnd, PCDSOP_INIT_INFO pInitInfo, IBindHelper *pBindHelper) { HRESULT hr = S_OK; dspAssert(IsWindow(hwnd)); dspAssert(pBindHelper); m_hwndObjPicker = hwnd; m_pInitInfo = pInitInfo; m_pBinder = pBindHelper; return hr; } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::BuildQueryString // // Synopsis: Used to build the query string from the securityGroupExtraClasses // and nonSecurityGroupExtraClasses in the DisplaySpecifiers // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::BuildQueryString(PWSTR* ppszFilterString) { CStrW szFilterString = L"(|"; BOOL bSecurityGroup = (m_dwType & GROUP_TYPE_SECURITY_ENABLED) ? TRUE : FALSE; if (bSecurityGroup) { if (m_dwSecurityGroupExtraClassesCount == 0) { return S_FALSE; } for (DWORD idx = 0; idx < m_dwSecurityGroupExtraClassesCount; idx++) { if (m_pszSecurityGroupExtraClasses[idx] != NULL) { szFilterString += L"(objectClass="; szFilterString += m_pszSecurityGroupExtraClasses[idx]; szFilterString += L")"; } } } else { if (m_dwNonSecurityGroupExtraClassesCount == 0) { return S_FALSE; } for (DWORD idx = 0; idx < m_dwNonSecurityGroupExtraClassesCount; idx++) { if (m_pszNonSecurityGroupExtraClasses[idx] != NULL) { szFilterString += L"(objectClass="; szFilterString += m_pszNonSecurityGroupExtraClasses[idx]; szFilterString += L")"; } } } szFilterString += L")"; *ppszFilterString = new WCHAR[szFilterString.GetLength() + 1]; CHECK_NULL_REPORT(*ppszFilterString, GetHWnd(), return E_OUTOFMEMORY); wcscpy(*ppszFilterString, szFilterString); return S_OK; } //+---------------------------------------------------------------------------- // // Method: CDsSelectionListWrapper::CreateSelectionList // // Synopsis: Used to convert a CDsSelectionListWrapper to a PDS_SELECTION_LIST // //----------------------------------------------------------------------------- PDS_SELECTION_LIST CDsSelectionListWrapper::CreateSelectionList(CDsSelectionListWrapper* pHead) { if (pHead == NULL) { return NULL; } PDS_SELECTION_LIST pSelectionList = NULL; UINT nCount = CDsSelectionListWrapper::GetCount(pHead); if (nCount > 0) { pSelectionList = (PDS_SELECTION_LIST)malloc(sizeof(DS_SELECTION_LIST) + (sizeof(DS_SELECTION) * (nCount - 1))); if (pSelectionList != NULL) { memset(pSelectionList, 0, sizeof(DS_SELECTION_LIST) + (sizeof(DS_SELECTION) * (nCount - 1))); pSelectionList->cItems = nCount; pSelectionList->cFetchedAttributes = 0; // // Now fill in the selection list by walking the wrapper list // UINT idx = 0; CDsSelectionListWrapper* pCurrentItem = pHead; while (pCurrentItem != NULL) { memcpy(&(pSelectionList->aDsSelection[idx]), pCurrentItem->m_pSelection, sizeof(DS_SELECTION)); pCurrentItem = pCurrentItem->m_pNext; idx++; } } } return pSelectionList; } //+---------------------------------------------------------------------------- // // Method: CDsSelectionListWrapper::CreateSelectionList // // Synopsis: Counts the number of items in the CDsSelectionListWrapper // //----------------------------------------------------------------------------- UINT CDsSelectionListWrapper::GetCount(CDsSelectionListWrapper* pHead) { CDsSelectionListWrapper* pCurrentItem = pHead; UINT nCount = 0; while (pCurrentItem != NULL) { nCount++; pCurrentItem = pCurrentItem->m_pNext; } return nCount; } //+---------------------------------------------------------------------------- // // Method: CDsSelectionListWrapper::DetachItemsAndDeleteList // // Synopsis: Counts the number of items in the CDsSelectionListWrapper // //----------------------------------------------------------------------------- void CDsSelectionListWrapper::DetachItemsAndDeleteList(CDsSelectionListWrapper* pHead) { CDsSelectionListWrapper* pNextItem = pHead; CDsSelectionListWrapper* pDeleteItem = NULL; while (pNextItem != NULL) { pDeleteItem = pNextItem; pNextItem = pNextItem->m_pNext; delete pDeleteItem->m_pSelection; delete pDeleteItem; } } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::CollectDsObjects // // Synopsis: Used by AddObjects and PrefixSearch to retrieve the dataobject // of the additional objects // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::CollectDsObjects(PWSTR pszFilter, IDsObjectPickerScope *pDsScope, CDsPropDataObj *pdo) { HRESULT hr = S_OK; dspAssert(pdo != NULL); if (pdo == NULL) { return E_POINTER; } // // Prepare the search object // PWSTR pszScopePath = NULL; hr = pDsScope->GetADsPath(&pszScopePath); CHECK_HRESULT(hr, return hr;); CDSSearch searchObj; hr = searchObj.Init(pszScopePath); CHECK_HRESULT(hr, return hr;); PWSTR pszAttributes[] = { g_wzADsPath }; hr = searchObj.SetAttributeList(pszAttributes, 1); CHECK_HRESULT(hr, return hr); dspAssert(pszFilter != NULL); if (pszFilter == NULL) { return E_INVALIDARG; } hr = searchObj.SetFilterString(pszFilter); CHECK_HRESULT(hr, return hr); hr = searchObj.SetSearchScope(ADS_SCOPE_SUBTREE); CHECK_HRESULT(hr, return hr); // // Prepare the linked list for temporary storage of DS_SELECTION items // CDsSelectionListWrapper* pListHead = NULL; CDsSelectionListWrapper* pCurrentListItem = NULL; // // Get the path cracker // CComPtr spPathCracker; hr = GetADsPathname(spPathCracker); CHECK_HRESULT(hr, return hr); // // Execute the query // hr = searchObj.DoQuery(); while (SUCCEEDED(hr)) { hr = searchObj.GetNextRow(); if (S_ADS_NOMORE_ROWS == hr) { hr = S_OK; break; } if (SUCCEEDED(hr)) { ADS_SEARCH_COLUMN PathColumn, ClassColumn; ::ZeroMemory( &PathColumn, sizeof(PathColumn) ); ::ZeroMemory(&ClassColumn, sizeof(ClassColumn)); // // Get the ADsPath // hr = searchObj.GetColumn(pszAttributes[0], &PathColumn); CHECK_HRESULT(hr, continue); dspAssert(PathColumn.pADsValues->dwType == ADSTYPE_CASE_IGNORE_STRING); // // Get the objectClass // CComPtr spDirObject; hr = ADsOpenObject(PathColumn.pADsValues->CaseIgnoreString, NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IDirectoryObject, (PVOID*)&spDirObject); CHECK_HRESULT(hr, continue); // // Get the object info // ADS_OBJECT_INFO* pADsObjectInfo = NULL; hr = spDirObject->GetObjectInformation(&pADsObjectInfo); CHECK_HRESULT(hr, continue); dspAssert(pADsObjectInfo != NULL); PDS_SELECTION pSelection = new DS_SELECTION; CHECK_NULL(pSelection, return E_OUTOFMEMORY); ::ZeroMemory(pSelection, sizeof(DS_SELECTION)); if (!AllocWStr(PathColumn.pADsValues->CaseIgnoreString, &(pSelection->pwzADsPath))) { CHECK_HRESULT(E_OUTOFMEMORY, return E_OUTOFMEMORY); } // // Assume that the class we are interested in is the first in the multivalued attribute // if (!AllocWStr(pADsObjectInfo->pszClassName, &(pSelection->pwzClass))) { CHECK_HRESULT(E_OUTOFMEMORY, return E_OUTOFMEMORY); } hr = spPathCracker->Set(PathColumn.pADsValues->CaseIgnoreString, ADS_SETTYPE_FULL); CHECK_HRESULT(hr, continue); hr = spPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY); CHECK_HRESULT(hr, continue); // CODEWORK 122531 Should we be turning off escaped mode here? CComBSTR bstrName; hr = spPathCracker->Retrieve(ADS_FORMAT_LEAF, &bstrName); CHECK_HRESULT(hr, continue); // // Return the display to full // hr = spPathCracker->SetDisplayType(ADS_DISPLAY_FULL); dspAssert(SUCCEEDED(hr)); if (!AllocWStr(bstrName, &(pSelection->pwzName))) { CHECK_HRESULT(E_OUTOFMEMORY, return E_OUTOFMEMORY); } CDsSelectionListWrapper* pNewItem = new CDsSelectionListWrapper; CHECK_NULL(pNewItem, return E_OUTOFMEMORY); pNewItem->m_pSelection = pSelection; // // Add selection item to list // if (pListHead == NULL) { pListHead = pNewItem; pCurrentListItem = pNewItem; } else { pCurrentListItem->m_pNext = pNewItem; pCurrentListItem = pNewItem; } searchObj.FreeColumn(&PathColumn); searchObj.FreeColumn(&ClassColumn); } } if (pListHead != NULL) { PDS_SELECTION_LIST pSelectionList = CDsSelectionListWrapper::CreateSelectionList(pListHead); if (pSelectionList != NULL) { hr = pdo->Init(pSelectionList); CHECK_HRESULT(hr, return hr); } CDsSelectionListWrapper::DetachItemsAndDeleteList(pListHead); } else { hr = S_FALSE; } return hr; } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::AddObjects // // Synopsis: Called by the Object Picker UI to add additional objects to the UI // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::AddObjects(IDsObjectPickerScope *pDsScope, IDataObject **ppdo) { HRESULT hr = S_OK; // // Prepare the data object // CDsPropDataObj* pDataObj = new CDsPropDataObj(GetHWnd(), m_fReadOnly); CHECK_NULL(pDataObj, return E_OUTOFMEMORY); *ppdo = pDataObj; PWSTR pszFilter = NULL; hr = BuildQueryString(&pszFilter); if (FAILED(hr) || !pszFilter || hr == S_FALSE) { delete pDataObj; pDataObj = NULL; *ppdo = NULL; hr = S_FALSE; } else { hr = CollectDsObjects(pszFilter, pDsScope, pDataObj); } if (pszFilter != NULL) { delete[] pszFilter; pszFilter = NULL; } return hr; } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::GetQueryInfoByScope // // Synopsis: Called by the Object Picker UI // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::GetQueryInfoByScope(IDsObjectPickerScope*, PDSQUERYINFO *ppdsqi) { return E_NOTIMPL; } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::PrefixSearch // // Synopsis: Called by the Object Picker UI to get additional objects starting // with a specific string // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::PrefixSearch(IDsObjectPickerScope *pDsScope, PCWSTR pwzSearchFor, IDataObject **ppdo) { HRESULT hr = S_OK; // // Prepare the data object // CDsPropDataObj* pDataObj = new CDsPropDataObj(GetHWnd(), m_fReadOnly); CHECK_NULL(pDataObj, return E_OUTOFMEMORY); *ppdo = pDataObj; CStrW szFilter; PWSTR pszFilter = NULL; hr = BuildQueryString(&pszFilter); if (FAILED(hr) || hr == S_FALSE || pszFilter == NULL) { delete pDataObj; pDataObj = NULL; *ppdo = NULL; if (pszFilter) { delete[] pszFilter; pszFilter = NULL; } hr = S_FALSE; } else { szFilter = pszFilter; CStrW szPrefix; szPrefix = L"(&(name="; szPrefix += pwzSearchFor; szPrefix += L"*)"; szFilter = szPrefix + szFilter + L")"; hr = CollectDsObjects(szFilter.GetBuffer(szFilter.GetLength() + 1), pDsScope, pDataObj); } return hr; } //+---------------------------------------------------------------------------- // // Function: CreateGroupMembersPage // // Synopsis: Creates an instance of the group membership page window. // //----------------------------------------------------------------------------- HRESULT CreateGroupMembersPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj, PWSTR pwzADsPath, PWSTR pwzClass, HWND hNotifyObj, DWORD dwFlags, CDSBasePathsInfo* pBasePathsInfo, HPROPSHEETPAGE * phPage) { TRACE_FUNCTION(CreateGroupMembersPage); CDsGrpMembersPage * pPageObj = new CDsGrpMembersPage(pDsPage, pDataObj, hNotifyObj, dwFlags); CHECK_NULL(pPageObj, return E_OUTOFMEMORY); pPageObj->Init(pwzADsPath, pwzClass, pBasePathsInfo); return pPageObj->CreatePage(phPage); } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::DlgProc // // Synopsis: per-instance dialog proc // //----------------------------------------------------------------------------- LRESULT CDsGrpMembersPage::DlgProc(HWND, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: return InitDlg(lParam); case WM_NOTIFY: return OnNotify(wParam, lParam); case WM_SHOWWINDOW: return OnShowWindow(); case WM_SETFOCUS: return OnSetFocus((HWND)wParam); case WM_HELP: return OnHelp((LPHELPINFO)lParam); case WM_COMMAND: if (m_fInInit) { return TRUE; } return(OnCommand(GET_WM_COMMAND_ID(wParam, lParam), GET_WM_COMMAND_HWND(wParam, lParam), GET_WM_COMMAND_CMD(wParam, lParam))); case WM_DESTROY: return OnDestroy(); default: return(FALSE); } return(TRUE); } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::OnInitDialog // // Synopsis: Set the initial control values from the corresponding DS // attributes. // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::OnInitDialog(LPARAM lParam) { return OnInitDialog(lParam, TRUE); } HRESULT CDsGrpMembersPage::OnInitDialog(LPARAM, BOOL fShowIcons) { TRACE(CDsGrpMembersPage,OnInitDialog); HRESULT hr; PADS_ATTR_INFO pAttrs = NULL; DWORD cAttrs = 0; m_fShowIcons = (0 != g_ulMemberFilterCount) ? fShowIcons : FALSE; CWaitCursor Wait; if (!ADsPropSetHwnd(m_hNotifyObj, m_hPage)) { m_pWritableAttrs = NULL; } PTSTR ptzRDN; if (!UnicodeToTchar(m_pwszRDName, &ptzRDN)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); return S_OK; } SetDlgItemText(m_hPage, IDC_CN, ptzRDN); delete ptzRDN; GetDomainMode(this, &m_fMixed); // // Get the group RID. // PWSTR rgpwzAttrNames[] = {L"primaryGroupToken"}; hr = m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs); if (!CHECK_ADS_HR(&hr, m_hPage)) { return S_OK; } dspAssert(cAttrs); if (cAttrs == 1) { dspAssert(pAttrs); m_dwGroupRID = pAttrs->pADsValues->Integer; FreeADsMem(pAttrs); } GetGroupType(this, &m_dwType); dspDebugOut((DEB_ITRACE, "Group Type = 0x%x\n", m_dwType)); // // Get the membership list and fill the listview control. // m_pList = new CDsMembershipList(m_hPage, IDC_MEMBER_LIST); CHECK_NULL_REPORT(m_pList, m_hPage, return S_OK); hr = m_pList->Init(m_fShowIcons); CHECK_HRESULT(hr, return S_OK); hr = FillGroupList(); CHECK_HRESULT(hr, return S_OK); m_fMemberWritable = CheckIfWritable(g_wzMemberAttr); if (!m_fMemberWritable) { EnableWindow(GetDlgItem(m_hPage, IDC_ADD_BTN), FALSE); EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), FALSE); } //Set the focus on first item in the list ListView_SetItemState(GetDlgItem(m_hPage, IDC_MEMBER_LIST), 0, LVIS_SELECTED, LVIS_SELECTED); BOOL bSecurityGroup = (m_dwType & GROUP_TYPE_SECURITY_ENABLED) ? TRUE : FALSE; hr = LoadGroupExtraClasses(bSecurityGroup); return S_OK; } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::OnApply // // Synopsis: Handles the Apply notification. // //----------------------------------------------------------------------------- LRESULT CDsGrpMembersPage::OnApply(void) { TRACE(CDsGrpMembersPage,OnApply); HRESULT hr = S_OK; ADS_ATTR_INFO AttrInfo; PADS_ATTR_INFO pAttrs = &AttrInfo; AttrInfo.pszAttrName = g_wzMemberAttr; AttrInfo.dwADsType = ADSTYPE_DN_STRING; // // Read the list of members and do additions. // DWORD cModified; int i, cMembers = m_pList->GetCount(); if (cMembers > 0) { AttrInfo.dwControlCode = ADS_ATTR_APPEND; PADSVALUE rgADsValues; rgADsValues = new ADSVALUE[cMembers]; CHECK_NULL(rgADsValues, return PSNRET_INVALID_NOCHANGEPAGE); memset(rgADsValues, 0, cMembers * sizeof(ADSVALUE)); pAttrs->pADsValues = rgADsValues; pAttrs->dwNumValues = 0; CMemberListItem * pItem; for (i = 0; i < cMembers; i++) { if (FAILED(m_pList->GetItem(i, &pItem))) { dspAssert(FALSE && "List Error"); return PSNRET_INVALID_NOCHANGEPAGE; } if (!pItem) { dspAssert(pItem); return PSNRET_INVALID_NOCHANGEPAGE; } if (pItem->m_fIsAlreadyMember) { continue; } dspAssert(pItem->m_pwzDN); rgADsValues[pAttrs->dwNumValues].DNString = pItem->m_pwzDN; rgADsValues[pAttrs->dwNumValues].dwType = ADSTYPE_DN_STRING; pItem->m_fIsAlreadyMember = TRUE; pAttrs->dwNumValues++; } if (pAttrs->dwNumValues) { hr = m_pDsObj->SetObjectAttributes(pAttrs, 1, &cModified); if (!CheckGroupUpdate(hr, m_hPage)) { delete rgADsValues; return PSNRET_INVALID_NOCHANGEPAGE; } } delete rgADsValues; } // // Do removals. // DWORD cDelItems = m_DelList.GetItemCount(); if (cDelItems) { AttrInfo.dwControlCode = ADS_ATTR_DELETE; PADSVALUE rgADsValues; rgADsValues = new ADSVALUE[cDelItems]; CHECK_NULL(rgADsValues, return PSNRET_INVALID_NOCHANGEPAGE); memset(rgADsValues, 0, cDelItems * sizeof(ADSVALUE)); pAttrs->pADsValues = rgADsValues; pAttrs->dwNumValues = 0; CMemberListItem * pDelItem = m_DelList.RemoveFirstItem(); while (pDelItem) { if (pDelItem->m_fIsExternal) { hr = GetRealDN(pDelItem); CHECK_HRESULT_REPORT(hr, m_hPage, continue); } dspAssert(pDelItem->m_pwzDN); rgADsValues[pAttrs->dwNumValues].DNString = pDelItem->m_pwzDN; rgADsValues[pAttrs->dwNumValues].dwType = ADSTYPE_DN_STRING; pAttrs->dwNumValues++; pDelItem = m_DelList.RemoveFirstItem(); } if (pAttrs->dwNumValues) { hr = m_pDsObj->SetObjectAttributes(pAttrs, 1, &cModified); if (!CheckGroupUpdate(hr, m_hPage, FALSE)) { delete rgADsValues; return PSNRET_INVALID_NOCHANGEPAGE; } } delete rgADsValues; } return (SUCCEEDED(hr)) ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE; } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::OnCommand // // Synopsis: Handle control notifications. // //----------------------------------------------------------------------------- LRESULT CDsGrpMembersPage::OnCommand(int id, HWND hwndCtl, UINT codeNotify) { if (m_fInInit) { return 0; } switch (codeNotify) { case BN_CLICKED: TRACE(CDsGrpMembersPage,OnCommand); if (id == IDC_ADD_BTN) { InvokeUserQuery(); } if (id == IDC_REMOVE_BTN) { RemoveMember(); } break; } return CDsPropPageBase::OnCommand(id, hwndCtl, codeNotify); } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::OnNotify // // Synopsis: Handles list notification messages // //----------------------------------------------------------------------------- LRESULT CDsGrpMembersPage::OnNotify(WPARAM wParam, LPARAM lParam) { if (m_fInInit) { return 0; } switch (((LPNMHDR)lParam)->code) { case LVN_ITEMCHANGED: if (m_fMemberWritable) { EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), TRUE); } break; case NM_DBLCLK: // // Display properties for the selected item. First, find out // which item is selected. // CMemberListItem * pItem; if (!m_pList->GetCurListItem(NULL, NULL, &pItem)) { break; } dspAssert(pItem); if (pItem->m_fIsExternal) { HRESULT hr = GetRealDN(pItem); if (hr == HRESULT_FROM_WIN32(ERROR_DS_OBJ_NOT_FOUND)) { MsgBox(IDS_CANT_VIEW_EXTERNAL, m_hPage); break; } CHECK_HRESULT_REPORT(hr, m_hPage, break); } if (pItem->m_ulScopeType & DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN) { // // We cannot show the properties for downlevel users // // Put a useful message up PTSTR ptzTitle, ptzMsg; if (!LoadStringToTchar(IDS_MSG_NO_DOWNLEVEL_PROPERTIES, &ptzMsg)) { break; } if (!LoadStringToTchar(IDS_MSG_TITLE, &ptzTitle)) { break; } MessageBox(m_hPage, ptzMsg, ptzTitle, MB_OK | MB_ICONEXCLAMATION); delete[] ptzTitle; delete[] ptzMsg; break; } PostPropSheet(pItem->m_pwzDN, this); break; } return CDsPropPageBase::OnNotify(wParam, lParam); } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::InvokeUserQuery // // Synopsis: Bring up the query dialog to search for users and groups. // //----------------------------------------------------------------------------- void CDsGrpMembersPage::InvokeUserQuery(void) { TRACE(CDsGrpMembersPage,InvokeUserQuery); HRESULT hr; UINT i; CWaitCursor WaitCursor; CSmartWStr cstrCleanDN; IDsObjectPicker * pObjSel; BOOL fIsObjSelInited, fNativeModeUSG = FALSE; CStr strExternMemberList; hr = GetObjSel(&pObjSel, &fIsObjSelInited); CHECK_HRESULT(hr, return); if (!fIsObjSelInited) { CStrW cstrDC; hr = GetLdapServerName(m_pDsObj, cstrDC); CHECK_HRESULT_REPORT(hr, m_hPage, return); dspDebugOut((DEB_ITRACE, "ObjSel targetted to %ws\n", (LPCWSTR)cstrDC)); DSOP_SCOPE_INIT_INFO rgScopes[5]; DSOP_INIT_INFO InitInfo; ZeroMemory(rgScopes, sizeof(rgScopes)); ZeroMemory(&InitInfo, sizeof(InitInfo)); // The first scope is the local domain. All group types can contain // users, computers, and contacts from the local domain. // rgScopes[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); rgScopes[0].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN; rgScopes[0].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE | DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS | DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS; rgScopes[0].pwzDcName = cstrDC; rgScopes[0].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS | DSOP_FILTER_CONTACTS | DSOP_FILTER_COMPUTERS; // The second scope is the local forest. // rgScopes[1].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); rgScopes[1].flType = DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN; rgScopes[1].flScope = DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS | DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS; // The third scope is the GC. // rgScopes[2].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); rgScopes[2].flType = DSOP_SCOPE_TYPE_GLOBAL_CATALOG; rgScopes[2].flScope = DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS | DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS; // The fourth scope is uplevel external trusted domains. // rgScopes[3].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); rgScopes[3].flType = DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN; rgScopes[3].flScope = DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS | DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS; // The fifth scope is downlevel external trusted domains. // rgScopes[4].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); rgScopes[4].flType = DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN; rgScopes[4].flScope = DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS | DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS; if (m_dwType & GROUP_TYPE_ACCOUNT_GROUP) // Global group { if (!(m_fMixed && (m_dwType & GROUP_TYPE_SECURITY_ENABLED))) { // if it is not mixed-mode, security-enabled, add global. // rgScopes[0].FilterFlags.Uplevel.flBothModes |= DSOP_FILTER_GLOBAL_GROUPS_DL | DSOP_FILTER_GLOBAL_GROUPS_SE; } rgScopes[1].FilterFlags.Uplevel.flBothModes = rgScopes[2].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_CONTACTS; InitInfo.cDsScopeInfos = 3; // Enterprise scope. } else if (m_dwType & GROUP_TYPE_RESOURCE_GROUP) // Local group. { rgScopes[0].FilterFlags.Uplevel.flBothModes |= DSOP_FILTER_UNIVERSAL_GROUPS_DL | DSOP_FILTER_UNIVERSAL_GROUPS_SE | DSOP_FILTER_GLOBAL_GROUPS_DL | DSOP_FILTER_GLOBAL_GROUPS_SE; if (!(m_fMixed && (m_dwType & GROUP_TYPE_SECURITY_ENABLED)) && !(m_dwType & GROUP_TYPE_BUILTIN_LOCAL_GROUP)) { // If this is not a mixed-mode security-enabled local group // or a builtin group, then add local groups. // rgScopes[0].FilterFlags.Uplevel.flBothModes |= DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL | DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE; } //bug 37724 if( m_dwType & GROUP_TYPE_BUILTIN_LOCAL_GROUP ) rgScopes[0].FilterFlags.Uplevel.flBothModes |= DSOP_FILTER_WELL_KNOWN_PRINCIPALS; rgScopes[1].FilterFlags.Uplevel.flBothModes = rgScopes[2].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS | DSOP_FILTER_CONTACTS | DSOP_FILTER_COMPUTERS | DSOP_FILTER_UNIVERSAL_GROUPS_DL | DSOP_FILTER_UNIVERSAL_GROUPS_SE | DSOP_FILTER_GLOBAL_GROUPS_DL | DSOP_FILTER_GLOBAL_GROUPS_SE; // // Uplevel external domains: // rgScopes[3].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS | DSOP_FILTER_COMPUTERS | DSOP_FILTER_UNIVERSAL_GROUPS_DL | DSOP_FILTER_UNIVERSAL_GROUPS_SE | DSOP_FILTER_GLOBAL_GROUPS_DL | DSOP_FILTER_GLOBAL_GROUPS_SE; // // Downlevel external domains: // rgScopes[4].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS | DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS; InitInfo.cDsScopeInfos = 5; // Any trusted domain. } else if (m_dwType & GROUP_TYPE_UNIVERSAL_GROUP) { rgScopes[0].FilterFlags.Uplevel.flBothModes = rgScopes[1].FilterFlags.Uplevel.flBothModes = rgScopes[2].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_USERS | DSOP_FILTER_CONTACTS | DSOP_FILTER_COMPUTERS | DSOP_FILTER_UNIVERSAL_GROUPS_DL | DSOP_FILTER_UNIVERSAL_GROUPS_SE | DSOP_FILTER_GLOBAL_GROUPS_DL | DSOP_FILTER_GLOBAL_GROUPS_SE; InitInfo.cDsScopeInfos = 3; // Enterprise scope. } InitInfo.cbSize = sizeof(DSOP_INIT_INFO); InitInfo.aDsScopeInfos = rgScopes; InitInfo.pwzTargetComputer = cstrDC; InitInfo.flOptions = DSOP_FLAG_MULTISELECT; InitInfo.cAttributesToFetch = 2; LPCWSTR rgAttrNames[] = {g_wzObjectSID, g_wzUserAccountControl}; InitInfo.apwzAttributeNames = rgAttrNames; hr = pObjSel->Initialize(&InitInfo); CHECK_HRESULT_REPORT(hr, m_hPage, return); ObjSelInited(); } IDataObject * pdoSelections = NULL; CComPtr spObjPickerEx; hr = pObjSel->QueryInterface(IID_IDsObjectPickerEx, (void**)&spObjPickerEx); CHECK_HRESULT_REPORT(hr, m_hPage, return); hr = spObjPickerEx->InvokeDialogEx(m_hPage, this, &pdoSelections); // hr = pObjSel->InvokeDialog(m_hPage, &pdoSelections); CHECK_HRESULT_REPORT(hr, m_hPage, return); if (hr == S_FALSE || !pdoSelections) { return; } // Security enabled universal groups shouldn't contain members // from mixed-mode domains BUT, we have to allow it for Exchange's // non-standard public folder security model. // if (!m_fMixed && (m_dwType & GROUP_TYPE_UNIVERSAL_GROUP) && (m_dwType & GROUP_TYPE_SECURITY_ENABLED)) { fNativeModeUSG = TRUE; } m_MixedModeMembers.Init(this); CSmartWStr cstrCleanGroup; FORMATETC fmte = {g_cfDsSelList, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium = {TYMED_NULL, NULL, NULL}; hr = pdoSelections->GetData(&fmte, &medium); CHECK_HRESULT_REPORT(hr, m_hPage, return); PDS_SELECTION_LIST pSelList = (PDS_SELECTION_LIST)GlobalLock(medium.hGlobal); if (!pSelList) { goto ExitCleanup; } WaitCursor.SetWait(); // Clean the group name so it can be compared with those returned by the // user's selection. // hr = SkipPrefix(m_pwszObjPathName, &cstrCleanGroup); CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup); // // Insert the returned items into the group. // for (i = 0; i < pSelList->cItems; i++) { CMemberListItem * pItemInDelList = NULL; PSID pSid = NULL; if (!pSelList->aDsSelection[i].pwzADsPath) continue; // Check for an object from an external trusted domain. These objects // have a path of the form "LDAP://". SAM will create // an FSPO for this member and will then store that DN rather than the // above path. We won't know this DN until after the member is added, // so use its object-SID to identify it. // if ((pSelList->aDsSelection[i].flScopeType == DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN) || (pSelList->aDsSelection[i].flScopeType == DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN)) { dspAssert(pSelList->aDsSelection[i].pvarFetchedAttributes); if (pSelList->aDsSelection[i].pvarFetchedAttributes[0].vt != (VT_ARRAY | VT_UI1)) { REPORT_ERROR(ERROR_DATATYPE_MISMATCH, m_hPage); continue; } pSid = pSelList->aDsSelection[i].pvarFetchedAttributes[0].parray->pvData; dspAssert(IsValidSid(pSid)); // // Check if the item is in the delete list, if so remove it. // pItemInDelList = m_DelList.FindItemRemove(pSid); } else { hr = SkipPrefix(pSelList->aDsSelection[i].pwzADsPath, &cstrCleanDN); CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup); // See if the user is trying to add the group to itself. // if (_wcsicmp(cstrCleanDN, cstrCleanGroup) == 0) { if (pSelList->cItems == 1) { ErrMsg(IDS_ERROR_GRP_SELF, m_hPage); goto ExitCleanup; } continue; } // Check if the item is in the delete list, if so remove it. // pItemInDelList = m_DelList.FindItemRemove(cstrCleanDN); } if (pItemInDelList) { hr = m_pList->InsertIntoList(pItemInDelList); } else { if (pSid) { CComPtr spPathCracker; hr = GetADsPathname(spPathCracker); CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup); hr = spPathCracker->Set(pSelList->aDsSelection[i].pwzADsPath, ADS_SETTYPE_FULL); CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup); PWSTR pwzName; BSTR bstr; hr = spPathCracker->Retrieve(ADS_FORMAT_PROVIDER, &bstr); CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup); if (_wcsicmp(bstr, L"LDAP") == 0) { SysFreeString(bstr); hr = SkipPrefix(pSelList->aDsSelection[i].pwzADsPath, &cstrCleanDN); CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup); hr = CrackName(cstrCleanDN, &pwzName, GET_OBJ_CAN_NAME_EX, m_hPage); CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup); hr = m_pList->InsertIntoList(pSid, pwzName); LocalFreeStringW(&pwzName); } else { SysFreeString(bstr); hr = spPathCracker->Retrieve(ADS_FORMAT_WINDOWS_DN, &bstr); CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup); hr = m_pList->InsertIntoList(pSid, bstr); SysFreeString(bstr); } } else { int iIcon = -1; if (m_fShowIcons) { BOOL fDisabled = FALSE; if (pSelList->aDsSelection[i].pvarFetchedAttributes[1].vt == VT_I4) { fDisabled = pSelList->aDsSelection[i].pvarFetchedAttributes[1].lVal & UF_ACCOUNTDISABLE; } iIcon = g_ClassIconCache.GetClassIconIndex(pSelList->aDsSelection[i].pwzClass, fDisabled); if (iIcon == -1) { iIcon = g_ClassIconCache.AddClassIcon(pSelList->aDsSelection[i].pwzClass, fDisabled); } } if (fNativeModeUSG && ((DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN == pSelList->aDsSelection[i].flScopeType) || (DSOP_SCOPE_TYPE_GLOBAL_CATALOG == pSelList->aDsSelection[i].flScopeType))) { // member from domain in forest is DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN // member from the same domain is DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN // m_MixedModeMembers.CheckMember(cstrCleanDN); } dspDebugOut((DEB_ITRACE, "New member scope is 0x%x\n", pSelList->aDsSelection[i].flScopeType)); hr = m_pList->InsertIntoList(cstrCleanDN, iIcon); } } if (hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS)) { continue; } CHECK_HRESULT(hr, goto ExitCleanup); } m_MixedModeMembers.ListExternalMembers(strExternMemberList); if (!strExternMemberList.IsEmpty()) { CStr strMessage, strFormat; strFormat.LoadString(g_hInstance, IDS_USG_MIXED_WARNING); strMessage.Format(strFormat, strExternMemberList); ReportErrorWorker(m_hPage, (LPTSTR)(LPCTSTR)strMessage); } SetDirty(); ExitCleanup: GlobalUnlock(medium.hGlobal); ReleaseStgMedium(&medium); pdoSelections->Release(); } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::FillGroupList // // Synopsis: Fill the list box with the names of the group members. // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::FillGroupList(void) { TRACE(CDsGrpMembersPage,FillGroupList); return ::FillGroupList(this, m_pList, m_dwGroupRID); } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::RemoveMember // // Synopsis: Removes the selected users. // //----------------------------------------------------------------------------- void CDsGrpMembersPage::RemoveMember(void) { TRACE(CDsGrpMembersPage,RemoveMember); if (!m_pList) { return; } int* pIndex = NULL; CMemberListItem ** ppItem; int nNumSelected = 0; // // Compose the confirmation message and post it. // TCHAR szMsg[160]; if (!LoadStringReport(IDS_RM_MBR_MSG, szMsg, 160, m_hPage)) { return; } TCHAR szTitle[80]; if (!LoadStringReport(IDS_MSG_TITLE, szTitle, 80, m_hPage)) { return; } LONG iRet = MessageBox(m_hPage, szMsg, szTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2); if (iRet == IDNO) { // The user declined, so go home. // return; } CWaitCursor cWait; if (!m_pList->GetCurListItems(&pIndex, NULL, &ppItem, &nNumSelected)) { return; } for (int idx = 0; idx < nNumSelected; idx++) { if (!ppItem[idx]) { if (pIndex != NULL) { delete[] pIndex; pIndex = 0; } delete[] ppItem; return; } if (ppItem[idx]->m_fIsPrimary) { ErrMsg(IDS_RM_USR_PRI_GRP, m_hPage); continue; } // // Put the item into the delete list and remove it from the list box. // if (!m_DelList.AddItem(ppItem[idx])) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); if (pIndex != NULL) { delete[] pIndex; pIndex = 0; } delete[] ppItem; return; } m_pList->RemoveListItem(pIndex[idx]); for (int idx2 = idx; idx2 < nNumSelected; idx2++) { if (pIndex[idx2] > pIndex[idx]) { pIndex[idx2]--; } } SetDirty(); } // // Disable the Remove button, since nothing in the list box should have // the selection at this point. // //Since Remove Button has focus now, set focus to add button //before disabling SetFocus(GetDlgItem(m_hPage,IDC_ADD_BTN)); EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), FALSE); if (pIndex != NULL) { delete[] pIndex; pIndex = 0; } delete[] ppItem; return; } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::GetRealDN // // Synopsis: If a member from an external domain that was added to the // group during this instance of the page, we won't yet have the // path to the FPO as the DN. So, search for the FPO using the // object-SID. // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::GetRealDN(CMemberListItem * pItem) { return ::GetRealDN(this, pItem); } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::OnDestroy // // Synopsis: Exit cleanup // //----------------------------------------------------------------------------- LRESULT CDsGrpMembersPage::OnDestroy(void) { if (m_pList) { m_pList->ClearList(); } CDsPropPageBase::OnDestroy(); // If an application processes this message, it should return zero. return 0; } HRESULT HrVariantToStringArray(const CComVariant& refvar, PWSTR** pppszStringArray, DWORD* pdwCount) { HRESULT hr = S_OK; long start, end, current; *pdwCount = 0; *pppszStringArray = NULL; if (V_VT(&refvar) == VT_BSTR) { CComBSTR bstrVal = V_BSTR(&refvar); *pppszStringArray = new PWSTR[1]; if (*pppszStringArray != NULL) { size_t length = wcslen(bstrVal); PWSTR pszVal = new WCHAR[length + 1]; if (pszVal != NULL) { wcscpy(pszVal, bstrVal); (*pppszStringArray)[0] = pszVal; } else { delete[] *pppszStringArray; *pppszStringArray = NULL; *pdwCount = 0; return E_OUTOFMEMORY; } } *pdwCount = 1; return S_OK; } // // Check the VARIANT to make sure we have // an array of variants. // if ( V_VT(&refvar) != ( VT_ARRAY | VT_VARIANT ) ) { dspAssert(FALSE); return E_UNEXPECTED; } SAFEARRAY *saAttributes = V_ARRAY( &refvar ); // // Figure out the dimensions of the array. // hr = SafeArrayGetLBound( saAttributes, 1, &start ); if( FAILED(hr) ) return hr; hr = SafeArrayGetUBound( saAttributes, 1, &end ); if( FAILED(hr) ) return hr; CComVariant SingleResult; // // Process the array elements. // *pppszStringArray = new PWSTR[(end - start) + 1]; if (*pppszStringArray != NULL) { for ( current = start; current <= end; current++) { hr = SafeArrayGetElement( saAttributes, ¤t, &SingleResult ); if( FAILED(hr) ) return hr; if ( V_VT(&SingleResult) != VT_BSTR ) return E_UNEXPECTED; CComBSTR bstrVal = V_BSTR(&SingleResult); size_t length = wcslen(bstrVal); PWSTR pszVal = new WCHAR[length + 1]; if (pszVal != NULL) { wcscpy(pszVal, bstrVal); long lCount = static_cast(*pdwCount); if (lCount < (end - start) + 1) { (*pppszStringArray)[(*pdwCount)++] = pszVal; } } else { return E_OUTOFMEMORY; } } } else { hr = E_OUTOFMEMORY; *pdwCount = 0; } return hr; } //+---------------------------------------------------------------------------- // // Method: CDsGrpMembersPage::LoadGroupExtraClasses // // Synopsis: Read the extra classes that need to be displayed from the // DisplaySpecifiers // //----------------------------------------------------------------------------- HRESULT CDsGrpMembersPage::LoadGroupExtraClasses(BOOL bSecurity) { HRESULT hr = S_OK; dspAssert(m_pDsObj != NULL); if (m_pDsObj == NULL) { return E_INVALIDARG; } static LPCWSTR lpszSettingsObjectClass = L"dsUISettings"; static LPCWSTR lpszSettingsObject = L"cn=DS-UI-Default-Settings"; static LPCWSTR lpszSecurityGroupProperty = L"msDS-Security-Group-Extra-Classes"; static LPCWSTR lpszNonSecurityGroupProperty = L"msDS-Non-Security-Group-Extra-Classes"; // // Not AddRef'd so don't use a smart pointer // IDsDisplaySpecifier* pDispSpec; hr = GetIDispSpec(&pDispSpec); CHECK_HRESULT_REPORT(hr, GetHWnd(), return hr); // // get the display specifiers locale container (e.g. 409) // CComPtr spLocaleContainer; hr = pDispSpec->GetDisplaySpecifier(NULL, IID_IADsContainer, (void**)&spLocaleContainer); if (FAILED(hr)) { return hr; } // // bind to the settings object // CComPtr spIDispatchObject; hr = spLocaleContainer->GetObject((LPWSTR)lpszSettingsObjectClass, (LPWSTR)lpszSettingsObject, &spIDispatchObject); if (FAILED(hr)) { return hr; } CComPtr spSettingsObject; hr = spIDispatchObject->QueryInterface(IID_IADs, (void**)&spSettingsObject); if (FAILED(hr)) { return hr; } if (bSecurity) { // // get the security group extra classes as a CStringList // CComVariant var; hr = spSettingsObject->Get((LPWSTR)lpszSecurityGroupProperty, &var); if (SUCCEEDED(hr)) { hr = HrVariantToStringArray(var, &m_pszSecurityGroupExtraClasses, &m_dwSecurityGroupExtraClassesCount); } } else { // // get the non-security group extra classes as a CStringList // CComVariant var; hr = spSettingsObject->Get((LPWSTR)lpszNonSecurityGroupProperty, &var); if (SUCCEEDED(hr)) { hr = HrVariantToStringArray(var, &m_pszNonSecurityGroupExtraClasses, &m_dwNonSecurityGroupExtraClassesCount); } } return hr; } //+---------------------------------------------------------------------------- // // Function: GetDomainMode // // Synopsis: Is the domain to which the indicated object belongs in mixed // or native mode? // //----------------------------------------------------------------------------- HRESULT GetDomainMode(CDsPropPageBase * pObj, PBOOL pfMixed) { HRESULT hr; CComBSTR cbstrDomain; hr = GetDomainScope(pObj, &cbstrDomain); CHECK_HRESULT_REPORT(hr, pObj->GetHWnd(), return hr); return GetDomainMode(cbstrDomain, pObj->GetHWnd(), pfMixed); } HRESULT GetDomainMode(PWSTR pwzDomain, HWND hWnd, PBOOL pfMixed) { HRESULT hr; WCHAR wzMixedAttr[] = L"nTMixedDomain"; PWSTR rgpwzAttrNames[] = {wzMixedAttr}; CComPtr pDomObj; PADS_ATTR_INFO pAttrs = NULL; DWORD cAttrs = 0; dspDebugOut((DEB_ITRACE, "GetDomainMode targetted to %ws\n", pwzDomain)); hr = ADsOpenObject(pwzDomain, NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IDirectoryObject, (void **)&pDomObj); CHECK_HRESULT_REPORT(hr, hWnd, return hr); hr = pDomObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs); CHECK_HRESULT_REPORT(hr, hWnd, return hr); if (cAttrs && pAttrs && (_wcsicmp(pAttrs->pszAttrName, wzMixedAttr) == 0)) { *pfMixed = (BOOL)pAttrs->pADsValues->Integer; FreeADsMem(pAttrs); } else { *pfMixed = 0; } return hr; } //+---------------------------------------------------------------------------- // // Function: GetGroupType // //----------------------------------------------------------------------------- HRESULT GetGroupType(CDsPropPageBase * pObj, DWORD * pdwType) { HRESULT hr; PWSTR rgpwzAttrNames[] = {g_wzGroupType}; PADS_ATTR_INFO pAttrs = NULL; DWORD cAttrs = 0; hr = pObj->m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs); CHECK_HRESULT_REPORT(hr, pObj->GetHWnd(), return hr); if (cAttrs && pAttrs && (_wcsicmp(pAttrs->pszAttrName, g_wzGroupType) == 0)) { *pdwType = pAttrs->pADsValues->Integer; FreeADsMem(pAttrs); } else { *pdwType = 0; } return hr; } //+---------------------------------------------------------------------------- // // Function: FillGroupList // // Synopsis: Fill the list box with the names of the group members. // //----------------------------------------------------------------------------- HRESULT FillGroupList(CDsPropPageBase * pPage, CDsMembershipList * pList, DWORD dwGroupRID) { TRACE_FUNCTION(FillGroupList); HRESULT hr = S_OK; Smart_PADS_ATTR_INFO spAttrs; DWORD i, cAttrs = 0; WCHAR wzMemberAttr[MAX_PATH] = L"member;range=0-*"; const WCHAR wcSep = L'-'; const WCHAR wcEnd = L'*'; const WCHAR wzFormat[] = L"member;range=%ld-*"; PWSTR pwzAttrName[] = {wzMemberAttr}, pwzPath; BOOL fMoreRemain = FALSE, fNameNotMapped = FALSE; CComPtr spDsSearch; // // Read the membership list from the object using range (incremental) // retrieval. // do { hr = pPage->m_pDsObj->GetObjectAttributes(pwzAttrName, 1, &spAttrs, &cAttrs); if (!CHECK_ADS_HR_IGNORE_UNFOUND_ATTR(&hr, pPage->GetHWnd())) { return hr; } if (cAttrs > 0 && spAttrs != NULL) { for (i = 0; i < spAttrs->dwNumValues; i++) { hr = pList->InsertIntoNewList(spAttrs->pADsValues[i].CaseIgnoreString); if (DS_NAME_ERROR_NO_MAPPING == HRESULT_CODE(hr)) { fNameNotMapped = TRUE; hr = S_OK; } else { CHECK_HRESULT(hr, return hr); } } // // Check to see if there is more data. If the last char of the // attribute name string is an asterisk, then we have everything. // size_t cchEnd = wcslen(spAttrs->pszAttrName); fMoreRemain = spAttrs->pszAttrName[cchEnd - 1] != wcEnd; if (fMoreRemain) { PWSTR pwz = wcsrchr(spAttrs->pszAttrName, wcSep); if (!pwz) { dspAssert(FALSE && spAttrs->pszAttrName); fMoreRemain = FALSE; } else { pwz++; // move past the hyphen to the range end value. dspAssert(*pwz); long lEnd = _wtol(pwz); lEnd++; // start with the next value. wsprintfW(wzMemberAttr, wzFormat, lEnd); dspDebugOut((DEB_ITRACE, "Range returned is %ws, now asking for %ws\n", spAttrs->pszAttrName, wzMemberAttr)); } } } } while (fMoreRemain); // // Query for all users/computers who have this as their primary group. // // Filter out interdomain-trust accounts (0x30000002). // This value is defined in ds\src\dsamain\include\mappings.h // WCHAR wzSearchFormat[] = L"(&(primaryGroupID=%u)(sAMAccountType<=805306369))"; CStrW strSearchFilter; strSearchFilter.Format(wzSearchFormat, dwGroupRID); BSTR bstrDomain; hr = GetDomainScope(pPage, &bstrDomain); CHECK_HRESULT(hr, return hr); pwzAttrName[0] = g_wzADsPath; CDSSearch Search; hr = Search.Init((LPCWSTR)bstrDomain); SysFreeString(bstrDomain); CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr); Search.SetFilterString(const_cast((LPCWSTR)strSearchFilter)); Search.SetAttributeList(pwzAttrName, 1); Search.SetSearchScope(ADS_SCOPE_SUBTREE); hr = Search.DoQuery(); while (SUCCEEDED(hr)) { hr = Search.GetNextRow(); if (hr == S_ADS_NOMORE_ROWS) { hr = S_OK; break; } CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr); ADS_SEARCH_COLUMN Column = {0}; hr = Search.GetColumn(g_wzADsPath, &Column); CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr); hr = pPage->SkipPrefix(Column.pADsValues->CaseIgnoreString, &pwzPath); Search.FreeColumn(&Column); CHECK_HRESULT_REPORT(hr, pPage->GetHWnd(), return hr); hr = pList->InsertIntoNewList(pwzPath, TRUE); delete pwzPath; CHECK_HRESULT(hr, return hr); } if (pList->GetCount() < 1) { EnableWindow(GetDlgItem(pPage->GetHWnd(), IDC_REMOVE_BTN), FALSE); } else if (((CDsGrpMembersPage *)pPage)->m_fShowIcons) { // Get class and userAccountControl for the group members and use // those values to select icons. // pList->SetMemberIcons(pPage); } if (fNameNotMapped) { MsgBox(IDS_GRP_NO_NAME_MAPPING, pPage->GetHWnd()); } return hr; } //+---------------------------------------------------------------------------- // // Function: GetRealDN // // Synopsis: If a member from an external domain that was added to the // group during this instance of the page, we won't yet have the // path to the FPO as the DN. So, search for the FPO using the // object-SID. // //----------------------------------------------------------------------------- HRESULT GetRealDN(CDsPropPageBase * pPage, CMemberListItem * pItem) { HRESULT hr = S_OK; if (!pItem->GetSid()) { return E_FAIL; } CComBSTR cbstrDomain; hr = GetDomainScope(pPage, &cbstrDomain); CHECK_HRESULT(hr, return hr); CStrW strDN; hr = FindFPO(pItem->GetSid(), cbstrDomain, strDN); //Don't show this eror here if(FAILED(hr)) return hr; PWSTR pwzOldDN = pItem->m_pwzDN; if (!AllocWStr(const_cast((LPCWSTR)strDN), &pItem->m_pwzDN)) { REPORT_ERROR(E_OUTOFMEMORY, pPage->GetHWnd()); return E_OUTOFMEMORY; } DO_DEL(pwzOldDN); pItem->m_fIsExternal = FALSE; return S_OK; } //+---------------------------------------------------------------------------- // // Member: CDsGrpShlGenPage::CDsGrpShlGenPage // //----------------------------------------------------------------------------- CDsGrpShlGenPage::CDsGrpShlGenPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj, HWND hNotifyObj, DWORD dwFlags) : m_pCIcon(NULL), m_fDescrWritable(FALSE), m_fDescrDirty(FALSE), CDsGrpMembersPage(pDsPage, pDataObj, hNotifyObj, dwFlags) { TRACE(CDsGrpShlGenPage,CDsGrpShlGenPage); #ifdef _DEBUG strcpy(szClass, "CDsGrpShlGenPage"); #endif } //+---------------------------------------------------------------------------- // // Member: CDsGrpShlGenPage::~CDsGrpShlGenPage // //----------------------------------------------------------------------------- CDsGrpShlGenPage::~CDsGrpShlGenPage() { TRACE(CDsGrpShlGenPage,~CDsGrpShlGenPage); } //+---------------------------------------------------------------------------- // // Function: CreateGrpShlGenPage // // Synopsis: Creates an instance of the group shell general page window. // //----------------------------------------------------------------------------- HRESULT CreateGrpShlGenPage(PDSPAGE pDsPage, LPDATAOBJECT pDataObj, PWSTR pwzADsPath, PWSTR pwzClass, HWND hNotifyObj, DWORD dwFlags, CDSBasePathsInfo* pBasePathsInfo, HPROPSHEETPAGE * phPage) { TRACE_FUNCTION(CreateGroupMembersPage); CDsGrpShlGenPage * pPageObj = new CDsGrpShlGenPage(pDsPage, pDataObj, hNotifyObj, dwFlags); CHECK_NULL(pPageObj, return E_OUTOFMEMORY); pPageObj->Init(pwzADsPath, pwzClass, pBasePathsInfo); return pPageObj->CreatePage(phPage); } //+---------------------------------------------------------------------------- // // Method: CDsGrpShlGenPage::OnInitDialog // // Synopsis: Set the initial control values from the corresponding DS // attributes. // //----------------------------------------------------------------------------- HRESULT CDsGrpShlGenPage::OnInitDialog(LPARAM lParam) { TRACE(CDsGrpShlGenPage,OnInitDialog); HRESULT hr; Smart_PADS_ATTR_INFO spAttrs; DWORD cAttrs = 0; CWaitCursor Wait; // // Get the icon from the DS and put it on the page. // ATTR_DATA ad = {0, 0}; hr = GeneralPageIcon(this, &GenIcon, NULL, 0, &ad, fInit); CHECK_HRESULT_REPORT(hr, m_hPage, return S_OK); m_pCIcon = (CDsIconCtrl *)ad.pVoid; // // Get the name. // LPTSTR ptz; if (!UnicodeToTchar(m_pwszRDName, &ptz)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); return S_OK; } SetDlgItemText(m_hPage, IDC_CN, ptz); delete [] ptz; m_fDescrWritable = CheckIfWritable(g_wzDescription); // // Get the description // PWSTR rgpwzAttrNames[] = {g_wzDescription}; hr = m_pDsObj->GetObjectAttributes(rgpwzAttrNames, 1, &spAttrs, &cAttrs); if (!CHECK_ADS_HR_IGNORE_UNFOUND_ATTR(&hr, m_hPage)) { return S_OK; } if (1 == cAttrs) { dspAssert(spAttrs); if (!UnicodeToTchar(spAttrs->pADsValues->CaseIgnoreString, &ptz)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); return S_OK; } SetDlgItemText(m_hPage, IDC_DESCRIPTION_EDIT, ptz); delete [] ptz; } if (m_fDescrWritable) { SendDlgItemMessage(m_hPage, IDC_DESCRIPTION_EDIT, EM_LIMITTEXT, DSPROP_DESCRIPTION_RANGE_UPPER, 0); } else { SendDlgItemMessage(m_hPage, IDC_DESCRIPTION_EDIT, EM_SETREADONLY, (WPARAM)TRUE, 0); } HRESULT hRes = CDsGrpMembersPage::OnInitDialog(lParam, FALSE); #if !defined(DSADMIN) // in the Win95 shell, we do not want to have the buttons // because we do not have object picker MakeNotWritable(); EnableWindow(GetDlgItem(m_hPage, IDC_ADD_BTN), FALSE); #endif EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), FALSE); return hRes; } //+---------------------------------------------------------------------------- // // Method: CDsGrpShlGenPage::OnCommand // // Synopsis: Handle control notifications. // //----------------------------------------------------------------------------- LRESULT CDsGrpShlGenPage::OnCommand(int id, HWND hwndCtl, UINT codeNotify) { if (m_fInInit) { return 0; } if (EN_CHANGE == codeNotify && IDC_DESCRIPTION_EDIT == id) { m_fDescrDirty = TRUE; } TRACE(CDsGrpShlGenPage,OnCommand); return CDsGrpMembersPage::OnCommand(id, hwndCtl, codeNotify); } //+---------------------------------------------------------------------------- // // Method: CDsGrpShlGenPage::OnApply // // Synopsis: Write changes // //----------------------------------------------------------------------------- LRESULT CDsGrpShlGenPage::OnApply(void) { TRACE(CDsGrpShlGenPage,OnApply); ADS_ATTR_INFO AttrInfoDesc = {g_wzDescription, ADS_ATTR_UPDATE, ADSTYPE_CASE_IGNORE_STRING, NULL, 0}; ADSVALUE ADsValueDesc = {ADSTYPE_CASE_IGNORE_STRING, NULL}; AttrInfoDesc.pADsValues = &ADsValueDesc; AttrInfoDesc.dwNumValues = 1; LPTSTR ptsz; if (m_fDescrDirty) { dspAssert(m_fDescrWritable); ptsz = new TCHAR[DSPROP_DESCRIPTION_RANGE_UPPER + 1]; CHECK_NULL_REPORT(ptsz, m_hPage, return -1); if (GetDlgItemText(m_hPage, IDC_DESCRIPTION_EDIT, ptsz, DSPROP_DESCRIPTION_RANGE_UPPER + 1) == 0) { // An empty control means remove the attribute value from the // object. // AttrInfoDesc.dwNumValues = 0; AttrInfoDesc.pADsValues = NULL; AttrInfoDesc.dwControlCode = ADS_ATTR_CLEAR; } else { if (!TcharToUnicode(ptsz, &ADsValueDesc.CaseIgnoreString)) { REPORT_ERROR(E_OUTOFMEMORY, m_hPage); delete ptsz; return -1; } } delete ptsz; DWORD cModified; HRESULT hr = m_pDsObj->SetObjectAttributes(&AttrInfoDesc, 1, &cModified); if (!CHECK_ADS_HR(&hr, m_hPage)) { goto Cleanup; } m_fDescrDirty = FALSE; } Cleanup: DO_DEL(ADsValueDesc.CaseExactString); return CDsGrpMembersPage::OnApply(); } //+---------------------------------------------------------------------------- // // Method: CDsGrpShlGenPage::OnDestroy // // Synopsis: Exit cleanup // //----------------------------------------------------------------------------- LRESULT CDsGrpShlGenPage::OnDestroy(void) { ATTR_DATA ad = {0, (LPARAM)m_pCIcon}; GeneralPageIcon(this, &GenIcon, NULL, 0, &ad, fOnDestroy); CDsGrpMembersPage::OnDestroy(); // If an application processes this message, it should return zero. return 0; } //+---------------------------------------------------------------------------- // // Function: CheckGroupUpdate // // Synopsis: Checks the result code to see if a group-specific error has // occured. // //----------------------------------------------------------------------------- BOOL CheckGroupUpdate(HRESULT hr, HWND hPage, BOOL fAdd, PWSTR pwzDN) { if (SUCCEEDED(hr)) { return TRUE; } if (hPage == NULL) { hPage = GetDesktopWindow(); } DWORD dwErr = 0; WCHAR wszErrBuf[MAX_PATH+1]; WCHAR wszNameBuf[MAX_PATH+1]; ADsGetLastError(&dwErr, wszErrBuf, MAX_PATH, wszNameBuf, MAX_PATH); // // ERROR_DS_CONSTRAINT_VIOLATION is the error returned for // duplicate name. // if ((LDAP_RETCODE)dwErr == LDAP_CONSTRAINT_VIOLATION || hr == HRESULT_FROM_WIN32(ERROR_DS_CONSTRAINT_VIOLATION)) { PTSTR ptzTitle, ptzMsg; if (!LoadStringToTchar(IDS_MSG_TITLE, &ptzTitle)) { goto FatalError; } if (!LoadStringToTchar((fAdd) ? IDS_ERRMSG_GROUP_CONSTRAINT : IDS_ERRMSG_GROUP_DELETE, &ptzMsg)) { delete ptzTitle; goto FatalError; } MessageBox(hPage, ptzMsg, ptzTitle, MB_OK | MB_ICONEXCLAMATION); delete [] ptzTitle; delete [] ptzMsg; } else if (HRESULT_CODE(hr) == ERROR_DS_NO_SUCH_OBJECT && fAdd) { // Put a useful message up PTSTR ptzTitle = 0, ptzMsg = 0; if (!LoadStringToTchar(IDS_MSG_USER_NOT_PRESENT, &ptzMsg)) { goto FatalError; } if (!LoadStringToTchar(IDS_MSG_TITLE, &ptzTitle)) { goto FatalError; } MessageBox(hPage, ptzMsg, ptzTitle, MB_OK | MB_ICONEXCLAMATION); delete[] ptzTitle; delete[] ptzMsg; } else if (HRESULT_CODE(dwErr) == ERROR_DS_NO_ATTRIBUTE_OR_VALUE && !fAdd) { // No message needed return FALSE; } else if (HRESULT_CODE(dwErr) == ERROR_MEMBER_NOT_IN_ALIAS && !fAdd) { // Put a useful message up bool bShowGenericMessage = true; if (pwzDN) { // // Crack the DN into the name // CComPtr spPathcracker; hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (PVOID *)&spPathcracker); if (SUCCEEDED(hr)) { hr = spPathcracker->Set(pwzDN, ADS_SETTYPE_DN); if (SUCCEEDED(hr)) { hr = spPathcracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY); if (SUCCEEDED(hr)) { CComBSTR sbstrName; hr = spPathcracker->Retrieve(ADS_FORMAT_LEAF, &sbstrName); if (SUCCEEDED(hr)) { ErrMsgParam(IDS_MSG_MEMBER_ALREADY_GONE, (LPARAM)(PWSTR)sbstrName, hPage); bShowGenericMessage = false; } } } } } if (bShowGenericMessage) { PTSTR ptzTitle = 0, ptzMsg = 0; if (!LoadStringToTchar(IDS_MSG_MEMBER_ALREADY_GONE2, &ptzMsg)) { goto FatalError; } if (!LoadStringToTchar(IDS_MSG_TITLE, &ptzTitle)) { goto FatalError; } MessageBox(hPage, ptzMsg, ptzTitle, MB_OK | MB_ICONEXCLAMATION); delete[] ptzTitle; delete[] ptzMsg; } } else { if (dwErr) { dspDebugOut((DEB_ERROR, "Extended Error 0x%x: %ws %ws.\n", dwErr, wszErrBuf, wszNameBuf)); ReportError(dwErr, IDS_ADS_ERROR_FORMAT, hPage); } else { dspDebugOut((DEB_ERROR, "Error %08lx\n", hr)); ReportError(hr, IDS_ADS_ERROR_FORMAT, hPage); } } return FALSE; FatalError: MessageBoxA(hPage, "A Fatal Error has occured!", "Active Directory Service", MB_OK | MB_ICONEXCLAMATION); return FALSE; } //+---------------------------------------------------------------------------- // // Function: FindFPO // // Synopsis: Given a SID, look for a corresponding FPO. // //----------------------------------------------------------------------------- HRESULT FindFPO(PSID pSid, PWSTR pwzDomain, CStrW & strFPODN) { HRESULT hr; CDSSearch Srch; hr = Srch.Init(pwzDomain); CHECK_HRESULT(hr, return hr); PWSTR rgpwzAttrNames[] = {g_wzDN}; hr = Srch.SetAttributeList(rgpwzAttrNames, 1); CHECK_HRESULT(hr, return hr); Srch.SetSearchScope(ADS_SCOPE_SUBTREE); WCHAR wzSearchFormat[] = L"(&(objectCategory=foreignSecurityPrincipal)(objectSid=%s))"; PWSTR pwzSID; CStrW strSearchFilter; hr = ADsEncodeBinaryData((PBYTE)pSid, GetLengthSid(pSid), &pwzSID); CHECK_HRESULT(hr, return hr); strSearchFilter.Format(wzSearchFormat, pwzSID); FreeADsMem(pwzSID); Srch.SetFilterString(const_cast((LPCWSTR)strSearchFilter)); hr = Srch.DoQuery(); CHECK_HRESULT(hr, return hr); hr = Srch.GetNextRow(); if (hr == S_ADS_NOMORE_ROWS) { // No object has a matching SID, the FPO must have been deleted. // return HRESULT_FROM_WIN32(ERROR_DS_OBJ_NOT_FOUND); } CHECK_HRESULT(hr, return hr); ADS_SEARCH_COLUMN Column; hr = Srch.GetColumn(g_wzDN, &Column); CHECK_HRESULT(hr, return hr); strFPODN = Column.pADsValues->CaseIgnoreString; if (strFPODN.IsEmpty()) { Srch.FreeColumn(&Column); return E_OUTOFMEMORY; } Srch.FreeColumn(&Column); return S_OK; } //+---------------------------------------------------------------------------- // // Class: CMemberDomainMode // // Purpose: Maintains a list of all domains in the enterprise from which // members have been added along with those domains' mode. Keeps // a second list of members who have been added from mixed-mode // domains. // //----------------------------------------------------------------------------- void CMemberDomainMode::Init(CDsPropPageBase * pPage) { m_pPage = pPage; m_MemberList.Clear(); } HRESULT CMemberDomainMode::CheckMember(PWSTR pwzMemberDN) { HRESULT hr; CComBSTR cbstrDomain; BOOL fMixed = FALSE; hr = GetObjectsDomain(m_pPage, pwzMemberDN, &cbstrDomain); if (SUCCEEDED(hr) && cbstrDomain) { if (!m_DomainList.Find(cbstrDomain, &fMixed)) { // The member's domain is not already in the list. Read the domain // mode and then add it. // hr = GetDomainMode(cbstrDomain, m_pPage->GetHWnd(), &fMixed); CHECK_HRESULT(hr, return hr); hr = m_DomainList.Insert(cbstrDomain, fMixed); CHECK_HRESULT_REPORT(hr, m_pPage->GetHWnd(), return hr); } } if (fMixed) { PWSTR pwzCanEx; PTSTR ptzCanEx; CStr strName; hr = CrackName(pwzMemberDN, &pwzCanEx, GET_OBJ_CAN_NAME_EX, m_pPage->GetHWnd()); CHECK_HRESULT(hr, return hr); if (!UnicodeToTchar(pwzCanEx, &ptzCanEx)) { LocalFreeStringW(&pwzCanEx); REPORT_ERROR(E_OUTOFMEMORY, m_pPage->GetHWnd()); return E_OUTOFMEMORY; } LocalFreeStringW(&pwzCanEx); CStr cstrFolder; GetNameParts(ptzCanEx, cstrFolder, strName); DO_DEL(ptzCanEx); hr = m_MemberList.Insert(strName); CHECK_HRESULT_REPORT(hr, m_pPage->GetHWnd(), return hr); } return S_OK; } HRESULT CMemberDomainMode::ListExternalMembers(CStr & strList) { m_MemberList.GetList(strList); return S_OK; } //+---------------------------------------------------------------------------- // // CMemberDomainMode helper classes // //----------------------------------------------------------------------------- HRESULT CMMMemberList::Insert(LPCTSTR ptzName) { CMMMemberListItem * pItem = new CMMMemberListItem; if (!pItem) { return E_OUTOFMEMORY; } pItem->m_strName = ptzName; if (m_pListHead == NULL) { m_pListHead = pItem; } else { pItem->LinkAfter(m_pListHead); } return S_OK; } #define MAX_MMMLISTING 25 void CMMMemberList::GetList(CStr & strList) { int nCount = 0; strList.Empty(); CMMMemberListItem * pItem = m_pListHead; while (pItem) { strList += pItem->m_strName; nCount++; pItem = pItem->Next(); if (pItem) { if (nCount > MAX_MMMLISTING) { strList += TEXT("..."); return; } else { strList += TEXT(", "); } } } } void CMMMemberList::Clear(void) { CMMMemberListItem * pItem = m_pListHead, * pNext; while (pItem) { pNext = pItem->Next(); delete pItem; pItem = pNext; } m_pListHead = NULL; } CDomainModeList::~CDomainModeList(void) { CDomainModeListItem * pItem = m_pListHead, * pNext; while (pItem) { pNext = pItem->Next(); delete pItem; pItem = pNext; } } HRESULT CDomainModeList::Insert(PWSTR pwzDomain, BOOL fMixed) { CDomainModeListItem * pItem = new CDomainModeListItem; if (!pItem) { return E_OUTOFMEMORY; } pItem->m_strName = pwzDomain; pItem->m_fMixed = fMixed; if (m_pListHead == NULL) { m_pListHead = pItem; } else { pItem->LinkAfter(m_pListHead); } return S_OK; } BOOL CDomainModeList::Find(LPCWSTR pwzDomain, PBOOL pfMixed) { CDomainModeListItem * pItem = m_pListHead; while (pItem) { if (_wcsicmp(pwzDomain, pItem->m_strName) == 0) { *pfMixed = pItem->m_fMixed; return TRUE; } pItem = pItem->Next(); } return FALSE; }