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.
3982 lines
114 KiB
3982 lines
114 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// 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, const CDSSmartBasePathsInfo& basePathsInfo,
|
|
HPROPSHEETPAGE * phPage)
|
|
{
|
|
TRACE_FUNCTION(CreateGroupGenObjPage);
|
|
|
|
CDsGroupGenObjPage * pPageObj = new CDsGroupGenObjPage(pDsPage, pDataObj,
|
|
hNotifyObj, dwFlags);
|
|
CHECK_NULL(pPageObj, return E_OUTOFMEMORY);
|
|
|
|
pPageObj->Init(pwzADsPath, pwzClass, basePathsInfo);
|
|
|
|
return pPageObj->CreatePage(phPage);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CDsPropPageBase::DlgProc
|
|
//
|
|
// Synopsis: per-instance dialog proc
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
INT_PTR CALLBACK
|
|
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);
|
|
|
|
SetGroupTypeButtons();
|
|
|
|
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::SetGroupTypeButtons
|
|
//
|
|
// Synopsis: Enables/Disables and checks the appropriate group type radio
|
|
// buttons based on the domain mode, and groupType attribute
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDsGroupGenObjPage::SetGroupTypeButtons()
|
|
{
|
|
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))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// 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<PWSTR>((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;
|
|
}
|
|
|
|
// Store the new group type and change the group type buttons
|
|
|
|
m_dwType = ADsValueType.Integer;
|
|
SetGroupTypeButtons();
|
|
|
|
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=";
|
|
if(_wcsicmp(m_pszNonSecurityGroupExtraClasses[idx],L"MSMQ-Queue")==0)
|
|
{
|
|
szFilterString +=L"MSMQQueue";
|
|
}
|
|
else
|
|
{
|
|
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<IADsPathname> 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<IDirectoryObject> spDirObject;
|
|
hr = DSAdminOpenObject(PathColumn.pADsValues->CaseIgnoreString,
|
|
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(CComBSTR(PathColumn.pADsValues->CaseIgnoreString), ADS_SETTYPE_FULL);
|
|
CHECK_HRESULT(hr, continue);
|
|
|
|
hr = spPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
|
CHECK_HRESULT(hr, continue);
|
|
|
|
hr = spPathCracker->put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
|
|
CHECK_HRESULT(hr, continue);
|
|
|
|
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));
|
|
|
|
hr = spPathCracker->put_EscapedMode(ADS_ESCAPEDMODE_ON);
|
|
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 szEscapedSearchFor;
|
|
|
|
GetFilterEscapedValue(pwzSearchFor, szEscapedSearchFor);
|
|
|
|
CStrW szPrefix;
|
|
szPrefix = L"(&(name=";
|
|
szPrefix += szEscapedSearchFor;
|
|
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, const CDSSmartBasePathsInfo& basePathsInfo,
|
|
HPROPSHEETPAGE * phPage)
|
|
{
|
|
TRACE_FUNCTION(CreateGroupMembersPage);
|
|
|
|
CDsGrpMembersPage * pPageObj = new CDsGrpMembersPage(pDsPage, pDataObj,
|
|
hNotifyObj, dwFlags);
|
|
CHECK_NULL(pPageObj, return E_OUTOFMEMORY);
|
|
|
|
pPageObj->Init(pwzADsPath, pwzClass, basePathsInfo);
|
|
|
|
return pPageObj->CreatePage(phPage);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CDsGrpMembersPage::DlgProc
|
|
//
|
|
// Synopsis: per-instance dialog proc
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
INT_PTR CALLBACK
|
|
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 and selection on first item in the list
|
|
ListView_SetItemState(GetDlgItem(m_hPage, IDC_MEMBER_LIST), 0,
|
|
LVIS_SELECTED, LVIS_SELECTED);
|
|
ListView_SetItemState(GetDlgItem(m_hPage, IDC_MEMBER_LIST), 0,
|
|
LVIS_FOCUSED, LVIS_FOCUSED);
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
// Now mark all entries as members
|
|
|
|
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;
|
|
}
|
|
|
|
pItem->m_fIsAlreadyMember = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
{
|
|
LRESULT selected = SendDlgItemMessage(m_hPage, IDC_MEMBER_LIST, LVM_GETSELECTEDCOUNT, 0, 0);
|
|
if (selected > 0)
|
|
{
|
|
EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), TRUE);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(GetDlgItem(m_hPage, IDC_REMOVE_BTN), FALSE);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
// NTRAID#NTBUG9-651756-2002/07/15-artm
|
|
//
|
|
// Helper function to copy LsaStrings to string objects.
|
|
//
|
|
#ifdef DSADMIN
|
|
HRESULT
|
|
CopyLsaString(const LSA_UNICODE_STRING& lsaString, CStrW& dest)
|
|
{
|
|
TRACE_FUNCTION(CopyLsaString);
|
|
dest.Empty();
|
|
|
|
// Get string length in characters (+1 for NULL).
|
|
int buffsize = (lsaString.Length / sizeof(WCHAR)) + 1;
|
|
|
|
WCHAR* buffer = dest.GetBuffer(buffsize);
|
|
|
|
if (!buffer)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr = StringCchCopyN(
|
|
buffer,
|
|
buffsize,
|
|
lsaString.Buffer,
|
|
buffsize - 1);
|
|
|
|
buffer = NULL;
|
|
dest.ReleaseBuffer();
|
|
|
|
// We don't expect that the copy should ever fail.
|
|
dspAssert(SUCCEEDED(hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the localized name and domain for a SID.
|
|
//
|
|
HRESULT
|
|
LocalizeSid(PSID psid, CStrW& localizedName, CStrW& localizedDomain)
|
|
{
|
|
TRACE_FUNCTION(LocalizeSid);
|
|
|
|
PLSA_TRANSLATED_NAME pTranslatedName = NULL;
|
|
PLSA_REFERENCED_DOMAIN_LIST pReferencedDomains = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
NTSTATUS nts = STATUS_SUCCESS;
|
|
|
|
LSA_HANDLE hLsa = NULL;
|
|
LSA_OBJECT_ATTRIBUTES lsaAttr;
|
|
|
|
::ZeroMemory(&lsaAttr, sizeof(lsaAttr));
|
|
|
|
do
|
|
{
|
|
if (!psid || !IsValidSid(psid))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
// Acquire the LSA resource.
|
|
// We call this in the same way as object picker.
|
|
nts = LsaOpenPolicy(NULL, &lsaAttr, POLICY_LOOKUP_NAMES, &hLsa);
|
|
if (nts != STATUS_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_NT(nts);
|
|
break;
|
|
}
|
|
|
|
// Convert the SID to a localized name.
|
|
nts = LsaLookupSids(hLsa,
|
|
1,
|
|
&psid,
|
|
&pReferencedDomains,
|
|
&pTranslatedName);
|
|
if (nts != STATUS_SUCCESS)
|
|
{
|
|
hr = HRESULT_FROM_NT(nts);
|
|
break;
|
|
}
|
|
|
|
// Verify assumption that the SID we had was valid.
|
|
if (pTranslatedName->Use == SidTypeInvalid ||
|
|
pTranslatedName->Use == SidTypeUnknown ||
|
|
pTranslatedName->Use == SidTypeDomain)
|
|
{
|
|
dspAssert(pTranslatedName->Use != SidTypeInvalid);
|
|
dspAssert(pTranslatedName->Use != SidTypeUnknown);
|
|
dspAssert(pTranslatedName->Use != SidTypeDomain);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
hr = CopyLsaString(pTranslatedName->Name, localizedName);
|
|
CHECK_HRESULT(hr, break);
|
|
|
|
// If there are no referenced domains for the localized name we
|
|
// don't need to do any more work.
|
|
if (pTranslatedName->DomainIndex < 0L)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ULONG domainIndex = pTranslatedName->DomainIndex;
|
|
|
|
// Should _never_ happen. Implies a bug either in Lsa functions
|
|
// or in their documentation.
|
|
dspAssert(pReferencedDomains->Entries >= domainIndex);
|
|
|
|
hr = CopyLsaString(
|
|
pReferencedDomains->Domains[domainIndex].Name,
|
|
localizedDomain);
|
|
CHECK_HRESULT(hr, break);
|
|
|
|
} while (0);
|
|
|
|
// Clean up any resources we've accumulated.
|
|
if (pTranslatedName)
|
|
{
|
|
LsaFreeMemory(static_cast<PVOID>(pTranslatedName));
|
|
}
|
|
|
|
if (pReferencedDomains)
|
|
{
|
|
LsaFreeMemory(static_cast<PVOID>(pReferencedDomains));
|
|
}
|
|
|
|
if (hLsa)
|
|
{
|
|
nts = LsaClose(hLsa);
|
|
hLsa = NULL;
|
|
|
|
// No conceivable reason why this should fail.
|
|
dspAssert(nts == STATUS_SUCCESS);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // DSADMIN
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// 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 |
|
|
DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP;
|
|
|
|
// 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;
|
|
}
|
|
|
|
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;
|
|
|
|
//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 |=
|
|
DSOP_FILTER_WELL_KNOWN_PRINCIPALS;
|
|
rgScopes[2].FilterFlags.Uplevel.flBothModes |=
|
|
DSOP_FILTER_WELL_KNOWN_PRINCIPALS;
|
|
}
|
|
|
|
//
|
|
// 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<IDsObjectPickerEx> 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://<SID=01050xxxx>". 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.
|
|
//
|
|
|
|
#ifdef DSADMIN
|
|
// NTRAID#NTBUG9-651756-2002/07/03-artm Need to use SID for well known security principals.
|
|
PCWSTR objectClass = pSelList->aDsSelection[i].pwzClass;
|
|
bool isWellKnownSecurityPrincipal = false;
|
|
if (objectClass && (_wcsicmp(objectClass, g_wzFPO) == 0) )
|
|
{
|
|
isWellKnownSecurityPrincipal = true;
|
|
}
|
|
#endif //DSADMIN
|
|
|
|
if ((pSelList->aDsSelection[i].flScopeType == DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN) ||
|
|
(pSelList->aDsSelection[i].flScopeType == DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN)
|
|
#ifdef DSADMIN
|
|
|| isWellKnownSecurityPrincipal
|
|
#endif //DSADMIN
|
|
)
|
|
{
|
|
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
|
|
{
|
|
// NTRAID#NTBUG9-651756-2002/07/03-artm
|
|
#ifdef DSADMIN
|
|
if (isWellKnownSecurityPrincipal)
|
|
{
|
|
dspAssert(pSid);
|
|
|
|
CStrW localizedName, domainName;
|
|
|
|
hr = LocalizeSid(pSid, localizedName, domainName);
|
|
CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
|
|
|
|
// Dummy up the canonical version of the localized name.
|
|
CStrW canonicalNameEx = domainName + L"\n" + localizedName;
|
|
|
|
hr = m_pList->InsertIntoList(pSid, canonicalNameEx);
|
|
}
|
|
else if (pSid)
|
|
#else
|
|
if (pSid)
|
|
#endif // DSADMIN
|
|
{
|
|
CComPtr<IADsPathname> spPathCracker;
|
|
|
|
hr = GetADsPathname(spPathCracker);
|
|
|
|
CHECK_HRESULT_REPORT(hr, m_hPage, goto ExitCleanup);
|
|
|
|
hr = spPathCracker->Set(CComBSTR(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));
|
|
PostMessage(GetDlgItem(m_hPage, IDC_ADD_BTN), BM_SETSTYLE, BS_DEFPUSHBUTTON, MAKELPARAM(TRUE, 0));
|
|
|
|
PostMessage(GetDlgItem(m_hPage, IDC_REMOVE_BTN), BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0));
|
|
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<long>(*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<IADsContainer> spLocaleContainer;
|
|
hr = pDispSpec->GetDisplaySpecifier(NULL, IID_IADsContainer, (void**)&spLocaleContainer);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// bind to the settings object
|
|
//
|
|
CComPtr<IDispatch> spIDispatchObject;
|
|
hr = spLocaleContainer->GetObject(CComBSTR(lpszSettingsObjectClass),
|
|
CComBSTR(lpszSettingsObject),
|
|
&spIDispatchObject);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IADs> 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(CComBSTR(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(CComBSTR(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 <IDirectoryObject> pDomObj;
|
|
PADS_ATTR_INFO pAttrs = NULL;
|
|
DWORD cAttrs = 0;
|
|
|
|
dspDebugOut((DEB_ITRACE, "GetDomainMode targetted to %ws\n", pwzDomain));
|
|
|
|
hr = DSAdminOpenObject(pwzDomain,
|
|
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 <IDirectorySearch> 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<LPWSTR>((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<PWSTR>((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, const CDSSmartBasePathsInfo& basePathsInfo,
|
|
HPROPSHEETPAGE * phPage)
|
|
{
|
|
TRACE_FUNCTION(CreateGroupMembersPage);
|
|
|
|
CDsGrpShlGenPage * pPageObj = new CDsGrpShlGenPage(pDsPage, pDataObj,
|
|
hNotifyObj, dwFlags);
|
|
CHECK_NULL(pPageObj, return E_OUTOFMEMORY);
|
|
|
|
pPageObj->Init(pwzADsPath, pwzClass, basePathsInfo);
|
|
|
|
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<IADsPathname> spPathcracker;
|
|
hr = CoCreateInstance(CLSID_Pathname,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IADsPathname,
|
|
(PVOID *)&spPathcracker);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = spPathcracker->Set(CComBSTR(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<LPWSTR>((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;
|
|
}
|